diff --git a/cypress/e2e/tests/pages/explorer2/storage/configmap.spec.ts b/cypress/e2e/tests/pages/explorer2/storage/configmap.spec.ts index 2caf33967a..ccfda261ec 100644 --- a/cypress/e2e/tests/pages/explorer2/storage/configmap.spec.ts +++ b/cypress/e2e/tests/pages/explorer2/storage/configmap.spec.ts @@ -1,10 +1,13 @@ import { ConfigMapPagePo } from '@/cypress/e2e/po/pages/explorer/config-map.po'; import ConfigMapPo from '@/cypress/e2e/po/components/storage/config-map.po'; +import SortableTablePo from '@/cypress/e2e/po/components/sortable-table.po'; +import ClusterDashboardPagePo from '@/cypress/e2e/po/pages/explorer/cluster-dashboard.po'; const configMapPage = new ConfigMapPagePo('local'); +const cluster = 'local'; describe('ConfigMap', { testIsolation: 'off', tags: ['@explorer2', '@adminUser'] }, () => { - beforeEach(() => { + before(() => { cy.login(); }); @@ -83,4 +86,236 @@ skipGeometric=true`; // Error banner should be displayed configMapPo.errorBanner().should('exist').and('be.visible'); }); + + describe('List', { tags: ['@vai', '@adminUser'] }, () => { + const uniqueConfigMap = SortableTablePo.firstByDefaultName('cm'); + const cmNamesList = []; + let nsName1: string; + let nsName2: string; + let rootResourceName: string; + + before(() => { + cy.login(); + }); + + before('set up', () => { + cy.getRootE2EResourceName().then((root) => { + rootResourceName = root; + }); + + cy.createE2EResourceName('ns1').then((ns1) => { + nsName1 = ns1; + // create namespace + cy.createNamespace(nsName1); + + // create configmaps + let i = 0; + + while (i < 25) { + const cmName = Cypress._.uniqueId(Date.now().toString()); + + cy.createConfigMap(nsName1, cmName).then((name) => { + cmNamesList.push(name); + }); + + i++; + } + + cy.createE2EResourceName('ns2').then((ns2) => { + nsName2 = ns2; + + // create namespace + cy.createNamespace(nsName2); + + cy.createConfigMap(nsName2, uniqueConfigMap).then((name) => { + cmNamesList.push(name); + }); + + cy.tableRowsPerPageAndNamespaceFilter(10, cluster, 'none', `{\"local\":[\"ns://${ nsName1 }\",\"ns://${ nsName2 }\"]}`); + }); + }); + }); + + it('pagination is visible and user is able to navigate through configmaps data', () => { + ClusterDashboardPagePo.goToAndConfirmNsValues(cluster, { nsProject: { values: [nsName1, nsName2] } }); + + configMapPage.goTo(); + configMapPage.waitForPage(); + + // check configmaps count + // A kube-root-ca.crt configmap per namespace in the formula has to be included + const count = cmNamesList.length + 2; + + cy.waitForRancherResources('v1', 'configmaps', count - 1, true).then((resp: Cypress.Response) => { + // pagination is visible + configMapPage.list().resourceTable().sortableTable().pagination() + .checkVisible(); + + // basic checks on navigation buttons + configMapPage.list().resourceTable().sortableTable().pagination() + .beginningButton() + .isDisabled(); + configMapPage.list().resourceTable().sortableTable().pagination() + .leftButton() + .isDisabled(); + configMapPage.list().resourceTable().sortableTable().pagination() + .rightButton() + .isEnabled(); + configMapPage.list().resourceTable().sortableTable().pagination() + .endButton() + .isEnabled(); + + // check text before navigation + configMapPage.list().resourceTable().sortableTable().pagination() + .paginationText() + .then((el) => { + expect(el.trim()).to.eq(`1 - 10 of ${ count } ConfigMaps`); + }); + + // navigate to next page - right button + configMapPage.list().resourceTable().sortableTable().pagination() + .rightButton() + .click(); + + // check text and buttons after navigation + configMapPage.list().resourceTable().sortableTable().pagination() + .paginationText() + .then((el) => { + expect(el.trim()).to.eq(`11 - 20 of ${ count } ConfigMaps`); + }); + configMapPage.list().resourceTable().sortableTable().pagination() + .beginningButton() + .isEnabled(); + configMapPage.list().resourceTable().sortableTable().pagination() + .leftButton() + .isEnabled(); + + // navigate to first page - left button + configMapPage.list().resourceTable().sortableTable().pagination() + .leftButton() + .click(); + + // check text and buttons after navigation + configMapPage.list().resourceTable().sortableTable().pagination() + .paginationText() + .then((el) => { + expect(el.trim()).to.eq(`1 - 10 of ${ count } ConfigMaps`); + }); + configMapPage.list().resourceTable().sortableTable().pagination() + .beginningButton() + .isDisabled(); + configMapPage.list().resourceTable().sortableTable().pagination() + .leftButton() + .isDisabled(); + + // navigate to last page - end button + configMapPage.list().resourceTable().sortableTable().pagination() + .endButton() + .scrollIntoView() + .click(); + + // row count on last page + let lastPageCount = count % 10; + + if (lastPageCount === 0) { + lastPageCount = 10; + } + + // check text after navigation + configMapPage.list().resourceTable().sortableTable().pagination() + .paginationText() + .then((el) => { + expect(el.trim()).to.eq(`${ count - (lastPageCount) + 1 } - ${ count } of ${ count } ConfigMaps`); + }); + + // navigate to first page - beginning button + configMapPage.list().resourceTable().sortableTable().pagination() + .beginningButton() + .click(); + + // check text and buttons after navigation + configMapPage.list().resourceTable().sortableTable().pagination() + .paginationText() + .then((el) => { + expect(el.trim()).to.eq(`1 - 10 of ${ count } ConfigMaps`); + }); + configMapPage.list().resourceTable().sortableTable().pagination() + .beginningButton() + .isDisabled(); + configMapPage.list().resourceTable().sortableTable().pagination() + .leftButton() + .isDisabled(); + }); + }); + + it('sorting changes the order of paginated configmaps data', () => { + configMapPage.goTo(); + configMapPage.waitForPage(); + // use filter to only show test data + configMapPage.list().resourceTable().sortableTable().filter(rootResourceName); + + // check table is sorted by name in ASC order by default + configMapPage.list().resourceTable().sortableTable().tableHeaderRow() + .checkSortOrder(1, 'down'); + + // ConfigMap name should be visible on first page (sorted in ASC order) + configMapPage.list().resourceTable().sortableTable().tableHeaderRow() + .self() + .scrollIntoView(); + configMapPage.list().resourceTable().sortableTable().rowElementWithName(cmNamesList[0]) + .scrollIntoView() + .should('be.visible'); + + // sort by name in DESC order + configMapPage.list().resourceTable().sortableTable().sort(1) + .click({ force: true }); + configMapPage.list().resourceTable().sortableTable().tableHeaderRow() + .checkSortOrder(1, 'up'); + + // ConfigMap name should be NOT visible on first page (sorted in DESC order) + configMapPage.list().resourceTable().sortableTable().rowElementWithName(cmNamesList[0]) + .should('not.exist'); + + // navigate to last page + configMapPage.list().resourceTable().sortableTable().pagination() + .endButton() + .scrollIntoView() + .click(); + + // ConfigMap name should be visible on last page (sorted in DESC order) + configMapPage.list().resourceTable().sortableTable().rowElementWithName(cmNamesList[0]) + .scrollIntoView() + .should('be.visible'); + }); + + it('filter configmaps', () => { + configMapPage.goTo(); + configMapPage.waitForPage(); + + configMapPage.list().resourceTable().sortableTable().checkVisible(); + configMapPage.list().resourceTable().sortableTable().checkLoadingIndicatorNotVisible(); + configMapPage.list().resourceTable().sortableTable().checkRowCount(false, 10); + + // filter by name + configMapPage.list().resourceTable().sortableTable().filter(cmNamesList[0]); + configMapPage.list().resourceTable().sortableTable().checkRowCount(false, 1); + configMapPage.list().resourceTable().sortableTable().rowElementWithName(cmNamesList[0]) + .should('be.visible'); + + // filter by namespace + configMapPage.list().resourceTable().sortableTable().filter(nsName2); + configMapPage.list().resourceTable().sortableTable().checkRowCount(false, 1); + configMapPage.list().resourceTable().sortableTable().rowElementWithName(uniqueConfigMap) + .should('be.visible'); + }); + + after('clean up', () => { + // Ensure the default rows per page value is set after running the tests + cy.tableRowsPerPageAndNamespaceFilter(100, cluster, 'none', '{"local":["all://user"]}'); + + // delete namespace (this will also delete all configmaps in it) + cy.deleteRancherResource('v1', 'namespaces', nsName1); + cy.deleteRancherResource('v1', 'namespaces', nsName2); + }); + }); }); diff --git a/cypress/globals.d.ts b/cypress/globals.d.ts index 0c8473cfdf..e92f32fd48 100644 --- a/cypress/globals.d.ts +++ b/cypress/globals.d.ts @@ -96,6 +96,7 @@ declare global { createAmazonRke2Cluster(params: CreateAmazonRke2ClusterParams): Chainable; createAmazonRke2ClusterWithoutMachineConfig(params: CreateAmazonRke2ClusterWithoutMachineConfigParams): Chainable; createSecret(namespace: string, name: string, options?: { type?: string; metadata?: any; data?: any }): Chainable; + createConfigMap(namespace: string, name: string, options?: { metadata?: any; data?: any }): Chainable; createService(namespace: string, name: string, options?: { type?: string; ports?: any[]; spec?: any; metadata?: any }): Chainable; getRancherResource(prefix: 'v3' | 'v1', resourceType: string, resourceId?: string, expectedStatusCode?: number): Chainable; diff --git a/cypress/support/commands/rancher-api-commands.ts b/cypress/support/commands/rancher-api-commands.ts index 4cbcb37904..fe976fb660 100644 --- a/cypress/support/commands/rancher-api-commands.ts +++ b/cypress/support/commands/rancher-api-commands.ts @@ -1139,6 +1139,26 @@ Cypress.Commands.add('createSecret', (namespace: string, name: string, options: }); }); +/** + * Create a configmap via api request + */ +Cypress.Commands.add('createConfigMap', (namespace: string, name: string, options: { metadata?: any; data?: any } = {}) => { + const defaultData = { foo: 'bar' }; + + const body = { + metadata: { + namespace, + name, + ...(options.metadata || {}) + }, + data: options.data || defaultData + }; + + return cy.createRancherResource('v1', 'configmaps', body).then((resp) => { + return resp.body.metadata.name; + }); +}); + /** * Create a service via api request */