Skip to content

Commit

Permalink
Merge pull request #2724 from ppadti/administration-page
Browse files Browse the repository at this point in the history
Cypress test for Administration tab
  • Loading branch information
openshift-merge-bot[bot] authored Apr 23, 2024
2 parents 9014a05 + 017d2bd commit d5464e3
Show file tree
Hide file tree
Showing 13 changed files with 349 additions and 5 deletions.
14 changes: 14 additions & 0 deletions frontend/src/__mocks__/mockAllowedUsers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { AllowedUser, PrivilegeState } from '~/pages/notebookController/screens/admin/types';

type MockAllowedUsersType = {
username?: string;
privilege?: PrivilegeState;
};
export const mockAllowedUsers = ({
username = 'test-user',
privilege = PrivilegeState.USER,
}: MockAllowedUsersType): AllowedUser => ({
username,
privilege,
lastActivity: '2024-02-14T14:22:05Z',
});
95 changes: 95 additions & 0 deletions frontend/src/__mocks__/mockNotebookImageInfo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/* eslint-disable camelcase */
import { ImageInfo } from '~/types';

export const mockNotebookImageInfo = (): ImageInfo[] => [
{
name: 'code-server-notebook',
description:
'code-server workbench allows users to code, build, and collaborate on projects directly from web.',
url: 'https://github.com/opendatahub-io/notebooks/tree/main/codeserver',
display_name: 'code-server',
tags: [
{
content: {
software: [
{
name: 'Python',
version: 'v3.9',
},
],
dependencies: [
{
name: 'code-server',
version: '4.11',
},
],
},
name: '2023.1',
recommended: false,
default: false,
},
{
content: {
software: [
{
name: 'Python',
version: 'v3.9',
},
],
dependencies: [
{
name: 'code-server',
version: '4.16',
},
],
},
name: '2023.2',
recommended: true,
default: false,
},
],
order: 8,
dockerImageRepo:
'image-registry.openshift-image-registry.svc:5000/opendatahub/code-server-notebook',
},
{
name: 'custom-test-with-accelerators',
description: '',
url: 'quay.io/opendatahub/workbench-images:jupyter-minimal-ubi8-python-3.8-pr-89',
display_name: 'Test with accelerators',
tags: [
{
content: {
software: [],
dependencies: [],
},
name: 'jupyter-minimal-ubi8-python-3.8-pr-89',
recommended: false,
default: false,
},
],
order: 100,
dockerImageRepo:
'image-registry.openshift-image-registry.svc:5000/opendatahub/custom-test-with-accelerators',
},

{
name: 'custom-image',
description: '',
url: 'quay.io/pnaik/custom-image',
display_name: 'image',
tags: [
{
content: {
software: [],
dependencies: [],
},
name: 'latest',
recommended: false,
default: false,
},
],
order: 100,
dockerImageRepo: 'image-registry.openshift-image-registry.svc:5000/opendatahub/custom-image',
},
];
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import { mockRoleBindingK8sResource } from '~/__mocks__/mockRoleBindingK8sResource';
import { mockK8sResourceList } from '~/__mocks__';
import { RoleBindingSubject } from '~/types';
import { mockAllowedUsers } from '~/__mocks__/mockAllowedUsers';
import { mockNotebookImageInfo } from '~/__mocks__/mockNotebookImageInfo';
import {
administration,
notebookController,
} from '~/__tests__/cypress/cypress/pages/administration';
import { be } from '~/__tests__/cypress/cypress/utils/should';
import { asProductAdminUser, asProjectEditUser } from '~/__tests__/cypress/cypress/utils/users';

const groupSubjects: RoleBindingSubject[] = [
{
kind: 'Group',
apiGroup: 'rbac.authorization.k8s.io',
name: 'group-1',
},
];

