Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cypress e2e - cypress-recurse #3826

Draft
wants to merge 13 commits into
base: main
Choose a base branch
from
Draft
18 changes: 18 additions & 0 deletions frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
Original file line number Diff line number Diff line change
@@ -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 {
Expand Down Expand Up @@ -125,7 +126,6 @@ declare global {
*/
// eslint-disable-next-line @typescript-eslint/method-signature-style
findByTestId(id: Matcher | Matcher[], options?: MatcherOptions): Chainable<JQuery>;

/**
* Overwrite `findAllByTestId` to support an array of Matchers.
* When an array of Matches is supplied, parses the data-testid attribute value as a
Expand Down Expand Up @@ -176,6 +176,20 @@ declare global {
getNotebookControllerCullerConfig: (
key?: string,
) => Cypress.Chainable<DashboardConfig | unknown>;

/**
* 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<void>
*/
retryClick: (
// eslint-disable-next-line @typescript-eslint/no-explicit-any
getElement: () => Cypress.Chainable<any>,
maxAttempts?: number,
delay?: number,
) => Cypress.Chainable<void>;
}
}
}
Expand Down Expand Up @@ -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<any>,
maxAttempts = 3,
delay = 1000,
): Cypress.Chainable<void> {
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<any>, maxAttempts = 3, delay = 1000) => {
return retryClick(getElement, maxAttempts, delay);
},
);

Cypress.Commands.add('findKebab', { prevSubject: 'element' }, (subject, isDropdownToggle) => {
Cypress.log({ displayName: 'findKebab' });
return cy
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
},
);
},
);
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,15 @@ import {
retryableBefore,
wasSetupPerformed,
} from '~/__tests__/cypress/cypress/utils/retryableHooks';
import { retryClickTab } from '~/__tests__/cypress/cypress/utils/tabUtils';

let testData: DataScienceProjectData;
let projectName: string;
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')) {
Expand Down Expand Up @@ -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();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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');
},
);
});
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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();
Expand Down
Loading