Skip to content

Commit

Permalink
Registered model table follow-up and cypress testing
Browse files Browse the repository at this point in the history
  • Loading branch information
dpanshug committed Apr 19, 2024
1 parent 14f6605 commit 6199aa6
Show file tree
Hide file tree
Showing 17 changed files with 351 additions and 43 deletions.
20 changes: 20 additions & 0 deletions frontend/src/__mocks__/mockModelVersion.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { ModelVersion, ModelVersionState } from '~/concepts/modelRegistry/types';

type MockModelVersionType = {
author?: string;
registeredModelID?: string;
};

export const mockModelVersion = ({
author = 'Test author',
registeredModelID = '1',
}: MockModelVersionType): ModelVersion => ({
author,
createTimeSinceEpoch: '1712234877179',
customProperties: {},
id: '26',
lastUpdateTimeSinceEpoch: '1712234877179',
name: 'fraud detection model version 1',
state: ModelVersionState.ARCHIVED,
registeredModelID,
});
9 changes: 9 additions & 0 deletions frontend/src/__mocks__/mockModelVersionList.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { ModelVersionList } from '~/concepts/modelRegistry/types';
import { mockModelVersion } from './mockModelVersion';

export const mockModelVersionList = (): ModelVersionList => ({
items: [mockModelVersion({ author: 'Author 1', registeredModelID: '1' })],
nextPageToken: '',
pageSize: 0,
size: 1,
});
23 changes: 18 additions & 5 deletions frontend/src/__mocks__/mockRegisteredModel.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,30 @@
import { RegisteredModel, RegisteredModelState } from '~/concepts/modelRegistry/types';
import {
ModelRegistryBase,
RegisteredModel,
RegisteredModelState,
} from '~/concepts/modelRegistry/types';

type MockRegisteredModelType = { name?: string; state?: RegisteredModelState };
type MockRegisteredModelType = {
id?: string;
name?: string;
state?: RegisteredModelState;
description?: string;
customProperties?: ModelRegistryBase['customProperties'];
};

export const mockRegisteredModel = ({
name = 'test',
state = RegisteredModelState.LIVE,
description = '',
customProperties = {},
id = '1',
}: MockRegisteredModelType): RegisteredModel => ({
createTimeSinceEpoch: '1710404288975',
description: 'test',
description,
externalID: '1234132asdfasdf',
id: '1',
id,
lastUpdateTimeSinceEpoch: '1710404288975',
name,
state,
customProperties: {},
customProperties,
});
112 changes: 110 additions & 2 deletions frontend/src/__mocks__/mockRegisteredModelsList.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,118 @@
/* eslint-disable camelcase */
import { RegisteredModelList } from '~/concepts/modelRegistry/types';
import { mockRegisteredModel } from './mockRegisteredModel';

