X-Git-Url: https://git.arvados.org/arvados.git/blobdiff_plain/74da68a31fc788818cab7276e67db8951cb28220..a6b824e63de4df4296a17d74d80ff2101820762d:/cypress/integration/process.spec.js diff --git a/cypress/integration/process.spec.js b/cypress/integration/process.spec.js index b2942e4d33..19544c9ca5 100644 --- a/cypress/integration/process.spec.js +++ b/cypress/integration/process.spec.js @@ -106,13 +106,50 @@ describe('Process tests', function() { }, event_type: 'stdout' }).then(function(log) { - cy.get('[data-cy=process-logs]') + cy.get('[data-cy=process-logs]', {timeout: 7000}) .should('not.contain', 'No logs yet') .and('contain', 'hello world'); }) }); }); + it('shows process details', function() { + createContainerRequest( + activeUser, + `test_container_request ${Math.floor(Math.random() * 999999)}`, + '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', containerRequest.name); + cy.get('[data-cy=process-details-attributes-modifiedby-user]').contains(`Active User (${activeUser.user.uuid})`); + cy.get('[data-cy=process-details-attributes-runtime-user]').should('not.exist'); + }); + + // Fake submitted by another user + cy.intercept({method: 'GET', url: '**/arvados/v1/container_requests/*'}, (req) => { + req.reply((res) => { + res.body.modified_by_user_uuid = 'zzzzz-tpzed-000000000000000'; + }); + }); + + createContainerRequest( + activeUser, + `test_container_request ${Math.floor(Math.random() * 999999)}`, + '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', containerRequest.name); + cy.get('[data-cy=process-details-attributes-modifiedby-user]').contains(`zzzzz-tpzed-000000000000000`); + 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', @@ -169,7 +206,7 @@ describe('Process tests', function() { cy.loginAs(activeUser); cy.goToPath(`/processes/${containerRequest.uuid}`); // Should show main logs by default - cy.get('[data-cy=process-logs-filter]').should('contain', 'Main logs'); + cy.get('[data-cy=process-logs-filter]', {timeout: 7000}).should('contain', 'Main logs'); cy.get('[data-cy=process-logs]') .should('contain', stdoutLogs[Math.floor(Math.random() * stdoutLogs.length)]) .and('not.contain', nodeInfoLogs[Math.floor(Math.random() * nodeInfoLogs.length)]) @@ -263,14 +300,14 @@ describe('Process tests', function() { cy.getAll('@containerRequest').then(function([containerRequest]) { cy.goToPath(`/processes/${containerRequest.uuid}`); - cy.get('[data-cy=process-runtime-status-retry-warning]') + cy.get('[data-cy=process-runtime-status-retry-warning]', {timeout: 7000}) .should('contain', 'Process retried 1 time'); }); cy.getAll('@containerRequest').then(function([containerRequest]) { containerCount = 3; cy.goToPath(`/processes/${containerRequest.uuid}`); - cy.get('[data-cy=process-runtime-status-retry-warning]') + cy.get('[data-cy=process-runtime-status-retry-warning]', {timeout: 7000}) .should('contain', 'Process retried 2 times'); }); }); @@ -403,6 +440,9 @@ describe('Process tests', function() { "location": "keep:00000000000000000000000000000000+03/input3-2.txt" } ] + }, + { + "$import": "import_path" } ] } @@ -426,6 +466,9 @@ describe('Process tests', function() { "basename": "11111111111111111111111111111111+03", "class": "Directory", "location": "keep:11111111111111111111111111111111+03" + }, + { + "$import": "import_path" } ] } @@ -442,7 +485,10 @@ describe('Process tests', function() { "input_int_array": [ 1, 3, - 5 + 5, + { + "$import": "import_path" + } ] } }, @@ -457,7 +503,10 @@ describe('Process tests', function() { input: { "input_long_array": [ 10, - 20 + 20, + { + "$import": "import_path" + } ] } }, @@ -473,7 +522,10 @@ describe('Process tests', function() { "input_float_array": [ 10.2, 10.4, - 10.6 + 10.6, + { + "$import": "import_path" + } ] } }, @@ -489,7 +541,10 @@ describe('Process tests', function() { "input_double_array": [ 20.1, 20.2, - 20.3 + 20.3, + { + "$import": "import_path" + } ] } }, @@ -505,9 +560,91 @@ describe('Process tests', function() { "input_string_array": [ "Hello", "World", - "!" + "!", + { + "$import": "import_path" + } ] } + }, + { + definition: { + "id": "#main/input_bool_include", + "type": "boolean" + }, + input: { + "input_bool_include": { + "$include": "include_path" + } + } + }, + { + definition: { + "id": "#main/input_int_include", + "type": "int" + }, + input: { + "input_int_include": { + "$include": "include_path" + } + } + }, + { + definition: { + "id": "#main/input_float_include", + "type": "float" + }, + input: { + "input_float_include": { + "$include": "include_path" + } + } + }, + { + definition: { + "id": "#main/input_string_include", + "type": "string" + }, + input: { + "input_string_include": { + "$include": "include_path" + } + } + }, + { + definition: { + "id": "#main/input_file_include", + "type": "File" + }, + input: { + "input_file_include": { + "$include": "include_path" + } + } + }, + { + definition: { + "id": "#main/input_directory_include", + "type": "Directory" + }, + input: { + "input_directory_include": { + "$include": "include_path" + } + } + }, + { + definition: { + "id": "#main/input_file_url", + "type": "File" + }, + input: { + "input_file_url": { + "basename": "index.html", + "class": "File", + "location": "http://example.com/index.html" + } + } } ]; @@ -749,7 +886,7 @@ describe('Process tests', function() { const verifyIOParameter = (name, label, doc, val, collection, multipleRows) => { cy.get('table tr').contains(name).parents('tr').within(($mainRow) => { - doc && cy.contains(doc); + label && cy.contains(label); if (multipleRows) { cy.get($mainRow).nextUntil('[data-cy="process-io-param"]').as('secondaryRows'); @@ -869,52 +1006,59 @@ describe('Process tests', function() { 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(); - verifyIOParameter('input_file', null, "Label Description", 'input1.tar', 'keep:00000000000000000000000000000000+01'); - verifyIOParameter('input_file', null, "Label Description", 'input1-2.txt', 'keep:00000000000000000000000000000000+01', true); - verifyIOParameter('input_file', null, "Label Description", 'input1-3.txt', 'keep:00000000000000000000000000000000+01', true); - verifyIOParameter('input_file', null, "Label Description", 'input1-4.txt', 'keep:00000000000000000000000000000000+01', true); - verifyIOParameter('input_dir', null, "Doc Description", 'No value', 'keep:11111111111111111111111111111111+01'); + 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', 'keep:00000000000000000000000000000000+02'); - verifyIOParameter('input_file_array', null, null, 'input3.tar', 'keep:00000000000000000000000000000000+03', true); - verifyIOParameter('input_file_array', null, null, 'input3-2.txt', 'keep:00000000000000000000000000000000+03', true); - verifyIOParameter('input_dir_array', null, null, 'No value', 'keep:11111111111111111111111111111111+02'); - verifyIOParameter('input_dir_array', null, null, 'No value', 'keep: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", "!"]); + 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_file_array', null, null, 'Cannot display value', undefined, true); + verifyIOParameter('input_dir_array', null, null, '/', '11111111111111111111111111111111+02'); + verifyIOParameter('input_dir_array', null, null, '/', '11111111111111111111111111111111+03', true); + verifyIOParameter('input_dir_array', null, null, 'Cannot display value', undefined, true); + verifyIOParameter('input_int_array', null, null, ["1", "3", "5", "Cannot display value"]); + verifyIOParameter('input_long_array', null, null, ["10", "20", "Cannot display value"]); + verifyIOParameter('input_float_array', null, null, ["10.2", "10.4", "10.6", "Cannot display value"]); + verifyIOParameter('input_double_array', null, null, ["20.1", "20.2", "20.3", "Cannot display value"]); + verifyIOParameter('input_string_array', null, null, ["Hello", "World", "!", "Cannot display value"]); + verifyIOParameter('input_bool_include', null, null, "Cannot display value"); + verifyIOParameter('input_int_include', null, null, "Cannot display value"); + verifyIOParameter('input_float_include', null, null, "Cannot display value"); + verifyIOParameter('input_string_include', null, null, "Cannot display value"); + verifyIOParameter('input_file_include', null, null, "Cannot display value"); + verifyIOParameter('input_directory_include', null, null, "Cannot display value"); + verifyIOParameter('input_file_url', null, null, "http://example.com/index.html"); }); cy.get('[data-cy=process-io-card] h6').contains('Outputs') .parents('[data-cy=process-io-card]').within((ctx) => { cy.get(ctx).scrollIntoView(); - cy.waitForDom().get('[data-cy="io-preview-image-toggle"]').click(); + cy.get('[data-cy="io-preview-image-toggle"]').click(); const outPdh = testOutputCollection.portable_data_hash; - verifyIOParameter('output_file', null, "Label Description", 'cat.png', `keep:${outPdh}`); + 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', `keep:${outPdh}`); - verifyIOParameter('output_file_with_secondary', null, "Doc Description", 'secondary.dat', `keep:${outPdh}`, true); - verifyIOParameter('output_file_with_secondary', null, "Doc Description", 'secondary2.dat', `keep:${outPdh}`, true); - verifyIOParameter('output_dir', null, "Doc desc 1, Doc desc 2", 'outdir1', `keep:${outPdh}`); + 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', `keep:${outPdh}`); - verifyIOParameter('output_file_array', null, null, 'output3.tar', `keep:${outPdh}`, true); - verifyIOParameter('output_dir_array', null, null, 'outdir2', `keep:${outPdh}`); - verifyIOParameter('output_dir_array', null, null, 'outdir3', `keep:${outPdh}`, true); + 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"]); @@ -1000,4 +1144,217 @@ describe('Process tests', function() { }); }); + + it('allows copying processes', function() { + const crName = 'first_container_request'; + const copiedCrName = 'copied_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]').find('button[title="More options"]').click(); + cy.get('ul[data-cy=context-menu]').contains("Copy and re-run process").click(); + }); + + cy.get('[data-cy=form-dialog]').within(() => { + cy.get('input[name=name]').clear().type(copiedCrName); + cy.get('[data-cy=projects-tree-home-tree-picker]').click(); + cy.get('[data-cy=form-submit-btn]').click(); + }); + + cy.get('[data-cy=process-details]').should('contain', copiedCrName); + cy.get('[data-cy=process-details]').find('button').contains('Run'); + }); + + const getFakeContainer = (fakeContainerUuid) => ({ + href: `/containers/${fakeContainerUuid}`, + kind: "arvados#container", + etag: "ecfosljpnxfari9a8m7e4yv06", + uuid: fakeContainerUuid, + owner_uuid: "zzzzz-tpzed-000000000000000", + created_at: "2023-02-13T15:55:47.308915000Z", + modified_by_client_uuid: "zzzzz-ozdt8-q6dzdi1lcc03155", + modified_by_user_uuid: "zzzzz-tpzed-000000000000000", + modified_at: "2023-02-15T19:12:45.987086000Z", + command: [ + "arvados-cwl-runner", + "--api=containers", + "--local", + "--project-uuid=zzzzz-j7d0g-yr18k784zplfeza", + "/var/lib/cwl/workflow.json#main", + "/var/lib/cwl/cwl.input.json", + ], + container_image: "4ad7d11381df349e464694762db14e04+303", + cwd: "/var/spool/cwl", + environment: {}, + exit_code: null, + finished_at: null, + locked_by_uuid: null, + log: null, + output: null, + output_path: "/var/spool/cwl", + progress: null, + runtime_constraints: { + API: true, + cuda: { + device_count: 0, + driver_version: "", + hardware_capability: "", + }, + keep_cache_disk: 2147483648, + keep_cache_ram: 0, + ram: 1342177280, + vcpus: 1, + }, + runtime_status: {}, + started_at: null, + auth_uuid: null, + scheduling_parameters: { + max_run_time: 0, + partitions: [], + preemptible: false, + }, + runtime_user_uuid: "zzzzz-tpzed-vllbpebicy84rd5", + runtime_auth_scopes: ["all"], + lock_count: 2, + gateway_address: null, + interactive_session_started: false, + output_storage_classes: ["default"], + output_properties: {}, + cost: 0.0, + subrequests_cost: 0.0, + }); + + it('shows cancel button when appropriate', function() { + // Ignore collection requests + cy.intercept({method: 'GET', url: `**/arvados/v1/collections/*`}, { + statusCode: 200, + body: {} + }); + + // Uncommitted container + const crUncommitted = `Test process ${Math.floor(Math.random() * 999999)}`; + createContainerRequest( + activeUser, + crUncommitted, + 'arvados/jobs', + ['echo', 'hello world'], + false, 'Uncommitted') + .then(function(containerRequest) { + // Navigate to process and verify run / cancel button + cy.goToPath(`/processes/${containerRequest.uuid}`); + cy.waitForDom(); + cy.get('[data-cy=process-details]').should('contain', crUncommitted); + cy.get('[data-cy=process-run-button]').should('exist'); + cy.get('[data-cy=process-cancel-button]').should('not.exist'); + }); + + // Queued container + const crQueued = `Test process ${Math.floor(Math.random() * 999999)}`; + const fakeCrUuid = 'zzzzz-dz642-000000000000001'; + createContainerRequest( + activeUser, + crQueued, + 'arvados/jobs', + ['echo', 'hello world'], + false, 'Committed') + .then(function(containerRequest) { + // Fake container uuid + cy.intercept({method: 'GET', url: `**/arvados/v1/container_requests/${containerRequest.uuid}`}, (req) => { + req.reply((res) => { + res.body.output_uuid = fakeCrUuid; + res.body.priority = 500; + res.body.state = "Committed"; + }); + }); + + // Fake container + const container = getFakeContainer(fakeCrUuid); + cy.intercept({method: 'GET', url: `**/arvados/v1/container/${fakeCrUuid}`}, { + statusCode: 200, + body: {...container, state: "Queued", priority: 500} + }); + + // Navigate to process and verify cancel button + cy.goToPath(`/processes/${containerRequest.uuid}`); + cy.waitForDom(); + cy.get('[data-cy=process-details]').should('contain', crQueued); + cy.get('[data-cy=process-cancel-button]').contains('Cancel'); + }); + + // Locked container + const crLocked = `Test process ${Math.floor(Math.random() * 999999)}`; + const fakeCrLockedUuid = 'zzzzz-dz642-000000000000002'; + createContainerRequest( + activeUser, + crLocked, + 'arvados/jobs', + ['echo', 'hello world'], + false, 'Committed') + .then(function(containerRequest) { + // Fake container uuid + cy.intercept({method: 'GET', url: `**/arvados/v1/container_requests/${containerRequest.uuid}`}, (req) => { + req.reply((res) => { + res.body.output_uuid = fakeCrLockedUuid; + res.body.priority = 500; + res.body.state = "Committed"; + }); + }); + + // Fake container + const container = getFakeContainer(fakeCrLockedUuid); + cy.intercept({method: 'GET', url: `**/arvados/v1/container/${fakeCrLockedUuid}`}, { + statusCode: 200, + body: {...container, state: "Locked", priority: 500} + }); + + // Navigate to process and verify cancel button + cy.goToPath(`/processes/${containerRequest.uuid}`); + cy.waitForDom(); + cy.get('[data-cy=process-details]').should('contain', crLocked); + cy.get('[data-cy=process-cancel-button]').contains('Cancel'); + }); + + // On Hold container + const crOnHold = `Test process ${Math.floor(Math.random() * 999999)}`; + const fakeCrOnHoldUuid = 'zzzzz-dz642-000000000000003'; + createContainerRequest( + activeUser, + crOnHold, + 'arvados/jobs', + ['echo', 'hello world'], + false, 'Committed') + .then(function(containerRequest) { + // Fake container uuid + cy.intercept({method: 'GET', url: `**/arvados/v1/container_requests/${containerRequest.uuid}`}, (req) => { + req.reply((res) => { + res.body.output_uuid = fakeCrOnHoldUuid; + res.body.priority = 0; + res.body.state = "Committed"; + }); + }); + + // Fake container + const container = getFakeContainer(fakeCrOnHoldUuid); + cy.intercept({method: 'GET', url: `**/arvados/v1/container/${fakeCrOnHoldUuid}`}, { + statusCode: 200, + body: {...container, state: "Queued", priority: 0} + }); + + // Navigate to process and verify cancel button + cy.goToPath(`/processes/${containerRequest.uuid}`); + cy.waitForDom(); + cy.get('[data-cy=process-details]').should('contain', crOnHold); + cy.get('[data-cy=process-run-button]').should('exist'); + cy.get('[data-cy=process-cancel-button]').should('not.exist'); + }); + }); + });