From e8b195be402198cbb9cd0a2fbfaa273963aa75c9 Mon Sep 17 00:00:00 2001 From: Jonathan Kilzi Date: Fri, 21 Jul 2023 12:01:16 +0300 Subject: [PATCH] Fixes a flaky test (#2270) Fixes test-case: "Selecting external partner integrations checkbox enables custom manifests as well" Reporting: "AssertionError: Timed out retrying after 4000ms: expected '' to be 'checked'" --- libs/ui-lib-tests/cypress/@types/Cypress.d.ts | 6 + .../with-external-partner-integrations.cy.ts | 50 +++++---- libs/ui-lib-tests/cypress/support/commands.ts | 4 + .../ClusterDetails/ClusterDetailsForm.ts | 40 +++++++ .../ClusterDetails/Fields/BaseDomainField.ts | 15 +++ .../ClusterDetails/Fields/ClusterNameField.ts | 15 +++ .../Fields/CustomManifestsField.ts | 15 +++ .../ExternalPartnerIntegrationsField.ts | 44 ++++++++ .../Fields/HostsNetworkConfigurationField.ts | 27 +++++ .../Fields/OpenShiftVersionField.ts | 27 +++++ .../cypress/views/forms/ClusterDetailsForm.ts | 105 ------------------ .../cypress/views/pages/NewClusterPage.ts | 2 +- libs/ui-lib-tests/package.json | 4 +- 13 files changed, 222 insertions(+), 132 deletions(-) create mode 100644 libs/ui-lib-tests/cypress/views/forms/ClusterDetails/ClusterDetailsForm.ts create mode 100644 libs/ui-lib-tests/cypress/views/forms/ClusterDetails/Fields/BaseDomainField.ts create mode 100644 libs/ui-lib-tests/cypress/views/forms/ClusterDetails/Fields/ClusterNameField.ts create mode 100644 libs/ui-lib-tests/cypress/views/forms/ClusterDetails/Fields/CustomManifestsField.ts create mode 100644 libs/ui-lib-tests/cypress/views/forms/ClusterDetails/Fields/ExternalPartnerIntegrationsField.ts create mode 100644 libs/ui-lib-tests/cypress/views/forms/ClusterDetails/Fields/HostsNetworkConfigurationField.ts create mode 100644 libs/ui-lib-tests/cypress/views/forms/ClusterDetails/Fields/OpenShiftVersionField.ts delete mode 100644 libs/ui-lib-tests/cypress/views/forms/ClusterDetailsForm.ts diff --git a/libs/ui-lib-tests/cypress/@types/Cypress.d.ts b/libs/ui-lib-tests/cypress/@types/Cypress.d.ts index 34db0f231e..b5cc311809 100644 --- a/libs/ui-lib-tests/cypress/@types/Cypress.d.ts +++ b/libs/ui-lib-tests/cypress/@types/Cypress.d.ts @@ -1,5 +1,11 @@ declare namespace Cypress { interface Chainable { + /** + * If `ancestorAlias` is defined, executes `cy.get(ancestorAlias).find(childSelector)`. Otherwise, executes `cy.get(childSelector)`. + * @param childSelector The CSS selector + * @param ancestorAlias The alias of the child's ancestor + */ + findWithinOrGet(childSelector: string, ancestorAlias?: string): Chainable>; pasteText(selector: string, text: string): Chainable; newByDataTestId(selector: string, timeout?: number): Chainable>; hostDetailSelector(i: number, label: string, timeout?: number): Chainable; diff --git a/libs/ui-lib-tests/cypress/integration/use-cases/create-cluster/with-external-partner-integrations.cy.ts b/libs/ui-lib-tests/cypress/integration/use-cases/create-cluster/with-external-partner-integrations.cy.ts index 85beca633e..89177a0c1b 100644 --- a/libs/ui-lib-tests/cypress/integration/use-cases/create-cluster/with-external-partner-integrations.cy.ts +++ b/libs/ui-lib-tests/cypress/integration/use-cases/create-cluster/with-external-partner-integrations.cy.ts @@ -1,6 +1,5 @@ -import { clusterDetailsPage } from '../../../views/clusterDetails'; -import ClusterDetailsForm from '../../../views/forms/ClusterDetailsForm'; -import NewClusterPage from '../../../views/pages/NewClusterPage'; +import { NewClusterPage } from '../../../views/pages/NewClusterPage'; +import { ClusterDetailsForm } from '../../../views/forms/ClusterDetails/ClusterDetailsForm'; describe('Create a new cluster with external partner integrations', () => { const setTestStartSignal = (activeSignal: string) => { @@ -18,57 +17,60 @@ describe('Create a new cluster with external partner integrations', () => { // This test case is disabled intentionally because it requires tweaking the // props passed to the LibRouter in the app. it('The user cannot see the external partner integrations checkbox', () => { - // Disable somehow Features.STANDALONE_DEPLOYMENT_ENABLED_FEATURES.ASSISTED_INSTALLER_PLATFORM_OCI, then... - // ClusterDetailsForm.externalPartnerIntegrationsControl.findLabel().should('not.exist'); + // Disable somehow Features.STANDALONE_DEPLOYMENT_ENABLED_FEATURES.ASSISTED_INSTALLER_PLATFORM_OCI, and then... + // ClusterDetailsForm.init().externalPartnerIntegrationsControl.findLabel().should('not.exist'); }); }); context('When the feature is enabled:', () => { beforeEach(() => { NewClusterPage.visit(); + ClusterDetailsForm.init(); }); it('The user can select the external partner integrations checkbox', () => { - ClusterDetailsForm.externalPartnerIntegrationsControl.findLabel().click(); + ClusterDetailsForm.externalPartnerIntegrationsField.findLabel().click(); }); it('There is a popover and helper text next to the checkbox label', () => { - ClusterDetailsForm.externalPartnerIntegrationsControl.findPopoverButton().click(); - ClusterDetailsForm.externalPartnerIntegrationsControl.findPopoverContent(); - ClusterDetailsForm.externalPartnerIntegrationsControl.findHelperText(); + ClusterDetailsForm.externalPartnerIntegrationsField.findPopoverButton().click(); + ClusterDetailsForm.externalPartnerIntegrationsField.findPopoverContent(); + ClusterDetailsForm.externalPartnerIntegrationsField.findHelperText(); }); it('Selecting external partner integrations checkbox enables custom manifests as well', () => { - clusterDetailsPage.inputOpenshiftVersion('4.14'); - ClusterDetailsForm.externalPartnerIntegrationsControl.findLabel().click(); - ClusterDetailsForm.customManifestsControl + ClusterDetailsForm.openshiftVersionField.selectVersion('4.14'); + ClusterDetailsForm.externalPartnerIntegrationsField.findLabel().click(); + ClusterDetailsForm.customManifestsField .findCheckbox() .should('be.checked') .and('be.disabled'); }); it('External partner integrations checkbox is unselected after OCP < v4.14 is selected', () => { - clusterDetailsPage.inputOpenshiftVersion('4.14'); - ClusterDetailsForm.externalPartnerIntegrationsControl.findLabel().click(); - clusterDetailsPage.inputOpenshiftVersion('4.13'); - ClusterDetailsForm.externalPartnerIntegrationsControl.findCheckbox().should('not.be.checked'); + ClusterDetailsForm.openshiftVersionField.selectVersion('4.14'); + ClusterDetailsForm.externalPartnerIntegrationsField.findLabel().click(); + ClusterDetailsForm.openshiftVersionField.selectVersion('4.13'); + ClusterDetailsForm.externalPartnerIntegrationsField.findCheckbox().should('not.be.checked'); }); it("Hosts' Network Configuration control is disabled when external partner integration is selected", () => { - clusterDetailsPage.getStaticIpNetworkConfig().click(); - clusterDetailsPage.inputOpenshiftVersion('4.14'); - ClusterDetailsForm.externalPartnerIntegrationsControl.findLabel().click(); - clusterDetailsPage.getStaticIpNetworkConfig().should('be.disabled').and('not.be.checked'); + ClusterDetailsForm.hostsNetworkConfigurationField.findStaticIpRadioLabel().click(); + ClusterDetailsForm.openshiftVersionField.selectVersion('4.14'); + ClusterDetailsForm.externalPartnerIntegrationsField.findLabel().click(); + ClusterDetailsForm.hostsNetworkConfigurationField + .findStaticIpRadioButton() + .should('be.disabled') + .and('not.be.checked'); }); xit('The minimal ISO is presented by default', () => { // TODO(jkilzi): WIP... - // ClusterDetailsForm.clusterNameControl + // ClusterDetailsForm.clusterNameField // .findInputField() - // .scrollIntoView() // .type(Cypress.env('CLUSTER_NAME')); - // ClusterDetailsForm.baseDomainControl.findInputField().scrollIntoView().type('redhat.com'); - // ClusterDetailsForm.openshiftVersionControl.findInputField().scrollIntoView().type('redhat.com'); + // ClusterDetailsForm.baseDomainField.findInputField().scrollIntoView().type('redhat.com'); + // ClusterDetailsForm.openshiftVersionField.findInputField().scrollIntoView().type('redhat.com'); }); }); }); diff --git a/libs/ui-lib-tests/cypress/support/commands.ts b/libs/ui-lib-tests/cypress/support/commands.ts index 3c69f11386..fab990a922 100644 --- a/libs/ui-lib-tests/cypress/support/commands.ts +++ b/libs/ui-lib-tests/cypress/support/commands.ts @@ -18,3 +18,7 @@ Cypress.Commands.add('pasteText', (selector, text) => { cy.get(selector).type(' {backspace}'); }); }); + +Cypress.Commands.add('findWithinOrGet', (childSelector: string, ancestorAlias?: string) => { + return ancestorAlias ? cy.get(ancestorAlias).find(childSelector) : cy.get(childSelector); +}); diff --git a/libs/ui-lib-tests/cypress/views/forms/ClusterDetails/ClusterDetailsForm.ts b/libs/ui-lib-tests/cypress/views/forms/ClusterDetails/ClusterDetailsForm.ts new file mode 100644 index 0000000000..f6d8daa4d2 --- /dev/null +++ b/libs/ui-lib-tests/cypress/views/forms/ClusterDetails/ClusterDetailsForm.ts @@ -0,0 +1,40 @@ +import { BaseDomainField } from './Fields/BaseDomainField'; +import { ClusterNameField } from './Fields/ClusterNameField'; +import { CustomManifestsField } from './Fields/CustomManifestsField'; +import { ExternalPartnerIntegrationsField } from './Fields/ExternalPartnerIntegrationsField'; +import { HostsNetworkConfigurationField } from './Fields/HostsNetworkConfigurationField'; +import { OpenShiftVersionField } from './Fields/OpenShiftVersionField'; + +export class ClusterDetailsForm { + static readonly alias = `@${ClusterDetailsForm.name}`; + static readonly selector = '#wizard-cluster-details__form'; + + static init(ancestorAlias?: string) { + cy.findWithinOrGet(ClusterDetailsForm.selector, ancestorAlias).as(ClusterDetailsForm.name); + return ClusterDetailsForm; + } + + static get externalPartnerIntegrationsField() { + return ExternalPartnerIntegrationsField.init(ClusterDetailsForm.alias); + } + + static get customManifestsField() { + return CustomManifestsField.init(ClusterDetailsForm.alias); + } + + static get clusterNameField() { + return ClusterNameField.init(ClusterDetailsForm.alias); + } + + static get baseDomainField() { + return BaseDomainField.init(ClusterDetailsForm.alias); + } + + static get openshiftVersionField() { + return OpenShiftVersionField.init(ClusterDetailsForm.alias); + } + + static get hostsNetworkConfigurationField() { + return HostsNetworkConfigurationField.init(ClusterDetailsForm.alias); + } +} diff --git a/libs/ui-lib-tests/cypress/views/forms/ClusterDetails/Fields/BaseDomainField.ts b/libs/ui-lib-tests/cypress/views/forms/ClusterDetails/Fields/BaseDomainField.ts new file mode 100644 index 0000000000..0c395a219c --- /dev/null +++ b/libs/ui-lib-tests/cypress/views/forms/ClusterDetails/Fields/BaseDomainField.ts @@ -0,0 +1,15 @@ +export class BaseDomainField { + static readonly alias = `${BaseDomainField.name}`; + static readonly selector = '#form-control__form-input-baseDnsDomain-field'; + + static init(ancestorAlias: string) { + cy.findWithinOrGet(BaseDomainField.selector, ancestorAlias).as(BaseDomainField.name); + return BaseDomainField; + } + + static findInputField() { + return cy.get(BaseDomainField.alias).findByRole('textbox', { + name: /base domain/i, + }); + } +} diff --git a/libs/ui-lib-tests/cypress/views/forms/ClusterDetails/Fields/ClusterNameField.ts b/libs/ui-lib-tests/cypress/views/forms/ClusterDetails/Fields/ClusterNameField.ts new file mode 100644 index 0000000000..12c44dd4ec --- /dev/null +++ b/libs/ui-lib-tests/cypress/views/forms/ClusterDetails/Fields/ClusterNameField.ts @@ -0,0 +1,15 @@ +export class ClusterNameField { + static readonly alias = `@${ClusterNameField}`; + static readonly selector = '#form-control__form-input-name-field'; + + static init(ancestorAlias?: string) { + cy.findWithinOrGet(ClusterNameField.selector, ancestorAlias).as(ClusterNameField.name); + return ClusterNameField; + } + + static findInputField() { + return cy.get(ClusterNameField.alias).findByRole('textbox', { + name: /cluster name/i, + }); + } +} diff --git a/libs/ui-lib-tests/cypress/views/forms/ClusterDetails/Fields/CustomManifestsField.ts b/libs/ui-lib-tests/cypress/views/forms/ClusterDetails/Fields/CustomManifestsField.ts new file mode 100644 index 0000000000..0cc50f06c4 --- /dev/null +++ b/libs/ui-lib-tests/cypress/views/forms/ClusterDetails/Fields/CustomManifestsField.ts @@ -0,0 +1,15 @@ +export class CustomManifestsField { + static readonly alias = `@${CustomManifestsField.name}`; + static readonly selector = '#form-control__form-input-addCustomManifest-field'; + + static init(ancestorAlias?: string) { + cy.findWithinOrGet(CustomManifestsField.selector, ancestorAlias).as(CustomManifestsField.name); + return CustomManifestsField; + } + + static findCheckbox() { + return cy.get(CustomManifestsField.alias).findByRole('checkbox', { + name: /include custom manifests/i, + }); + } +} diff --git a/libs/ui-lib-tests/cypress/views/forms/ClusterDetails/Fields/ExternalPartnerIntegrationsField.ts b/libs/ui-lib-tests/cypress/views/forms/ClusterDetails/Fields/ExternalPartnerIntegrationsField.ts new file mode 100644 index 0000000000..f73eea0128 --- /dev/null +++ b/libs/ui-lib-tests/cypress/views/forms/ClusterDetails/Fields/ExternalPartnerIntegrationsField.ts @@ -0,0 +1,44 @@ +export class ExternalPartnerIntegrationsField { + static readonly alias = `@${ExternalPartnerIntegrationsField.name}`; + static readonly selector = '#form-control__form-checkbox-externalPartnerIntegrations-field'; + + static init(ancestorAlias?: string) { + cy.findWithinOrGet(ExternalPartnerIntegrationsField.selector, ancestorAlias).as( + ExternalPartnerIntegrationsField.name, + ); + return ExternalPartnerIntegrationsField; + } + + static findPopoverButton() { + return cy.get(ExternalPartnerIntegrationsField.alias).findByRole('img', { + hidden: true, + }); + } + + static findPopoverContent() { + // The popover is attached to the bottom of the body by default. + // No need to search anything related to it within this form field. + return cy + .get('#popover-externalPartnerIntegrations-body') + .findByText( + /to integrate with an external partner \(for example, oracle cloud\), you'll need to provide a custom manifest\./i, + ); + } + static findLabel() { + return cy + .get(ExternalPartnerIntegrationsField.alias) + .findByText(/external partner integrations/i); + } + + static findHelperText() { + return cy + .get(ExternalPartnerIntegrationsField.alias) + .findByText(/integrate with other platforms using custom manifests\./i); + } + + static findCheckbox() { + return cy.get(ExternalPartnerIntegrationsField.alias).findByRole('checkbox', { + name: /external partner integrations/i, + }); + } +} diff --git a/libs/ui-lib-tests/cypress/views/forms/ClusterDetails/Fields/HostsNetworkConfigurationField.ts b/libs/ui-lib-tests/cypress/views/forms/ClusterDetails/Fields/HostsNetworkConfigurationField.ts new file mode 100644 index 0000000000..12fe33817f --- /dev/null +++ b/libs/ui-lib-tests/cypress/views/forms/ClusterDetails/Fields/HostsNetworkConfigurationField.ts @@ -0,0 +1,27 @@ +export class HostsNetworkConfigurationField { + static readonly alias = `@${HostsNetworkConfigurationField.name}`; + static readonly selector = '#form-control__form-radio-hostsNetworkConfigurationType-field'; + + static init(ancestorAlias?: string) { + cy.findWithinOrGet(HostsNetworkConfigurationField.selector, ancestorAlias).as( + HostsNetworkConfigurationField.name, + ); + return HostsNetworkConfigurationField; + } + + static findLabel() { + return cy.get(HostsNetworkConfigurationField.alias).findByText(/hosts' network configuration/i); + } + + static findStaticIpRadioButton() { + return cy + .get(HostsNetworkConfigurationField.alias) + .find('#form-radio-hostsNetworkConfigurationType-static-field'); + } + + static findStaticIpRadioLabel() { + return cy + .get(HostsNetworkConfigurationField.alias) + .findByText(/static ip, bridges, and bonds/i); + } +} diff --git a/libs/ui-lib-tests/cypress/views/forms/ClusterDetails/Fields/OpenShiftVersionField.ts b/libs/ui-lib-tests/cypress/views/forms/ClusterDetails/Fields/OpenShiftVersionField.ts new file mode 100644 index 0000000000..9a8e4c4e4f --- /dev/null +++ b/libs/ui-lib-tests/cypress/views/forms/ClusterDetails/Fields/OpenShiftVersionField.ts @@ -0,0 +1,27 @@ +export class OpenShiftVersionField { + static readonly alias = `@${OpenShiftVersionField.name}`; + static readonly selector = '#form-control__form-input-openshiftVersion-field'; + + static init(ancestorAlias?: string) { + cy.findWithinOrGet(OpenShiftVersionField.selector, ancestorAlias).as( + OpenShiftVersionField.name, + ); + + return OpenShiftVersionField; + } + + static findLabel() { + return cy.get(OpenShiftVersionField.alias).findByText(/openshift version/i); + } + + static findDropdown() { + return cy.get(OpenShiftVersionField.alias).find('#form-input-openshiftVersion-field'); + } + + static selectVersion(version: string) { + OpenShiftVersionField.findDropdown().click(); + OpenShiftVersionField.findDropdown().within(() => { + cy.findByRole('menuitem', { name: new RegExp(`openshift ${version}`, 'i') }).click(); + }); + } +} diff --git a/libs/ui-lib-tests/cypress/views/forms/ClusterDetailsForm.ts b/libs/ui-lib-tests/cypress/views/forms/ClusterDetailsForm.ts deleted file mode 100644 index a4fa2eeafe..0000000000 --- a/libs/ui-lib-tests/cypress/views/forms/ClusterDetailsForm.ts +++ /dev/null @@ -1,105 +0,0 @@ -export default class ClusterDetailsForm { - static get body() { - return cy.get('#wizard-cluster-details__form'); - } - - static get externalPartnerIntegrationsControl() { - return ExternalPartnerIntegrationsControl; - } - - static get customManifestsControl() { - return CustomManifestsControl; - } - - static get clusterNameControl() { - return ClusterNameControl; - } - - static get baseDomainControl() { - return BaseDomainControl; - } -} - -/** @private */ -class ClusterNameControl { - static get body() { - return ClusterDetailsForm.body.find('#form-control__form-input-name-field'); - } - - static findInputField() { - return ClusterNameControl.body.findByRole('textbox', { - name: /cluster name/i, - }); - } -} - -/** @private */ -class BaseDomainControl { - static get body() { - return ClusterDetailsForm.body.find('#form-control__form-input-baseDnsDomain-field'); - } - - static findInputField() { - return BaseDomainControl.body.findByRole('textbox', { - name: /base domain/i, - }); - } -} - -/** @private */ -class ExternalPartnerIntegrationsControl { - static get body() { - return ClusterDetailsForm.body.find( - '#form-control__form-checkbox-externalPartnerIntegrations-field', - ); - } - - static findPopoverButton() { - return ExternalPartnerIntegrationsControl.body.findByRole('img', { - hidden: true, - }); - } - - static findPopoverContent() { - return ExternalPartnerIntegrationsControl.body - .scrollIntoView() - .get('#popover-externalPartnerIntegrations-body'); - } - - static findLabel() { - return ExternalPartnerIntegrationsControl.body.findByText(/external partner integrations/i); - } - - static findHelperText() { - return ExternalPartnerIntegrationsControl.body.findByText( - /integrate with other platforms using custom manifests\./i, - ); - } - - static findCheckbox() { - return ExternalPartnerIntegrationsControl.body.findByRole('checkbox', { - name: /external partner integrations/i, - }); - } -} - -/** @private */ -class CustomManifestsControl { - static get body() { - return ClusterDetailsForm.body - .find('#form-control__form-input-addCustomManifest-field') - .parent(); - } - - static findCheckbox() { - return CustomManifestsControl.body.findByRole('checkbox', { - name: /include custom manifests/i, - }); - } - - static findLabel() { - return ExternalPartnerIntegrationsControl.body - .scrollIntoView() - .findByText(/include custom manifests/i); - } -} diff --git a/libs/ui-lib-tests/cypress/views/pages/NewClusterPage.ts b/libs/ui-lib-tests/cypress/views/pages/NewClusterPage.ts index 218bee9e6c..3d29bbbdbc 100644 --- a/libs/ui-lib-tests/cypress/views/pages/NewClusterPage.ts +++ b/libs/ui-lib-tests/cypress/views/pages/NewClusterPage.ts @@ -1,4 +1,4 @@ -export default class NewClusterPage { +export class NewClusterPage { static visit(options?: Partial) { return cy.visit('/clusters/~new', options); } diff --git a/libs/ui-lib-tests/package.json b/libs/ui-lib-tests/package.json index 7715027a64..fc1ca835d0 100644 --- a/libs/ui-lib-tests/package.json +++ b/libs/ui-lib-tests/package.json @@ -18,7 +18,7 @@ "preview:assisted-ui": "yarn workspace @openshift-assisted/assisted-ui run preview", "serve:assisted-ui": "yarn workspace @openshift-assisted/assisted-ui run serve", "cy:run": "yarn run start-test 'yarn preview:assisted-ui' http://localhost:4173 'cypress run --headless --browser chrome'", - "cy:open": "yarn run start-test 'yarn preview:assisted-ui' http://localhost:4173 'cypress open'", - "cy:open:dev": "yarn run start-test 'yarn serve:assisted-ui --port 4173' http://localhost:4173 'cypress open -c defaultCommandTimeout=15000'" + "cy:open": "yarn build:all && yarn run start-test 'yarn preview:assisted-ui' http://localhost:4173 'cypress open'", + "cy:open:dev": "yarn build:all && yarn run start-test 'yarn serve:assisted-ui --port 4173' http://localhost:4173 'cypress open -c defaultCommandTimeout=15000'" } }