Skip to content

Commit

Permalink
Model version details test
Browse files Browse the repository at this point in the history
Refactoring model registry folder structure
  • Loading branch information
dpanshug committed May 13, 2024
1 parent 93c0851 commit 493e169
Show file tree
Hide file tree
Showing 32 changed files with 273 additions and 57 deletions.
13 changes: 13 additions & 0 deletions frontend/src/__mocks__/mockModelArtifact.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { ModelArtifact } from '~/concepts/modelRegistry/types';

export const mockModelArtifact = (): ModelArtifact => ({
createTimeSinceEpoch: '1712234877179',
id: '1',
lastUpdateTimeSinceEpoch: '1712234877179',
name: 'fraud detection model version 1',
description: 'Description of model version',
artifactType: 'model-artifact',
customProperties: {},
storagePath: 'test path',
uri: 'https://huggingface.io/mnist.onnx',
});
10 changes: 10 additions & 0 deletions frontend/src/__mocks__/mockModelArtifactList.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/* eslint-disable camelcase */
import { ModelArtifactList } from '~/concepts/modelRegistry/types';
import { mockModelArtifact } from './mockModelArtifact';

export const mockModelArtifactList = (): ModelArtifactList => ({
items: [mockModelArtifact()],
nextPageToken: '',
pageSize: 0,
size: 1,
});
1 change: 1 addition & 0 deletions frontend/src/__mocks__/mockModelVersion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,5 @@ export const mockModelVersion = ({
name,
state: ModelVersionState.ARCHIVED,
registeredModelId,
description: 'Description of model version',
});
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ describe('Model Registry', () => {

modelRegistry.tabEnabled();
});

