From 2760f9332459b1d59ffbf26433bd435fcef26956 Mon Sep 17 00:00:00 2001 From: Anthony Coughlin <33689325+antowaddle@users.noreply.github.com> Date: Wed, 5 Feb 2025 15:36:46 +0000 Subject: [PATCH] Cypress e2e Test - Workbench Status Tests (#3726) * Initial WIP version of resource creation test * Experimental changes to poll the UI for updates * Working version if resource is present * increase card timeout and delete active wait * Added changes to find namespace from variables * Final changes to read variables, cleaned up utils * Small change to a comment * Dummy change to trigger mocks * Save changes on cypress-RHOAIENG-12649 * Changed file directories and names as requested on a PR comment * Saving changes to current branch * Additional directory/file name changes * Additional changes to save * Resolving timeout issue breaking mock tests, also resolved latest PR comments * Further changes for this test * Changes to revert the exist method appended to getCardView. * Fixed linting * Linting fixes * Final comments added * Fixed merge conflict * Small change to page object name * dummy commit * Removed RHOAI bug workaround * Removed comments * Last comment change * Committing working test --------- Co-authored-by: Fede Alonso --- .../testWorkbenchStatus.yaml | 3 + .../cypress/cypress/pages/workbench.ts | 38 +++++++ .../testClusterStorageCreation.cy.ts | 4 +- .../workbenches/testWorkbenchEditing.cy.ts | 4 +- .../workbenches/testWorkbenchStatus.cy.ts | 102 ++++++++++++++++++ .../src/__tests__/cypress/cypress/types.ts | 5 + .../cypress/cypress/utils/dataLoader.ts | 9 ++ 7 files changed, 162 insertions(+), 3 deletions(-) create mode 100644 frontend/src/__tests__/cypress/cypress/fixtures/e2e/dataScienceProjects/testWorkbenchStatus.yaml create mode 100644 frontend/src/__tests__/cypress/cypress/tests/e2e/dataScienceProjects/workbenches/testWorkbenchStatus.cy.ts diff --git a/frontend/src/__tests__/cypress/cypress/fixtures/e2e/dataScienceProjects/testWorkbenchStatus.yaml b/frontend/src/__tests__/cypress/cypress/fixtures/e2e/dataScienceProjects/testWorkbenchStatus.yaml new file mode 100644 index 0000000000..efcc208eb7 --- /dev/null +++ b/frontend/src/__tests__/cypress/cypress/fixtures/e2e/dataScienceProjects/testWorkbenchStatus.yaml @@ -0,0 +1,3 @@ +# testWorkbenchStatus.cy.ts Test Data # + wbStatusTestNamespace: 'dsp-wb-status-test' + wbStatusTestDescription: 'This is a test description' \ No newline at end of file diff --git a/frontend/src/__tests__/cypress/cypress/pages/workbench.ts b/frontend/src/__tests__/cypress/cypress/pages/workbench.ts index 7efa10cb13..8e0d3e178b 100644 --- a/frontend/src/__tests__/cypress/cypress/pages/workbench.ts +++ b/frontend/src/__tests__/cypress/cypress/pages/workbench.ts @@ -449,6 +449,43 @@ class NotFoundSpawnerPage { } } +class WorkbenchStatusModal extends Modal { + constructor() { + super('Workbench status'); + } + + findProgressTab() { + return cy.findByTestId('expand-progress'); + } + + findProgressSteps() { + return cy.findByTestId('notebook-startup-steps').find('[data-testid^="step-status-"]'); + } + + getStepTitle($step: JQuery) { + return cy.wrap($step).find('[id$="-title"]').invoke('text'); + } + + assertStepSuccess($step: JQuery) { + return cy + .wrap($step) + .should('have.attr', 'data-testid') + .and('match', /^step-status-Success/); + } + + findEventlogTab() { + return cy.findByTestId('expand-logs'); + } + + findLogEntry(text: string) { + return cy.get('ul[data-id="event-logs"]').find('li span').contains(text); + } + + getNotebookStatus(expectedStatus: string) { + return cy.get('[data-testid="notebook-status-text"]').should('contain.text', expectedStatus); + } +} + export const workbenchPage = new WorkbenchPage(); export const createSpawnerPage = new CreateSpawnerPage(); export const notebookConfirmModal = new NotebookConfirmModal(); @@ -458,3 +495,4 @@ export const storageModal = new StorageModal(); export const notFoundSpawnerPage = new NotFoundSpawnerPage(); export const attachConnectionModal = new AttachConnectionModal(); export const attachExistingStorageModal = new AttachExistingStorageModal(); +export const workbenchStatusModal = new WorkbenchStatusModal(); 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 634e6af7b4..f45e3caf9c 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 @@ -94,10 +94,12 @@ describe('Verify Cluster Storage - Creating, Editing and Deleting', () => { // Delete the Cluster Storage and confirm that the deletion was successful cy.step('Delete the Cluster Storage and verify deletion'); + // Note reload is required to ensure that the new edited name is propagated + cy.reload(); clusterStorage.findKebabToggle().click(); clusterStorage.getClusterStorageRow(pvStorageName).findKebabAction('Delete storage').click(); deleteModal.shouldBeOpen(); - deleteModal.findInput().type(pvStorageName); + deleteModal.findInput().type(pvStorageNameEdited); deleteModal.findSubmitButton().should('be.enabled').click(); clusterStorage.findEmptyState().should('exist'); }, 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 9c941e9ebe..64cbb5cba7 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 @@ -6,7 +6,7 @@ import { loadPVCEditFixture } from '~/__tests__/cypress/cypress/utils/dataLoader import { createCleanProject } from '~/__tests__/cypress/cypress/utils/projectChecker'; import { deleteOpenShiftProject } from '~/__tests__/cypress/cypress/utils/oc_commands/project'; -describe('Edit and Update a Workbench in RHOAI', () => { +describe('[Known Bug: RHOAIENG-18414 ]Edit and Update a Workbench in RHOAI', () => { let editTestNamespace: string; let editedTestNamespace: string; let editedTestDescription: string; @@ -41,7 +41,7 @@ describe('Edit and Update a Workbench in RHOAI', () => { it( 'Editing Workbench Name and Description', - { tags: ['@Sanity', '@SanitySet1', '@ODS-1931', '@Dashboard'] }, + { tags: ['@Sanity', '@SanitySet1', '@ODS-1931', '@Dashboard', '@Bug'] }, () => { const workbenchName = editTestNamespace.replace('dsp-', ''); 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 new file mode 100644 index 0000000000..4fb05790da --- /dev/null +++ b/frontend/src/__tests__/cypress/cypress/tests/e2e/dataScienceProjects/workbenches/testWorkbenchStatus.cy.ts @@ -0,0 +1,102 @@ +import type { WBStatusTestData } from '~/__tests__/cypress/cypress/types'; +import { projectDetails, projectListPage } from '~/__tests__/cypress/cypress/pages/projects'; +import { + workbenchPage, + createSpawnerPage, + workbenchStatusModal, +} from '~/__tests__/cypress/cypress/pages/workbench'; +import { HTPASSWD_CLUSTER_ADMIN_USER } from '~/__tests__/cypress/cypress/utils/e2eUsers'; +import { loadWBStatusFixture } from '~/__tests__/cypress/cypress/utils/dataLoader'; +import { createCleanProject } from '~/__tests__/cypress/cypress/utils/projectChecker'; +import { deleteOpenShiftProject } from '~/__tests__/cypress/cypress/utils/oc_commands/project'; + +describe('Workbenches - status tests', () => { + let projectName: string; + let projectDescription: string; + + // Setup: Load test data and ensure clean state + before(() => { + return loadWBStatusFixture('e2e/dataScienceProjects/testWorkbenchStatus.yaml') + .then((fixtureData: WBStatusTestData) => { + projectName = fixtureData.wbStatusTestNamespace; + projectDescription = fixtureData.wbStatusTestDescription; + + if (!projectName) { + throw new Error('Project name is undefined or empty in the loaded fixture'); + } + cy.log(`Loaded project name: ${projectName}`); + return createCleanProject(projectName); + }) + .then(() => { + cy.log(`Project ${projectName} confirmed to be created and verified successfully`); + }); + }); + + after(() => { + // Delete provisioned Project + if (projectName) { + cy.log(`Deleting Project ${projectName} after the test has finished.`); + deleteOpenShiftProject(projectName); + } + }); + + it( + 'Verify user can access progress and event log - validate status and successful workbench creation', + { tags: ['@Sanity', '@SanitySet2', '@ODS-1970', '@Dashboard'] }, + () => { + const workbenchName = projectName.replace('dsp-', ''); + + // Authentication and navigation + cy.step('Log into the application'); + cy.visitWithLogin('/', HTPASSWD_CLUSTER_ADMIN_USER); + + // Project navigation and select workbenches + cy.step(`Navigate to workbenches tab of Project ${projectName}`); + projectListPage.navigate(); + projectListPage.filterProjectByName(projectName); + projectListPage.findProjectLink(projectName).click(); + projectDetails.findSectionTab('workbenches').click(); + + // Create workbench + cy.step(`Create workbench ${workbenchName}`); + workbenchPage.findCreateButton().click(); + createSpawnerPage.getNameInput().fill(workbenchName); + createSpawnerPage.getDescriptionInput().type(projectDescription); + createSpawnerPage.findNotebookImage('code-server-notebook').click(); + createSpawnerPage.findSubmitButton().click(); + + // Wait for workbench to run + cy.step(`Wait for workbench ${workbenchName} to display a "Running" status`); + const notebookRow = workbenchPage.getNotebookRow(workbenchName); + notebookRow.findNotebookDescription(projectDescription); + notebookRow.expectStatusLabelToBe('Running', 120000); + notebookRow.shouldHaveNotebookImageName('code-server'); + notebookRow.shouldHaveContainerSize('Small'); + + // Click on 'Running' status and validate the Progress steps + cy.step( + 'Click on Running status, validate the Running status and navigate to the Progress tab', + ); + notebookRow.findHaveNotebookStatusText().click(); + workbenchStatusModal.getNotebookStatus('Running'); + + cy.step('Verify that each Progress Step in the list displays with a Success icon'); + workbenchStatusModal.findProgressTab().click(); + workbenchStatusModal.findProgressTab().click(); + workbenchStatusModal.findProgressSteps().each(($step) => { + workbenchStatusModal.assertStepSuccess($step).then(() => { + workbenchStatusModal.getStepTitle($step).then((stepTitle) => { + cy.log(`✅ Step "${stepTitle}" is successful`); + }); + }); + }); + + // Click on the Events log and validate that successful list messages display + cy.step('Verify that each Events log in the list displays with a Successful Message'); + workbenchStatusModal.findEventlogTab().click(); + workbenchStatusModal.findLogEntry('Created container oauth-proxy'); + workbenchStatusModal.findLogEntry('Started container oauth-proxy'); + workbenchStatusModal.findLogEntry('Successfully pulled image'); + }, + ); +}); diff --git a/frontend/src/__tests__/cypress/cypress/types.ts b/frontend/src/__tests__/cypress/cypress/types.ts index 91bb21317e..d436964679 100644 --- a/frontend/src/__tests__/cypress/cypress/types.ts +++ b/frontend/src/__tests__/cypress/cypress/types.ts @@ -105,6 +105,11 @@ export type WBVariablesTestData = { FAKE_VALUE: string; }; +export type WBStatusTestData = { + wbStatusTestNamespace: string; + wbStatusTestDescription: string; +}; + export type CommandLineResult = { code: number; stdout: string; diff --git a/frontend/src/__tests__/cypress/cypress/utils/dataLoader.ts b/frontend/src/__tests__/cypress/cypress/utils/dataLoader.ts index 44bee8210e..08711db1dc 100644 --- a/frontend/src/__tests__/cypress/cypress/utils/dataLoader.ts +++ b/frontend/src/__tests__/cypress/cypress/utils/dataLoader.ts @@ -6,6 +6,7 @@ import type { WBEditTestData, WBControlSuiteTestData, WBVariablesTestData, + WBStatusTestData, } from '~/__tests__/cypress/cypress/types'; // Load fixture function that returns DataScienceProjectData @@ -56,3 +57,11 @@ export const loadWBVariablesFixture = ( return data; }); }; + +export const loadWBStatusFixture = (fixturePath: string): Cypress.Chainable => { + return cy.fixture(fixturePath, 'utf8').then((yamlContent: string) => { + const data = yaml.load(yamlContent) as WBStatusTestData; + + return data; + }); +};