const initIntercepts = () => {
cy.interceptOdh('GET /api/status/openshift-ai-notebooks/allowedUsers', [
mockAllowedUsers({}),
mockAllowedUsers({ username: 'regularuser1' }),
]);
cy.interceptOdh(
'GET /api/rolebindings/opendatahub/openshift-ai-notebooks-image-pullers',
mockK8sResourceList([
mockRoleBindingK8sResource({
name: 'group-1',
subjects: groupSubjects,
roleRefName: 'edit',
}),
]),
);
cy.interceptOdh('GET /api/images/:type', { path: { type: 'jupyter' } }, mockNotebookImageInfo());
};

it('Administartion tab should not be accessible for non-project admins', () => {
initIntercepts();
asProjectEditUser();
notebookController.visit();
notebookController.findAdministrationTab().should('not.exist');
notebookController.findSpawnerTab().should('not.exist');
notebookController.findAppTitle().should('contain', 'Start a notebook server');
});

describe('Administration Tab', () => {
beforeEach(() => {
initIntercepts();
asProductAdminUser();
notebookController.visit();
notebookController.findAdministrationTab().click();
});

it('Check table with users details', () => {
administration.shouldHaveManageUsersAlert();
administration.findStopAllServersButton().should('be.disabled');
const userRow = administration.getRow('test-user');
userRow.shouldHavePrivilege('User');
userRow.shouldHaveLastActivity('2 months ago');
});

it('Users table sorting', () => {
// By user
administration.findTableHeaderButton('User').click();
administration.findTableHeaderButton('User').should(be.sortDescending);
administration.findTableHeaderButton('User').click();
administration.findTableHeaderButton('User').should(be.sortAscending);

// By privilege
administration.findTableHeaderButton('Privilege').click();
administration.findTableHeaderButton('Privilege').should(be.sortAscending);
administration.findTableHeaderButton('Privilege').click();
administration.findTableHeaderButton('Privilege').should(be.sortDescending);

// By last activity
administration.findTableHeaderButton('Last activity').click();
administration.findTableHeaderButton('Last activity').should(be.sortAscending);
administration.findTableHeaderButton('Last activity').click();
administration.findTableHeaderButton('Last activity').should(be.sortDescending);

// By server status
administration.findTableHeaderButton('Server status').click();
administration.findTableHeaderButton('Server status').should(be.sortAscending);
administration.findTableHeaderButton('Server status').click();
administration.findTableHeaderButton('Server status').should(be.sortDescending);
});

it('Validate that clicking on "Start server" button will open a form in administartion tab and "Start your server" button will navigate to notebook server tab', () => {
let userRow = administration.getRow('regularuser1');

// open a form in administartion tab with impersonate alert
userRow.findServerStatusButton().click();
administration.shouldHaveImpersonateAlert();
administration.shouldHaveNotebookServerForm();
administration.findImageSelectorRadio('code-server-notebook').should('be.checked');
administration.findStartServerButton().should('be.enabled');
administration.findReturnToAdminViewButton().click();

// Navigate to notebook server tab
userRow = administration.getRow('test-user');
userRow.findServerStatusButton().click();
notebookController.findAppTitle().should('contain', 'Start a notebook server');
notebookController.findAppTitle().should('not.contain', 'Administration');
});
});
97 changes: 97 additions & 0 deletions frontend/src/__tests__/cypress/cypress/pages/administration.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import { TableRow } from './components/table';

class NotebookController {
visit() {
cy.visit('/notebookController/spawner');
this.wait();
}

private wait() {
cy.findByTestId('app-page-title');
cy.testA11y();
}

findAppTitle() {
return cy.findByTestId('app-page-title');
}

findAdministrationTab() {
return cy.findByTestId('admin-tab');
}

findSpawnerTab() {
return cy.findByTestId('spawner-tab');
}
}

