From 7a1f6efb00336fc2ba5cc6c5aa2848dc58269332 Mon Sep 17 00:00:00 2001 From: Alexandre Alves Date: Mon, 26 Aug 2024 16:09:56 +0100 Subject: [PATCH] neuvector e2e tests now working --- .../extensions-compatibility-tests.yaml | 1 + .../extensions/neuvector/neuvector.utils.ts | 17 +- .../extensions/neuvector/neuvector.spec.ts | 280 ++++++------------ 3 files changed, 90 insertions(+), 208 deletions(-) diff --git a/.github/workflows/extensions-compatibility-tests.yaml b/.github/workflows/extensions-compatibility-tests.yaml index 0c600c779ef..e532dc9bf16 100644 --- a/.github/workflows/extensions-compatibility-tests.yaml +++ b/.github/workflows/extensions-compatibility-tests.yaml @@ -28,6 +28,7 @@ jobs: features: [ ['@elemental', 'elemental'], ['@kubewarden', 'kubewarden'], + ['@neuvector', 'neuvector'], ] rancherEnv: [ # ['2.10', 'v2.10-befdf11fc6753ed4a2322b28c5f3ebe7f47e4d96-head'], diff --git a/cypress/e2e/po/extensions/neuvector/neuvector.utils.ts b/cypress/e2e/po/extensions/neuvector/neuvector.utils.ts index f5df1ac76e3..17f9e8769a0 100644 --- a/cypress/e2e/po/extensions/neuvector/neuvector.utils.ts +++ b/cypress/e2e/po/extensions/neuvector/neuvector.utils.ts @@ -1,7 +1,4 @@ import ExtensionsCompatibilityUtils from '~/cypress/e2e/po/extensions/extensions-compatibility.utils'; -import BannersPo from '@/cypress/e2e/po/components/banners.po'; -import LabeledSelectPo from '@/cypress/e2e/po/components/labeled-select.po'; -import RadioGroupInputPo from '@/cypress/e2e/po/components/radio-group-input.po'; import PagePo from '@/cypress/e2e/po/pages/page.po'; class NeuvectorDashboardPagePo extends PagePo { @@ -19,7 +16,7 @@ class NeuvectorDashboardPagePo extends PagePo { } waitForTitleAfterControllerInstall(): Cypress.Chainable { - return this.self().find('[data-testid="kw-dashboard-title"]').should('contain', 'Welcome to Kubewarden'); + return this.self().find('[data-testid="nv-dashboard-title"]').should('contain', 'Dashboard'); } controllerInstallBtnClick(): Cypress.Chainable { @@ -31,16 +28,4 @@ export default class NeuvectorPo extends ExtensionsCompatibilityUtils { dashboard() { return new NeuvectorDashboardPagePo(); } - - // elementalClusterSelectorTemplateBanner() { - // return new BannersPo('[provider="machineinventoryselectortemplate"] .banner.warning'); - // } - - // updateGroupTargetClustersSelect() { - // return new LabeledSelectPo('[data-testid="cluster-target"]'); - // } - - // updateGroupImageOption() { - // return new RadioGroupInputPo('[data-testid="upgrade-choice-selector"]'); - // } } diff --git a/cypress/e2e/tests/extensions/neuvector/neuvector.spec.ts b/cypress/e2e/tests/extensions/neuvector/neuvector.spec.ts index 219822f8b36..c98afeaf85f 100644 --- a/cypress/e2e/tests/extensions/neuvector/neuvector.spec.ts +++ b/cypress/e2e/tests/extensions/neuvector/neuvector.spec.ts @@ -1,13 +1,10 @@ import ExtensionsPagePo from '@/cypress/e2e/po/pages/extensions.po'; import NeuvectorPo from '~/cypress/e2e/po/extensions/neuvector/neuvector.utils'; import { NamespaceFilterPo } from '@/cypress/e2e/po/components/namespace-filter.po'; -import * as jsyaml from 'js-yaml'; -import LabeledInputPo from '@/cypress/e2e/po/components/labeled-input.po'; import { MEDIUM_TIMEOUT_OPT } from '~/cypress/support/utils/timeouts'; const EXTENSION_NAME = 'neuvector-ui-ext'; const EXTENSION_TITLE = 'NeuVector UI Extension'; -const EXTENSION_VERSION = '1.0.0'; const EXTENSION_REPO = 'https://github.com/neuvector/manager-ext'; const EXTENSION_BRANCH = 'gh-pages'; const EXTENSION_CLUSTER_REPO_NAME = 'neuvector-ui-extension'; @@ -21,40 +18,39 @@ describe('Extensions Compatibility spec', { tags: ['@neuvector', '@adminUser'] } cy.login(); }); - // it('add extension repository', () => { - // // // This should be in a `before` however is flaky. Move it to an `it` to let cypress retry - // const extensionsPo = new ExtensionsPagePo(); + it('add extension repository', () => { + // // This should be in a `before` however is flaky. Move it to an `it` to let cypress retry + const extensionsPo = new ExtensionsPagePo(); - // extensionsPo.addExtensionsRepositoryDirectLink(EXTENSION_REPO, EXTENSION_BRANCH, EXTENSION_CLUSTER_REPO_NAME, true); - // // let's wait a bit so that the repo is available in extensions screen - // cy.wait(10000); // eslint-disable-line cypress/no-unnecessary-waiting - // }); + extensionsPo.addExtensionsRepositoryDirectLink(EXTENSION_REPO, EXTENSION_BRANCH, EXTENSION_CLUSTER_REPO_NAME, true); + // let's wait a bit so that the repo is available in extensions screen + cy.wait(10000); // eslint-disable-line cypress/no-unnecessary-waiting + }); - // it('Should install an extension', () => { - // const extensionsPo = new ExtensionsPagePo(); + it('Should install an extension', () => { + const extensionsPo = new ExtensionsPagePo(); - // extensionsPo.goTo(); + extensionsPo.goTo(); - // extensionsPo.extensionTabAvailableClick(); + extensionsPo.extensionTabAvailableClick(); - // // click on install button on card - // extensionsPo.extensionCardInstallClick(EXTENSION_NAME); - // extensionsPo.extensionInstallModal().should('be.visible'); + // click on install button on card + extensionsPo.extensionCardInstallClick(EXTENSION_NAME); + extensionsPo.extensionInstallModal().should('be.visible'); - // // select version and click install - // // extensionsPo.installModalSelectVersionLabel(EXTENSION_VERSION); There's only one version available... - // extensionsPo.installModalInstallClick(); + // There's only one version available... we can just click install + extensionsPo.installModalInstallClick(); - // // let's check the extension reload banner and reload the page - // extensionsPo.extensionReloadBanner().should('be.visible', MEDIUM_TIMEOUT_OPT); - // extensionsPo.extensionReloadClick(); + // let's check the extension reload banner and reload the page + extensionsPo.extensionReloadBanner().should('be.visible', MEDIUM_TIMEOUT_OPT); + extensionsPo.extensionReloadClick(); - // // make sure extension card is in the installed tab - // extensionsPo.extensionTabInstalledClick(); - // extensionsPo.extensionCardClick(EXTENSION_NAME); - // extensionsPo.extensionDetailsTitle().should('contain', EXTENSION_TITLE); - // extensionsPo.extensionDetailsCloseClick(); - // }); + // make sure extension card is in the installed tab + extensionsPo.extensionTabInstalledClick(); + extensionsPo.extensionCardClick(EXTENSION_NAME); + extensionsPo.extensionDetailsTitle().should('contain', EXTENSION_TITLE); + extensionsPo.extensionDetailsCloseClick(); + }); it('Should setup all of the needed backend parts', () => { cy.intercept('POST', 'v1/catalog.cattle.io.clusterrepos/rancher-charts?action=install').as(EXTENSION_CHART_CREATION); @@ -70,169 +66,69 @@ describe('Extensions Compatibility spec', { tags: ['@neuvector', '@adminUser'] } neuvectorPo.chartInstallPage().nextPage(); neuvectorPo.chartInstallPage().installChart(); - neuvectorPo.appsPage().waitForInstallCloseTerminal(EXTENSION_CHART_CREATION, ['neuvector-crd', 'neuvector']); + + // with the install of the neuvector controller, all pods are up, but the extension access depends on the service proxy + // being up as well and that takes some time that we don't really know how much... + // the extension does an "auth" request https://github.com/neuvector/manager-ext/blame/main/pkg/neuvector-ui-ext/plugins/dashboard-class.js#L5-L15 to assertain this + // otherwise it throws an error: "Auth error" or "Authentication error" + const maxPollingRetries = 18; + let pollingCounter = 0; + + function pollingAuthControlRequest() { + cy + .request({ + url: 'k8s/clusters/local/api/v1/namespaces/cattle-neuvector-system/services/https:neuvector-service-webui:8443/proxy/auth', + method: 'POST', + failOnStatusCode: false, + body: { + username: '', + password: '', + isRancherSSOUrl: true + } + }) + .then((resp) => { + pollingCounter++; + + if (resp.status === 200 || pollingCounter === maxPollingRetries) { + return; + } + + if (pollingCounter === maxPollingRetries) { + throw new Error('schemaDefinition polling failed'); + } + + // let's wait for a bit so that we don't overload the server + // with requests + cy.wait(10000); // eslint-disable-line cypress/no-unnecessary-waiting + pollingAuthControlRequest(); + }); + } + + neuvectorPo.appsPage().waitForInstallCloseTerminal(EXTENSION_CHART_CREATION, ['neuvector-crd', 'neuvector']).then(pollingAuthControlRequest); + + neuvectorPo.dashboard().goTo(); + neuvectorPo.dashboard().waitForTitleAfterControllerInstall(); }); - // it('Should setup all of the needed backend parts', () => { - // cy.intercept('POST', 'v1/catalog.cattle.io.clusterrepos/rancher-charts?action=install').as(EXTENSION_CHART_CREATION); - - // elementalPo.dashboard().goTo(); - // elementalPo.dashboard().waitForTitle(); - - // elementalPo.dashboard().installOperator(); - // elementalPo.chartInstallPage().waitForChartPage('rancher-charts', 'elemental'); - - // // we need to change the namespace picker in order for the install check on the list view - // namespaceFilter.toggle(); - // namespaceFilter.clickOptionByLabel('All Namespaces'); - // namespaceFilter.closeDropdown(); - - // elementalPo.chartInstallPage().nextPage(); - // elementalPo.chartInstallPage().installChart(); - // elementalPo.appsPage().waitForInstallCloseTerminal(EXTENSION_CHART_CREATION, ['elemental-operator-crds', 'elemental-operator']); - - // elementalPo.dashboard().goTo(); - // cy.get('[data-testid="elemental-main-title"]').invoke('text').should('contain', 'OS Management Dashboard'); - // }); - - // it('Should create an Elemental registration endpoint', () => { - // cy.intercept('POST', 'v1/elemental.cattle.io.machineregistrations/fleet-default').as('machineRegCreation'); - - // elementalPo.dashboard().goTo(); - // elementalPo.dashboard().productNav().navToSideMenuEntryByLabel('Registration Endpoints'); - - // elementalPo.genericResourceList().masthead().create(); - // elementalPo.genericNameNsDescription().name().set(REG_ENDPOINT_NAME); - // elementalPo.genericCodeMirror().value() - // .then((val) => { - // // convert yaml into json to update values - // const json: any = jsyaml.load(val); - - // json.config.elemental.install.device = REG_ENDPOINT_DEVICE_PATH; - - // elementalPo.genericCodeMirror().set(jsyaml.dump(json)); - // elementalPo.genericResourceDetail().cruResource().saveOrCreate().click(); - - // cy.wait('@machineRegCreation', { requestTimeout: 15000 }).then(({ response }) => { - // expect(response?.statusCode).to.eq(201); - // expect(response?.body.metadata).to.have.property('name', REG_ENDPOINT_NAME); - // expect(response?.body.spec.config.elemental.install).to.have.property('device', REG_ENDPOINT_DEVICE_PATH); - // }); - // }); - // }); - - // it('Should create an Elemental resource via YAML (Inventory of Machines)', () => { - // const maxPollingRetries = 36; - // let pollingCounter = 0; - - // function pollingSchemaDefinition() { - // cy - // .request({ - // url: 'v1/schemaDefinitions/elemental.cattle.io.machineinventory', - // method: 'GET', - // failOnStatusCode: false - // }) - // .then((resp) => { - // pollingCounter++; - - // if (resp.status === 200 || pollingCounter === maxPollingRetries) { - // return; - // } - - // if (pollingCounter === maxPollingRetries) { - // throw new Error('schemaDefinition polling failed'); - // } - - // // let's wait for a bit so that we don't overload the server - // // with requests - // cy.wait(5000); // eslint-disable-line cypress/no-unnecessary-waiting - // pollingSchemaDefinition(); - // }); - // } - - // elementalPo.dashboard().goTo(); - // elementalPo.dashboard().productNav().navToSideMenuEntryByLabel('Inventory of Machines'); - - // // after we hit create from YAML we need to pool for the schemaDefinition since - // // that takes while to be available https://docs.cypress.io/api/commands/request#Request-Polling - // elementalPo.genericResourceList().masthead().createYaml().then(pollingSchemaDefinition); - - // elementalPo.genericResourceDetail().resourceYaml().codeMirror().value() - // .then((val) => { - // // convert yaml into json to update values - // const json: any = jsyaml.load(val); - - // json.metadata.name = MACHINE_INV_NAME; - - // elementalPo.genericResourceDetail().resourceYaml().codeMirror().set(jsyaml.dump(json)); - // elementalPo.genericResourceDetail().resourceYaml().saveOrCreate().click(); - - // elementalPo.genericPage('/elemental/c/_/elemental.cattle.io.machineinventory').waitForPage(); - // elementalPo.genericResourceList().resourceTable().sortableTable().rowWithName(MACHINE_INV_NAME) - // .column(2) - // .should('contain', MACHINE_INV_NAME); - // }); - // }); - - // it('Should create an Elemental cluster, targeting all of the inventory of machines', () => { - // cy.intercept('POST', 'v1/provisioning.cattle.io.clusters').as('elementalClusterCreation'); - - // elementalPo.dashboard().goTo(); - // elementalPo.dashboard().createElementalCluster(); - - // elementalPo.genericNameNsDescription().name().set(ELEMENTAL_CLUSTER_NAME); - // elementalPo.elementalClusterSelectorTemplateBanner().banner().contains(ELEMENTAL_CLUSTER_BANNER_TEXT); - // // hit create button - // cy.get('[data-testid="rke2-custom-create-save"]').click(); - - // cy.wait('@elementalClusterCreation', { requestTimeout: 15000 }).then(({ response }) => { - // expect(response?.statusCode).to.eq(201); - // expect(response?.body.metadata).to.have.property('name', ELEMENTAL_CLUSTER_NAME); - // expect(response?.body.spec.rkeConfig.machinePools[0].machineConfigRef).to.have.property('kind', ELEMENTAL_CLUSTER_MACHINE_CONFIG_REF); - // }); - // }); - - // it('Should create an Upgrade Group', () => { - // cy.intercept('POST', 'v1/elemental.cattle.io.managedosimages').as('elementalUpdateGroupCreation'); - - // elementalPo.dashboard().goTo(); - // elementalPo.dashboard().createUpdateGroupClick(); - - // elementalPo.genericNameNsDescription().name().set(UPDATE_GROUP_NAME); - // elementalPo.updateGroupTargetClustersSelect().toggle(); - // elementalPo.updateGroupTargetClustersSelect().clickOptionWithLabel(ELEMENTAL_CLUSTER_NAME); - // elementalPo.updateGroupImageOption().set(1); - - // LabeledInputPo.byLabel(cy.get('.dashboard-root'), 'Image path').set(UPDATE_GROUP_IMAGE_PATH); - // elementalPo.genericResourceDetail().cruResource().saveOrCreate().click(); - - // cy.wait('@elementalUpdateGroupCreation', { requestTimeout: 15000 }).then(({ response }) => { - // expect(response?.statusCode).to.eq(201); - // expect(response?.body.metadata).to.have.property('name', UPDATE_GROUP_NAME); - // expect(response?.body.spec.clusterTargets[0]).to.have.property('clusterName', ELEMENTAL_CLUSTER_NAME); - // expect(response?.body.spec).to.have.property('osImage', UPDATE_GROUP_IMAGE_PATH); - // }); - // }); - - // it('Should uninstall the extension', () => { - // const extensionsPo = new ExtensionsPagePo(); - - // extensionsPo.goTo(); - // extensionsPo.extensionTabInstalledClick(); - - // // click on uninstall button on card - // extensionsPo.extensionCardUninstallClick(EXTENSION_NAME); - // extensionsPo.extensionUninstallModal().should('be.visible'); - // extensionsPo.uninstallModaluninstallClick(); - // extensionsPo.extensionReloadBanner().should('be.visible'); - - // // let's check the extension reload banner and reload the page - // extensionsPo.extensionReloadBanner().should('be.visible'); - // extensionsPo.extensionReloadClick(); - -// // make sure extension card is in the available tab -// extensionsPo.extensionTabAvailableClick(); -// extensionsPo.extensionCardClick(EXTENSION_NAME); -// extensionsPo.extensionDetailsTitle().should('contain', EXTENSION_NAME); -// }); + it('Should uninstall the extension', () => { + const extensionsPo = new ExtensionsPagePo(); + + extensionsPo.goTo(); + extensionsPo.extensionTabInstalledClick(); + + // click on uninstall button on card + extensionsPo.extensionCardUninstallClick(EXTENSION_NAME); + extensionsPo.extensionUninstallModal().should('be.visible'); + extensionsPo.uninstallModaluninstallClick(); + extensionsPo.extensionReloadBanner().should('be.visible'); + + // let's check the extension reload banner and reload the page + extensionsPo.extensionReloadBanner().should('be.visible'); + extensionsPo.extensionReloadClick(); + + // make sure extension card is in the available tab + extensionsPo.extensionTabAvailableClick(); + extensionsPo.extensionCardClick(EXTENSION_NAME); + extensionsPo.extensionDetailsTitle().should('contain', EXTENSION_NAME); + }); });