diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 1c33f48795..8c045a0d10 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -151,6 +151,7 @@ "cypress-mochawesome-reporter": "^3.8.2", "cypress-multi-reporters": "^1.6.4", "cypress-plugin-steps": "^1.1.1", + "cypress-recurse": "^1.35.3", "eslint": "^8.57.0", "eslint-config-prettier": "^8.6.0", "eslint-import-resolver-node": "^0.3.7", @@ -9030,6 +9031,16 @@ "cypress": ">=10" } }, + "node_modules/cypress-recurse": { + "version": "1.35.3", + "resolved": "https://registry.npmjs.org/cypress-recurse/-/cypress-recurse-1.35.3.tgz", + "integrity": "sha512-NbFOpEuZT4tFqAB0jQqel7WtVNDe8pvSHE2TfXvYk4pspf3wq98OC2RhhLn3bMnoCnPtY4IHO7e37c+CZ9HnMA==", + "license": "MIT", + "optional": true, + "dependencies": { + "humanize-duration": "^3.27.3" + } + }, "node_modules/cypress/node_modules/bluebird": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", @@ -12707,6 +12718,13 @@ "node": ">=8.12.0" } }, + "node_modules/humanize-duration": { + "version": "3.32.1", + "resolved": "https://registry.npmjs.org/humanize-duration/-/humanize-duration-3.32.1.tgz", + "integrity": "sha512-inh5wue5XdfObhu/IGEMiA1nUXigSGcaKNemcbLRKa7jXYGDZXr3LoT9pTIzq2hPEbld7w/qv9h+ikWGz8fL1g==", + "license": "Unlicense", + "optional": true + }, "node_modules/hyperdyperid": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/hyperdyperid/-/hyperdyperid-1.2.0.tgz", diff --git a/frontend/package.json b/frontend/package.json index eac48f47ae..ef91ca4b69 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -193,6 +193,7 @@ "cypress-mochawesome-reporter": "^3.8.2", "cypress-multi-reporters": "^1.6.4", "cypress-plugin-steps": "^1.1.1", + "cypress-recurse": "^1.35.3", "eslint": "^8.57.0", "eslint-config-prettier": "^8.6.0", "eslint-import-resolver-node": "^0.3.7", diff --git a/frontend/src/__tests__/cypress/cypress/support/commands/application.ts b/frontend/src/__tests__/cypress/cypress/support/commands/application.ts index 632e4cf481..2c20d902ec 100644 --- a/frontend/src/__tests__/cypress/cypress/support/commands/application.ts +++ b/frontend/src/__tests__/cypress/cypress/support/commands/application.ts @@ -1,5 +1,6 @@ import type { MatcherOptions } from '@testing-library/cypress'; import type { Matcher, MatcherOptions as DTLMatcherOptions } from '@testing-library/dom'; +import { recurse } from 'cypress-recurse'; import type { UserAuthConfig, DashboardConfig } from '~/__tests__/cypress/cypress/types'; import { HTPASSWD_CLUSTER_ADMIN_USER } from '~/__tests__/cypress/cypress/utils/e2eUsers'; import { @@ -125,7 +126,6 @@ declare global { */ // eslint-disable-next-line @typescript-eslint/method-signature-style findByTestId(id: Matcher | Matcher[], options?: MatcherOptions): Chainable; - /** * Overwrite `findAllByTestId` to support an array of Matchers. * When an array of Matches is supplied, parses the data-testid attribute value as a @@ -176,6 +176,20 @@ declare global { getNotebookControllerCullerConfig: ( key?: string, ) => Cypress.Chainable; + + /** + * Custom command to retry clicking an element multiple times. + * @param getElement Function that returns a Cypress chainable representing the element to be clicked. + * @param maxAttempts Maximum number of attempts to click the element (default: 3). + * @param delay Delay in milliseconds between attempts (default: 1000). + * @returns Cypress.Chainable + */ + retryClick: ( + // eslint-disable-next-line @typescript-eslint/no-explicit-any + getElement: () => Cypress.Chainable, + maxAttempts?: number, + delay?: number, + ) => Cypress.Chainable; } } } @@ -213,6 +227,39 @@ Cypress.Commands.add('visitWithLogin', (relativeUrl, credentials = HTPASSWD_CLUS } }); +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore +function retryClick( + // eslint-disable-next-line @typescript-eslint/no-explicit-any + getElement: () => Cypress.Chainable, + maxAttempts = 3, + delay = 1000, +): Cypress.Chainable { + return recurse( + () => getElement(), + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + ($el) => { + $el.click(); + return true; + }, + { + log: false, + timeout: maxAttempts * delay, + delay, + limit: maxAttempts, + error: `Failed to click element after ${maxAttempts} attempts`, + }, + ); +} + +Cypress.Commands.add( + 'retryClick', + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (getElement: () => Cypress.Chainable, maxAttempts = 3, delay = 1000) => { + return retryClick(getElement, maxAttempts, delay); + }, +); + Cypress.Commands.add('findKebab', { prevSubject: 'element' }, (subject, isDropdownToggle) => { Cypress.log({ displayName: 'findKebab' }); return cy diff --git a/frontend/src/__tests__/cypress/cypress/tests/e2e/dashboardNavigation/testManifestLinks.cy.ts b/frontend/src/__tests__/cypress/cypress/tests/e2e/dashboardNavigation/testManifestLinks.cy.ts index 5354d121e5..b5aa7e6436 100644 --- a/frontend/src/__tests__/cypress/cypress/tests/e2e/dashboardNavigation/testManifestLinks.cy.ts +++ b/frontend/src/__tests__/cypress/cypress/tests/e2e/dashboardNavigation/testManifestLinks.cy.ts @@ -2,7 +2,7 @@ import * as yaml from 'js-yaml'; import { isUrlExcluded } from '~/__tests__/cypress/cypress/utils/urlExtractor'; import { retryableBefore } from '~/__tests__/cypress/cypress/utils/retryableHooks'; -describe('[Product bugs: RHOAIENG-9365,RHOAIENG-9187,RHOAIENG-16956, RHOAIENG-16959] Verify that all the URLs referenced in the Manifest directory are operational', () => { +describe('[Dashboard Bugs: RHOAIENG-9365,RHOAIENG-9187,RHOAIENG-16956, RHOAIENG-16959] Verify that all the URLs referenced in the Manifest directory are operational', () => { let excludedSubstrings: string[]; // Setup: Load test data diff --git a/frontend/src/__tests__/cypress/cypress/tests/e2e/dataSciencePipelines/pipelines.cy.ts b/frontend/src/__tests__/cypress/cypress/tests/e2e/dataSciencePipelines/pipelines.cy.ts index 429be45e29..c48872b372 100644 --- a/frontend/src/__tests__/cypress/cypress/tests/e2e/dataSciencePipelines/pipelines.cy.ts +++ b/frontend/src/__tests__/cypress/cypress/tests/e2e/dataSciencePipelines/pipelines.cy.ts @@ -19,59 +19,63 @@ const testPipelineName = 'test-pipelines-pipeline'; const testRunName = 'test-pipelines-run'; const awsBucket = 'BUCKET_3' as const; -describe('An admin user can import and run a pipeline', { testIsolation: false }, () => { - retryableBefore(() => { - // Create a Project for pipelines - provisionProjectForPipelines(projectName, dspaSecretName, awsBucket); - }); +describe( + '[Pipelines Bug: RHOAIENG-20642] An admin user can import and run a pipeline', + { testIsolation: false }, + () => { + retryableBefore(() => { + // Create a Project for pipelines + provisionProjectForPipelines(projectName, dspaSecretName, awsBucket); + }); - after(() => { - //Check if the Before Method was executed to perform the setup - if (!wasSetupPerformed()) return; + after(() => { + //Check if the Before Method was executed to perform the setup + if (!wasSetupPerformed()) return; - // Delete provisioned Project - deleteOpenShiftProject(projectName); - }); + // Delete provisioned Project + deleteOpenShiftProject(projectName); + }); - it( - 'An admin User can Import and Run a Pipeline', - { tags: ['@Smoke', '@SmokeSet1', '@Dashboard'] }, - () => { - cy.step('Navigate to DSP ${projectName}'); - cy.visitWithLogin('/', HTPASSWD_CLUSTER_ADMIN_USER); - projectListPage.navigate(); - projectListPage.filterProjectByName(projectName); - projectListPage.findProjectLink(projectName).click(); + it( + 'An admin User can Import and Run a Pipeline', + { tags: ['@Smoke', '@SmokeSet1', '@Dashboard', '@Bug'] }, + () => { + cy.step('Navigate to DSP ${projectName}'); + cy.visitWithLogin('/', HTPASSWD_CLUSTER_ADMIN_USER); + projectListPage.navigate(); + projectListPage.filterProjectByName(projectName); + projectListPage.findProjectLink(projectName).click(); - cy.step('Import a pipeline by URL'); - // Increasing the timeout to ~3mins so the DSPA can be loaded - projectDetails.findImportPipelineButton(180000).click(); - // Fill the Import Pipeline modal - pipelineImportModal.findPipelineNameInput().type(testPipelineName); - pipelineImportModal.findPipelineDescriptionInput().type('Pipeline Description'); - pipelineImportModal.findImportPipelineRadio().click(); - pipelineImportModal - .findPipelineUrlInput() - .type( - 'https://raw.githubusercontent.com/opendatahub-io/odh-dashboard/refs/heads/main/frontend/src/__tests__/resources/pipelines_samples/dummy_pipeline_compiled.yaml', - ); - pipelineImportModal.submit(); + cy.step('Import a pipeline by URL'); + // Increasing the timeout to ~3mins so the DSPA can be loaded + projectDetails.findImportPipelineButton(180000).click(); + // Fill the Import Pipeline modal + pipelineImportModal.findPipelineNameInput().type(testPipelineName); + pipelineImportModal.findPipelineDescriptionInput().type('Pipeline Description'); + pipelineImportModal.findImportPipelineRadio().click(); + pipelineImportModal + .findPipelineUrlInput() + .type( + 'https://raw.githubusercontent.com/opendatahub-io/odh-dashboard/refs/heads/main/frontend/src/__tests__/resources/pipelines_samples/dummy_pipeline_compiled.yaml', + ); + pipelineImportModal.submit(); - // Verify that we are at the details page of the pipeline by checking the title - // It can take a little longer to load - pipelineDetails.findPageTitle(60000).should('have.text', testPipelineName); + // Verify that we are at the details page of the pipeline by checking the title + // It can take a little longer to load + pipelineDetails.findPageTitle(60000).should('have.text', testPipelineName); - cy.step('Run the pipeline from the Actions button in the pipeline detail view'); - pipelineDetails.selectActionDropdownItem('Create run'); - createRunPage.experimentSelect.findToggleButton().click(); - createRunPage.selectExperimentByName('Default'); - createRunPage.fillName(testRunName); - createRunPage.fillDescription('Run Description'); - createRunPage.findSubmitButton().click(); + cy.step('Run the pipeline from the Actions button in the pipeline detail view'); + pipelineDetails.selectActionDropdownItem('Create run'); + createRunPage.experimentSelect.findToggleButton().click(); + createRunPage.selectExperimentByName('Default'); + createRunPage.fillName(testRunName); + createRunPage.fillDescription('Run Description'); + createRunPage.findSubmitButton().click(); - cy.step('Expect the run to Succeed'); - //Redirected to the Graph view of the created run - pipelineRunDetails.expectStatusLabelToBe('Succeeded', 180000); - }, - ); -}); + cy.step('Expect the run to Succeed'); + //Redirected to the Graph view of the created run + pipelineRunDetails.expectStatusLabelToBe('Succeeded', 180000); + }, + ); + }, +); diff --git a/frontend/src/__tests__/cypress/cypress/tests/e2e/dataScienceProjects/clusterStorage/testClusterStorageCreation.cy.ts b/frontend/src/__tests__/cypress/cypress/tests/e2e/dataScienceProjects/clusterStorage/testClusterStorageCreation.cy.ts index a3ecd0dedb..02fea3811b 100644 --- a/frontend/src/__tests__/cypress/cypress/tests/e2e/dataScienceProjects/clusterStorage/testClusterStorageCreation.cy.ts +++ b/frontend/src/__tests__/cypress/cypress/tests/e2e/dataScienceProjects/clusterStorage/testClusterStorageCreation.cy.ts @@ -14,6 +14,7 @@ import { retryableBefore, wasSetupPerformed, } from '~/__tests__/cypress/cypress/utils/retryableHooks'; +import { retryClickTab } from '~/__tests__/cypress/cypress/utils/tabUtils'; describe('Verify Cluster Storage - Creating, Editing and Deleting', () => { let testData: DataScienceProjectData; @@ -78,7 +79,7 @@ describe('Verify Cluster Storage - Creating, Editing and Deleting', () => { //Navigate to Cluster Storage and click to Add Storage cy.step('Navigate to Cluster Storage and click to create Cluster Storage'); - projectDetails.findSectionTab('cluster-storages').click(); + retryClickTab(() => projectDetails.findSectionTab('cluster-storages'), 'cluster-storages'); clusterStorage.findCreateButton().click(); // Enter validate Cluster Storage details into the Cluster Storage Modal diff --git a/frontend/src/__tests__/cypress/cypress/tests/e2e/dataScienceProjects/connections/testDataConnectionCreation.cy.ts b/frontend/src/__tests__/cypress/cypress/tests/e2e/dataScienceProjects/connections/testDataConnectionCreation.cy.ts index 3661b2b4d5..74059438bb 100644 --- a/frontend/src/__tests__/cypress/cypress/tests/e2e/dataScienceProjects/connections/testDataConnectionCreation.cy.ts +++ b/frontend/src/__tests__/cypress/cypress/tests/e2e/dataScienceProjects/connections/testDataConnectionCreation.cy.ts @@ -11,6 +11,7 @@ import { retryableBefore, wasSetupPerformed, } from '~/__tests__/cypress/cypress/utils/retryableHooks'; +import { retryClickTab } from '~/__tests__/cypress/cypress/utils/tabUtils'; describe('Verify Data Connections - Creation and Deletion', () => { let testData: DataScienceProjectData; @@ -76,7 +77,7 @@ describe('Verify Data Connections - Creation and Deletion', () => { //Navigate to Data Connections and create Connection cy.step('Navigate to Connections and click to create Connection'); - projectDetails.findSectionTab('connections').click(); + retryClickTab(() => projectDetails.findSectionTab('connections'), 'connections'); connectionsPage.findCreateConnectionButton().click(); // Enter validate Data Connection details into the Data Connection Modal diff --git a/frontend/src/__tests__/cypress/cypress/tests/e2e/dataScienceProjects/models/testMultiModelAdminCreation.cy.ts b/frontend/src/__tests__/cypress/cypress/tests/e2e/dataScienceProjects/models/testMultiModelAdminCreation.cy.ts index f1d27f18b9..6cb301236c 100644 --- a/frontend/src/__tests__/cypress/cypress/tests/e2e/dataScienceProjects/models/testMultiModelAdminCreation.cy.ts +++ b/frontend/src/__tests__/cypress/cypress/tests/e2e/dataScienceProjects/models/testMultiModelAdminCreation.cy.ts @@ -18,6 +18,7 @@ import { retryableBefore, wasSetupPerformed, } from '~/__tests__/cypress/cypress/utils/retryableHooks'; +import { retryClickTab } from '~/__tests__/cypress/cypress/utils/tabUtils'; let testData: DataScienceProjectData; let projectName: string; @@ -91,7 +92,7 @@ describe('Automation Bug RHOAIENG-20591] Verify Admin Multi Model Creation and V // Navigate to Model Serving tab and Deploy a Multi Model cy.step('Navigate to Model Serving and click to Deploy a Model Server'); - projectDetails.findSectionTab('model-server').click(); + retryClickTab(() => projectDetails.findSectionTab('model-server'), 'model-server'); modelServingGlobal.findMultiModelButton().click(); modelServingSection.findAddModelServerButton().click(); createServingRuntimeModal.findModelServerName().type(testData.multiModelAdminName); diff --git a/frontend/src/__tests__/cypress/cypress/tests/e2e/dataScienceProjects/models/testSingleModelAdminCreation.cy.ts b/frontend/src/__tests__/cypress/cypress/tests/e2e/dataScienceProjects/models/testSingleModelAdminCreation.cy.ts index eadca81692..39f41e0df8 100644 --- a/frontend/src/__tests__/cypress/cypress/tests/e2e/dataScienceProjects/models/testSingleModelAdminCreation.cy.ts +++ b/frontend/src/__tests__/cypress/cypress/tests/e2e/dataScienceProjects/models/testSingleModelAdminCreation.cy.ts @@ -17,6 +17,7 @@ import { retryableBefore, wasSetupPerformed, } from '~/__tests__/cypress/cypress/utils/retryableHooks'; +import { retryClickTab } from '~/__tests__/cypress/cypress/utils/tabUtils'; let testData: DataScienceProjectData; let projectName: string; @@ -24,7 +25,7 @@ let modelName: string; let modelFilePath: string; const awsBucket = 'BUCKET_1' as const; -describe('[Product Bug RHOAIENG-20213] Verify Admin Single Model Creation and Validation using the UI', () => { +describe('[Model Serving Bug RHOAIENG-20213] Verify Admin Single Model Creation and Validation using the UI', () => { retryableBefore(() => { Cypress.on('uncaught:exception', (err) => { if (err.message.includes('Error: secrets "ds-pipeline-config" already exists')) { @@ -80,7 +81,7 @@ describe('[Product Bug RHOAIENG-20213] Verify Admin Single Model Creation and Va // Navigate to Model Serving tab and Deploy a Single Model cy.step('Navigate to Model Serving and click to Deploy a Single Model'); - projectDetails.findSectionTab('model-server').click(); + retryClickTab(() => projectDetails.findSectionTab('model-server'), 'model-server'); modelServingGlobal.findSingleServingModelButton().click(); modelServingGlobal.findDeployModelButton().click(); diff --git a/frontend/src/__tests__/cypress/cypress/tests/e2e/dataScienceProjects/models/testSingleModelContributorCreation.cy.ts b/frontend/src/__tests__/cypress/cypress/tests/e2e/dataScienceProjects/models/testSingleModelContributorCreation.cy.ts index 32f8b436bb..889c2cde37 100644 --- a/frontend/src/__tests__/cypress/cypress/tests/e2e/dataScienceProjects/models/testSingleModelContributorCreation.cy.ts +++ b/frontend/src/__tests__/cypress/cypress/tests/e2e/dataScienceProjects/models/testSingleModelContributorCreation.cy.ts @@ -19,6 +19,7 @@ import { retryableBefore, wasSetupPerformed, } from '~/__tests__/cypress/cypress/utils/retryableHooks'; +import { retryClickTab } from '~/__tests__/cypress/cypress/utils/tabUtils'; let testData: DataScienceProjectData; let projectName: string; @@ -85,7 +86,7 @@ describe('Verify Model Creation and Validation using the UI', () => { // Navigate to Model Serving tab and Deploy a Single Model cy.step('Navigate to Model Serving and click to Deploy a Single Model'); - projectDetails.findSectionTab('model-server').click(); + retryClickTab(() => projectDetails.findSectionTab('model-server'), 'model-server'); modelServingGlobal.findSingleServingModelButton().click(); modelServingGlobal.findDeployModelButton().click(); diff --git a/frontend/src/__tests__/cypress/cypress/tests/e2e/dataScienceProjects/testProjectAdminPermissions.cy.ts b/frontend/src/__tests__/cypress/cypress/tests/e2e/dataScienceProjects/testProjectAdminPermissions.cy.ts index b398dd6ce2..ed0327fcee 100644 --- a/frontend/src/__tests__/cypress/cypress/tests/e2e/dataScienceProjects/testProjectAdminPermissions.cy.ts +++ b/frontend/src/__tests__/cypress/cypress/tests/e2e/dataScienceProjects/testProjectAdminPermissions.cy.ts @@ -13,6 +13,7 @@ import { retryableBefore, wasSetupPerformed, } from '~/__tests__/cypress/cypress/utils/retryableHooks'; +import { retryClickTab } from '~/__tests__/cypress/cypress/utils/tabUtils'; describe('Verify that users can provide admin project permissions to non-admin users/groups', () => { let testData: DataScienceProjectData; @@ -62,7 +63,7 @@ describe('Verify that users can provide admin project permissions to non-admin u projectListPage.navigate(); projectListPage.filterProjectByName(testData.projectPermissionResourceName); projectListPage.findProjectLink(testData.projectPermissionResourceName).click(); - projectDetails.findSectionTab('permissions').click(); + retryClickTab(() => projectDetails.findSectionTab('permissions'), 'permissions'); cy.step('Assign admin user Project Permissions'); permissions.findAddUserButton().click(); @@ -98,7 +99,7 @@ describe('Verify that users can provide admin project permissions to non-admin u projectListPage.navigate(); projectListPage.filterProjectByName(testData.projectPermissionResourceName); projectListPage.findProjectLink(testData.projectPermissionResourceName).click(); - projectDetails.findSectionTab('permissions').click(); + retryClickTab(() => projectDetails.findSectionTab('permissions'), 'permissions'); cy.step('Assign admin group Project Permissions'); permissions.findAddGroupButton().click(); @@ -126,7 +127,7 @@ describe('Verify that users can provide admin project permissions to non-admin u projectListPage.navigate(); projectListPage.filterProjectByName(testData.projectPermissionResourceName); projectListPage.findProjectLink(testData.projectPermissionResourceName).click(); - projectDetails.findSectionTab('permissions').click(); + retryClickTab(() => projectDetails.findSectionTab('permissions'), 'permissions'); }, ); }); diff --git a/frontend/src/__tests__/cypress/cypress/tests/e2e/dataScienceProjects/testProjectContributorPermissions.cy.ts b/frontend/src/__tests__/cypress/cypress/tests/e2e/dataScienceProjects/testProjectContributorPermissions.cy.ts index 7e2c9f13d7..7f670f12d4 100644 --- a/frontend/src/__tests__/cypress/cypress/tests/e2e/dataScienceProjects/testProjectContributorPermissions.cy.ts +++ b/frontend/src/__tests__/cypress/cypress/tests/e2e/dataScienceProjects/testProjectContributorPermissions.cy.ts @@ -12,6 +12,7 @@ import { retryableBefore, wasSetupPerformed, } from '~/__tests__/cypress/cypress/utils/retryableHooks'; +import { retryClickTab } from '~/__tests__/cypress/cypress/utils/tabUtils'; describe('Verify that users can provide contributor project permissions to non-admin users', () => { let testData: DataScienceProjectData; @@ -59,7 +60,7 @@ describe('Verify that users can provide contributor project permissions to non-a projectListPage.navigate(); projectListPage.filterProjectByName(testData.projectContributorResourceName); projectListPage.findProjectLink(testData.projectContributorResourceName).click(); - projectDetails.findSectionTab('permissions').click(); + retryClickTab(() => projectDetails.findSectionTab('permissions'), 'permissions'); cy.step('Assign contributor user Project Permissions'); permissions.findAddUserButton().click(); diff --git a/frontend/src/__tests__/cypress/cypress/tests/e2e/dataScienceProjects/workbenches/testWorkbenchControlSuite.cy.ts b/frontend/src/__tests__/cypress/cypress/tests/e2e/dataScienceProjects/workbenches/testWorkbenchControlSuite.cy.ts index 7bfc6affa3..e9547d7206 100644 --- a/frontend/src/__tests__/cypress/cypress/tests/e2e/dataScienceProjects/workbenches/testWorkbenchControlSuite.cy.ts +++ b/frontend/src/__tests__/cypress/cypress/tests/e2e/dataScienceProjects/workbenches/testWorkbenchControlSuite.cy.ts @@ -15,6 +15,7 @@ import { retryableBefore, wasSetupPerformed, } from '~/__tests__/cypress/cypress/utils/retryableHooks'; +import { retryClickTab } from '~/__tests__/cypress/cypress/utils/tabUtils'; describe('Start, Stop, Launch and Delete a Workbench in RHOAI', () => { let controlSuiteTestNamespace: string; @@ -75,7 +76,7 @@ describe('Start, Stop, Launch and Delete a Workbench in RHOAI', () => { projectListPage.navigate(); projectListPage.filterProjectByName(controlSuiteTestNamespace); projectListPage.findProjectLink(controlSuiteTestNamespace).click(); - projectDetails.findSectionTab('workbenches').click(); + retryClickTab(() => projectDetails.findSectionTab('workbenches'), 'workbenches'); // Create workbench cy.step(`Create workbench ${controlSuiteTestNamespace}`); @@ -138,7 +139,7 @@ describe('Start, Stop, Launch and Delete a Workbench in RHOAI', () => { projectListPage.navigate(); projectListPage.filterProjectByName(controlSuiteTestNamespace); projectListPage.findProjectLink(controlSuiteTestNamespace).click(); - projectDetails.findSectionTab('workbenches').click(); + retryClickTab(() => projectDetails.findSectionTab('workbenches'), 'workbenches'); // Create workbench cy.step(`Create workbench ${controlSuiteTestNamespace}`); diff --git a/frontend/src/__tests__/cypress/cypress/tests/e2e/dataScienceProjects/workbenches/testWorkbenchEditing.cy.ts b/frontend/src/__tests__/cypress/cypress/tests/e2e/dataScienceProjects/workbenches/testWorkbenchEditing.cy.ts index 462d240045..674bff3694 100644 --- a/frontend/src/__tests__/cypress/cypress/tests/e2e/dataScienceProjects/workbenches/testWorkbenchEditing.cy.ts +++ b/frontend/src/__tests__/cypress/cypress/tests/e2e/dataScienceProjects/workbenches/testWorkbenchEditing.cy.ts @@ -14,6 +14,7 @@ import { retryableBefore, wasSetupPerformed, } from '~/__tests__/cypress/cypress/utils/retryableHooks'; +import { retryClickTab } from '~/__tests__/cypress/cypress/utils/tabUtils'; describe('Edit and Update a Workbench in RHOAI', () => { let editTestNamespace: string; @@ -66,7 +67,7 @@ describe('Edit and Update a Workbench in RHOAI', () => { projectListPage.navigate(); projectListPage.filterProjectByName(editTestNamespace); projectListPage.findProjectLink(editTestNamespace).click(); - projectDetails.findSectionTab('workbenches').click(); + retryClickTab(() => projectDetails.findSectionTab('workbenches'), 'workbenches'); // Create workbench cy.step(`Create workbench ${editTestNamespace} using storage ${pvcEditDisplayName}`); diff --git a/frontend/src/__tests__/cypress/cypress/tests/e2e/dataScienceProjects/workbenches/testWorkbenchNegativeTests.cy.ts b/frontend/src/__tests__/cypress/cypress/tests/e2e/dataScienceProjects/workbenches/testWorkbenchNegativeTests.cy.ts index c13040ef3c..0f46af4529 100644 --- a/frontend/src/__tests__/cypress/cypress/tests/e2e/dataScienceProjects/workbenches/testWorkbenchNegativeTests.cy.ts +++ b/frontend/src/__tests__/cypress/cypress/tests/e2e/dataScienceProjects/workbenches/testWorkbenchNegativeTests.cy.ts @@ -13,6 +13,7 @@ import { retryableBeforeEach, wasSetupPerformed, } from '~/__tests__/cypress/cypress/utils/retryableHooks'; +import { retryClickTab } from '~/__tests__/cypress/cypress/utils/tabUtils'; describe('Workbenches - negative tests', () => { let testData: WBNegativeTestsData; @@ -63,7 +64,7 @@ describe('Workbenches - negative tests', () => { projectListPage.navigate(); projectListPage.filterProjectByName(projectName); projectListPage.findProjectLink(projectName).click(); - projectDetails.findSectionTab('workbenches').click(); + retryClickTab(() => projectDetails.findSectionTab('workbenches'), 'workbenches'); // Create workbench cy.step(`Create workbench ${workbenchName}`); @@ -98,7 +99,7 @@ describe('Workbenches - negative tests', () => { projectListPage.navigate(); projectListPage.filterProjectByName(projectName); projectListPage.findProjectLink(projectName).click(); - projectDetails.findSectionTab('workbenches').click(); + retryClickTab(() => projectDetails.findSectionTab('workbenches'), 'workbenches'); // Create workbench cy.step(`Create workbench ${workbenchName}`); diff --git a/frontend/src/__tests__/cypress/cypress/tests/e2e/dataScienceProjects/workbenches/testWorkbenchStatus.cy.ts b/frontend/src/__tests__/cypress/cypress/tests/e2e/dataScienceProjects/workbenches/testWorkbenchStatus.cy.ts index 6b5fb30572..2d1d6923c5 100644 --- a/frontend/src/__tests__/cypress/cypress/tests/e2e/dataScienceProjects/workbenches/testWorkbenchStatus.cy.ts +++ b/frontend/src/__tests__/cypress/cypress/tests/e2e/dataScienceProjects/workbenches/testWorkbenchStatus.cy.ts @@ -13,6 +13,7 @@ import { retryableBefore, wasSetupPerformed, } from '~/__tests__/cypress/cypress/utils/retryableHooks'; +import { retryClickTab } from '~/__tests__/cypress/cypress/utils/tabUtils'; describe('Workbenches - status tests', () => { let projectName: string; @@ -62,7 +63,7 @@ describe('Workbenches - status tests', () => { projectListPage.navigate(); projectListPage.filterProjectByName(projectName); projectListPage.findProjectLink(projectName).click(); - projectDetails.findSectionTab('workbenches').click(); + retryClickTab(() => projectDetails.findSectionTab('workbenches'), 'workbenches'); // Create workbench cy.step(`Create workbench ${workbenchName}`); diff --git a/frontend/src/__tests__/cypress/cypress/tests/e2e/dataScienceProjects/workbenches/testWorkbenchTolerations.cy.ts b/frontend/src/__tests__/cypress/cypress/tests/e2e/dataScienceProjects/workbenches/testWorkbenchTolerations.cy.ts index ba1c7ddba5..9ecea0d209 100644 --- a/frontend/src/__tests__/cypress/cypress/tests/e2e/dataScienceProjects/workbenches/testWorkbenchTolerations.cy.ts +++ b/frontend/src/__tests__/cypress/cypress/tests/e2e/dataScienceProjects/workbenches/testWorkbenchTolerations.cy.ts @@ -24,6 +24,7 @@ import { retryableBefore, wasSetupPerformed, } from '~/__tests__/cypress/cypress/utils/retryableHooks'; +import { retryClickTab } from '~/__tests__/cypress/cypress/utils/tabUtils'; describe('[Automation Bug RHOAIENG-20099] Workbenches - tolerations tests', () => { let testData: WBTolerationsTestData; @@ -96,7 +97,7 @@ describe('[Automation Bug RHOAIENG-20099] Workbenches - tolerations tests', () = projectListPage.navigate(); projectListPage.filterProjectByName(projectName); projectListPage.findProjectLink(projectName).click(); - projectDetails.findSectionTab('workbenches').click(); + retryClickTab(() => projectDetails.findSectionTab('workbenches'), 'workbenches'); // Create workbench and verify it starts running cy.step(`Create workbench ${testData.workbenchName}`); @@ -141,7 +142,7 @@ describe('[Automation Bug RHOAIENG-20099] Workbenches - tolerations tests', () = projectListPage.navigate(); projectListPage.filterProjectByName(projectName); projectListPage.findProjectLink(projectName).click(); - projectDetails.findSectionTab('workbenches').click(); + retryClickTab(() => projectDetails.findSectionTab('workbenches'), 'workbenches'); // Stop workbench and verify it stops running cy.step(`Stop workbench ${testData.workbenchName}`); @@ -180,7 +181,7 @@ describe('[Automation Bug RHOAIENG-20099] Workbenches - tolerations tests', () = projectListPage.navigate(); projectListPage.filterProjectByName(projectName); projectListPage.findProjectLink(projectName).click(); - projectDetails.findSectionTab('workbenches').click(); + retryClickTab(() => projectDetails.findSectionTab('workbenches'), 'workbenches'); // Stop workbench and verify it stops running cy.step(`Restart workbench ${testData.workbenchName} and validate it has been started`); @@ -217,7 +218,7 @@ describe('[Automation Bug RHOAIENG-20099] Workbenches - tolerations tests', () = projectListPage.navigate(); projectListPage.filterProjectByName(projectName); projectListPage.findProjectLink(projectName).click(); - projectDetails.findSectionTab('workbenches').click(); + retryClickTab(() => projectDetails.findSectionTab('workbenches'), 'workbenches'); // Create a second workbench with Config Map variables by uploading a yaml file cy.step(`Create a second workbench ${testData.workbenchName2} using config map variables`); diff --git a/frontend/src/__tests__/cypress/cypress/tests/e2e/dataScienceProjects/workbenches/testWorkbenchVariables.cy.ts b/frontend/src/__tests__/cypress/cypress/tests/e2e/dataScienceProjects/workbenches/testWorkbenchVariables.cy.ts index ce2e0e8d16..252fd71405 100644 --- a/frontend/src/__tests__/cypress/cypress/tests/e2e/dataScienceProjects/workbenches/testWorkbenchVariables.cy.ts +++ b/frontend/src/__tests__/cypress/cypress/tests/e2e/dataScienceProjects/workbenches/testWorkbenchVariables.cy.ts @@ -10,6 +10,7 @@ import { retryableBeforeEach, wasSetupPerformed, } from '~/__tests__/cypress/cypress/utils/retryableHooks'; +import { retryClickTab } from '~/__tests__/cypress/cypress/utils/tabUtils'; describe('Workbenches - variable tests', () => { let projectName: string; @@ -59,7 +60,7 @@ describe('Workbenches - variable tests', () => { projectListPage.navigate(); projectListPage.filterProjectByName(projectName); projectListPage.findProjectLink(projectName).click(); - projectDetails.findSectionTab('workbenches').click(); + retryClickTab(() => projectDetails.findSectionTab('workbenches'), 'workbenches'); // Create workbench with Secret variables by uploading a yaml file cy.step(`Create workbench ${workbenchName} using secret variables`); @@ -135,7 +136,7 @@ describe('Workbenches - variable tests', () => { projectListPage.navigate(); projectListPage.filterProjectByName(projectName); projectListPage.findProjectLink(projectName).click(); - projectDetails.findSectionTab('workbenches').click(); + retryClickTab(() => projectDetails.findSectionTab('workbenches'), 'workbenches'); // Create workbench with Secret variables via Key / Value cy.step(`Create workbench ${workbenchName} using secret variables`); diff --git a/frontend/src/__tests__/cypress/cypress/tests/e2e/dataScienceProjects/workbenches/workbenches.cy.ts b/frontend/src/__tests__/cypress/cypress/tests/e2e/dataScienceProjects/workbenches/workbenches.cy.ts index cd87960b87..8dacc22adf 100644 --- a/frontend/src/__tests__/cypress/cypress/tests/e2e/dataScienceProjects/workbenches/workbenches.cy.ts +++ b/frontend/src/__tests__/cypress/cypress/tests/e2e/dataScienceProjects/workbenches/workbenches.cy.ts @@ -15,6 +15,7 @@ import { retryableBefore, wasSetupPerformed, } from '~/__tests__/cypress/cypress/utils/retryableHooks'; +import { retryClickTab } from '~/__tests__/cypress/cypress/utils/tabUtils'; describe('Workbench and PVSs tests', () => { let projectName: string; @@ -76,7 +77,7 @@ describe('Workbench and PVSs tests', () => { projectListPage.navigate(); projectListPage.filterProjectByName(projectName); projectListPage.findProjectLink(projectName).click(); - projectDetails.findSectionTab('workbenches').click(); + retryClickTab(() => projectDetails.findSectionTab('workbenches'), 'workbenches'); cy.step(`Create Workbench ${projectName} using storage ${PVCDisplayName}`); workbenchPage.findCreateButton().click(); @@ -96,7 +97,7 @@ describe('Workbench and PVSs tests', () => { notebookRow.shouldHaveContainerSize('Small'); cy.step(`Check the cluster storage ${PVCDisplayName} is now connected to ${workbenchName}`); - projectDetails.findSectionTab('cluster-storages').click(); + retryClickTab(() => projectDetails.findSectionTab('cluster-storages'), 'cluster-storages'); const csRow = clusterStorage.getClusterStorageRow(PVCDisplayName); csRow.findConnectedWorkbenches().should('have.text', workbenchName); }, diff --git a/frontend/src/__tests__/cypress/cypress/tests/e2e/learningResources/testCustomResourceCreation.cy.ts b/frontend/src/__tests__/cypress/cypress/tests/e2e/learningResources/testCustomResourceCreation.cy.ts index b524260f0e..2201f6d8a8 100644 --- a/frontend/src/__tests__/cypress/cypress/tests/e2e/learningResources/testCustomResourceCreation.cy.ts +++ b/frontend/src/__tests__/cypress/cypress/tests/e2e/learningResources/testCustomResourceCreation.cy.ts @@ -13,7 +13,7 @@ import { wasSetupPerformed, } from '~/__tests__/cypress/cypress/utils/retryableHooks'; -describe('[Product Bug: RHOAIENG-5317]Create a custom resource Quickstart by using Dashboard CRDs', () => { +describe('[Dashboard Bug: RHOAIENG-5317]Create a custom resource Quickstart by using Dashboard CRDs', () => { let resourcesData: ResourcesData; let resourceNames: ReturnType; diff --git a/frontend/src/__tests__/cypress/cypress/tests/e2e/storageClasses/clusterStorage.cy.ts b/frontend/src/__tests__/cypress/cypress/tests/e2e/storageClasses/clusterStorage.cy.ts index 8d2a8f613d..3bbe814be8 100644 --- a/frontend/src/__tests__/cypress/cypress/tests/e2e/storageClasses/clusterStorage.cy.ts +++ b/frontend/src/__tests__/cypress/cypress/tests/e2e/storageClasses/clusterStorage.cy.ts @@ -11,6 +11,7 @@ import { retryableBefore, wasSetupPerformed, } from '~/__tests__/cypress/cypress/utils/retryableHooks'; +import { retryClickTab } from '~/__tests__/cypress/cypress/utils/tabUtils'; const dspName = 'qe-cluster-storage-sc-dsp'; @@ -40,7 +41,7 @@ describe('Regular Users can make use of the Storage Classes in the Cluster Stora projectListPage.findProjectLink(dspName).click(); cy.step('Navigate to the Cluster Storage tab and disable all non-default storage classes'); // Go to cluster storage tab - projectDetails.findSectionTab('cluster-storages').click(); + retryClickTab(() => projectDetails.findSectionTab('cluster-storages'), 'cluster-storages'); // Disable all non-default storage classes disableNonDefaultStorageClasses().then(() => { // Open the Create cluster storage Modal diff --git a/frontend/src/__tests__/cypress/cypress/utils/tabUtils.ts b/frontend/src/__tests__/cypress/cypress/utils/tabUtils.ts new file mode 100644 index 0000000000..ad5e1abc77 --- /dev/null +++ b/frontend/src/__tests__/cypress/cypress/utils/tabUtils.ts @@ -0,0 +1,14 @@ +// utils/tabUtils.ts + +/** + * Clicks a section tab with retry mechanism and verifies URL after click. + * @param getTabSelector A function that returns the selector for the tab element. + * @param sectionId The ID of the section to verify in the URL (e.g., 'workbenches'). + */ +export function retryClickTab( + getTabSelector: () => Cypress.Chainable>, + sectionId: string, +): void { + cy.retryClick(getTabSelector); + cy.url().should('contain', `?section=${sectionId}`); +}