diff --git a/.github/workflows/cypress.yml b/.github/workflows/cypress.yml index 78f7ef972a..b0f3fd5c13 100644 --- a/.github/workflows/cypress.yml +++ b/.github/workflows/cypress.yml @@ -28,6 +28,7 @@ jobs: test: - 'screenshots' - 'approval' + - 'approval_modal' - 'collections' - 'ee_controller' - 'execution_environments' diff --git a/CHANGES/2248.task b/CHANGES/2248.task new file mode 100644 index 0000000000..101d4c9c81 --- /dev/null +++ b/CHANGES/2248.task @@ -0,0 +1 @@ +cypress tests for Approval with multiple repositories diff --git a/src/containers/certification-dashboard/certification-dashboard.tsx b/src/containers/certification-dashboard/certification-dashboard.tsx index 3f6d18ac4d..1e811ed038 100644 --- a/src/containers/certification-dashboard/certification-dashboard.tsx +++ b/src/containers/certification-dashboard/certification-dashboard.tsx @@ -454,8 +454,9 @@ class CertificationDashboard extends React.Component { private renderRow(collectionData: CollectionVersionSearch, index) { const { collection_version: version, repository } = collectionData; + const data_cy = `CertificationDashboard-row-${collectionData.repository.name}-${collectionData.collection_version.namespace}-${collectionData.collection_version.name}`; return ( - + {version.namespace} {version.name} diff --git a/test/cypress/e2e/approval/approval_dashboard_list.js b/test/cypress/e2e/approval/approval_dashboard_list.js index cb46ade7fb..a7789b963b 100644 --- a/test/cypress/e2e/approval/approval_dashboard_list.js +++ b/test/cypress/e2e/approval/approval_dashboard_list.js @@ -87,13 +87,13 @@ describe('Approval Dashboard list tests for sorting, paging and filtering', () = cy.get('[data-cy="sort_name"]').click(); cy.get('[data-cy="body"]').contains('approval'); - cy.get('[data-cy="CertificationDashboard-row"]:first').contains( + cy.get('[data-cy^="CertificationDashboard-row"]:first').contains( items[items.length - 1].name, ); - cy.get('[data-cy="CertificationDashboard-row"]').contains( + cy.get('[data-cy^="CertificationDashboard-row"]').contains( items[items.length - 2].name, ); - cy.get('[data-cy="CertificationDashboard-row"]').contains( + cy.get('[data-cy^="CertificationDashboard-row"]').contains( items[items.length - 3].name, ); }); diff --git a/test/cypress/e2e/approval/approval_process.js b/test/cypress/e2e/approval/approval_process.js index 8181dc7c55..9658d6e6f3 100644 --- a/test/cypress/e2e/approval/approval_process.js +++ b/test/cypress/e2e/approval/approval_process.js @@ -21,16 +21,16 @@ describe('Approval Dashboard process', () => { // should approve cy.visit(`${uiPrefix}approval-dashboard`); - cy.contains('[data-cy="CertificationDashboard-row"]', 'Needs review'); + cy.contains('[data-cy^="CertificationDashboard-row"]', 'Needs review'); cy.contains( - '[data-cy="CertificationDashboard-row"] button', + '[data-cy^="CertificationDashboard-row"] button', 'Sign and approve', ).click(); cy.contains('.body', 'No results found', { timeout: 8000 }); cy.visit(`${uiPrefix}approval-dashboard`); cy.contains('button', 'Clear all filters').click(); cy.contains( - '[data-cy="CertificationDashboard-row"]', + '[data-cy^="CertificationDashboard-row"]', 'Signed and approved', ); @@ -45,7 +45,7 @@ describe('Approval Dashboard process', () => { { force: true }, ); cy.contains('Reject').click({ force: true }); - cy.contains('[data-cy="CertificationDashboard-row"]', 'Rejected'); + cy.contains('[data-cy^="CertificationDashboard-row"]', 'Rejected'); // should not see items in collections cy.visit(`${uiPrefix}collections`); diff --git a/test/cypress/e2e/approval/collection_approval.js b/test/cypress/e2e/approval/collection_approval.js index 6daa85b6a7..be1292b073 100644 --- a/test/cypress/e2e/approval/collection_approval.js +++ b/test/cypress/e2e/approval/collection_approval.js @@ -26,7 +26,7 @@ describe('tests the approval list screen ', () => { cy.get('button[aria-label="Actions"]:first').click(); cy.contains('Reject').click(); cy.contains( - '[data-cy="CertificationDashboard-row"]:first-child', + '[data-cy^="CertificationDashboard-row"]:first-child', 'Rejected', ); @@ -34,7 +34,7 @@ describe('tests the approval list screen ', () => { cy.get('button[aria-label="Actions"]:first').click(); cy.contains('Sign and approve').click(); cy.contains( - '[data-cy="CertificationDashboard-row"]:first-child', + '[data-cy^="CertificationDashboard-row"]:first-child', 'Signed and approved', ); }); diff --git a/test/cypress/e2e/approval_modal/approval_multiple_repos.js b/test/cypress/e2e/approval_modal/approval_multiple_repos.js new file mode 100644 index 0000000000..9cf4ee9ece --- /dev/null +++ b/test/cypress/e2e/approval_modal/approval_multiple_repos.js @@ -0,0 +1,217 @@ +import { range } from 'lodash'; + +const uiPrefix = Cypress.env('uiPrefix'); +const apiPrefix = Cypress.env('apiPrefix'); + +function openModal(menu) { + cy.visit(`${uiPrefix}approval-dashboard`); + cy.contains('Clear all filters').click(); + + if (menu) { + cy.get( + '[data-cy^="CertificationDashboard-row"] [aria-label="Actions"]', + ).click(); + cy.contains('a', 'Sign and approve').click(); + } else { + cy.contains( + '[data-cy^="CertificationDashboard-row"] button', + 'Sign and approve', + ).click(); + } + + cy.contains('Select repositories'); +} + +function toggleItem(name) { + cy.get('.modal-body [data-cy="compound_filter"] input') + .clear() + .type(name + '{enter}'); + cy.get(`[data-cy="ApproveModal-CheckboxRow-row-${name}"] input`).click(); +} + +function menuActionClick(repo, action) { + cy.get( + `[data-cy="CertificationDashboard-row-${repo}-namespace-collection1"] [aria-label="Actions"]`, + ).click(); + cy.contains('[data-cy="kebab-toggle"] a', action).click(); +} + +function rejectItem(repo) { + menuActionClick(repo, 'Reject'); + cy.contains( + 'Certification status for collection "namespace collection1 v1.0.0" has been successfully updated.', + { timeout: 10000 }, + ); + cy.visit(`${uiPrefix}approval-dashboard`); + cy.contains('Clear all filters').click(); + cy.contains( + `[data-cy="CertificationDashboard-row-rejected-namespace-collection1"]`, + 'Rejected', + ); +} + +const reposList = []; + +describe('Approval Dashboard process with multiple repos', () => { + before(() => { + cy.deleteNamespacesAndCollections(); + cy.galaxykit('-i namespace create', 'namespace'); + cy.galaxykit('-i collection upload', 'namespace', 'collection1'); + + const max = 11; + range(1, max).forEach((i) => { + reposList.push('repo' + i); + }); + + reposList.push('published'); + + cy.login(); + + range(1, max).forEach((i) => { + cy.galaxykit('-i distribution delete', 'repo' + i); + }); + + cy.galaxykit('-i task wait all'); + + cy.request(apiPrefix + 'pulp/api/v3/repositories/ansible/ansible/').then( + (data) => { + const list = data.body.results; + list.forEach((repo) => { + if ( + repo.pulp_labels?.pipeline == 'approved' && + repo.name != 'published' + ) { + cy.log('deleting repository' + repo.name); + cy.galaxykit('-i repository delete', repo.name); + } + }); + cy.galaxykit('-i task wait all'); + range(1, max).forEach((i) => { + cy.galaxykit( + `-i repository create`, + 'repo' + i, + '--pipeline=approved', + ); + cy.galaxykit('-i distribution create', 'repo' + i); + }); + cy.galaxykit('-i task wait all'); + }, + ); + + // prepare another staging + cy.galaxykit('-i distribution delete', 'staging2'); + cy.galaxykit('-i repository delete', 'staging2'); + + cy.galaxykit('-i repository create', 'staging2', '--pipeline=staging'); + cy.galaxykit('-i distribution create', 'staging2'); + }); + + beforeEach(() => { + cy.login(); + }); + + it('should contains no colletctions in list.', () => { + cy.visit(`${uiPrefix}collections`); + cy.contains('No collections yet'); + }); + + it('should approve, reject and reapprove.', () => { + openModal(); + toggleItem('repo1'); + toggleItem('repo2'); + toggleItem('published'); + cy.contains('button', 'Select').click(); + cy.contains( + 'Certification status for collection "namespace collection1 v1.0.0" has been successfully updated.', + { timeout: 20000 }, + ); + + cy.visit(`${uiPrefix}approval-dashboard`); + cy.contains('No results found'); + cy.contains('Clear all filters').click(); + cy.contains('[aria-label="Collection versions"]', 'repo1'); + cy.contains('[aria-label="Collection versions"]', 'repo2'); + cy.contains('[aria-label="Collection versions"]', 'published'); + + rejectItem('repo1'); + rejectItem('published'); + + // 2 items should be left there + cy.contains('.toolbar', '1 - 2 of 2'); + cy.get( + '[data-cy="CertificationDashboard-row-rejected-namespace-collection1"]', + ); + cy.get( + '[data-cy="CertificationDashboard-row-repo2-namespace-collection1"]', + ); + cy.get( + '[data-cy="CertificationDashboard-row-repo1-namespace-collection1"]', + ).should('not.exist'); + cy.get( + '[data-cy="CertificationDashboard-row-published-namespace-collection1"]', + ).should('not.exist'); + + // reapprove + menuActionClick('rejected', 'Sign and approve'); + cy.contains('Select repositories'); + cy.contains('[aria-label="Label group category"]', 'repo2'); + toggleItem('repo1'); + cy.contains('button', 'Select').click(); + cy.contains( + 'Certification status for collection "namespace collection1 v1.0.0" has been successfully updated.', + { timeout: 20000 }, + ); + + cy.visit(`${uiPrefix}approval-dashboard`); + cy.contains('Clear all filters').click(); + cy.contains('.toolbar', '1 - 2 of 2'); + cy.get( + '[data-cy="CertificationDashboard-row-repo2-namespace-collection1"]', + ); + cy.get( + '[data-cy="CertificationDashboard-row-repo1-namespace-collection1"]', + ); + cy.get( + '[data-cy="CertificationDashboard-row-published-namespace-collection1"]', + ).should('not.exist'); + cy.get( + '[data-cy="CertificationDashboard-row-rejected-namespace-collection1"]', + ).should('not.exist'); + }); + + it('should be able to approve from different staging repo', () => { + cy.deleteNamespacesAndCollections(); + cy.galaxykit('-i namespace create', 'namespace'); + cy.galaxykit('-i collection upload', 'namespace', 'collection1'); + cy.galaxykit( + '-i collection move', + 'namespace', + 'collection1', + '1.0.0', + 'staging', + 'staging2', + ); + + cy.visit(`${uiPrefix}approval-dashboard`); + cy.get( + '[data-cy="CertificationDashboard-row-staging2-namespace-collection1"]', + ); + + openModal(); + toggleItem('repo1'); + toggleItem('repo2'); + toggleItem('published'); + cy.contains('button', 'Select').click(); + cy.contains( + 'Certification status for collection "namespace collection1 v1.0.0" has been successfully updated.', + { timeout: 20000 }, + ); + + cy.visit(`${uiPrefix}approval-dashboard`); + cy.contains('No results found'); + cy.contains('Clear all filters').click(); + cy.contains('[aria-label="Collection versions"]', 'repo1'); + cy.contains('[aria-label="Collection versions"]', 'repo2'); + cy.contains('[aria-label="Collection versions"]', 'published'); + }); +}); diff --git a/test/cypress/e2e/approval_modal/approval_multiple_repos_list.js b/test/cypress/e2e/approval_modal/approval_multiple_repos_list.js new file mode 100644 index 0000000000..46218fea3e --- /dev/null +++ b/test/cypress/e2e/approval_modal/approval_multiple_repos_list.js @@ -0,0 +1,195 @@ +import { range } from 'lodash'; + +const uiPrefix = Cypress.env('uiPrefix'); +const apiPrefix = Cypress.env('apiPrefix'); + +function openModal(menu) { + cy.visit(`${uiPrefix}approval-dashboard`); + cy.contains('Clear all filters').click(); + + if (menu) { + cy.get( + '[data-cy^="CertificationDashboard-row"] [aria-label="Actions"]', + ).click(); + cy.contains('a', 'Sign and approve').click(); + } else { + cy.contains( + '[data-cy^="CertificationDashboard-row"] button', + 'Sign and approve', + ).click(); + } + + cy.contains('Select repositories'); +} + +function toggleItem(name) { + cy.get('.modal-body [data-cy="compound_filter"] input') + .clear() + .type(name + '{enter}'); + cy.get(`[data-cy="ApproveModal-CheckboxRow-row-${name}"] input`).click(); +} + +const reposList = []; + +describe('Approval Dashboard process with multiple repos', () => { + before(() => { + cy.deleteNamespacesAndCollections(); + cy.galaxykit('-i namespace create', 'namespace'); + cy.galaxykit('-i collection upload', 'namespace', 'collection1'); + + const max = 11; + range(1, max).forEach((i) => { + reposList.push('repo' + i); + }); + + reposList.push('published'); + + cy.login(); + + range(1, max).forEach((i) => { + cy.galaxykit('-i distribution delete', 'repo' + i); + }); + + cy.galaxykit('-i task wait all'); + + cy.request(apiPrefix + 'pulp/api/v3/repositories/ansible/ansible/').then( + (data) => { + const list = data.body.results; + list.forEach((repo) => { + if ( + repo.pulp_labels?.pipeline == 'approved' && + repo.name != 'published' + ) { + cy.log('deleting repository' + repo.name); + cy.galaxykit('-i repository delete', repo.name); + } + }); + cy.galaxykit('-i task wait all'); + range(1, max).forEach((i) => { + cy.galaxykit( + `-i repository create`, + 'repo' + i, + '--pipeline=approved', + ); + cy.galaxykit('-i distribution create', 'repo' + i); + }); + cy.galaxykit('-i task wait all'); + }, + ); + + // prepare another staging + cy.galaxykit('-i distribution delete', 'staging2'); + cy.galaxykit('-i repository delete', 'staging2'); + + cy.galaxykit('-i repository create', 'staging2', '--pipeline=staging'); + cy.galaxykit('-i distribution create', 'staging2'); + }); + + beforeEach(() => { + cy.login(); + }); + + it('should test paging.', () => { + openModal(); + cy.contains('.modal-body .toolbar', '1 - 10 of 11'); + cy.contains('.modal-body', 'repo1'); + cy.contains('.modal-body', 'published'); + cy.get('.modal-body .toolbar [data-action="next"]').click(); + cy.contains('.modal-body .toolbar', '11 - 11 of 11'); + cy.contains('.modal-body', 'repo9'); + }); + + it('should test ordering.', () => { + openModal(); + cy.contains('.modal-body', 'repo9').should('not.exist'); + cy.get('.modal-body [data-cy="sort_name"]').click(); + cy.contains('.modal-body', 'repo9'); + cy.contains('.modal-body', 'published').should('not.exist'); + }); + + it('should test filtering.', () => { + openModal(); + cy.contains('.modal-body', 'repo9').should('not.exist'); + cy.get('.modal-body [data-cy="compound_filter"] input').type('repo{enter}'); + cy.contains('.modal-body', 'repo9'); + cy.contains('.modal-body', 'repo1'); + cy.contains('.modal-body', 'published').should('not.exist'); + + cy.get('.modal-body [data-cy="compound_filter"] input') + .clear() + .type('repo2{enter}'); + cy.contains('.modal-body', 'repo2'); + cy.contains('.modal-body', 'repo1').should('not.exist'); + cy.contains('.modal-body', 'published').should('not.exist'); + }); + + it('should test select/deselect all/page.', () => { + openModal(); + + // select all + cy.get('.toolbar [aria-label="Select"] svg').click(); + cy.contains('a', 'Select all (11 items)').click(); + cy.contains('[aria-label="Label group category"] button', '8 more').click(); + reposList.forEach((repo) => { + cy.contains('[aria-label="Label group category"]', repo); + }); + + // deselect all + cy.get('.toolbar [aria-label="Select"] svg').click(); + cy.contains('a', 'Deselect all (11 items)').click(); + reposList.forEach((repo) => { + cy.contains('[aria-label="Label group category"]', repo).should( + 'not.exist', + ); + }); + + // select page + cy.get('.toolbar [aria-label="Select"] svg').click(); + cy.contains('a', 'Select page (10 items)').click(); + reposList.forEach((repo) => { + if (repo != 'repo9') { + cy.contains('[aria-label="Label group category"]', repo); + } else { + cy.contains('[aria-label="Label group category"]', repo).should( + 'not.exist', + ); + } + }); + + // select repo9 + toggleItem('repo9'); + cy.contains('Clear all filters').click(); + + // deselect page and repo9 should remain here + cy.get('.toolbar [aria-label="Select"] svg').click(); + cy.contains('a', 'Deselect page (10 items)').click(); + reposList.forEach((repo) => { + if (repo != 'repo9') { + cy.contains('[aria-label="Label group category"]', repo).should( + 'not.exist', + ); + } else { + cy.contains('[aria-label="Label group category"]', repo); + } + }); + }); + + it('should test selection.', () => { + openModal(); + toggleItem('repo1'); + toggleItem('published'); + cy.contains('[aria-label="Label group category"]', 'repo1'); + cy.contains('[aria-label="Label group category"]', 'published'); + + toggleItem('published'); + cy.contains('[aria-label="Label group category"]', 'published').should( + 'not.exist', + ); + + toggleItem('published'); + cy.get('[aria-label="Close published"]').click(); + cy.contains('[aria-label="Label group category"]', 'published').should( + 'not.exist', + ); + }); +});