export const mockRegisteredModelList = ({
size = 2,
size = 5,
}: Partial<RegisteredModelList>): RegisteredModelList => ({
items: [mockRegisteredModel({ name: 'test-1' }), mockRegisteredModel({ name: 'test-2' })],
items: [
mockRegisteredModel({ name: 'test-1' }),
mockRegisteredModel({ name: 'test-2' }),
mockRegisteredModel({
name: 'Fraud detection model',
description:
'A machine learning model trained to detect fraudulent transactions in financial data',
customProperties: {
Financial: {
metadataType: 'MetadataStringValue',
string_value: 'non-empty',
},
'Financial data': {
metadataType: 'MetadataStringValue',
string_value: '',
},
'Fraud detection': {
metadataType: 'MetadataStringValue',
string_value: '',
},
'Test label': {
metadataType: 'MetadataStringValue',
string_value: '',
},
'Machine learning': {
metadataType: 'MetadataStringValue',
string_value: '',
},
'Next data to be overflow': {
metadataType: 'MetadataStringValue',
string_value: '',
},
},
}),
mockRegisteredModel({
name: 'Credit Scoring',
customProperties: {
'Credit Score Predictor': {
metadataType: 'MetadataStringValue',
string_value: '',
},
'Creditworthiness scoring system': {
metadataType: 'MetadataStringValue',
string_value: '',
},
'Default Risk Analyzer': {
metadataType: 'MetadataStringValue',
string_value: '',
},
'Portfolio Management': {
metadataType: 'MetadataStringValue',
string_value: '',
},
'Risk Assessment': {
metadataType: 'MetadataStringValue',
string_value: '',
},
},
}),
mockRegisteredModel({
name: 'Label modal',
description:
'A machine learning model trained to detect fraudulent transactions in financial data',
customProperties: {
'Testing label': {
metadataType: 'MetadataStringValue',
string_value: '',
},
Financial: {
metadataType: 'MetadataStringValue',
string_value: 'non-empty',
},
'Financial data': {
metadataType: 'MetadataStringValue',
string_value: '',
},
'Fraud detection': {
metadataType: 'MetadataStringValue',
string_value: '',
},
'Long label data to be truncated abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc':
{
metadataType: 'MetadataStringValue',
string_value: '',
},
'Machine learning': {
metadataType: 'MetadataStringValue',
string_value: '',
},
'Next data to be overflow': {
metadataType: 'MetadataStringValue',
string_value: '',
},
'Label x': {
metadataType: 'MetadataStringValue',
string_value: '',
},
'Label y': {
metadataType: 'MetadataStringValue',
string_value: '',
},
'Label z': {
metadataType: 'MetadataStringValue',
string_value: '',
},
},
}),
],
nextPageToken: '',
pageSize: 0,
size,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { mockK8sResourceList, mockRouteK8sResourceModelRegistry } from '~/__mocks__';
import { mockComponents } from '~/__mocks__/mockComponents';
import { mockDashboardConfig } from '~/__mocks__/mockDashboardConfig';
import { mockRegisteredModelList } from '~/__mocks__/mockRegisteredModelsList';
import { MODEL_REGISTRY_API_VERSION } from '~/concepts/modelRegistry/const';
import { mockModelRegistry } from '~/__mocks__/mockModelRegistry';
import { modelRegistry } from '~/__tests__/cypress/cypress/pages/modelRegistry';
import { mockK8sResourceList, mockRouteK8sResourceModelRegistry } from '~/__mocks__';
import { mockModelVersionList } from '~/__mocks__/mockModelVersionList';
import { mockRegisteredModelList } from '~/__mocks__/mockRegisteredModelsList';
import { labelModal, modelRegistry } from '~/__tests__/cypress/cypress/pages/modelRegistry';
import { be } from '~/__tests__/cypress/cypress/utils/should';
import { ModelRegistryModel, RouteModel } from '~/__tests__/cypress/cypress/utils/models';
import { MODEL_REGISTRY_API_VERSION } from '~/concepts/modelRegistry/const';

type HandlersProps = {
disableModelRegistryFeature?: boolean;
Expand All @@ -20,6 +22,7 @@ const initIntercepts = ({ disableModelRegistryFeature = false, size = 4 }: Handl
}),
);
cy.interceptOdh('GET /api/components', { query: { installed: 'true' } }, mockComponents());

cy.interceptK8sList(
ModelRegistryModel,
mockK8sResourceList([mockModelRegistry({}), mockModelRegistry({ name: 'test-registry' })]),
Expand All @@ -38,9 +41,14 @@ const initIntercepts = ({ disableModelRegistryFeature = false, size = 4 }: Handl
`GET /api/service/modelregistry/modelregistry-sample/api/model_registry/${MODEL_REGISTRY_API_VERSION}/registered_models`,
mockRegisteredModelList({ size }),
);

cy.interceptOdh(
`GET /api/service/modelregistry/modelregistry-sample/api/model_registry/${MODEL_REGISTRY_API_VERSION}/registered_models/1/versions`,
mockModelVersionList(),
);
};

describe('Model Registry Global', () => {
describe('Model Registry', () => {
it('Model Registry Disabled in the cluster', () => {
initIntercepts({
disableModelRegistryFeature: true,
Expand Down Expand Up @@ -71,4 +79,58 @@ describe('Model Registry Global', () => {
modelRegistry.shouldtableToolbarExist();
modelRegistry.shouldregisteredModelsEmpty();
});

it('Model registry table', () => {
initIntercepts({
disableModelRegistryFeature: false,
});

modelRegistry.visit();

const registeredModelRow = modelRegistry.getRow('Fraud detection model');
registeredModelRow.findName().contains('Fraud detection model');
registeredModelRow
.findDescription()
.contains(
'A machine learning model trained to detect fraudulent transactions in financial data',
);
registeredModelRow.findOwner().contains('Author 1');

// Label popover
registeredModelRow.findLabelPopoverText().contains('2 more');
registeredModelRow.findLabelPopoverText().click();
registeredModelRow.shouldContainsPopoverLabels([
'Machine learning',
'Next data to be overflow',
]);

// Label modal
const registeredModelRow2 = modelRegistry.getRow('Label modal');
registeredModelRow2.findLabelModalText().contains('6 more');
registeredModelRow2.findLabelModalText().click();
labelModal.shouldContainsModalLabels([
'Testing label',
'Financial',
'Financial data',
'Fraud detection',
'Machine learning',
'Next data to be overflow',
'Label x',
'Label y',
'Label z',
]);
labelModal.findModalSearchInput().type('Financial');
labelModal.shouldContainsModalLabels(['Financial', 'Financial data']);
labelModal.findCloseModal().click();

// sort by modelName
modelRegistry.findRegisteredModelTableHeaderButton('Model name').should(be.sortAscending);
modelRegistry.findRegisteredModelTableHeaderButton('Model name').click();
modelRegistry.findRegisteredModelTableHeaderButton('Model name').should(be.sortDescending);

// filtering by keyword
modelRegistry.findTableSearch().type('Fraud detection model');
modelRegistry.findTableRows().should('have.length', 1);
modelRegistry.findTableRows().contains('Fraud detection model');
});
});
71 changes: 71 additions & 0 deletions frontend/src/__tests__/cypress/cypress/pages/modelRegistry.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,52 @@
import { appChrome } from '~/__tests__/cypress/cypress/pages/appChrome';
import { TableRow } from './components/table';
import { Modal } from './components/Modal';

class LabelModal extends Modal {
constructor() {
super('Labels');
}

findModalSearchInput() {
return cy.findByTestId('label-modal-search');
}

findCloseModal() {
return cy.findByTestId('close-modal');
}

shouldContainsModalLabels(labels: string[]) {
cy.findByTestId('modal-label-group').within(() => labels.map((label) => cy.contains(label)));
return this;
}
}

class RegisteredModelTableRow extends TableRow {
findName() {
return this.find().findByTestId('model-name');
}

findDescription() {
return this.find().findByTestId('description');
}

findOwner() {
return this.find().findByTestId('registered-model-owner');
}

findLabelPopoverText() {
return this.find().findByTestId('popover-label-text');
}

findLabelModalText() {
return this.find().findByTestId('modal-label-text');
}

shouldContainsPopoverLabels(labels: string[]) {
cy.findByTestId('popover-label-group').within(() => labels.map((label) => cy.contains(label)));
return this;
}
}

class ModelRegistry {
landingPage() {
Expand Down Expand Up @@ -47,6 +95,29 @@ class ModelRegistry {
appChrome.findNavItem('Model Registry').should('not.exist');
return this;
}

findTable() {
return cy.findByTestId('registered-model-table');
}

findTableRows() {
return this.findTable().find('tbody tr');
}

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

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

findTableSearch() {
return cy.findByTestId('registered-model-table-search');
}
}

export const modelRegistry = new ModelRegistry();
export const labelModal = new LabelModal();
Loading

0 comments on commit 6199aa6

Please sign in to comment.