X-Git-Url: https://git.arvados.org/arvados-workbench2.git/blobdiff_plain/c492c89c16f1e5061da277e112aa06777de4461e..904f8f0605168301fafd64936756607b0439d41c:/cypress/integration/process.spec.js diff --git a/cypress/integration/process.spec.js b/cypress/integration/process.spec.js index 55290fa3..bb7492e1 100644 --- a/cypress/integration/process.spec.js +++ b/cypress/integration/process.spec.js @@ -113,6 +113,22 @@ describe('Process tests', function() { }); }); + it('shows process details', function() { + const crName = 'test_container_request'; + createContainerRequest( + activeUser, + crName, + 'arvados/jobs', + ['echo', 'hello world'], + false, 'Committed') + .then(function(containerRequest) { + cy.loginAs(activeUser); + cy.goToPath(`/processes/${containerRequest.uuid}`); + cy.get('[data-cy=process-details]').should('contain', crName); + cy.get('[data-cy=process-details-attributes-runtime-user]').contains(`Active User (${activeUser.user.uuid})`); + }); + }); + it('filters process logs by event type', function() { const nodeInfoLogs = [ 'Host Information', @@ -274,4 +290,728 @@ describe('Process tests', function() { .should('contain', 'Process retried 2 times'); }); }); + + + const testInputs = [ + { + definition: { + "id": "#main/input_file", + "label": "Label Description", + "type": "File" + }, + input: { + "input_file": { + "basename": "input1.tar", + "class": "File", + "location": "keep:00000000000000000000000000000000+01/input1.tar", + "secondaryFiles": [ + { + "basename": "input1-2.txt", + "class": "File", + "location": "keep:00000000000000000000000000000000+01/input1-2.txt" + }, + { + "basename": "input1-3.txt", + "class": "File", + "location": "keep:00000000000000000000000000000000+01/input1-3.txt" + }, + { + "basename": "input1-4.txt", + "class": "File", + "location": "keep:00000000000000000000000000000000+01/input1-4.txt" + } + ] + } + } + }, + { + definition: { + "id": "#main/input_dir", + "doc": "Doc Description", + "type": "Directory" + }, + input: { + "input_dir": { + "basename": "11111111111111111111111111111111+01", + "class": "Directory", + "location": "keep:11111111111111111111111111111111+01" + } + } + }, + { + definition: { + "id": "#main/input_bool", + "doc": ["Doc desc 1", "Doc desc 2"], + "type": "boolean" + }, + input: { + "input_bool": true, + } + }, + { + definition: { + "id": "#main/input_int", + "type": "int" + }, + input: { + "input_int": 1, + } + }, + { + definition: { + "id": "#main/input_long", + "type": "long" + }, + input: { + "input_long" : 1, + } + }, + { + definition: { + "id": "#main/input_float", + "type": "float" + }, + input: { + "input_float": 1.5, + } + }, + { + definition: { + "id": "#main/input_double", + "type": "double" + }, + input: { + "input_double": 1.3, + } + }, + { + definition: { + "id": "#main/input_string", + "type": "string" + }, + input: { + "input_string": "Hello World", + } + }, + { + definition: { + "id": "#main/input_file_array", + "type": { + "items": "File", + "type": "array" + } + }, + input: { + "input_file_array": [ + { + "basename": "input2.tar", + "class": "File", + "location": "keep:00000000000000000000000000000000+02/input2.tar" + }, + { + "basename": "input3.tar", + "class": "File", + "location": "keep:00000000000000000000000000000000+03/input3.tar", + "secondaryFiles": [ + { + "basename": "input3-2.txt", + "class": "File", + "location": "keep:00000000000000000000000000000000+03/input3-2.txt" + } + ] + } + ] + } + }, + { + definition: { + "id": "#main/input_dir_array", + "type": { + "items": "Directory", + "type": "array" + } + }, + input: { + "input_dir_array": [ + { + "basename": "11111111111111111111111111111111+02", + "class": "Directory", + "location": "keep:11111111111111111111111111111111+02" + }, + { + "basename": "11111111111111111111111111111111+03", + "class": "Directory", + "location": "keep:11111111111111111111111111111111+03" + } + ] + } + }, + { + definition: { + "id": "#main/input_int_array", + "type": { + "items": "int", + "type": "array" + } + }, + input: { + "input_int_array": [ + 1, + 3, + 5 + ] + } + }, + { + definition: { + "id": "#main/input_long_array", + "type": { + "items": "long", + "type": "array" + } + }, + input: { + "input_long_array": [ + 10, + 20 + ] + } + }, + { + definition: { + "id": "#main/input_float_array", + "type": { + "items": "float", + "type": "array" + } + }, + input: { + "input_float_array": [ + 10.2, + 10.4, + 10.6 + ] + } + }, + { + definition: { + "id": "#main/input_double_array", + "type": { + "items": "double", + "type": "array" + } + }, + input: { + "input_double_array": [ + 20.1, + 20.2, + 20.3 + ] + } + }, + { + definition: { + "id": "#main/input_string_array", + "type": { + "items": "string", + "type": "array" + } + }, + input: { + "input_string_array": [ + "Hello", + "World", + "!" + ] + } + } + ]; + + const testOutputs = [ + { + definition: { + "id": "#main/output_file", + "label": "Label Description", + "type": "File" + }, + output: { + "output_file": { + "basename": "cat.png", + "class": "File", + "location": "cat.png" + } + } + }, + { + definition: { + "id": "#main/output_file_with_secondary", + "doc": "Doc Description", + "type": "File" + }, + output: { + "output_file_with_secondary": { + "basename": "main.dat", + "class": "File", + "location": "main.dat", + "secondaryFiles": [ + { + "basename": "secondary.dat", + "class": "File", + "location": "secondary.dat" + }, + { + "basename": "secondary2.dat", + "class": "File", + "location": "secondary2.dat" + } + ] + } + } + }, + { + definition: { + "id": "#main/output_dir", + "doc": ["Doc desc 1", "Doc desc 2"], + "type": "Directory" + }, + output: { + "output_dir": { + "basename": "outdir1", + "class": "Directory", + "location": "outdir1" + } + } + }, + { + definition: { + "id": "#main/output_bool", + "type": "boolean" + }, + output: { + "output_bool": true + } + }, + { + definition: { + "id": "#main/output_int", + "type": "int" + }, + output: { + "output_int": 1 + } + }, + { + definition: { + "id": "#main/output_long", + "type": "long" + }, + output: { + "output_long": 1 + } + }, + { + definition: { + "id": "#main/output_float", + "type": "float" + }, + output: { + "output_float": 100.5 + } + }, + { + definition: { + "id": "#main/output_double", + "type": "double" + }, + output: { + "output_double": 100.3 + } + }, + { + definition: { + "id": "#main/output_string", + "type": "string" + }, + output: { + "output_string": "Hello output" + } + }, + { + definition: { + "id": "#main/output_file_array", + "type": { + "items": "File", + "type": "array" + } + }, + output: { + "output_file_array": [ + { + "basename": "output2.tar", + "class": "File", + "location": "output2.tar" + }, + { + "basename": "output3.tar", + "class": "File", + "location": "output3.tar" + } + ] + } + }, + { + definition: { + "id": "#main/output_dir_array", + "type": { + "items": "Directory", + "type": "array" + } + }, + output: { + "output_dir_array": [ + { + "basename": "outdir2", + "class": "Directory", + "location": "outdir2" + }, + { + "basename": "outdir3", + "class": "Directory", + "location": "outdir3" + } + ] + } + }, + { + definition: { + "id": "#main/output_int_array", + "type": { + "items": "int", + "type": "array" + } + }, + output: { + "output_int_array": [ + 10, + 11, + 12 + ] + } + }, + { + definition: { + "id": "#main/output_long_array", + "type": { + "items": "long", + "type": "array" + } + }, + output: { + "output_long_array": [ + 51, + 52 + ] + } + }, + { + definition: { + "id": "#main/output_float_array", + "type": { + "items": "float", + "type": "array" + } + }, + output: { + "output_float_array": [ + 100.2, + 100.4, + 100.6 + ] + } + }, + { + definition: { + "id": "#main/output_double_array", + "type": { + "items": "double", + "type": "array" + } + }, + output: { + "output_double_array": [ + 100.1, + 100.2, + 100.3 + ] + } + }, + { + definition: { + "id": "#main/output_string_array", + "type": { + "items": "string", + "type": "array" + } + }, + output: { + "output_string_array": [ + "Hello", + "Output", + "!" + ] + } + } + ]; + + const verifyIOParameter = (name, label, doc, val, collection, multipleRows) => { + cy.get('table tr').contains(name).parents('tr').within(($mainRow) => { + label && cy.contains(label); + + if (multipleRows) { + cy.get($mainRow).nextUntil('[data-cy="process-io-param"]').as('secondaryRows'); + if (val) { + if (Array.isArray(val)) { + val.forEach(v => cy.get('@secondaryRows').contains(v)); + } else { + cy.get('@secondaryRows').contains(val); + } + } + if (collection) { + cy.get('@secondaryRows').contains(collection); + } + } else { + if (val) { + if (Array.isArray(val)) { + val.forEach(v => cy.contains(v)); + } else { + cy.contains(val); + } + } + if (collection) { + cy.contains(collection); + } + } + + + }); + }; + + const verifyIOParameterImage = (name, url) => { + cy.get('table tr').contains(name).parents('tr').within(() => { + cy.get('[alt="Inline Preview"]') + .should('be.visible') + .and(($img) => { + expect($img[0].naturalWidth).to.be.greaterThan(0); + expect($img[0].src).contains(url); + }) + }); + }; + + it('displays IO parameters with keep links and previews', function() { + // Create output collection for real files + cy.createCollection(adminUser.token, { + name: `Test collection ${Math.floor(Math.random() * 999999)}`, + owner_uuid: activeUser.user.uuid, + }).then((testOutputCollection) => { + cy.loginAs(activeUser); + + cy.goToPath(`/collections/${testOutputCollection.uuid}`); + + cy.get('[data-cy=upload-button]').click(); + + cy.fixture('files/cat.png', 'base64').then(content => { + cy.get('[data-cy=drag-and-drop]').upload(content, 'cat.png'); + cy.get('[data-cy=form-submit-btn]').click(); + cy.waitForDom().get('[data-cy=form-submit-btn]').should('not.exist'); + // Confirm final collection state. + cy.get('[data-cy=collection-files-panel]') + .contains('cat.png').should('exist'); + }); + + cy.getCollection(activeUser.token, testOutputCollection.uuid).as('testOutputCollection'); + }); + + // Get updated collection pdh + cy.getAll('@testOutputCollection').then(([testOutputCollection]) => { + // Add output uuid and inputs to container request + cy.intercept({method: 'GET', url: '**/arvados/v1/container_requests/*'}, (req) => { + req.reply((res) => { + res.body.output_uuid = testOutputCollection.uuid; + res.body.mounts["/var/lib/cwl/cwl.input.json"] = { + content: testInputs.map((param) => (param.input)).reduce((acc, val) => (Object.assign(acc, val)), {}) + }; + res.body.mounts["/var/lib/cwl/workflow.json"] = { + content: { + $graph: [{ + id: "#main", + inputs: testInputs.map((input) => (input.definition)), + outputs: testOutputs.map((output) => (output.definition)) + }] + } + }; + }); + }); + + // Stub fake output collection + cy.intercept({method: 'GET', url: `**/arvados/v1/collections/${testOutputCollection.uuid}*`}, { + statusCode: 200, + body: { + uuid: testOutputCollection.uuid, + portable_data_hash: testOutputCollection.portable_data_hash, + } + }); + + // Stub fake output json + cy.intercept({method: 'GET', url: '**/c%3Dzzzzz-4zz18-zzzzzzzzzzzzzzz/cwl.output.json'}, { + statusCode: 200, + body: testOutputs.map((param) => (param.output)).reduce((acc, val) => (Object.assign(acc, val)), {}) + }); + + // Stub webdav response, points to output json + cy.intercept({method: 'PROPFIND', url: '*'}, { + fixture: 'webdav-propfind-outputs.xml', + }); + }); + + createContainerRequest( + activeUser, + 'test_container_request', + 'arvados/jobs', + ['echo', 'hello world'], + false, 'Committed') + .as('containerRequest'); + + cy.getAll('@containerRequest', '@testOutputCollection').then(function([containerRequest, testOutputCollection]) { + cy.goToPath(`/processes/${containerRequest.uuid}`); + cy.get('[data-cy=process-io-card] h6').contains('Inputs') + .parents('[data-cy=process-io-card]').within(() => { + verifyIOParameter('input_file', null, "Label Description", 'input1.tar', '00000000000000000000000000000000+01'); + verifyIOParameter('input_file', null, "Label Description", 'input1-2.txt', undefined, true); + verifyIOParameter('input_file', null, "Label Description", 'input1-3.txt', undefined, true); + verifyIOParameter('input_file', null, "Label Description", 'input1-4.txt', undefined, true); + verifyIOParameter('input_dir', null, "Doc Description", '/', '11111111111111111111111111111111+01'); + verifyIOParameter('input_bool', null, "Doc desc 1, Doc desc 2", 'true'); + verifyIOParameter('input_int', null, null, '1'); + verifyIOParameter('input_long', null, null, '1'); + verifyIOParameter('input_float', null, null, '1.5'); + verifyIOParameter('input_double', null, null, '1.3'); + verifyIOParameter('input_string', null, null, 'Hello World'); + verifyIOParameter('input_file_array', null, null, 'input2.tar', '00000000000000000000000000000000+02'); + verifyIOParameter('input_file_array', null, null, 'input3.tar', undefined, true); + verifyIOParameter('input_file_array', null, null, 'input3-2.txt', undefined, true); + verifyIOParameter('input_dir_array', null, null, '/', '11111111111111111111111111111111+02'); + verifyIOParameter('input_dir_array', null, null, '/', '11111111111111111111111111111111+03', true); + verifyIOParameter('input_int_array', null, null, ["1", "3", "5"]); + verifyIOParameter('input_long_array', null, null, ["10", "20"]); + verifyIOParameter('input_float_array', null, null, ["10.2", "10.4", "10.6"]); + verifyIOParameter('input_double_array', null, null, ["20.1", "20.2", "20.3"]); + verifyIOParameter('input_string_array', null, null, ["Hello", "World", "!"]); + }); + cy.get('[data-cy=process-io-card] h6').contains('Outputs') + .parents('[data-cy=process-io-card]').within((ctx) => { + cy.get(ctx).scrollIntoView(); + cy.get('[data-cy="io-preview-image-toggle"]').click(); + const outPdh = testOutputCollection.portable_data_hash; + + verifyIOParameter('output_file', null, "Label Description", 'cat.png', `${outPdh}`); + verifyIOParameterImage('output_file', `/c=${outPdh}/cat.png`); + verifyIOParameter('output_file_with_secondary', null, "Doc Description", 'main.dat', `${outPdh}`); + verifyIOParameter('output_file_with_secondary', null, "Doc Description", 'secondary.dat', undefined, true); + verifyIOParameter('output_file_with_secondary', null, "Doc Description", 'secondary2.dat', undefined, true); + verifyIOParameter('output_dir', null, "Doc desc 1, Doc desc 2", 'outdir1', `${outPdh}`); + verifyIOParameter('output_bool', null, null, 'true'); + verifyIOParameter('output_int', null, null, '1'); + verifyIOParameter('output_long', null, null, '1'); + verifyIOParameter('output_float', null, null, '100.5'); + verifyIOParameter('output_double', null, null, '100.3'); + verifyIOParameter('output_string', null, null, 'Hello output'); + verifyIOParameter('output_file_array', null, null, 'output2.tar', `${outPdh}`); + verifyIOParameter('output_file_array', null, null, 'output3.tar', undefined, true); + verifyIOParameter('output_dir_array', null, null, 'outdir2', `${outPdh}`); + verifyIOParameter('output_dir_array', null, null, 'outdir3', undefined, true); + verifyIOParameter('output_int_array', null, null, ["10", "11", "12"]); + verifyIOParameter('output_long_array', null, null, ["51", "52"]); + verifyIOParameter('output_float_array', null, null, ["100.2", "100.4", "100.6"]); + verifyIOParameter('output_double_array', null, null, ["100.1", "100.2", "100.3"]); + verifyIOParameter('output_string_array', null, null, ["Hello", "Output", "!"]); + }); + }); + }); + + it('displays IO parameters with no value', function() { + + const fakeOutputUUID = 'zzzzz-4zz18-abcdefghijklmno'; + const fakeOutputPDH = '11111111111111111111111111111111+99/'; + + cy.loginAs(activeUser); + + // Add output uuid and inputs to container request + cy.intercept({method: 'GET', url: '**/arvados/v1/container_requests/*'}, (req) => { + req.reply((res) => { + res.body.output_uuid = fakeOutputUUID; + res.body.mounts["/var/lib/cwl/cwl.input.json"] = { + content: {} + }; + res.body.mounts["/var/lib/cwl/workflow.json"] = { + content: { + $graph: [{ + id: "#main", + inputs: testInputs.map((input) => (input.definition)), + outputs: testOutputs.map((output) => (output.definition)) + }] + } + }; + }); + }); + + // Stub fake output collection + cy.intercept({method: 'GET', url: `**/arvados/v1/collections/${fakeOutputUUID}*`}, { + statusCode: 200, + body: { + uuid: fakeOutputUUID, + portable_data_hash: fakeOutputPDH, + } + }); + + // Stub fake output json + cy.intercept({method: 'GET', url: `**/c%3D${fakeOutputUUID}/cwl.output.json`}, { + statusCode: 200, + body: {} + }); + + cy.readFile('cypress/fixtures/webdav-propfind-outputs.xml').then((data) => { + // Stub webdav response, points to output json + cy.intercept({method: 'PROPFIND', url: '*'}, { + statusCode: 200, + body: data.replace(/zzzzz-4zz18-zzzzzzzzzzzzzzz/g, fakeOutputUUID) + }); + }); + + createContainerRequest( + activeUser, + 'test_container_request', + 'arvados/jobs', + ['echo', 'hello world'], + false, 'Committed') + .as('containerRequest'); + + cy.getAll('@containerRequest').then(function([containerRequest]) { + cy.goToPath(`/processes/${containerRequest.uuid}`); + cy.get('[data-cy=process-io-card] h6').contains('Inputs') + .parents('[data-cy=process-io-card]').within(() => { + cy.wait(2000); + cy.waitForDom(); + cy.get('tbody tr').each((item) => { + cy.wrap(item).contains('No value'); + }); + }); + cy.get('[data-cy=process-io-card] h6').contains('Outputs') + .parents('[data-cy=process-io-card]').within(() => { + cy.get('tbody tr').each((item) => { + cy.wrap(item).contains('No value'); + }); + }); + }); + }); + });