From b7dc2ae37a99524d7924d3746de9672a57f118f0 Mon Sep 17 00:00:00 2001 From: Yonas Berhe Date: Wed, 10 Apr 2024 08:58:09 -0700 Subject: [PATCH] fix failing/flaky tests --- .../blueprints/user_preferences/group_by.ts | 11 ++++++ .../pages/explorer/namespace-picker.spec.ts | 32 ++++++++-------- .../tests/pages/explorer/node-list.spec.ts | 23 ++++++----- cypress/e2e/tests/pages/fleet/gitrepo.spec.ts | 1 + cypress/e2e/tests/pages/generic/home.spec.ts | 38 +++++++++---------- .../manager/pod-security-admissions.spec.ts | 5 +++ .../tests/pages/manager/repositories.spec.ts | 6 +++ cypress/globals.d.ts | 2 +- 8 files changed, 73 insertions(+), 45 deletions(-) create mode 100644 cypress/e2e/blueprints/user_preferences/group_by.ts diff --git a/cypress/e2e/blueprints/user_preferences/group_by.ts b/cypress/e2e/blueprints/user_preferences/group_by.ts new file mode 100644 index 0000000000..ca9da072eb --- /dev/null +++ b/cypress/e2e/blueprints/user_preferences/group_by.ts @@ -0,0 +1,11 @@ +export function groupByPayload(userId: string, clusterName: string, groupBy:string, namespace: string):object { + return { + id: userId, + type: 'userpreference', + data: { + cluster: clusterName, + 'group-by': groupBy, + 'ns-by-cluster': namespace, + } + }; +} diff --git a/cypress/e2e/tests/pages/explorer/namespace-picker.spec.ts b/cypress/e2e/tests/pages/explorer/namespace-picker.spec.ts index d45e96b483..cce23e6d0b 100644 --- a/cypress/e2e/tests/pages/explorer/namespace-picker.spec.ts +++ b/cypress/e2e/tests/pages/explorer/namespace-picker.spec.ts @@ -2,6 +2,7 @@ import ClusterDashboardPagePo from '@/cypress/e2e/po/pages/explorer/cluster-dash import { NamespaceFilterPo } from '@/cypress/e2e/po/components/namespace-filter.po'; import { WorkloadsPodsListPagePo } from '@/cypress/e2e/po/pages/explorer/workloads-pods.po'; import HomePagePo from '@/cypress/e2e/po/pages/home.po'; +import { groupByPayload } from '@/cypress/e2e/blueprints/user_preferences/group_by'; const namespacePicker = new NamespaceFilterPo(); @@ -21,9 +22,16 @@ describe('Namespace picker', { testIsolation: 'off' }, () => { namespacePicker.closeDropdown(); }); - it('can filter workloads by project/namespace from the picker dropdown', { tags: ['@adminUser'] }, () => { + it('can filter workloads by project/namespace from the picker dropdown', { tags: ['@explorer', '@adminUser'] }, () => { // Verify 'Namespace: cattle-fleet-local-system' appears once when filtering by Namespace - // Vrify multiple namespaces within Project: System display when filtering by Project + // Verify multiple namespaces within Project: System display when filtering by Project + + // group workloads by namespace + cy.getRancherResource('v3', 'users?me=true').then((resp: Cypress.Response) => { + const userId = resp.body.data[0].id.trim(); + + cy.setRancherResource('v1', 'userpreferences', userId, groupByPayload(userId, 'local', 'metadata.namespace', '{"local":["all://user"]}')); + }); const workloadsPodPage = new WorkloadsPodsListPagePo('local'); @@ -54,7 +62,7 @@ describe('Namespace picker', { testIsolation: 'off' }, () => { workloadsPodPage.sortableTable().groupElementWithName('cattle-fleet-local-system').scrollIntoView().should('be.visible'); }); - it('can select only one of the top 5 resource filters at a time', { tags: ['@adminUser', '@standardUser'] }, () => { + it('can select only one of the top 5 resource filters at a time', { tags: ['@explorer', '@adminUser', '@standardUser'] }, () => { // Verify that user can only select one of the first 5 options namespacePicker.toggle(); @@ -85,7 +93,7 @@ describe('Namespace picker', { testIsolation: 'off' }, () => { namespacePicker.checkIcon().should('have.length', 1); }); - it('can select multiple projects/namespaces', { tags: ['@adminUser'] }, () => { + it('can select multiple projects/namespaces', { tags: ['@explorer', '@adminUser'] }, () => { // Verify that user can select multiple options (other than the first 5 options) namespacePicker.toggle(); @@ -119,7 +127,7 @@ describe('Namespace picker', { testIsolation: 'off' }, () => { namespacePicker.moreOptionsSelected().should('have.class', 'has-tooltip'); }); - it('can deselect options', { tags: ['@adminUser', '@standardUser'] }, () => { + it('can deselect options', { tags: ['@explorer', '@adminUser', '@standardUser'] }, () => { namespacePicker.toggle(); // Select 'default' option @@ -146,7 +154,7 @@ describe('Namespace picker', { testIsolation: 'off' }, () => { namespacePicker.checkIcon().should('have.length', 1); }); - it('can filter options by name', { tags: ['@adminUser', '@standardUser'] }, () => { + it('can filter options by name', { tags: ['@explorer', '@adminUser', '@standardUser'] }, () => { namespacePicker.toggle(); // filter 'cattle-fleet' @@ -167,7 +175,7 @@ describe('Namespace picker', { testIsolation: 'off' }, () => { namespacePicker.checkIcon().should('have.length', 1); }); - it('newly created project/namespace appears in namespace picker', { tags: ['@adminUser'] }, () => { + it('newly created project/namespace appears in namespace picker', { tags: ['@explorer', '@adminUser'] }, () => { const projName = `project${ +new Date() }`; const nsName = `namespace${ +new Date() }`; @@ -205,15 +213,7 @@ describe('Namespace picker', { testIsolation: 'off' }, () => { cy.getRancherResource('v3', 'users?me=true').then((resp: Cypress.Response) => { const userId = resp.body.data[0].id.trim(); - cy.setRancherResource('v1', 'userpreferences', userId, { - id: userId, - type: 'userpreference', - data: { - cluster: 'local', - 'group-by': 'none', - 'ns-by-cluster': '{"local":["all://user"]}', - } - }); + cy.setRancherResource('v1', 'userpreferences', userId, groupByPayload(userId, 'local', 'none', '{"local":["all://user"]}')); }); }); }); diff --git a/cypress/e2e/tests/pages/explorer/node-list.spec.ts b/cypress/e2e/tests/pages/explorer/node-list.spec.ts index f613873007..624fd400e3 100644 --- a/cypress/e2e/tests/pages/explorer/node-list.spec.ts +++ b/cypress/e2e/tests/pages/explorer/node-list.spec.ts @@ -19,6 +19,10 @@ describe('Nodes list', { tags: ['@explorer', '@adminUser'], testIsolation: 'off' }); it('should show the nodes list page', () => { + cy.getRancherResource('v1', 'nodes').then((resp: Cypress.Response) => { + cy.wrap(resp.body.count).as('count'); + }); + ClusterDashboardPagePo.navTo(); const nav = new ProductNavPo(); @@ -35,16 +39,17 @@ describe('Nodes list', { tags: ['@explorer', '@adminUser'], testIsolation: 'off' nodeList.sortableTable().checkLoadingIndicatorNotVisible(); // Check table has 2 tows - nodeList.sortableTable().rowElements({ timeout: 2500 }).should((rows: any) => { - expect(rows).not.to.equal(undefined); - expect(rows).to.have.length(2); - }); + cy.get('@count').then((count) => { + nodeList.sortableTable().rowElements({ timeout: 2500 }).should((rows: any) => { + expect(rows).not.to.equal(undefined); + expect(rows).to.have.length(count); + }); - // Check the node names - nodeList.sortableTable().rowNames().should((names: any) => { - expect(names).to.have.length(2); - // expect(names).to.contain('local-node'); - expect(names).to.contain('bigip1'); + // Check the node names + nodeList.sortableTable().rowNames().should((names: any) => { + expect(names).to.have.length(count); + expect(names).to.contain('bigip1'); + }); }); // Simple test to assert we haven't broken Node detail page diff --git a/cypress/e2e/tests/pages/fleet/gitrepo.spec.ts b/cypress/e2e/tests/pages/fleet/gitrepo.spec.ts index 1d7688918b..b7f516d944 100644 --- a/cypress/e2e/tests/pages/fleet/gitrepo.spec.ts +++ b/cypress/e2e/tests/pages/fleet/gitrepo.spec.ts @@ -25,6 +25,7 @@ describe('Git Repo', { tags: ['@fleet', '@adminUser'] }, () => { cy.intercept('POST', '/v1/fleet.cattle.io.gitrepos').as('interceptGitRepo'); gitRepoCreatePage.goTo(); + gitRepoCreatePage.waitForPage(); const { name } = gitRepoCreateRequest.metadata; const { diff --git a/cypress/e2e/tests/pages/generic/home.spec.ts b/cypress/e2e/tests/pages/generic/home.spec.ts index 53b80dc7eb..d366054b50 100644 --- a/cypress/e2e/tests/pages/generic/home.spec.ts +++ b/cypress/e2e/tests/pages/generic/home.spec.ts @@ -27,23 +27,8 @@ describe('Home Page', () => { describe('Home Page', { testIsolation: 'off' }, () => { before(() => { - // since I wasn't able to fully mock a list of clusters - // the next best thing is to add a description to the current local cluster - // testing https://github.com/rancher/dashboard/issues/10441 - cy.intercept('GET', `/v1/provisioning.cattle.io.clusters?*`, (req) => { - req.continue((res) => { - const localIndex = res.body.data.findIndex((item) => item.id.includes('/local')); - - if (localIndex >= 0) { - res.body.data[localIndex].metadata.annotations['field.cattle.io/description'] = longClusterDescription; - } - - res.send(res.body); - }); - }).as('provClusters'); - cy.login(); - HomePagePo.goToAndWaitForGet(); + HomePagePo.goTo(); }); it('Can navigate to release notes page for latest Rancher version', { tags: ['@generic', '@adminUser', '@standardUser'] }, () => { @@ -172,7 +157,7 @@ describe('Home Page', () => { genericCreateClusterPage.waitForPage(); }); - it('Can filter rows in the cluster list', { tags: ['@adminUser'] }, () => { + it('Can filter rows in the cluster list', { tags: ['@generic', '@adminUser'] }, () => { /** * Filter rows in the cluster list */ @@ -188,8 +173,23 @@ describe('Home Page', () => { }); }); - it('Should show cluster description information in the cluster list', { tags: ['@adminUser'] }, () => { - HomePagePo.navTo(); + it('Should show cluster description information in the cluster list', { tags: ['@generic', '@adminUser'] }, () => { + // since I wasn't able to fully mock a list of clusters + // the next best thing is to add a description to the current local cluster + // testing https://github.com/rancher/dashboard/issues/10441 + cy.intercept('GET', `/v1/provisioning.cattle.io.clusters?*`, (req) => { + req.continue((res) => { + const localIndex = res.body.data.findIndex((item) => item.id.includes('/local')); + + if (localIndex >= 0) { + res.body.data[localIndex].metadata.annotations['field.cattle.io/description'] = longClusterDescription; + } + + res.send(res.body); + }); + }).as('provClusters'); + + homePage.goTo(); const desc = homeClusterList.resourceTable().sortableTable().rowWithName('local').column(1) .get('.cluster-description'); diff --git a/cypress/e2e/tests/pages/manager/pod-security-admissions.spec.ts b/cypress/e2e/tests/pages/manager/pod-security-admissions.spec.ts index 42b480d987..ea959b0018 100644 --- a/cypress/e2e/tests/pages/manager/pod-security-admissions.spec.ts +++ b/cypress/e2e/tests/pages/manager/pod-security-admissions.spec.ts @@ -53,6 +53,7 @@ describe('Pod Security Admissions', { testIsolation: 'off', tags: ['@manager', ' it('can edit a policy security admission', function() { PodSecurityAdmissionsPagePo.navTo(); + podSecurityAdmissionsPage.waitForPage(); podSecurityAdmissionsPage.list().actionMenu(this.podSecurityAdmissionsName).getMenuItem('Edit Config').click(); podSecurityAdmissionsPage.createPodSecurityAdmissionForm(this.podSecurityAdmissionsName).waitForPage('mode=edit'); podSecurityAdmissionsPage.createPodSecurityAdmissionForm().nameNsDescription().description().set(`${ this.podSecurityAdmissionsName }-description-edit`); @@ -80,6 +81,7 @@ describe('Pod Security Admissions', { testIsolation: 'off', tags: ['@manager', ' it('can clone a policy security admission', function() { PodSecurityAdmissionsPagePo.navTo(); + podSecurityAdmissionsPage.waitForPage(); podSecurityAdmissionsPage.list().actionMenu(this.podSecurityAdmissionsName).getMenuItem('Clone').click(); podSecurityAdmissionsPage.createPodSecurityAdmissionForm(this.podSecurityAdmissionsName).waitForPage('mode=clone'); podSecurityAdmissionsPage.createPodSecurityAdmissionForm().nameNsDescription().name().set(`${ this.podSecurityAdmissionsName }-clone`); @@ -92,6 +94,7 @@ describe('Pod Security Admissions', { testIsolation: 'off', tags: ['@manager', ' it('can download YAML for a policy security admission', function() { PodSecurityAdmissionsPagePo.navTo(); + podSecurityAdmissionsPage.waitForPage(); podSecurityAdmissionsPage.list().actionMenu(this.podSecurityAdmissionsName).getMenuItem('Download YAML').click({ force: true }); const downloadedFilename = path.join(downloadsFolder, `${ this.podSecurityAdmissionsName }.yaml`); @@ -108,6 +111,7 @@ describe('Pod Security Admissions', { testIsolation: 'off', tags: ['@manager', ' it('can delete a policy security admission', function() { PodSecurityAdmissionsPagePo.navTo(); + podSecurityAdmissionsPage.waitForPage(); podSecurityAdmissionsPage.list().actionMenu(`${ this.podSecurityAdmissionsName }-clone`).getMenuItem('Delete').click(); const promptRemove = new PromptRemove(); @@ -124,6 +128,7 @@ describe('Pod Security Admissions', { testIsolation: 'off', tags: ['@manager', ' it('can delete a policy security admission via bulk actions', function() { PodSecurityAdmissionsPagePo.navTo(); + podSecurityAdmissionsPage.waitForPage(); podSecurityAdmissionsPage.list().details(this.podSecurityAdmissionsName, 0).click(); podSecurityAdmissionsPage.list().resourceTable().sortableTable().deleteButton() .click(); diff --git a/cypress/e2e/tests/pages/manager/repositories.spec.ts b/cypress/e2e/tests/pages/manager/repositories.spec.ts index a480e6bb42..9c3abfd812 100644 --- a/cypress/e2e/tests/pages/manager/repositories.spec.ts +++ b/cypress/e2e/tests/pages/manager/repositories.spec.ts @@ -37,6 +37,7 @@ describe('Cluster Management Helm Repositories', { testIsolation: 'off', tags: [ it('can refresh a repository', function() { ChartRepositoriesPagePo.navTo(); + repositoriesPage.waitForPage(); cy.intercept('PUT', `/v1/catalog.cattle.io.clusterrepos/${ this.repoName }`).as('refreshRepo'); repositoriesPage.list().actionMenu(this.repoName).getMenuItem('Refresh').click(); cy.wait('@refreshRepo').its('response.statusCode').should('eq', 200); @@ -48,6 +49,7 @@ describe('Cluster Management Helm Repositories', { testIsolation: 'off', tags: [ it('can edit a repository', function() { ChartRepositoriesPagePo.navTo(); + repositoriesPage.waitForPage(); repositoriesPage.list().actionMenu(this.repoName).getMenuItem('Edit Config').click(); repositoriesPage.createEditRepositories(this.repoName).waitForPage('mode=edit'); repositoriesPage.createEditRepositories().nameNsDescription().description().set(`${ this.repoName }-desc-edit`); @@ -61,6 +63,7 @@ describe('Cluster Management Helm Repositories', { testIsolation: 'off', tags: [ it('can clone a repository', function() { ChartRepositoriesPagePo.navTo(); + repositoriesPage.waitForPage(); repositoriesPage.list().actionMenu(this.repoName).getMenuItem('Clone').click(); repositoriesPage.createEditRepositories(this.repoName).waitForPage('mode=clone'); repositoriesPage.createEditRepositories().nameNsDescription().name().set(`${ this.repoName }-clone`); @@ -74,6 +77,7 @@ describe('Cluster Management Helm Repositories', { testIsolation: 'off', tags: [ it('can download YAML', function() { ChartRepositoriesPagePo.navTo(); + repositoriesPage.waitForPage(); repositoriesPage.list().actionMenu(this.repoName).getMenuItem('Download YAML').click({ force: true }); const downloadedFilename = path.join(downloadsFolder, `${ this.repoName }.yaml`); @@ -90,6 +94,7 @@ describe('Cluster Management Helm Repositories', { testIsolation: 'off', tags: [ it('can delete a repository', function() { ChartRepositoriesPagePo.navTo(); + repositoriesPage.waitForPage(); // delete original cloned Repository repositoriesPage.list().actionMenu(`${ this.repoName }-clone`).getMenuItem('Delete').click(); @@ -108,6 +113,7 @@ describe('Cluster Management Helm Repositories', { testIsolation: 'off', tags: [ it('can delete a repository via bulk actions', function() { ChartRepositoriesPagePo.navTo(); + repositoriesPage.waitForPage(); // delete original Repository repositoriesPage.list().resourceTable().sortableTable().rowSelectCtlWithName(this.repoName) diff --git a/cypress/globals.d.ts b/cypress/globals.d.ts index 6804506020..2be3ee865a 100644 --- a/cypress/globals.d.ts +++ b/cypress/globals.d.ts @@ -42,7 +42,7 @@ declare global { createAwsCloudCredentials(nsName: string, cloudCredName: string, defaultRegion: string, accessKey: string, secretKey: string): Chainable; getRancherResource(prefix: 'v3' | 'v1', resourceType: string, resourceId?: string, expectedStatusCode?: number): Chainable; - setRancherResource(prefix: 'v3' | 'v1', resourceType: string, resourceId: string, body: string): Chainable; + setRancherResource(prefix: 'v3' | 'v1', resourceType: string, resourceId: string, body: any): Chainable; createRancherResource(prefix: 'v3' | 'v1', resourceType: string, body: string): Chainable; deleteRancherResource(prefix: 'v3' | 'v1', resourceType: string, resourceId: string, failOnStatusCode?: boolean): Chainable; deleteNodeTemplate(nodeTemplateId: string)