it('No registered models in the selected Model Registry', () => {
initIntercepts({
disableModelRegistryFeature: false,
Expand All @@ -92,7 +93,7 @@ describe('Model Registry', () => {
modelRegistry.shouldregisteredModelsEmpty();
});

it('Model registry table', () => {
it('Registered model table', () => {
initIntercepts({
disableModelRegistryFeature: false,
});
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import {
mockDashboardConfig,
mockK8sResourceList,
mockRouteK8sResourceModelRegistry,
} from '~/__mocks__';
import { mockComponents } from '~/__mocks__/mockComponents';
import { mockModelRegistry } from '~/__mocks__/mockModelRegistry';
import { ModelRegistryModel, RouteModel } from '~/__tests__/cypress/cypress/utils/models';
import { MODEL_REGISTRY_API_VERSION } from '~/concepts/modelRegistry/const';
import { modelVersionDetails } from '~/__tests__/cypress/cypress/pages/modelRegistry/modelVersionDetails';
import { mockRegisteredModel } from '~/__mocks__/mockRegisteredModel';
import { mockModelVersion } from '~/__mocks__/mockModelVersion';
import { mockModelVersionList } from '~/__mocks__/mockModelVersionList';
import { mockModelArtifactList } from '~/__mocks__/mockModelArtifactList';
import { verifyRelativeURL } from '~/__tests__/cypress/cypress/utils/url';

const initIntercepts = () => {
cy.interceptOdh(
'GET /api/config',
mockDashboardConfig({
disableModelRegistry: false,
}),
);
cy.interceptOdh('GET /api/components', { query: { installed: 'true' } }, mockComponents());

cy.interceptK8sList(ModelRegistryModel, mockK8sResourceList([mockModelRegistry({})]));

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

cy.interceptK8s(
RouteModel,
mockRouteK8sResourceModelRegistry({
name: 'modelregistry-sample-http',
namespace: 'odh-model-registries',
}),
);

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

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

cy.interceptOdh(
`GET /api/service/modelregistry/modelregistry-sample/api/model_registry/${MODEL_REGISTRY_API_VERSION}/model_versions/:id`,
{ path: { id: '1' } },
mockModelVersion({}),
);

cy.interceptOdh(
`GET /api/service/modelregistry/modelregistry-sample/api/model_registry/${MODEL_REGISTRY_API_VERSION}/model_versions/:id`,
{ path: { id: '2' } },
mockModelVersion({ id: '2', name: 'Version 2' }),
);

cy.interceptOdh(
`GET /api/service/modelregistry/modelregistry-sample/api/model_registry/${MODEL_REGISTRY_API_VERSION}/model_versions/1/artifacts`,
mockModelArtifactList(),
);
};

describe('Model version details', () => {
beforeEach(() => {
initIntercepts();
modelVersionDetails.visit();
});

it('Model version details page header', () => {
verifyRelativeURL('/modelRegistry/modelregistry-sample/registeredModels/1/versions/1/details');
cy.findByTestId('app-page-title').should('have.text', 'Version 1');
cy.findByTestId('breadcrumb-version-name').should('have.text', 'Version 1');
});

it('Model version details tab', () => {
modelVersionDetails.findVersionId().contains('1');
modelVersionDetails.findDescription().should('have.text', 'Description of model version');
modelVersionDetails.findMoreLabelsButton().contains('6 more');
modelVersionDetails.findMoreLabelsButton().click();
modelVersionDetails.shouldContainsModalLabels([
'Testing label',
'Financial',
'Financial data',
'Fraud detection',
'Machine learning',
'Next data to be overflow',
'Label x',
'Label y',
'Label z',
]);
modelVersionDetails.findStorageLocation().contains('https://huggingface.io/mnist.onnx');
});

it('Switching model versions', () => {
modelVersionDetails.findVersionId().contains('1');
modelVersionDetails.findModelVersionDropdownButton().click();
modelVersionDetails.findModelVersionDropdownSearch().fill('Version 2');
modelVersionDetails.findModelVersionDropdownItem('Version 2').click();
modelVersionDetails.findVersionId().contains('2');
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { modelVersionUrl } from '~/pages/modelRegistry/screens/routeUtils';

class ModelVersionDetails {
visit() {
cy.visit(modelVersionUrl('1', '1', 'modelregistry-sample'));
this.wait();
}

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

findVersionId() {
return cy.findByTestId('model-version-id');
}

findDescription() {
return cy.findByTestId('model-version-description');
}

findMoreLabelsButton() {
return cy.findByTestId('label-group').find('button');
}

findStorageLocation() {
return cy.findByTestId('storage-location');
}

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

findModelVersionDropdownButton() {
return cy.findByTestId('model-version-toggle-button');
}

findModelVersionDropdownSearch() {
return cy.findByTestId('search-input');
}

findModelVersionDropdownItem(name: string) {
return cy.findByTestId('model-version-selector-list').find('li').contains(name);
}
}

export const modelVersionDetails = new ModelVersionDetails();
24 changes: 21 additions & 3 deletions frontend/src/__tests__/cypress/cypress/support/commands/odh.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { K8sResourceListResult, K8sStatus } from '@openshift/dynamic-plugin-sdk-
import type { GenericStaticResponse, RouteHandlerController } from 'cypress/types/net-stubbing';
import { BaseMetricCreationResponse, BaseMetricListResponse } from '~/api';
import {
ModelArtifactList,
ModelVersion,
ModelVersionList,
RegisteredModel,
RegisteredModelList,
Expand Down Expand Up @@ -321,16 +323,32 @@ declare global {
): Cypress.Chainable<null>;

interceptOdh(
type: 'DELETE /api/service/pipelines/:projectId/dspa/apis/v2beta1/recurringruns/:pipeline_id',
options: { path: { projectId: string; pipeline_id: string } },
response: OdhResponse<K8sStatus>,
type: `GET /api/service/modelregistry/modelregistry-sample/api/model_registry/v1alpha3/registered_models/1`,
response: OdhResponse<RegisteredModel | undefined>,
): Cypress.Chainable<null>;

interceptOdh(
type: `GET /api/service/modelregistry/modelregistry-sample/api/model_registry/v1alpha3/model_versions/:id`,
options: { path: { id: string } },
response: OdhResponse<ModelVersion | undefined>,
): Cypress.Chainable<null>;

interceptOdh(
type: `GET /api/service/modelregistry/modelregistry-sample/api/model_registry/v1alpha3/model_versions/1/artifacts`,
response: OdhResponse<ModelArtifactList | undefined>,
): Cypress.Chainable<null>;

interceptOdh(
type: 'GET /api/service/modelregistry/modelregistry-sample/api/model_registry/v1alpha3/registered_models/1/versions',
response: OdhResponse<ModelVersionList | undefined>,
): Cypress.Chainable<null>;

interceptOdh(
type: 'DELETE /api/service/pipelines/:projectId/dspa/apis/v2beta1/recurringruns/:pipeline_id',
options: { path: { projectId: string; pipeline_id: string } },
response: OdhResponse<K8sStatus>,
): Cypress.Chainable<null>;

interceptOdh(
type: 'GET /api/rolebindings/opendatahub/openshift-ai-notebooks-image-pullers',
response: OdhResponse<K8sResourceListResult<RoleBindingKind>>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,9 +151,9 @@ const EditableLabelsDescriptionListGroup: React.FC<EditableTextDescriptionListGr
setIsEditing(false);
}}
>
<LabelGroup>
<LabelGroup data-testid="label-group">
{labels.map((label) => (
<Label key={label} color="blue" data-testid="label">
<Label textMaxWidth="40ch" key={label} color="blue" data-testid="label">
{label}
</Label>
))}
Expand Down
5 changes: 4 additions & 1 deletion frontend/src/components/EditableTextDescriptionListGroup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,16 @@ type EditableTextDescriptionListGroupProps = Pick<
'title' | 'contentWhenEmpty'
> & {
value: string;
saveEditedValue: (value: string) => Promise<unknown>;
saveEditedValue: (value: string) => Promise<void>;
testid?: string;
};

const EditableTextDescriptionListGroup: React.FC<EditableTextDescriptionListGroupProps> = ({
title,
contentWhenEmpty,
value,
saveEditedValue,
testid,
}) => {
const [isEditing, setIsEditing] = React.useState(false);
const [unsavedValue, setUnsavedValue] = React.useState(value);
Expand Down Expand Up @@ -60,6 +62,7 @@ const EditableTextDescriptionListGroup: React.FC<EditableTextDescriptionListGrou
}}
>
<ExpandableSection
data-testid={testid}
variant="truncate"
truncateMaxLines={12}
toggleText={isTextExpanded ? 'Show less' : 'Show more'}
Expand Down
14 changes: 7 additions & 7 deletions frontend/src/pages/modelRegistry/ModelRegistryRoutes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import { Navigate, Route, Routes } from 'react-router-dom';
import { ModelRegistrySelectorContextProvider } from '~/concepts/modelRegistry/context/ModelRegistrySelectorContext';
import ModelRegistryCoreLoader from './ModelRegistryCoreLoader';
import ModelRegistry from './screens/ModelRegistry';
import { ModelVersionsTabs } from './screens/const';
import ModelVersions from './screens/ModelVersions';
import { ModelVersionsTab } from './screens/ModelVersions/const';
import ModelVersions from './screens/ModelVersions/ModelVersions';
import ModelVersionsDetails from './screens/ModelVersionDetails/ModelVersionDetails';
import { ModelVersionDetailsTab } from './screens/ModelVersionDetails/const';

Expand All @@ -21,14 +21,14 @@ const ModelRegistryRoutes: React.FC = () => (
>
<Route index element={<ModelRegistry />} />
<Route path="registeredModels/:registeredModelId">
<Route index element={<Navigate to={ModelVersionsTabs.VERSIONS} />} />
<Route index element={<Navigate to={ModelVersionsTab.VERSIONS} />} />
<Route
path={ModelVersionsTabs.VERSIONS}
element={<ModelVersions tab={ModelVersionsTabs.VERSIONS} empty={false} />}
path={ModelVersionsTab.VERSIONS}
element={<ModelVersions tab={ModelVersionsTab.VERSIONS} empty={false} />}
/>
<Route
path={ModelVersionsTabs.DETAILS}
element={<ModelVersions tab={ModelVersionsTabs.DETAILS} empty={false} />}
path={ModelVersionsTab.DETAILS}
element={<ModelVersions tab={ModelVersionsTab.DETAILS} empty={false} />}
/>
<Route path="versions/:modelVersionId">
<Route index element={<Navigate to={ModelVersionDetailsTab.DETAILS} />} />
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/pages/modelRegistry/screens/ModelRegistry.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import useRegisteredModels from '~/concepts/modelRegistry/apiHooks/useRegistered
import { ModelRegistrySelectorContext } from '~/concepts/modelRegistry/context/ModelRegistrySelectorContext';
import TitleWithIcon from '~/concepts/design/TitleWithIcon';
import { ProjectObjectType } from '~/concepts/design/utils';
import RegisteredModelListView from './RegisteredModelListView';
import RegisteredModelListView from './RegisteredModels/RegisteredModelListView';
import EmptyModelRegistryState from './EmptyModelRegistryState';
import ModelRegistrySelectorNavigator from './ModelRegistrySelectorNavigator';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,13 @@ const ModelVersionsDetails: React.FC<ModelVersionsDetailProps> = ({ tab, ...page
<BreadcrumbItem
render={() => (
<Link to={registeredModelUrl(rmId, preferredModelRegistry?.metadata.name)}>
{rm?.name}
{rm?.name || 'Loading...'}
</Link>
)}
/>
<BreadcrumbItem isActive>{mv?.name}</BreadcrumbItem>
<BreadcrumbItem data-testid="breadcrumb-version-name" isActive>
{mv?.name || 'Loading...'}
</BreadcrumbItem>
</Breadcrumb>
}
title={mv?.name}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ import {
getPatchBodyForModelVersion,
mergeUpdatedLabels,
} from '~/pages/modelRegistry/screens/utils';
import ModelTimestamp from '~/pages/modelRegistry/screens/ModelTimestamp';
import useModelArtifactsByVersionId from '~/concepts/modelRegistry/apiHooks/useModelArtifactsByVersionId';
import { ModelRegistryContext } from '~/concepts/modelRegistry/context/ModelRegistryContext';
import ModelTimestamp from '~/pages/modelRegistry/screens/components/ModelTimestamp';

type ModelVersionDetailsViewProps = {
modelVersion: ModelVersion;
Expand All @@ -35,6 +35,7 @@ const ModelVersionDetailsView: React.FC<ModelVersionDetailsViewProps> = ({
<FlexItem flex={{ default: 'flex_1' }}>
<DescriptionList isFillColumns>
<EditableTextDescriptionListGroup
testid="model-version-description"
title="Description"
contentWhenEmpty="No description"
value={mv.description || ''}
Expand Down Expand Up @@ -87,22 +88,32 @@ const ModelVersionDetailsView: React.FC<ModelVersionDetailsViewProps> = ({
isEmpty={!mv.id}
contentWhenEmpty="No model ID"
>
<ClipboardCopy hoverTip="Copy" clickTip="Copied" variant="inline-compact">
<ClipboardCopy
data-testid="model-version-id"
hoverTip="Copy"
clickTip="Copied"
variant="inline-compact"
>
{mv.id}
</ClipboardCopy>
</DashboardDescriptionListGroup>
<DashboardDescriptionListGroup
title="Storage location"
isEmpty={modelArtifact.size === 0}
isEmpty={modelArtifact.size === 0 || !modelArtifact.items[0].uri}
contentWhenEmpty="No storage location"
>
<ClipboardCopy hoverTip="Copy" clickTip="Copied" variant="inline-compact">
<ClipboardCopy
data-testid="storage-location"
hoverTip="Copy"
clickTip="Copied"
variant="inline-compact"
>
{modelArtifact.items[0]?.uri}
</ClipboardCopy>
</DashboardDescriptionListGroup>
<DashboardDescriptionListGroup
title="Source model format"
isEmpty={modelArtifact.size === 0}
isEmpty={modelArtifact.size === 0 || !modelArtifact.items[0].modelFormatName}
contentWhenEmpty="No source model format"
>
{modelArtifact.items[0]?.modelFormatName}
Expand Down
Loading

0 comments on commit 493e169

Please sign in to comment.