Skip to content

Commit

Permalink
Merge branch 'main' into multipleCQSupport
Browse files Browse the repository at this point in the history
  • Loading branch information
KPostOffice authored Dec 18, 2024
2 parents 635df77 + 065da14 commit de565da
Show file tree
Hide file tree
Showing 27 changed files with 1,375 additions and 301 deletions.
5 changes: 4 additions & 1 deletion backend/src/routes/api/modelRegistries/modelRegistryUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ const patchModelRegistry = async (
dryRun ? 'All' : undefined,
undefined,
undefined,
{ headers: { 'Content-type': PatchUtils.PATCH_FORMAT_JSON_PATCH } },
{ headers: { 'Content-type': PatchUtils.PATCH_FORMAT_JSON_MERGE_PATCH } },
// patchNamespacedCustomObject doesn't support TS generics and returns body as `object`, so we assert its real type
) as Promise<{ body: ModelRegistryKind }>);
return response.body;
Expand Down Expand Up @@ -279,6 +279,9 @@ const updateDatabasePassword = async (
},
undefined,
dryRun ? 'All' : undefined,
undefined,
undefined,
{ headers: { 'Content-type': PatchUtils.PATCH_FORMAT_JSON_MERGE_PATCH } },
);
} else {
await deleteDatabasePasswordSecret(fastify, modelRegistry, modelRegistryNamespace);
Expand Down
107 changes: 107 additions & 0 deletions frontend/src/__tests__/cypress/cypress/pages/hardwareProfile.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import { Contextual } from '~/__tests__/cypress/cypress/pages/components/Contextual';
import { TableRow } from './components/table';

class HardwareProfileTableToolbar extends Contextual<HTMLElement> {
findToggleButton(id: string) {
return this.find().pfSwitch(id).click();
}

findFilterMenuOption(id: string, name: string): Cypress.Chainable<JQuery<HTMLElement>> {
return this.findToggleButton(id).parents().findByRole('menuitem', { name });
}

findFilterInput(name: string): Cypress.Chainable<JQuery<HTMLElement>> {
return this.find().findByLabelText(`Filter by ${name}`);
}

findSearchInput(): Cypress.Chainable<JQuery<HTMLElement>> {
return this.find().findByTestId('filter-toolbar-text-field');
}

selectEnableFilter(name: string) {
this.find()
.findByTestId('hardware-profile-filter-enable-select')
.findSelectOption(name)
.click();
}
}

class HardwareProfileRow extends TableRow {
findDescription() {
return this.find().findByTestId('table-row-title-description');
}

findEnabled() {
return this.find().pfSwitchValue('enable-switch');
}

findEnableSwitch() {
return this.find().pfSwitch('enable-switch');
}

findExpandableSection() {
return this.find().parent().find('[data-label="Other information"]');
}

findNodeResourceTable() {
return this.findExpandableSection().findByTestId('hardware-profile-node-resources-table');
}

findNodeSelectorTable() {
return this.findExpandableSection().findByTestId('hardware-profile-node-selectors-table');
}

findTolerationTable() {
return this.findExpandableSection().findByTestId('hardware-profile-tolerations-table');
}
}

class HardwareProfile {
visit() {
cy.visitWithLogin('/hardwareProfiles');
this.wait();
}

private wait() {
this.findAppPage();
cy.testA11y();
}

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

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

private findTable() {
return cy.findByTestId('hardware-profile-table');
}

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

findRows() {
return this.findTable().find(`[data-label=Name]`);
}

getTableToolbar() {
return new HardwareProfileTableToolbar(() =>
cy.findByTestId('hardware-profiles-table-toolbar'),
);
}

findCreateButton() {
return cy.findByTestId('create-hardware-profile');
}

findClearFiltersButton() {
return cy.findByTestId('clear-filters-button');
}
}

export const hardwareProfile = new HardwareProfile();
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { appChrome } from './appChrome';
import { K8sNameDescriptionField } from './components/subComponents/K8sNameDescriptionField';

export enum FormFieldSelector {
NAME = '#mr-name',
RESOURCENAME = '#resource-mr-name',
HOST = '#mr-host',
PORT = '#mr-port',
USERNAME = '#mr-username',
Expand All @@ -27,6 +27,8 @@ export enum DatabaseDetailsTestId {
}

class ModelRegistrySettings {
k8sNameDescription = new K8sNameDescriptionField('mr');

visit(wait = true) {
cy.visitWithLogin('/modelRegistrySettings');
if (wait) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ declare global {
*/
interceptK8s: (<K extends K8sResourceCommon>(
modelOrOptions: K8sModelCommon | K8sOptions,
response?:
response:
| K
| K8sStatus
| Patch[]
Expand All @@ -67,7 +67,7 @@ declare global {
(<K extends K8sResourceCommon>(
method: 'DELETE' | 'GET' | 'PATCH' | 'POST' | 'PUT',
modelOrOptions: K8sModelCommon | K8sOptions,
response?:
response:
| K
| K8sStatus
| Patch[]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,13 @@ declare global {
type: 'GET /api/modelRegistries',
response: OdhResponse<K8sResourceListResult<ModelRegistryKind>>,
) => Cypress.Chainable<null>) &
((
type: 'PATCH /api/modelRegistries/:modelRegistryName',
options: {
path: { modelRegistryName: string };
},
response: OdhResponse<{ modelRegistry: ModelRegistryKind; databasePassword?: string }>,
) => Cypress.Chainable<null>) &
((
type: 'GET /api/modelRegistries/:modelRegistryName',
options: {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
import { hardwareProfile } from '~/__tests__/cypress/cypress/pages/hardwareProfile';
import { mockHardwareProfile } from '~/__mocks__/mockHardwareProfile';
import { deleteModal } from '~/__tests__/cypress/cypress/pages/components/DeleteModal';
import { HardwareProfileModel } from '~/__tests__/cypress/cypress/utils/models';
import { mock200Status, mockK8sResourceList } from '~/__mocks__';
import { be } from '~/__tests__/cypress/cypress/utils/should';
import { asProductAdminUser } from '~/__tests__/cypress/cypress/utils/mockUsers';
import { testPagination } from '~/__tests__/cypress/cypress/utils/pagination';

const initIntercepts = () => {
cy.interceptK8sList(
{ model: HardwareProfileModel, ns: 'opendatahub' },
mockK8sResourceList([
mockHardwareProfile({ displayName: 'Test Hardware Profile' }),
mockHardwareProfile({
name: 'test-hardware-profile-delete',
displayName: 'Test Hardware Profile Delete',
enabled: false,
}),
]),
);
};

describe('Hardware Profile', () => {
beforeEach(() => {
asProductAdminUser();
});

describe('main table', () => {
it('table sorting and pagination', () => {
const totalItems = 50;
cy.interceptK8sList(
{ model: HardwareProfileModel, ns: 'opendatahub' },
mockK8sResourceList(
Array.from({ length: totalItems }, (_, i) =>
mockHardwareProfile({
displayName: `Test Hardware Profile - ${i}`,
description: `hardware profile ${i}`,
}),
),
),
);
hardwareProfile.visit();
const tableRow = hardwareProfile.getRow('Test Hardware Profile - 0');
tableRow.findDescription().contains('hardware profile 0');

// top pagination
testPagination({
totalItems,
firstElement: 'Test Hardware Profile - 0',
paginationVariant: 'top',
});

// bottom pagination
testPagination({
totalItems,
firstElement: 'Test Hardware Profile - 0',
paginationVariant: 'bottom',
});

//sort by Name
hardwareProfile.findTableHeaderButton('Name').click();
hardwareProfile.findTableHeaderButton('Name').should(be.sortDescending);
hardwareProfile.findTableHeaderButton('Name').click();
hardwareProfile.findTableHeaderButton('Name').should(be.sortAscending);

// sort by last modified
hardwareProfile.findTableHeaderButton('Last modified').click();
hardwareProfile.findTableHeaderButton('Last modified').should(be.sortAscending);
hardwareProfile.findTableHeaderButton('Last modified').click();
hardwareProfile.findTableHeaderButton('Last modified').should(be.sortDescending);

hardwareProfile.findCreateButton().should('be.enabled');
});

it('table filtering and searching ', () => {
initIntercepts();
hardwareProfile.visit();

const hardwareProfileTableToolbar = hardwareProfile.getTableToolbar();
hardwareProfile.findRows().should('have.length', 2);

hardwareProfileTableToolbar.findSearchInput().type('Test Hardware Profile Delete');
hardwareProfile.findRows().should('have.length', 1);
hardwareProfile.getRow('Test Hardware Profile Delete').find().should('exist');

hardwareProfileTableToolbar.findFilterInput('name').clear();
hardwareProfile.findRows().should('have.length', 2);

hardwareProfileTableToolbar
.findFilterMenuOption('filter-toolbar-dropdown', 'Enabled')
.click();
hardwareProfileTableToolbar.selectEnableFilter('Enabled');
hardwareProfile.findRows().should('have.length', 1);
hardwareProfile.getRow('Test Hardware Profile').find().should('exist');

hardwareProfileTableToolbar.selectEnableFilter('Disabled');
hardwareProfile.findRows().should('have.length', 1);
hardwareProfile.getRow('Test Hardware Profile Delete').find().should('exist');
hardwareProfileTableToolbar.findFilterMenuOption('filter-toolbar-dropdown', 'Name').click();
hardwareProfileTableToolbar.findFilterInput('name').type('No match');
hardwareProfile.findRows().should('have.length', 0);
hardwareProfile.findClearFiltersButton().click();
hardwareProfile.findRows().should('have.length', 2);
});

it('delete hardware profile', () => {
initIntercepts();
cy.interceptK8s(
'DELETE',
{
model: HardwareProfileModel,
ns: 'test-project',
name: 'test-hardware-profile-delete',
},
mock200Status({}),
).as('delete');
hardwareProfile.visit();
hardwareProfile.getRow('Test Hardware Profile Delete').findKebabAction('Delete').click();
deleteModal.findSubmitButton().should('be.disabled');
deleteModal.findInput().fill('Test Hardware Profile Delete');
deleteModal.findSubmitButton().should('be.enabled').click();
cy.wait('@delete');
});

it('toggle hardware profile enablement', () => {
initIntercepts();
cy.interceptK8s('PATCH', HardwareProfileModel, mockHardwareProfile({})).as(
'toggleHardwareProfile',
);
hardwareProfile.visit();
hardwareProfile.getRow('Test Hardware Profile Delete').findEnabled().should('not.be.checked');
hardwareProfile.getRow('Test Hardware Profile').findEnabled().should('be.checked');
hardwareProfile.getRow('Test Hardware Profile').findEnableSwitch().click();

cy.wait('@toggleHardwareProfile').then((interception) => {
expect(interception.request.body).to.eql([
{ op: 'replace', path: '/spec/enabled', value: false },
]);
});
hardwareProfile.getRow('Test Hardware Profile').findEnabled().should('not.be.checked');
hardwareProfile.getRow('Test Hardware Profile').findEnableSwitch().click();
cy.wait('@toggleHardwareProfile').then((interception) => {
expect(interception.request.body).to.eql([
{ op: 'replace', path: '/spec/enabled', value: true },
]);
});
hardwareProfile.getRow('Test Hardware Profile').findEnabled().should('be.checked');
});
});

describe('expandable section', () => {
it('should hide nested tables that do not have data', () => {
cy.interceptK8sList(
{ model: HardwareProfileModel, ns: 'opendatahub' },
mockK8sResourceList([
mockHardwareProfile({
displayName: 'Test Hardware Profile',
}),
mockHardwareProfile({
displayName: 'Test Hardware Profile Empty',
nodeSelectors: [],
identifiers: [],
tolerations: [],
}),
]),
);
hardwareProfile.visit();
const row1 = hardwareProfile.getRow('Test Hardware Profile');
row1.findExpandButton().click();
row1.findNodeSelectorTable().should('exist');
row1.findNodeResourceTable().should('exist');
row1.findTolerationTable().should('exist');
const row2 = hardwareProfile.getRow('Test Hardware Profile Empty');
row2.findExpandButton().click();
row2.findNodeSelectorTable().should('not.exist');
row2.findNodeResourceTable().should('not.exist');
row2.findTolerationTable().should('not.exist');
});

it('should show dash when there is no value on the tolerations table', () => {
cy.interceptK8sList(
{ model: HardwareProfileModel, ns: 'opendatahub' },
mockK8sResourceList([
mockHardwareProfile({
displayName: 'Test Hardware Profile Empty',
nodeSelectors: [],
identifiers: [],
tolerations: [{ key: 'test-key' }],
}),
]),
);
hardwareProfile.visit();
const row = hardwareProfile.getRow('Test Hardware Profile Empty');
row.findExpandButton().click();
row.findNodeSelectorTable().should('not.exist');
row.findNodeResourceTable().should('not.exist');
row.findTolerationTable().should('exist');

row.findTolerationTable().find(`[data-label=Operator]`).should('contain.text', '-');
row.findTolerationTable().find(`[data-label=Key]`).should('contain.text', 'test-key');
row.findTolerationTable().find(`[data-label=Value]`).should('contain.text', '-');
row.findTolerationTable().find(`[data-label=Effect]`).should('contain.text', '-');
row
.findTolerationTable()
.find(`[data-label="Toleration seconds"]`)
.should('contain.text', '-');
});
});
});
Loading

0 comments on commit de565da

Please sign in to comment.