Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tests for model versions table and empty state #2782

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 10 additions & 4 deletions frontend/src/__mocks__/mockModelVersion.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,26 @@
import { ModelVersion, ModelVersionState } from '~/concepts/modelRegistry/types';
import { ModelRegistryBase, ModelVersion, ModelVersionState } from '~/concepts/modelRegistry/types';

type MockModelVersionType = {
author?: string;
id?: string;
registeredModelId?: string;
name?: string;
customProperties?: ModelRegistryBase['customProperties'];
};

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

export const mockModelVersionList = (): ModelVersionList => ({
items: [mockModelVersion({ author: 'Author 1', registeredModelId: '1' })],
export const mockModelVersionList = ({
items = [],
}: Partial<ModelVersionList>): ModelVersionList => ({
items,
nextPageToken: '',
pageSize: 0,
size: 1,
size: items.length,
});
lucferbux marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -1,20 +1,31 @@
/* eslint-disable camelcase */
import { mockK8sResourceList, mockRouteK8sResourceModelRegistry } from '~/__mocks__';
import { mockComponents } from '~/__mocks__/mockComponents';
import { mockDashboardConfig } from '~/__mocks__/mockDashboardConfig';
import { MODEL_REGISTRY_API_VERSION } from '~/concepts/modelRegistry/const';
import { mockModelRegistry } from '~/__mocks__/mockModelRegistry';
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 { mockModelVersionList } from '~/__mocks__/mockModelVersionList';
import { mockModelVersion } from '~/__mocks__/mockModelVersion';
import { ModelVersion } from '~/concepts/modelRegistry/types';

type HandlersProps = {
disableModelRegistryFeature?: boolean;
size?: number;
registeredModelsSize?: number;
modelVersions?: ModelVersion[];
};

const initIntercepts = ({ disableModelRegistryFeature = false, size = 4 }: HandlersProps) => {
const initIntercepts = ({
disableModelRegistryFeature = false,
registeredModelsSize = 4,
modelVersions = [
mockModelVersion({ author: 'Author 1' }),
mockModelVersion({ name: 'model version' }),
],
}: HandlersProps) => {
cy.interceptOdh(
'GET /api/config',
mockDashboardConfig({
Expand All @@ -37,14 +48,15 @@ const initIntercepts = ({ disableModelRegistryFeature = false, size = 4 }: Handl
namespace: 'odh-model-registries',
}),
);

cy.interceptOdh(
`GET /api/service/modelregistry/modelregistry-sample/api/model_registry/${MODEL_REGISTRY_API_VERSION}/registered_models`,
mockRegisteredModelList({ size }),
mockRegisteredModelList({ size: registeredModelsSize }),
);

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

Expand All @@ -71,7 +83,7 @@ describe('Model Registry', () => {
it('No registered models in the selected Model Registry', () => {
initIntercepts({
disableModelRegistryFeature: false,
size: 0,
registeredModelsSize: 0,
});

modelRegistry.visit();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
/* eslint-disable camelcase */
import { mockK8sResourceList } from '~/__mocks__';
import { mockDashboardConfig } from '~/__mocks__/mockDashboardConfig';
import { MODEL_REGISTRY_API_VERSION } from '~/concepts/modelRegistry/const';
import { mockModelRegistry } from '~/__mocks__/mockModelRegistry';
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 } from '~/__tests__/cypress/cypress/utils/models';
import { verifyRelativeURL } from '~/__tests__/cypress/cypress/utils/url';
import { mockRegisteredModel } from '~/__mocks__/mockRegisteredModel';
import { ModelVersion } from '~/concepts/modelRegistry/types';
import { mockModelVersion } from '~/__mocks__/mockModelVersion';

type HandlersProps = {
disableModelRegistryFeature?: boolean;
registeredModelsSize?: number;
modelVersions?: ModelVersion[];
};

const initIntercepts = ({
disableModelRegistryFeature = false,
registeredModelsSize = 4,
modelVersions = [
mockModelVersion({
author: 'Author 1',
id: '1',
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: '',
},
'Test label x': {
metadataType: 'MetadataStringValue',
string_value: '',
},
'Test label y': {
metadataType: 'MetadataStringValue',
string_value: '',
},
'Test label z': {
metadataType: 'MetadataStringValue',
string_value: '',
},
},
}),
mockModelVersion({ id: '2', name: 'model version' }),
],
}: HandlersProps) => {
cy.interceptOdh(
'GET /api/config',
mockDashboardConfig({
disableModelRegistry: disableModelRegistryFeature,
}),
);

cy.interceptK8sList(
ModelRegistryModel,
mockK8sResourceList([mockModelRegistry({}), mockModelRegistry({ name: 'test-registry' })]),
);

cy.interceptK8s(ModelRegistryModel, mockModelRegistry({}));

cy.interceptOdh(
`GET /api/service/modelregistry/modelregistry-sample/api/model_registry/${MODEL_REGISTRY_API_VERSION}/registered_models`,
mockRegisteredModelList({ size: registeredModelsSize }),
);

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

cy.interceptOdh(
`GET /api/service/modelregistry/modelregistry-sample/api/model_registry/${MODEL_REGISTRY_API_VERSION}/registered_models/1`,
mockRegisteredModel({}),
);
};
describe('Model Versions', () => {
it('No model versions in the selected registered model', () => {
initIntercepts({
disableModelRegistryFeature: false,
modelVersions: [],
});

modelRegistry.visit();
const registeredModelRow = modelRegistry.getRow('Fraud detection model');
registeredModelRow.findName().contains('Fraud detection model').click();
verifyRelativeURL(`/modelRegistry/modelregistry-sample/registeredModels/1/versions`);
modelRegistry.shouldmodelVersionsEmpty();
});

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

modelRegistry.visit();
const registeredModelRow = modelRegistry.getRow('Fraud detection model');
registeredModelRow.findName().contains('Fraud detection model').click();
verifyRelativeURL(`/modelRegistry/modelregistry-sample/registeredModels/1/versions`);
modelRegistry.findModelBreadcrumbItem().contains('test');
modelRegistry
.findModelVersionsTableKebab()
.findDropdownItem('View archived versions')
.should('be.disabled');
modelRegistry
.findModelVersionsHeaderAction()
.findDropdownItem('Archive model')
.should('be.disabled');
modelRegistry.findModelVersionsTable().should('be.visible');
modelRegistry.findModelVersionsTableRows().should('have.length', 2);

// Label modal
const modelVersionRow = modelRegistry.getModelVersionRow('new model version');

modelVersionRow.findLabelModalText().contains('5 more');
modelVersionRow.findLabelModalText().click();
labelModal.shouldContainsModalLabels([
'Financial',
'Financial data',
'Fraud detection',
'Test label',
'Machine learning',
'Next data to be overflow',
'Test label x',
'Test label y',
'Test label y',
]);
labelModal.findModalSearchInput().type('Financial');
labelModal.shouldContainsModalLabels(['Financial', 'Financial data']);
labelModal.findCloseModal().click();

// sort by model version name
modelRegistry.findModelVersionsTableHeaderButton('Version name').should(be.sortAscending);
modelRegistry.findModelVersionsTableHeaderButton('Version name').click();
modelRegistry.findModelVersionsTableHeaderButton('Version name').should(be.sortDescending);

// sort by model version owner
modelRegistry.findModelVersionsTableHeaderButton('Owner').click();
modelRegistry.findModelVersionsTableHeaderButton('Owner').should(be.sortAscending);
modelRegistry.findModelVersionsTableHeaderButton('Owner').click();
modelRegistry.findModelVersionsTableHeaderButton('Owner').should(be.sortDescending);

// filtering by keyword
modelRegistry.findModelVersionsTableSearch().type('new model version');
modelRegistry.findModelVersionsTableRows().should('have.length', 1);
modelRegistry.findModelVersionsTableRows().contains('new model version');
modelRegistry.findModelVersionsTableSearch().focused().clear();

// filtering by owner
modelRegistry.findModelVersionsTableFilter().findDropdownItem('Owner').click();
modelRegistry.findModelVersionsTableSearch().type('Test author');
modelRegistry.findModelVersionsTableRows().should('have.length', 1);
modelRegistry.findModelVersionsTableRows().contains('Test author');
});
});
52 changes: 49 additions & 3 deletions frontend/src/__tests__/cypress/cypress/pages/modelRegistry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class LabelModal extends Modal {
}
}

class RegisteredModelTableRow extends TableRow {
class ModelRegistryTableRow extends TableRow {
findName() {
return this.find().findByTestId('model-name');
}
Expand Down Expand Up @@ -66,6 +66,7 @@ class ModelRegistry {

private wait() {
cy.findByTestId('app-page-title').should('exist');
cy.findByTestId('app-page-title').contains('Registered models');
cy.testA11y();
}

Expand All @@ -79,7 +80,11 @@ class ModelRegistry {
}

shouldregisteredModelsEmpty() {
cy.findByTestId('empty-model-registry').should('exist');
cy.findByTestId('empty-registered-models').should('exist');
}

shouldmodelVersionsEmpty() {
cy.findByTestId('empty-model-versions').should('exist');
}

shouldModelRegistrySelectorExist() {
Expand All @@ -104,23 +109,64 @@ class ModelRegistry {
return cy.findByTestId('registered-model-table');
}

findModelVersionsTable() {
return cy.findByTestId('model-versions-table');
}

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

findModelVersionsTableRows() {
return this.findModelVersionsTable().find('tbody tr');
}

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

getModelVersionRow(name: string) {
return new ModelRegistryTableRow(() =>
this.findModelVersionsTable()
.find(`[data-label="Version name"]`)
.contains(name)
.parents('tr'),
);
}

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

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

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

findModelVersionsTableSearch() {
return cy.findByTestId('model-versions-table-search');
}

findModelBreadcrumbItem() {
return cy.findByTestId('breadcrumb-model');
}

findModelVersionsTableKebab() {
return cy.findByTestId('model-versions-table-kebab-action');
}

findModelVersionsHeaderAction() {
return cy.findByTestId('model-version-action-toggle');
}

findModelVersionsTableFilter() {
return cy.findByTestId('model-versions-table-filter');
}
}

export const modelRegistry = new ModelRegistry();
Expand Down
Loading
Loading