From ce579617031a4ea6d4d3b2ce543330cae75fb498 Mon Sep 17 00:00:00 2001 From: Ashley McEntee Date: Thu, 2 May 2024 09:25:18 -0400 Subject: [PATCH] Update ProjectList Cypress test --- .../cypress/e2e/projects/ProjectList.cy.ts | 135 +++++++++++++++++- .../cypress/cypress/pages/projects.ts | 35 +++++ 2 files changed, 163 insertions(+), 7 deletions(-) diff --git a/frontend/src/__tests__/cypress/cypress/e2e/projects/ProjectList.cy.ts b/frontend/src/__tests__/cypress/cypress/e2e/projects/ProjectList.cy.ts index f28ec1042a..2d721b66e1 100644 --- a/frontend/src/__tests__/cypress/cypress/e2e/projects/ProjectList.cy.ts +++ b/frontend/src/__tests__/cypress/cypress/e2e/projects/ProjectList.cy.ts @@ -4,11 +4,25 @@ import { createProjectModal, projectListPage } from '~/__tests__/cypress/cypress import { deleteModal } from '~/__tests__/cypress/cypress/pages/components/DeleteModal'; import { ProjectKind } from '~/k8sTypes'; import { incrementResourceVersion } from '~/__mocks__/mockUtils'; -import { ProjectModel, ProjectRequestModel } from '~/__tests__/cypress/cypress/utils/models'; +import { + NotebookModel, + PodModel, + ProjectModel, + ProjectRequestModel, + RouteModel, +} from '~/__tests__/cypress/cypress/utils/models'; import { mock200Status } from '~/__mocks__/mockK8sStatus'; +import { mockNotebookK8sResource, mockRouteK8sResource } from '~/__mocks__'; +import { mockPodK8sResource } from '~/__mocks__/mockPodK8sResource'; import { asProjectAdminUser } from '~/__tests__/cypress/cypress/utils/users'; +import { notebookConfirmModal } from '~/__tests__/cypress/cypress/pages/workbench'; import { testPagination } from '~/__tests__/cypress/cypress/utils/pagination'; +const mockProject = mockProjectK8sResource({}); +const initIntercepts = () => { + cy.interceptK8sList(ProjectModel, mockK8sResourceList([mockProject])); +}; + describe('Data science projects details', () => { it('should not have option to create new project', () => { asProjectAdminUser({ isSelfProvisioner: false }); @@ -45,8 +59,16 @@ describe('Data science projects details', () => { cy.url().should('include', '/projects/test-project'); }); + it('should test url for workbench creation', () => { + initIntercepts(); + projectListPage.visit(); + projectListPage.findCreateWorkbenchButton().click(); + + cy.url().should('include', '/projects/test-project/spawner'); + }); + it('should list the new project', () => { - cy.interceptK8sList(ProjectModel, mockK8sResourceList([mockProjectK8sResource({})])); + initIntercepts(); projectListPage.visit(); projectListPage.shouldHaveProjects(); const projectRow = projectListPage.getProjectRow('Test Project'); @@ -54,9 +76,7 @@ describe('Data science projects details', () => { }); it('should delete project', () => { - const mockProject = mockProjectK8sResource({}); - cy.interceptK8sList(ProjectModel, mockK8sResourceList([mockProject])); - + initIntercepts(); projectListPage.visit(); projectListPage.getProjectRow('Test Project').findKebabAction('Delete project').click(); deleteModal.shouldBeOpen(); @@ -83,7 +103,7 @@ describe('Data science projects details', () => { it('validate pagination', () => { const totalItems = 50; - const mockProject: ProjectKind[] = Array.from({ length: totalItems }, (_, i) => + const mockProjects: ProjectKind[] = Array.from({ length: totalItems }, (_, i) => mockProjectK8sResource({ k8sName: `ds-project-${i}`, displayName: `DS Project ${i}`, @@ -91,7 +111,7 @@ describe('Data science projects details', () => { }), ); mockProjectK8sResource({}); - cy.interceptK8sList(ProjectModel, mockK8sResourceList(mockProject)); + cy.interceptK8sList(ProjectModel, mockK8sResourceList(mockProjects)); projectListPage.visit(); // top pagination @@ -133,6 +153,107 @@ describe('Data science projects details', () => { projectListPage.findProjectLink('DS Project 2').should('not.exist'); projectListPage.findProjectLink('renamed').should('not.exist'); }); + + describe('Table filter', () => { + it('filter by name', () => { + initIntercepts(); + projectListPage.visit(); + + // Select the "Name" filter + const projectListToolbar = projectListPage.getTableToolbar(); + projectListToolbar.findFilterMenuOption('filter-dropdown-select', 'Name').click(); + projectListToolbar.findSearchInput().type('Test Project'); + // Verify only rows with the typed run name exist + projectListPage.getProjectRow('Test Project').find().should('exist'); + }); + + it('filter by user', () => { + initIntercepts(); + projectListPage.visit(); + + // Select the "User" filter + const projectListToolbar = projectListPage.getTableToolbar(); + projectListToolbar.findFilterMenuOption('filter-dropdown-select', 'User').click(); + projectListToolbar.findSearchInput().type('test-user'); + // Verify only rows with the typed run user exist + projectListPage.getProjectRow('Test Project').find().should('exist'); + }); + }); + + it('Validate that clicking on switch toggle will open modal to stop workbench', () => { + cy.interceptK8sList(ProjectModel, mockK8sResourceList([mockProjectK8sResource({})])); + cy.interceptK8s('PATCH', NotebookModel, mockNotebookK8sResource({})).as('stopWorkbench'); + cy.interceptK8sList(PodModel, mockK8sResourceList([mockPodK8sResource({})])); + cy.interceptK8s(RouteModel, mockRouteK8sResource({ notebookName: 'test-notebook' })).as( + 'getWorkbench', + ); + cy.interceptK8sList( + { model: NotebookModel, ns: 'test-project' }, + mockK8sResourceList([ + mockNotebookK8sResource({ + opts: { + spec: { + template: { + spec: { + containers: [ + { + name: 'test-notebook', + image: 'test-image:latest', + }, + ], + }, + }, + }, + metadata: { + name: 'test-notebook', + labels: { + 'opendatahub.io/notebook-image': 'true', + }, + annotations: { + 'opendatahub.io/image-display-name': 'Test image', + }, + }, + }, + }), + ]), + ); + projectListPage.visit(); + cy.wait('@getWorkbench'); + const projectTableRow = projectListPage.getProjectRow('Test Project'); + projectTableRow.findEnableSwitch().click(); + + //stop workbench + notebookConfirmModal.findStopWorkbenchButton().should('be.enabled'); + cy.interceptK8s( + NotebookModel, + mockNotebookK8sResource({ + opts: { + metadata: { + labels: { + 'opendatahub.io/notebook-image': 'true', + }, + annotations: { + 'kubeflow-resource-stopped': '2023-02-14T21:45:14Z', + 'opendatahub.io/image-display-name': 'Test image', + }, + }, + }, + }), + ); + cy.interceptK8sList(PodModel, mockK8sResourceList([mockPodK8sResource({ isRunning: false })])); + + notebookConfirmModal.findStopWorkbenchButton().click(); + cy.wait('@stopWorkbench').then((interception) => { + expect(interception.request.body).to.containSubset([ + { + op: 'add', + path: '/metadata/annotations/kubeflow-resource-stopped', + }, + ]); + }); + projectTableRow.findNotebookStatusText().should('have.text', 'Stopped '); + projectTableRow.findNotebookRouteLink().should('have.attr', 'aria-disabled', 'true'); + }); }); const deletedMockProjectResource = (resource: ProjectKind): ProjectKind => diff --git a/frontend/src/__tests__/cypress/cypress/pages/projects.ts b/frontend/src/__tests__/cypress/cypress/pages/projects.ts index f0978163bf..ab02c97cb8 100644 --- a/frontend/src/__tests__/cypress/cypress/pages/projects.ts +++ b/frontend/src/__tests__/cypress/cypress/pages/projects.ts @@ -2,7 +2,9 @@ import { Modal } from '~/__tests__/cypress/cypress/pages/components/Modal'; import { appChrome } from '~/__tests__/cypress/cypress/pages/appChrome'; import { DeleteModal } from '~/__tests__/cypress/cypress/pages/components/DeleteModal'; import { TableRow } from './components/table'; +import { TableToolbar } from './components/TableToolbar'; +class ProjectListToolbar extends TableToolbar {} class NotebookRow extends TableRow { findNotebookImageAvailability() { return cy.findByTestId('notebook-image-availability'); @@ -21,6 +23,18 @@ class ProjectRow extends TableRow { shouldHaveProjectIcon() { return this.find().findByTestId('ds-project-image').should('exist'); } + + findEnableSwitch() { + return this.find().pfSwitch('notebook-status-switch'); + } + + findNotebookRouteLink() { + return this.find().findByTestId('notebook-route-link'); + } + + findNotebookStatusText() { + return this.find().findByTestId('notebook-status-text'); + } } class ProjectListPage { @@ -44,6 +58,11 @@ class ProjectListPage { return this; } + shouldReturnNotFound() { + cy.findByTestId('not-found-page').should('exist'); + return this; + } + shouldBeEmpty() { cy.findByTestId('no-data-science-project').should('exist'); return this; @@ -64,6 +83,22 @@ class ProjectListPage { findProjectLink(projectName: string) { return this.findProjectsTable().findByRole('link', { name: projectName }); } + + findEmptyResults() { + return cy.findByTestId('no-result-found-title'); + } + + findSortButton(name: string) { + return this.findProjectsTable().find('thead').findByRole('button', { name }); + } + + getTableToolbar() { + return new ProjectListToolbar(() => cy.findByTestId('dashboard-table-toolbar')); + } + + findCreateWorkbenchButton() { + return cy.findByRole('button', { name: 'Create a workbench' }); + } } class CreateEditProjectModal extends Modal {