class AdministrationTab {
shouldHaveManageUsersAlert() {
cy.findByTestId('manage-users-alert').should(
'have.text',
'Custom alert:Manage users in OpenShiftCreate, delete, and manage permissions for Red Hat OpenShift AI users in OpenShift. Learn more about OpenShift user management',
);
return this;
}

findTableHeaderButton(name: string) {
return this.findTable().find('thead').findByRole('button', { name });
}

shouldHaveImpersonateAlert() {
cy.findByTestId('impersonate-alert').should(
'have.text',
'Info alert:This notebook server is being created for "regularuser1"Return to administration view',
);
return this;
}

findReturnToAdminViewButton() {
return cy.findByTestId('return-admin-view-button');
}

shouldHaveNotebookServerForm() {
cy.findByTestId('notebook-server-form');
return this;
}

findStartServerButton() {
return cy.findByTestId('start-server-button');
}

findImageSelectorRadio(id: string) {
return cy.findByTestId(['radio', id]);
}

findStopAllServersButton() {
return cy.findByTestId('stop-all-servers-button');
}

private findTable() {
return cy.findByTestId('administration-users-table');
}

getRow(name: string) {
return new AdministrationUsersRow(() =>
this.findTable().find(`[data-label=name]`).contains(name).parents('tr'),
);
}
}

class AdministrationUsersRow extends TableRow {
shouldHavePrivilege(privilege: string) {
this.find().find(`[data-label=privilege]`).should('have.text', privilege);
return this;
}

shouldHaveLastActivity(lastActivity: string) {
this.find().find(`[data-label=lastActivity]`).should('have.text', lastActivity);
return this;
}

findServerStatusButton() {
return this.find().findByTestId('server-button');
}
}

export const administration = new AdministrationTab();
export const notebookController = new NotebookController();
15 changes: 14 additions & 1 deletion frontend/src/__tests__/cypress/cypress/support/commands/odh.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@ import type {
DataScienceClusterInitializationKindStatus,
DataScienceClusterKindStatus,
OdhQuickStart,
RoleBindingKind,
ServingRuntimeKind,
TemplateKind,
} from '~/k8sTypes';
import { AllowedUser } from '~/pages/notebookController/screens/admin/types';
import type { StatusResponse } from '~/redux/types';
import type {
BYONImage,
Expand Down Expand Up @@ -87,6 +89,16 @@ declare global {
response: OdhResponse<StatusResponse>,
): Cypress.Chainable<null>;

interceptOdh(
type: 'GET /api/status/openshift-ai-notebooks/allowedUsers',
response: OdhResponse<AllowedUser[]>,
): Cypress.Chainable<null>;

interceptOdh(
type: 'GET /api/rolebindings/opendatahub/openshift-ai-notebooks-image-pullers',
response: OdhResponse<K8sResourceListResult<RoleBindingKind>>,
): Cypress.Chainable<null>;

interceptOdh(
type: 'GET /api/config',
response: OdhResponse<DashboardConfigKind>,
Expand Down Expand Up @@ -165,7 +177,8 @@ declare global {

interceptOdh(
type: 'GET /api/images/:type',
response: OdhResponse<ImageInfo>,
options: { path: { type: string } },
response: OdhResponse<ImageInfo[]>,
): Cypress.Chainable<null>;

interceptOdh(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,14 @@ const ImpersonateAlert: React.FC = () => {
variant="info"
title={`This notebook server is being created for "${impersonatedUsername}"`}
isInline
data-testid="impersonate-alert"
>
<Button data-id="return-admin-view-button" variant="link" onClick={() => setImpersonating()}>
<Button
data-id="return-admin-view-button"
data-testid="return-admin-view-button"
variant="link"
onClick={() => setImpersonating()}
>
Return to administration view
</Button>
</Alert>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,12 @@ const NotebookAdminControl: React.FC = () => {
>
<Stack hasGutter>
<StackItem>
<Alert title="Manage users in OpenShift" component="h2" isInline>
<Alert
title="Manage users in OpenShift"
component="h2"
isInline
data-testid="manage-users-alert"
>
Create, delete, and manage permissions for Red Hat OpenShift AI users in OpenShift.{' '}
<ExternalLink
text="Learn more about OpenShift user management"
Expand All @@ -65,6 +70,7 @@ const NotebookAdminControl: React.FC = () => {
aria-label="Users table"
variant="compact"
data={users}
data-testid="administration-users-table"
enablePagination
columns={columns}
rowRenderer={(user) => (
Expand Down
Loading

0 comments on commit d5464e3

Please sign in to comment.