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})`);
132 it('filters process logs by event type', function() {
133 const nodeInfoLogs = [
135 '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',
138 'vendor_id : GenuineIntel',
141 'model name : Intel(R) Xeon(R) CPU E5-2673 v4 @ 2.30GHz'
143 const crunchRunLogs = [
144 '2022-03-22T13:56:22.542417997Z using local keepstore process (pid 3733) at http://localhost:46837, writing logs to keepstore.txt in log collection',
145 '2022-03-22T13:56:26.237571754Z crunch-run 2.4.0~dev20220321141729 (go1.17.1) started',
146 '2022-03-22T13:56:26.244704134Z crunch-run process has uid=0(root) gid=0(root) groups=0(root)',
147 '2022-03-22T13:56:26.244862836Z Executing container \'zzzzz-dz642-1wokwvcct9s9du3\' using docker runtime',
148 '2022-03-22T13:56:26.245037738Z Executing on host \'compute-99cb150b26149780de44b929577e1aed-19rgca8vobuvc4p\'',
151 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec dui nisi, hendrerit porta sapien a, pretium dignissim purus.',
152 'Integer viverra, mauris finibus aliquet ultricies, dui mauris cursus justo, ut venenatis nibh ex eget neque.',
153 'In hac habitasse platea dictumst.',
154 'Fusce fringilla turpis id accumsan faucibus. Donec congue congue ex non posuere. In semper mi quis tristique rhoncus.',
155 'Interdum et malesuada fames ac ante ipsum primis in faucibus.',
156 'Quisque fermentum tortor ex, ut suscipit velit feugiat faucibus.',
157 'Donec vitae porta risus, at luctus nulla. Mauris gravida iaculis ipsum, id sagittis tortor egestas ac.',
158 'Maecenas condimentum volutpat nulla. Integer lacinia maximus risus eu posuere.',
159 'Donec vitae leo id augue gravida bibendum.',
160 'Nam libero libero, pretium ac faucibus elementum, mattis nec ex.',
161 'Nullam id laoreet nibh. Vivamus tellus metus, pretium quis justo ut, bibendum varius metus. Pellentesque vitae accumsan lorem, quis tincidunt augue.',
162 'Aliquam viverra nisi nulla, et efficitur dolor mattis in.',
163 '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.',
164 '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.',
165 'Phasellus non ex quis arcu tempus faucibus molestie in sapien.',
166 'Duis tristique semper dolor, vitae pulvinar risus.',
167 'Aliquam tortor elit, luctus nec tortor eget, porta tristique nulla.',
168 'Nulla eget mollis ipsum.',
171 createContainerRequest(
173 'test_container_request',
175 ['echo', 'hello world'],
177 .then(function(containerRequest) {
178 cy.logsForContainer(activeUser.token, containerRequest.container_uuid,
179 'node-info', nodeInfoLogs).as('nodeInfoLogs');
180 cy.logsForContainer(activeUser.token, containerRequest.container_uuid,
181 'crunch-run', crunchRunLogs).as('crunchRunLogs');
182 cy.logsForContainer(activeUser.token, containerRequest.container_uuid,
183 'stdout', stdoutLogs).as('stdoutLogs');
184 cy.getAll('@stdoutLogs', '@nodeInfoLogs', '@crunchRunLogs').then(function() {
185 cy.loginAs(activeUser);
186 cy.goToPath(`/processes/${containerRequest.uuid}`);
187 // Should show main logs by default
188 cy.get('[data-cy=process-logs-filter]').should('contain', 'Main logs');
189 cy.get('[data-cy=process-logs]')
190 .should('contain', stdoutLogs[Math.floor(Math.random() * stdoutLogs.length)])
191 .and('not.contain', nodeInfoLogs[Math.floor(Math.random() * nodeInfoLogs.length)])
192 .and('contain', crunchRunLogs[Math.floor(Math.random() * crunchRunLogs.length)]);
194 cy.get('[data-cy=process-logs-filter]').click();
195 cy.get('body').contains('li', 'All logs').click();
196 cy.get('[data-cy=process-logs]')
197 .should('contain', stdoutLogs[Math.floor(Math.random() * stdoutLogs.length)])
198 .and('contain', nodeInfoLogs[Math.floor(Math.random() * nodeInfoLogs.length)])
199 .and('contain', crunchRunLogs[Math.floor(Math.random() * crunchRunLogs.length)]);
200 // Select 'node-info' logs
201 cy.get('[data-cy=process-logs-filter]').click();
202 cy.get('body').contains('li', 'node-info').click();
203 cy.get('[data-cy=process-logs]')
204 .should('not.contain', stdoutLogs[Math.floor(Math.random() * stdoutLogs.length)])
205 .and('contain', nodeInfoLogs[Math.floor(Math.random() * nodeInfoLogs.length)])
206 .and('not.contain', crunchRunLogs[Math.floor(Math.random() * crunchRunLogs.length)]);
207 // Select 'stdout' logs
208 cy.get('[data-cy=process-logs-filter]').click();
209 cy.get('body').contains('li', 'stdout').click();
210 cy.get('[data-cy=process-logs]')
211 .should('contain', stdoutLogs[Math.floor(Math.random() * stdoutLogs.length)])
212 .and('not.contain', nodeInfoLogs[Math.floor(Math.random() * nodeInfoLogs.length)])
213 .and('not.contain', crunchRunLogs[Math.floor(Math.random() * crunchRunLogs.length)]);
218 it('should show runtime status indicators', function() {
219 // Setup running container with runtime_status error & warning messages
220 createContainerRequest(
222 'test_container_request',
224 ['echo', 'hello world'],
226 .as('containerRequest')
227 .then(function(containerRequest) {
228 expect(containerRequest.state).to.equal('Committed');
229 expect(containerRequest.container_uuid).not.to.be.equal('');
231 cy.getContainer(activeUser.token, containerRequest.container_uuid)
232 .then(function(queuedContainer) {
233 expect(queuedContainer.state).to.be.equal('Queued');
235 cy.updateContainer(adminUser.token, containerRequest.container_uuid, {
237 }).then(function(lockedContainer) {
238 expect(lockedContainer.state).to.be.equal('Locked');
240 cy.updateContainer(adminUser.token, lockedContainer.uuid, {
243 error: 'Something went wrong',
244 errorDetail: 'Process exited with status 1',
245 warning: 'Free disk space is low',
248 .as('runningContainer')
249 .then(function(runningContainer) {
250 expect(runningContainer.state).to.be.equal('Running');
251 expect(runningContainer.runtime_status).to.be.deep.equal({
252 'error': 'Something went wrong',
253 'errorDetail': 'Process exited with status 1',
254 'warning': 'Free disk space is low',
259 // Test that the UI shows the error and warning messages
260 cy.getAll('@containerRequest', '@runningContainer').then(function([containerRequest]) {
261 cy.loginAs(activeUser);
262 cy.goToPath(`/processes/${containerRequest.uuid}`);
263 cy.get('[data-cy=process-runtime-status-error]')
264 .should('contain', 'Something went wrong')
265 .and('contain', 'Process exited with status 1');
266 cy.get('[data-cy=process-runtime-status-warning]')
267 .should('contain', 'Free disk space is low')
268 .and('contain', 'No additional warning details available');
272 // Force container_count for testing
273 let containerCount = 2;
274 cy.intercept({method: 'GET', url: '**/arvados/v1/container_requests/*'}, (req) => {
276 res.body.container_count = containerCount;
280 cy.getAll('@containerRequest').then(function([containerRequest]) {
281 cy.goToPath(`/processes/${containerRequest.uuid}`);
282 cy.get('[data-cy=process-runtime-status-retry-warning]')
283 .should('contain', 'Process retried 1 time');
286 cy.getAll('@containerRequest').then(function([containerRequest]) {
288 cy.goToPath(`/processes/${containerRequest.uuid}`);
289 cy.get('[data-cy=process-runtime-status-retry-warning]')
290 .should('contain', 'Process retried 2 times');
298 "id": "#main/input_file",
299 "label": "Label Description",
304 "basename": "input1.tar",
306 "location": "keep:00000000000000000000000000000000+01/input1.tar",
309 "basename": "input1-2.txt",
311 "location": "keep:00000000000000000000000000000000+01/input1-2.txt"
314 "basename": "input1-3.txt",
316 "location": "keep:00000000000000000000000000000000+01/input1-3.txt"
319 "basename": "input1-4.txt",
321 "location": "keep:00000000000000000000000000000000+01/input1-4.txt"
329 "id": "#main/input_dir",
330 "doc": "Doc Description",
335 "basename": "11111111111111111111111111111111+01",
336 "class": "Directory",
337 "location": "keep:11111111111111111111111111111111+01"
343 "id": "#main/input_bool",
344 "doc": ["Doc desc 1", "Doc desc 2"],
353 "id": "#main/input_int",
362 "id": "#main/input_long",
371 "id": "#main/input_float",
380 "id": "#main/input_double",
389 "id": "#main/input_string",
393 "input_string": "Hello World",
398 "id": "#main/input_file_array",
405 "input_file_array": [
407 "basename": "input2.tar",
409 "location": "keep:00000000000000000000000000000000+02/input2.tar"
412 "basename": "input3.tar",
414 "location": "keep:00000000000000000000000000000000+03/input3.tar",
417 "basename": "input3-2.txt",
419 "location": "keep:00000000000000000000000000000000+03/input3-2.txt"
428 "id": "#main/input_dir_array",
430 "items": "Directory",
437 "basename": "11111111111111111111111111111111+02",
438 "class": "Directory",
439 "location": "keep:11111111111111111111111111111111+02"
442 "basename": "11111111111111111111111111111111+03",
443 "class": "Directory",
444 "location": "keep:11111111111111111111111111111111+03"
451 "id": "#main/input_int_array",
467 "id": "#main/input_long_array",
474 "input_long_array": [
482 "id": "#main/input_float_array",
489 "input_float_array": [
498 "id": "#main/input_double_array",
505 "input_double_array": [
514 "id": "#main/input_string_array",
521 "input_string_array": [
530 const testOutputs = [
533 "id": "#main/output_file",
534 "label": "Label Description",
539 "basename": "cat.png",
541 "location": "cat.png"
547 "id": "#main/output_file_with_secondary",
548 "doc": "Doc Description",
552 "output_file_with_secondary": {
553 "basename": "main.dat",
555 "location": "main.dat",
558 "basename": "secondary.dat",
560 "location": "secondary.dat"
563 "basename": "secondary2.dat",
565 "location": "secondary2.dat"
573 "id": "#main/output_dir",
574 "doc": ["Doc desc 1", "Doc desc 2"],
579 "basename": "outdir1",
580 "class": "Directory",
581 "location": "outdir1"
587 "id": "#main/output_bool",
596 "id": "#main/output_int",
605 "id": "#main/output_long",
614 "id": "#main/output_float",
618 "output_float": 100.5
623 "id": "#main/output_double",
627 "output_double": 100.3
632 "id": "#main/output_string",
636 "output_string": "Hello output"
641 "id": "#main/output_file_array",
648 "output_file_array": [
650 "basename": "output2.tar",
652 "location": "output2.tar"
655 "basename": "output3.tar",
657 "location": "output3.tar"
664 "id": "#main/output_dir_array",
666 "items": "Directory",
671 "output_dir_array": [
673 "basename": "outdir2",
674 "class": "Directory",
675 "location": "outdir2"
678 "basename": "outdir3",
679 "class": "Directory",
680 "location": "outdir3"
687 "id": "#main/output_int_array",
694 "output_int_array": [
703 "id": "#main/output_long_array",
710 "output_long_array": [
718 "id": "#main/output_float_array",
725 "output_float_array": [
734 "id": "#main/output_double_array",
741 "output_double_array": [
750 "id": "#main/output_string_array",
757 "output_string_array": [
766 const verifyIOParameter = (name, label, doc, val, collection, multipleRows) => {
767 cy.get('table tr').contains(name).parents('tr').within(($mainRow) => {
768 label && cy.contains(label);
771 cy.get($mainRow).nextUntil('[data-cy="process-io-param"]').as('secondaryRows');
773 if (Array.isArray(val)) {
774 val.forEach(v => cy.get('@secondaryRows').contains(v));
776 cy.get('@secondaryRows').contains(val);
780 cy.get('@secondaryRows').contains(collection);
784 if (Array.isArray(val)) {
785 val.forEach(v => cy.contains(v));
791 cy.contains(collection);
799 const verifyIOParameterImage = (name, url) => {
800 cy.get('table tr').contains(name).parents('tr').within(() => {
801 cy.get('[alt="Inline Preview"]')
802 .should('be.visible')
804 expect($img[0].naturalWidth).to.be.greaterThan(0);
805 expect($img[0].src).contains(url);
810 it('displays IO parameters with keep links and previews', function() {
811 // Create output collection for real files
812 cy.createCollection(adminUser.token, {
813 name: `Test collection ${Math.floor(Math.random() * 999999)}`,
814 owner_uuid: activeUser.user.uuid,
815 }).then((testOutputCollection) => {
816 cy.loginAs(activeUser);
818 cy.goToPath(`/collections/${testOutputCollection.uuid}`);
820 cy.get('[data-cy=upload-button]').click();
822 cy.fixture('files/cat.png', 'base64').then(content => {
823 cy.get('[data-cy=drag-and-drop]').upload(content, 'cat.png');
824 cy.get('[data-cy=form-submit-btn]').click();
825 cy.waitForDom().get('[data-cy=form-submit-btn]').should('not.exist');
826 // Confirm final collection state.
827 cy.get('[data-cy=collection-files-panel]')
828 .contains('cat.png').should('exist');
831 cy.getCollection(activeUser.token, testOutputCollection.uuid).as('testOutputCollection');
834 // Get updated collection pdh
835 cy.getAll('@testOutputCollection').then(([testOutputCollection]) => {
836 // Add output uuid and inputs to container request
837 cy.intercept({method: 'GET', url: '**/arvados/v1/container_requests/*'}, (req) => {
839 res.body.output_uuid = testOutputCollection.uuid;
840 res.body.mounts["/var/lib/cwl/cwl.input.json"] = {
841 content: testInputs.map((param) => (param.input)).reduce((acc, val) => (Object.assign(acc, val)), {})
843 res.body.mounts["/var/lib/cwl/workflow.json"] = {
847 inputs: testInputs.map((input) => (input.definition)),
848 outputs: testOutputs.map((output) => (output.definition))
855 // Stub fake output collection
856 cy.intercept({method: 'GET', url: `**/arvados/v1/collections/${testOutputCollection.uuid}*`}, {
859 uuid: testOutputCollection.uuid,
860 portable_data_hash: testOutputCollection.portable_data_hash,
864 // Stub fake output json
865 cy.intercept({method: 'GET', url: '**/c%3Dzzzzz-4zz18-zzzzzzzzzzzzzzz/cwl.output.json'}, {
867 body: testOutputs.map((param) => (param.output)).reduce((acc, val) => (Object.assign(acc, val)), {})
870 // Stub webdav response, points to output json
871 cy.intercept({method: 'PROPFIND', url: '*'}, {
872 fixture: 'webdav-propfind-outputs.xml',
876 createContainerRequest(
878 'test_container_request',
880 ['echo', 'hello world'],
882 .as('containerRequest');
884 cy.getAll('@containerRequest', '@testOutputCollection').then(function([containerRequest, testOutputCollection]) {
885 cy.goToPath(`/processes/${containerRequest.uuid}`);
886 cy.get('[data-cy=process-io-card] h6').contains('Inputs')
887 .parents('[data-cy=process-io-card]').within(() => {
888 verifyIOParameter('input_file', null, "Label Description", 'input1.tar', '00000000000000000000000000000000+01');
889 verifyIOParameter('input_file', null, "Label Description", 'input1-2.txt', undefined, true);
890 verifyIOParameter('input_file', null, "Label Description", 'input1-3.txt', undefined, true);
891 verifyIOParameter('input_file', null, "Label Description", 'input1-4.txt', undefined, true);
892 verifyIOParameter('input_dir', null, "Doc Description", '/', '11111111111111111111111111111111+01');
893 verifyIOParameter('input_bool', null, "Doc desc 1, Doc desc 2", 'true');
894 verifyIOParameter('input_int', null, null, '1');
895 verifyIOParameter('input_long', null, null, '1');
896 verifyIOParameter('input_float', null, null, '1.5');
897 verifyIOParameter('input_double', null, null, '1.3');
898 verifyIOParameter('input_string', null, null, 'Hello World');
899 verifyIOParameter('input_file_array', null, null, 'input2.tar', '00000000000000000000000000000000+02');
900 verifyIOParameter('input_file_array', null, null, 'input3.tar', undefined, true);
901 verifyIOParameter('input_file_array', null, null, 'input3-2.txt', undefined, true);
902 verifyIOParameter('input_dir_array', null, null, '/', '11111111111111111111111111111111+02');
903 verifyIOParameter('input_dir_array', null, null, '/', '11111111111111111111111111111111+03', true);
904 verifyIOParameter('input_int_array', null, null, ["1", "3", "5"]);
905 verifyIOParameter('input_long_array', null, null, ["10", "20"]);
906 verifyIOParameter('input_float_array', null, null, ["10.2", "10.4", "10.6"]);
907 verifyIOParameter('input_double_array', null, null, ["20.1", "20.2", "20.3"]);
908 verifyIOParameter('input_string_array', null, null, ["Hello", "World", "!"]);
910 cy.get('[data-cy=process-io-card] h6').contains('Outputs')
911 .parents('[data-cy=process-io-card]').within((ctx) => {
912 cy.get(ctx).scrollIntoView();
913 cy.get('[data-cy="io-preview-image-toggle"]').click();
914 const outPdh = testOutputCollection.portable_data_hash;
916 verifyIOParameter('output_file', null, "Label Description", 'cat.png', `${outPdh}`);
917 verifyIOParameterImage('output_file', `/c=${outPdh}/cat.png`);
918 verifyIOParameter('output_file_with_secondary', null, "Doc Description", 'main.dat', `${outPdh}`);
919 verifyIOParameter('output_file_with_secondary', null, "Doc Description", 'secondary.dat', undefined, true);
920 verifyIOParameter('output_file_with_secondary', null, "Doc Description", 'secondary2.dat', undefined, true);
921 verifyIOParameter('output_dir', null, "Doc desc 1, Doc desc 2", 'outdir1', `${outPdh}`);
922 verifyIOParameter('output_bool', null, null, 'true');
923 verifyIOParameter('output_int', null, null, '1');
924 verifyIOParameter('output_long', null, null, '1');
925 verifyIOParameter('output_float', null, null, '100.5');
926 verifyIOParameter('output_double', null, null, '100.3');
927 verifyIOParameter('output_string', null, null, 'Hello output');
928 verifyIOParameter('output_file_array', null, null, 'output2.tar', `${outPdh}`);
929 verifyIOParameter('output_file_array', null, null, 'output3.tar', undefined, true);
930 verifyIOParameter('output_dir_array', null, null, 'outdir2', `${outPdh}`);
931 verifyIOParameter('output_dir_array', null, null, 'outdir3', undefined, true);
932 verifyIOParameter('output_int_array', null, null, ["10", "11", "12"]);
933 verifyIOParameter('output_long_array', null, null, ["51", "52"]);
934 verifyIOParameter('output_float_array', null, null, ["100.2", "100.4", "100.6"]);
935 verifyIOParameter('output_double_array', null, null, ["100.1", "100.2", "100.3"]);
936 verifyIOParameter('output_string_array', null, null, ["Hello", "Output", "!"]);
941 it('displays IO parameters with no value', function() {
943 const fakeOutputUUID = 'zzzzz-4zz18-abcdefghijklmno';
944 const fakeOutputPDH = '11111111111111111111111111111111+99/';
946 cy.loginAs(activeUser);
948 // Add output uuid and inputs to container request
949 cy.intercept({method: 'GET', url: '**/arvados/v1/container_requests/*'}, (req) => {
951 res.body.output_uuid = fakeOutputUUID;
952 res.body.mounts["/var/lib/cwl/cwl.input.json"] = {
955 res.body.mounts["/var/lib/cwl/workflow.json"] = {
959 inputs: testInputs.map((input) => (input.definition)),
960 outputs: testOutputs.map((output) => (output.definition))
967 // Stub fake output collection
968 cy.intercept({method: 'GET', url: `**/arvados/v1/collections/${fakeOutputUUID}*`}, {
971 uuid: fakeOutputUUID,
972 portable_data_hash: fakeOutputPDH,
976 // Stub fake output json
977 cy.intercept({method: 'GET', url: `**/c%3D${fakeOutputUUID}/cwl.output.json`}, {
982 cy.readFile('cypress/fixtures/webdav-propfind-outputs.xml').then((data) => {
983 // Stub webdav response, points to output json
984 cy.intercept({method: 'PROPFIND', url: '*'}, {
986 body: data.replace(/zzzzz-4zz18-zzzzzzzzzzzzzzz/g, fakeOutputUUID)
990 createContainerRequest(
992 'test_container_request',
994 ['echo', 'hello world'],
996 .as('containerRequest');
998 cy.getAll('@containerRequest').then(function([containerRequest]) {
999 cy.goToPath(`/processes/${containerRequest.uuid}`);
1000 cy.get('[data-cy=process-io-card] h6').contains('Inputs')
1001 .parents('[data-cy=process-io-card]').within(() => {
1004 cy.get('tbody tr').each((item) => {
1005 cy.wrap(item).contains('No value');
1008 cy.get('[data-cy=process-io-card] h6').contains('Outputs')
1009 .parents('[data-cy=process-io-card]').within(() => {
1010 cy.get('tbody tr').each((item) => {
1011 cy.wrap(item).contains('No value');