1 // Copyright (C) The Arvados Authors. All rights reserved.
3 // SPDX-License-Identifier: AGPL-3.0
5 describe('Process tests', function() {
10 // Only set up common users once. These aren't set up as aliases because
11 // aliases are cleaned up after every test. Also it doesn't make sense
12 // to set the same users on beforeEach() over and over again, so we
13 // separate a little from Cypress' 'Best Practices' here.
14 cy.getUser('admin', 'Admin', 'User', true, true)
15 .as('adminUser').then(function() {
16 adminUser = this.adminUser;
19 cy.getUser('user', 'Active', 'User', false, true)
20 .as('activeUser').then(function() {
21 activeUser = this.activeUser;
26 beforeEach(function() {
28 cy.clearLocalStorage();
31 function setupDockerImage(image_name) {
32 // Create a collection that will be used as a docker image for the tests.
33 cy.createCollection(adminUser.token, {
35 manifest_text: ". d21353cfe035e3e384563ee55eadbb2f+67108864 5c77a43e329b9838cbec18ff42790e57+55605760 0:122714624:sha256:d8309758b8fe2c81034ffc8a10c36460b77db7bc5e7b448c4e5b684f9d95a678.tar\n"
36 }).as('dockerImage').then(function(dockerImage) {
37 // Give read permissions to the active user on the docker image.
38 cy.createLink(adminUser.token, {
39 link_class: 'permission',
41 tail_uuid: activeUser.user.uuid,
42 head_uuid: dockerImage.uuid
43 }).as('dockerImagePermission').then(function() {
44 // Set-up docker image collection tags
45 cy.createLink(activeUser.token, {
46 link_class: 'docker_image_repo+tag',
48 head_uuid: dockerImage.uuid,
49 }).as('dockerImageRepoTag');
50 cy.createLink(activeUser.token, {
51 link_class: 'docker_image_hash',
52 name: 'sha256:d8309758b8fe2c81034ffc8a10c36460b77db7bc5e7b448c4e5b684f9d95a678',
53 head_uuid: dockerImage.uuid,
54 }).as('dockerImageHash');
57 return cy.getAll('@dockerImage', '@dockerImageRepoTag', '@dockerImageHash',
58 '@dockerImagePermission').then(function([dockerImage]) {
63 function createContainerRequest(user, name, docker_image, command, reuse = false, state = 'Uncommitted') {
64 return setupDockerImage(docker_image).then(function(dockerImage) {
65 return cy.createContainerRequest(user.token, {
68 container_image: dockerImage.portable_data_hash, // for some reason, docker_image doesn't work here
69 output_path: 'stdout.txt',
71 runtime_constraints: {
87 it('shows process logs', function() {
88 const crName = 'test_container_request';
89 createContainerRequest(
93 ['echo', 'hello world'],
95 .then(function(containerRequest) {
96 cy.loginAs(activeUser);
97 cy.goToPath(`/processes/${containerRequest.uuid}`);
98 cy.get('[data-cy=process-details]').should('contain', crName);
99 cy.get('[data-cy=process-logs]')
100 .should('contain', 'No logs yet')
101 .and('not.contain', 'hello world');
102 cy.createLog(activeUser.token, {
103 object_uuid: containerRequest.container_uuid,
108 }).then(function(log) {
109 cy.get('[data-cy=process-logs]')
110 .should('not.contain', 'No logs yet')
111 .and('contain', 'hello world');
116 it('shows process details', function() {
117 const crName = 'test_container_request';
118 createContainerRequest(
122 ['echo', 'hello world'],
124 .then(function(containerRequest) {
125 cy.loginAs(activeUser);
126 cy.goToPath(`/processes/${containerRequest.uuid}`);
127 cy.get('[data-cy=process-details]').should('contain', crName);
128 cy.get('[data-cy=process-details-attributes-runtime-user]').contains(`Active User (${activeUser.user.uuid})`);
129 cy.get('[data-cy=process-details-attributes-modifiedby-user]').contains(`zzzzz-tpzed-000000000000000`);
133 it('filters process logs by event type', function() {
134 const nodeInfoLogs = [
136 'Linux compute-99cb150b26149780de44b929577e1aed-19rgca8vobuvc4p 5.4.0-1059-azure #62~18.04.1-Ubuntu SMP Tue Sep 14 17:53:18 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux',
139 'vendor_id : GenuineIntel',
142 'model name : Intel(R) Xeon(R) CPU E5-2673 v4 @ 2.30GHz'
144 const crunchRunLogs = [
145 '2022-03-22T13:56:22.542417997Z using local keepstore process (pid 3733) at http://localhost:46837, writing logs to keepstore.txt in log collection',
146 '2022-03-22T13:56:26.237571754Z crunch-run 2.4.0~dev20220321141729 (go1.17.1) started',
147 '2022-03-22T13:56:26.244704134Z crunch-run process has uid=0(root) gid=0(root) groups=0(root)',
148 '2022-03-22T13:56:26.244862836Z Executing container \'zzzzz-dz642-1wokwvcct9s9du3\' using docker runtime',
149 '2022-03-22T13:56:26.245037738Z Executing on host \'compute-99cb150b26149780de44b929577e1aed-19rgca8vobuvc4p\'',
152 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec dui nisi, hendrerit porta sapien a, pretium dignissim purus.',
153 'Integer viverra, mauris finibus aliquet ultricies, dui mauris cursus justo, ut venenatis nibh ex eget neque.',
154 'In hac habitasse platea dictumst.',
155 'Fusce fringilla turpis id accumsan faucibus. Donec congue congue ex non posuere. In semper mi quis tristique rhoncus.',
156 'Interdum et malesuada fames ac ante ipsum primis in faucibus.',
157 'Quisque fermentum tortor ex, ut suscipit velit feugiat faucibus.',
158 'Donec vitae porta risus, at luctus nulla. Mauris gravida iaculis ipsum, id sagittis tortor egestas ac.',
159 'Maecenas condimentum volutpat nulla. Integer lacinia maximus risus eu posuere.',
160 'Donec vitae leo id augue gravida bibendum.',
161 'Nam libero libero, pretium ac faucibus elementum, mattis nec ex.',
162 'Nullam id laoreet nibh. Vivamus tellus metus, pretium quis justo ut, bibendum varius metus. Pellentesque vitae accumsan lorem, quis tincidunt augue.',
163 'Aliquam viverra nisi nulla, et efficitur dolor mattis in.',
164 'Sed at enim sit amet nulla tincidunt mattis. Aenean eget aliquet ex, non ultrices ex. Nulla ex tortor, vestibulum aliquam tempor ac, aliquam vel est.',
165 'Fusce auctor faucibus libero id venenatis. Etiam sodales, odio eu cursus efficitur, quam sem blandit ex, quis porttitor enim dui quis lectus. In id tincidunt felis.',
166 'Phasellus non ex quis arcu tempus faucibus molestie in sapien.',
167 'Duis tristique semper dolor, vitae pulvinar risus.',
168 'Aliquam tortor elit, luctus nec tortor eget, porta tristique nulla.',
169 'Nulla eget mollis ipsum.',
172 createContainerRequest(
174 'test_container_request',
176 ['echo', 'hello world'],
178 .then(function(containerRequest) {
179 cy.logsForContainer(activeUser.token, containerRequest.container_uuid,
180 'node-info', nodeInfoLogs).as('nodeInfoLogs');
181 cy.logsForContainer(activeUser.token, containerRequest.container_uuid,
182 'crunch-run', crunchRunLogs).as('crunchRunLogs');
183 cy.logsForContainer(activeUser.token, containerRequest.container_uuid,
184 'stdout', stdoutLogs).as('stdoutLogs');
185 cy.getAll('@stdoutLogs', '@nodeInfoLogs', '@crunchRunLogs').then(function() {
186 cy.loginAs(activeUser);
187 cy.goToPath(`/processes/${containerRequest.uuid}`);
188 // Should show main logs by default
189 cy.get('[data-cy=process-logs-filter]').should('contain', 'Main logs');
190 cy.get('[data-cy=process-logs]')
191 .should('contain', stdoutLogs[Math.floor(Math.random() * stdoutLogs.length)])
192 .and('not.contain', nodeInfoLogs[Math.floor(Math.random() * nodeInfoLogs.length)])
193 .and('contain', crunchRunLogs[Math.floor(Math.random() * crunchRunLogs.length)]);
195 cy.get('[data-cy=process-logs-filter]').click();
196 cy.get('body').contains('li', 'All logs').click();
197 cy.get('[data-cy=process-logs]')
198 .should('contain', stdoutLogs[Math.floor(Math.random() * stdoutLogs.length)])
199 .and('contain', nodeInfoLogs[Math.floor(Math.random() * nodeInfoLogs.length)])
200 .and('contain', crunchRunLogs[Math.floor(Math.random() * crunchRunLogs.length)]);
201 // Select 'node-info' logs
202 cy.get('[data-cy=process-logs-filter]').click();
203 cy.get('body').contains('li', 'node-info').click();
204 cy.get('[data-cy=process-logs]')
205 .should('not.contain', stdoutLogs[Math.floor(Math.random() * stdoutLogs.length)])
206 .and('contain', nodeInfoLogs[Math.floor(Math.random() * nodeInfoLogs.length)])
207 .and('not.contain', crunchRunLogs[Math.floor(Math.random() * crunchRunLogs.length)]);
208 // Select 'stdout' logs
209 cy.get('[data-cy=process-logs-filter]').click();
210 cy.get('body').contains('li', 'stdout').click();
211 cy.get('[data-cy=process-logs]')
212 .should('contain', stdoutLogs[Math.floor(Math.random() * stdoutLogs.length)])
213 .and('not.contain', nodeInfoLogs[Math.floor(Math.random() * nodeInfoLogs.length)])
214 .and('not.contain', crunchRunLogs[Math.floor(Math.random() * crunchRunLogs.length)]);
219 it('should show runtime status indicators', function() {
220 // Setup running container with runtime_status error & warning messages
221 createContainerRequest(
223 'test_container_request',
225 ['echo', 'hello world'],
227 .as('containerRequest')
228 .then(function(containerRequest) {
229 expect(containerRequest.state).to.equal('Committed');
230 expect(containerRequest.container_uuid).not.to.be.equal('');
232 cy.getContainer(activeUser.token, containerRequest.container_uuid)
233 .then(function(queuedContainer) {
234 expect(queuedContainer.state).to.be.equal('Queued');
236 cy.updateContainer(adminUser.token, containerRequest.container_uuid, {
238 }).then(function(lockedContainer) {
239 expect(lockedContainer.state).to.be.equal('Locked');
241 cy.updateContainer(adminUser.token, lockedContainer.uuid, {
244 error: 'Something went wrong',
245 errorDetail: 'Process exited with status 1',
246 warning: 'Free disk space is low',
249 .as('runningContainer')
250 .then(function(runningContainer) {
251 expect(runningContainer.state).to.be.equal('Running');
252 expect(runningContainer.runtime_status).to.be.deep.equal({
253 'error': 'Something went wrong',
254 'errorDetail': 'Process exited with status 1',
255 'warning': 'Free disk space is low',
260 // Test that the UI shows the error and warning messages
261 cy.getAll('@containerRequest', '@runningContainer').then(function([containerRequest]) {
262 cy.loginAs(activeUser);
263 cy.goToPath(`/processes/${containerRequest.uuid}`);
264 cy.get('[data-cy=process-runtime-status-error]')
265 .should('contain', 'Something went wrong')
266 .and('contain', 'Process exited with status 1');
267 cy.get('[data-cy=process-runtime-status-warning]')
268 .should('contain', 'Free disk space is low')
269 .and('contain', 'No additional warning details available');
273 // Force container_count for testing
274 let containerCount = 2;
275 cy.intercept({method: 'GET', url: '**/arvados/v1/container_requests/*'}, (req) => {
277 res.body.container_count = containerCount;
281 cy.getAll('@containerRequest').then(function([containerRequest]) {
282 cy.goToPath(`/processes/${containerRequest.uuid}`);
283 cy.get('[data-cy=process-runtime-status-retry-warning]')
284 .should('contain', 'Process retried 1 time');
287 cy.getAll('@containerRequest').then(function([containerRequest]) {
289 cy.goToPath(`/processes/${containerRequest.uuid}`);
290 cy.get('[data-cy=process-runtime-status-retry-warning]')
291 .should('contain', 'Process retried 2 times');
299 "id": "#main/input_file",
300 "label": "Label Description",
305 "basename": "input1.tar",
307 "location": "keep:00000000000000000000000000000000+01/input1.tar",
310 "basename": "input1-2.txt",
312 "location": "keep:00000000000000000000000000000000+01/input1-2.txt"
315 "basename": "input1-3.txt",
317 "location": "keep:00000000000000000000000000000000+01/input1-3.txt"
320 "basename": "input1-4.txt",
322 "location": "keep:00000000000000000000000000000000+01/input1-4.txt"
330 "id": "#main/input_dir",
331 "doc": "Doc Description",
336 "basename": "11111111111111111111111111111111+01",
337 "class": "Directory",
338 "location": "keep:11111111111111111111111111111111+01"
344 "id": "#main/input_bool",
345 "doc": ["Doc desc 1", "Doc desc 2"],
354 "id": "#main/input_int",
363 "id": "#main/input_long",
372 "id": "#main/input_float",
381 "id": "#main/input_double",
390 "id": "#main/input_string",
394 "input_string": "Hello World",
399 "id": "#main/input_file_array",
406 "input_file_array": [
408 "basename": "input2.tar",
410 "location": "keep:00000000000000000000000000000000+02/input2.tar"
413 "basename": "input3.tar",
415 "location": "keep:00000000000000000000000000000000+03/input3.tar",
418 "basename": "input3-2.txt",
420 "location": "keep:00000000000000000000000000000000+03/input3-2.txt"
429 "id": "#main/input_dir_array",
431 "items": "Directory",
438 "basename": "11111111111111111111111111111111+02",
439 "class": "Directory",
440 "location": "keep:11111111111111111111111111111111+02"
443 "basename": "11111111111111111111111111111111+03",
444 "class": "Directory",
445 "location": "keep:11111111111111111111111111111111+03"
452 "id": "#main/input_int_array",
468 "id": "#main/input_long_array",
475 "input_long_array": [
483 "id": "#main/input_float_array",
490 "input_float_array": [
499 "id": "#main/input_double_array",
506 "input_double_array": [
515 "id": "#main/input_string_array",
522 "input_string_array": [
531 const testOutputs = [
534 "id": "#main/output_file",
535 "label": "Label Description",
540 "basename": "cat.png",
542 "location": "cat.png"
548 "id": "#main/output_file_with_secondary",
549 "doc": "Doc Description",
553 "output_file_with_secondary": {
554 "basename": "main.dat",
556 "location": "main.dat",
559 "basename": "secondary.dat",
561 "location": "secondary.dat"
564 "basename": "secondary2.dat",
566 "location": "secondary2.dat"
574 "id": "#main/output_dir",
575 "doc": ["Doc desc 1", "Doc desc 2"],
580 "basename": "outdir1",
581 "class": "Directory",
582 "location": "outdir1"
588 "id": "#main/output_bool",
597 "id": "#main/output_int",
606 "id": "#main/output_long",
615 "id": "#main/output_float",
619 "output_float": 100.5
624 "id": "#main/output_double",
628 "output_double": 100.3
633 "id": "#main/output_string",
637 "output_string": "Hello output"
642 "id": "#main/output_file_array",
649 "output_file_array": [
651 "basename": "output2.tar",
653 "location": "output2.tar"
656 "basename": "output3.tar",
658 "location": "output3.tar"
665 "id": "#main/output_dir_array",
667 "items": "Directory",
672 "output_dir_array": [
674 "basename": "outdir2",
675 "class": "Directory",
676 "location": "outdir2"
679 "basename": "outdir3",
680 "class": "Directory",
681 "location": "outdir3"
688 "id": "#main/output_int_array",
695 "output_int_array": [
704 "id": "#main/output_long_array",
711 "output_long_array": [
719 "id": "#main/output_float_array",
726 "output_float_array": [
735 "id": "#main/output_double_array",
742 "output_double_array": [
751 "id": "#main/output_string_array",
758 "output_string_array": [
767 const verifyIOParameter = (name, label, doc, val, collection, multipleRows) => {
768 cy.get('table tr').contains(name).parents('tr').within(($mainRow) => {
769 label && cy.contains(label);
772 cy.get($mainRow).nextUntil('[data-cy="process-io-param"]').as('secondaryRows');
774 if (Array.isArray(val)) {
775 val.forEach(v => cy.get('@secondaryRows').contains(v));
777 cy.get('@secondaryRows').contains(val);
781 cy.get('@secondaryRows').contains(collection);
785 if (Array.isArray(val)) {
786 val.forEach(v => cy.contains(v));
792 cy.contains(collection);
800 const verifyIOParameterImage = (name, url) => {
801 cy.get('table tr').contains(name).parents('tr').within(() => {
802 cy.get('[alt="Inline Preview"]')
803 .should('be.visible')
805 expect($img[0].naturalWidth).to.be.greaterThan(0);
806 expect($img[0].src).contains(url);
811 it('displays IO parameters with keep links and previews', function() {
812 // Create output collection for real files
813 cy.createCollection(adminUser.token, {
814 name: `Test collection ${Math.floor(Math.random() * 999999)}`,
815 owner_uuid: activeUser.user.uuid,
816 }).then((testOutputCollection) => {
817 cy.loginAs(activeUser);
819 cy.goToPath(`/collections/${testOutputCollection.uuid}`);
821 cy.get('[data-cy=upload-button]').click();
823 cy.fixture('files/cat.png', 'base64').then(content => {
824 cy.get('[data-cy=drag-and-drop]').upload(content, 'cat.png');
825 cy.get('[data-cy=form-submit-btn]').click();
826 cy.waitForDom().get('[data-cy=form-submit-btn]').should('not.exist');
827 // Confirm final collection state.
828 cy.get('[data-cy=collection-files-panel]')
829 .contains('cat.png').should('exist');
832 cy.getCollection(activeUser.token, testOutputCollection.uuid).as('testOutputCollection');
835 // Get updated collection pdh
836 cy.getAll('@testOutputCollection').then(([testOutputCollection]) => {
837 // Add output uuid and inputs to container request
838 cy.intercept({method: 'GET', url: '**/arvados/v1/container_requests/*'}, (req) => {
840 res.body.output_uuid = testOutputCollection.uuid;
841 res.body.mounts["/var/lib/cwl/cwl.input.json"] = {
842 content: testInputs.map((param) => (param.input)).reduce((acc, val) => (Object.assign(acc, val)), {})
844 res.body.mounts["/var/lib/cwl/workflow.json"] = {
848 inputs: testInputs.map((input) => (input.definition)),
849 outputs: testOutputs.map((output) => (output.definition))
856 // Stub fake output collection
857 cy.intercept({method: 'GET', url: `**/arvados/v1/collections/${testOutputCollection.uuid}*`}, {
860 uuid: testOutputCollection.uuid,
861 portable_data_hash: testOutputCollection.portable_data_hash,
865 // Stub fake output json
866 cy.intercept({method: 'GET', url: '**/c%3Dzzzzz-4zz18-zzzzzzzzzzzzzzz/cwl.output.json'}, {
868 body: testOutputs.map((param) => (param.output)).reduce((acc, val) => (Object.assign(acc, val)), {})
871 // Stub webdav response, points to output json
872 cy.intercept({method: 'PROPFIND', url: '*'}, {
873 fixture: 'webdav-propfind-outputs.xml',
877 createContainerRequest(
879 'test_container_request',
881 ['echo', 'hello world'],
883 .as('containerRequest');
885 cy.getAll('@containerRequest', '@testOutputCollection').then(function([containerRequest, testOutputCollection]) {
886 cy.goToPath(`/processes/${containerRequest.uuid}`);
887 cy.get('[data-cy=process-io-card] h6').contains('Inputs')
888 .parents('[data-cy=process-io-card]').within(() => {
889 verifyIOParameter('input_file', null, "Label Description", 'input1.tar', '00000000000000000000000000000000+01');
890 verifyIOParameter('input_file', null, "Label Description", 'input1-2.txt', undefined, true);
891 verifyIOParameter('input_file', null, "Label Description", 'input1-3.txt', undefined, true);
892 verifyIOParameter('input_file', null, "Label Description", 'input1-4.txt', undefined, true);
893 verifyIOParameter('input_dir', null, "Doc Description", '/', '11111111111111111111111111111111+01');
894 verifyIOParameter('input_bool', null, "Doc desc 1, Doc desc 2", 'true');
895 verifyIOParameter('input_int', null, null, '1');
896 verifyIOParameter('input_long', null, null, '1');
897 verifyIOParameter('input_float', null, null, '1.5');
898 verifyIOParameter('input_double', null, null, '1.3');
899 verifyIOParameter('input_string', null, null, 'Hello World');
900 verifyIOParameter('input_file_array', null, null, 'input2.tar', '00000000000000000000000000000000+02');
901 verifyIOParameter('input_file_array', null, null, 'input3.tar', undefined, true);
902 verifyIOParameter('input_file_array', null, null, 'input3-2.txt', undefined, true);
903 verifyIOParameter('input_dir_array', null, null, '/', '11111111111111111111111111111111+02');
904 verifyIOParameter('input_dir_array', null, null, '/', '11111111111111111111111111111111+03', true);
905 verifyIOParameter('input_int_array', null, null, ["1", "3", "5"]);
906 verifyIOParameter('input_long_array', null, null, ["10", "20"]);
907 verifyIOParameter('input_float_array', null, null, ["10.2", "10.4", "10.6"]);
908 verifyIOParameter('input_double_array', null, null, ["20.1", "20.2", "20.3"]);
909 verifyIOParameter('input_string_array', null, null, ["Hello", "World", "!"]);
911 cy.get('[data-cy=process-io-card] h6').contains('Outputs')
912 .parents('[data-cy=process-io-card]').within((ctx) => {
913 cy.get(ctx).scrollIntoView();
914 cy.get('[data-cy="io-preview-image-toggle"]').click();
915 const outPdh = testOutputCollection.portable_data_hash;
917 verifyIOParameter('output_file', null, "Label Description", 'cat.png', `${outPdh}`);
918 verifyIOParameterImage('output_file', `/c=${outPdh}/cat.png`);
919 verifyIOParameter('output_file_with_secondary', null, "Doc Description", 'main.dat', `${outPdh}`);
920 verifyIOParameter('output_file_with_secondary', null, "Doc Description", 'secondary.dat', undefined, true);
921 verifyIOParameter('output_file_with_secondary', null, "Doc Description", 'secondary2.dat', undefined, true);
922 verifyIOParameter('output_dir', null, "Doc desc 1, Doc desc 2", 'outdir1', `${outPdh}`);
923 verifyIOParameter('output_bool', null, null, 'true');
924 verifyIOParameter('output_int', null, null, '1');
925 verifyIOParameter('output_long', null, null, '1');
926 verifyIOParameter('output_float', null, null, '100.5');
927 verifyIOParameter('output_double', null, null, '100.3');
928 verifyIOParameter('output_string', null, null, 'Hello output');
929 verifyIOParameter('output_file_array', null, null, 'output2.tar', `${outPdh}`);
930 verifyIOParameter('output_file_array', null, null, 'output3.tar', undefined, true);
931 verifyIOParameter('output_dir_array', null, null, 'outdir2', `${outPdh}`);
932 verifyIOParameter('output_dir_array', null, null, 'outdir3', undefined, true);
933 verifyIOParameter('output_int_array', null, null, ["10", "11", "12"]);
934 verifyIOParameter('output_long_array', null, null, ["51", "52"]);
935 verifyIOParameter('output_float_array', null, null, ["100.2", "100.4", "100.6"]);
936 verifyIOParameter('output_double_array', null, null, ["100.1", "100.2", "100.3"]);
937 verifyIOParameter('output_string_array', null, null, ["Hello", "Output", "!"]);
942 it('displays IO parameters with no value', function() {
944 const fakeOutputUUID = 'zzzzz-4zz18-abcdefghijklmno';
945 const fakeOutputPDH = '11111111111111111111111111111111+99/';
947 cy.loginAs(activeUser);
949 // Add output uuid and inputs to container request
950 cy.intercept({method: 'GET', url: '**/arvados/v1/container_requests/*'}, (req) => {
952 res.body.output_uuid = fakeOutputUUID;
953 res.body.mounts["/var/lib/cwl/cwl.input.json"] = {
956 res.body.mounts["/var/lib/cwl/workflow.json"] = {
960 inputs: testInputs.map((input) => (input.definition)),
961 outputs: testOutputs.map((output) => (output.definition))
968 // Stub fake output collection
969 cy.intercept({method: 'GET', url: `**/arvados/v1/collections/${fakeOutputUUID}*`}, {
972 uuid: fakeOutputUUID,
973 portable_data_hash: fakeOutputPDH,
977 // Stub fake output json
978 cy.intercept({method: 'GET', url: `**/c%3D${fakeOutputUUID}/cwl.output.json`}, {
983 cy.readFile('cypress/fixtures/webdav-propfind-outputs.xml').then((data) => {
984 // Stub webdav response, points to output json
985 cy.intercept({method: 'PROPFIND', url: '*'}, {
987 body: data.replace(/zzzzz-4zz18-zzzzzzzzzzzzzzz/g, fakeOutputUUID)
991 createContainerRequest(
993 'test_container_request',
995 ['echo', 'hello world'],
997 .as('containerRequest');
999 cy.getAll('@containerRequest').then(function([containerRequest]) {
1000 cy.goToPath(`/processes/${containerRequest.uuid}`);
1001 cy.get('[data-cy=process-io-card] h6').contains('Inputs')
1002 .parents('[data-cy=process-io-card]').within(() => {
1005 cy.get('tbody tr').each((item) => {
1006 cy.wrap(item).contains('No value');
1009 cy.get('[data-cy=process-io-card] h6').contains('Outputs')
1010 .parents('[data-cy=process-io-card]').within(() => {
1011 cy.get('tbody tr').each((item) => {
1012 cy.wrap(item).contains('No value');