improvements: added unit tests for version pages (#12381)

* added unit tests for version page components for Dashboard, Pipelines, Tables and Topics

* fixed entity version page test mock

* added and improved unit tests for version page related components

* fixed failing unit tests

* moved all mock files in mock folder

* Fixed test titles
Worked on comments
This commit is contained in:
Aniket Katkar 2023-07-20 10:36:32 +05:30 committed by GitHub
parent 8bac9b6ba8
commit 43c1f1e3a2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 2093 additions and 280 deletions

View File

@ -18,8 +18,11 @@ import { CustomPropertyProps } from 'components/common/CustomPropertyTable/Custo
import DescriptionV1 from 'components/common/description/DescriptionV1';
import ErrorPlaceHolder from 'components/common/error-with-placeholder/ErrorPlaceHolder';
import DataAssetsVersionHeader from 'components/DataAssets/DataAssetsVersionHeader/DataAssetsVersionHeader';
import EntityVersionTimeLine from 'components/EntityVersionTimeLine/EntityVersionTimeLine';
import Loader from 'components/Loader/Loader';
import TabsLabel from 'components/TabsLabel/TabsLabel.component';
import TagsContainerV2 from 'components/Tag/TagsContainerV2/TagsContainerV2';
import VersionTable from 'components/VersionTable/VersionTable.component';
import { getVersionPathWithTab } from 'constants/constants';
import { EntityField } from 'constants/Feeds.constants';
import { ERROR_PLACEHOLDER_TYPE } from 'enums/common.enum';
@ -42,9 +45,6 @@ import {
getEntityVersionByField,
getEntityVersionTags,
} from '../../utils/EntityVersionUtils';
import EntityVersionTimeLine from '../EntityVersionTimeLine/EntityVersionTimeLine';
import Loader from '../Loader/Loader';
import VersionTable from '../VersionTable/VersionTable.component';
import { ContainerVersionProp } from './ContainerVersion.interface';
const ContainerVersion: React.FC<ContainerVersionProp> = ({

View File

@ -0,0 +1,201 @@
/*
* Copyright 2023 Collate.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { act, render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React from 'react';
import { DEFAULT_ENTITY_PERMISSION } from 'utils/PermissionsUtils';
import { containerVersionMockProps } from '../../mocks/ContainerVersion.mock';
import ContainerVersion from './ContainerVersion.component';
const mockPush = jest.fn();
jest.mock(
'components/DataAssets/DataAssetsVersionHeader/DataAssetsVersionHeader',
() => jest.fn().mockImplementation(() => <div>DataAssetsVersionHeader</div>)
);
jest.mock('components/TabsLabel/TabsLabel.component', () =>
jest.fn().mockImplementation(({ name }) => <div>{name}</div>)
);
jest.mock('components/Tag/TagsContainerV2/TagsContainerV2', () =>
jest.fn().mockImplementation(() => <div>TagsContainerV2</div>)
);
jest.mock('components/common/CustomPropertyTable/CustomPropertyTable', () => ({
CustomPropertyTable: jest
.fn()
.mockImplementation(() => <div>CustomPropertyTable</div>),
}));
jest.mock('components/common/description/DescriptionV1', () =>
jest.fn().mockImplementation(() => <div>DescriptionV1</div>)
);
jest.mock('components/common/error-with-placeholder/ErrorPlaceHolder', () =>
jest.fn().mockImplementation(() => <div>ErrorPlaceHolder</div>)
);
jest.mock('components/EntityVersionTimeLine/EntityVersionTimeLine', () =>
jest.fn().mockImplementation(() => <div>EntityVersionTimeLine</div>)
);
jest.mock('components/VersionTable/VersionTable.component', () =>
jest.fn().mockImplementation(() => <div>VersionTable</div>)
);
jest.mock('components/Loader/Loader', () =>
jest.fn().mockImplementation(() => <div>Loader</div>)
);
jest.mock('react-router-dom', () => ({
useHistory: jest.fn().mockImplementation(() => ({
push: mockPush,
})),
useParams: jest.fn().mockReturnValue({
tab: 'container',
}),
}));
describe('ContainerVersion tests', () => {
it('Should render component properly if not loading', async () => {
await act(async () => {
render(<ContainerVersion {...containerVersionMockProps} />);
});
const dataAssetsVersionHeader = screen.getByText('DataAssetsVersionHeader');
const description = screen.getByText('DescriptionV1');
const schemaTabLabel = screen.getByText('label.schema');
const customPropertyTabLabel = screen.getByText(
'label.custom-property-plural'
);
const entityVersionTimeLine = screen.getByText('EntityVersionTimeLine');
const versionTable = screen.getByText('VersionTable');
expect(dataAssetsVersionHeader).toBeInTheDocument();
expect(description).toBeInTheDocument();
expect(schemaTabLabel).toBeInTheDocument();
expect(customPropertyTabLabel).toBeInTheDocument();
expect(entityVersionTimeLine).toBeInTheDocument();
expect(versionTable).toBeInTheDocument();
});
it('Should display Loader if isVersionLoading is true', async () => {
await act(async () => {
render(
<ContainerVersion {...containerVersionMockProps} isVersionLoading />
);
});
const loader = screen.getByText('Loader');
const entityVersionTimeLine = screen.getByText('EntityVersionTimeLine');
const dataAssetsVersionHeader = screen.queryByText(
'DataAssetsVersionHeader'
);
const schemaTabLabel = screen.queryByText('label.schema');
const customPropertyTabLabel = screen.queryByText(
'label.custom-property-plural'
);
const versionTable = screen.queryByText('VersionTable');
expect(loader).toBeInTheDocument();
expect(entityVersionTimeLine).toBeInTheDocument();
expect(dataAssetsVersionHeader).toBeNull();
expect(schemaTabLabel).toBeNull();
expect(customPropertyTabLabel).toBeNull();
expect(versionTable).toBeNull();
});
it('Should display ErrorPlaceholder if no viewing permission', async () => {
await act(async () => {
render(
<ContainerVersion
{...containerVersionMockProps}
entityPermissions={DEFAULT_ENTITY_PERMISSION}
/>
);
});
const errorPlaceHolder = screen.getByText('ErrorPlaceHolder');
const loader = screen.queryByText('Loader');
const dataAssetsVersionHeader = screen.queryByText(
'DataAssetsVersionHeader'
);
const schemaTabLabel = screen.queryByText('label.schema');
const customPropertyTabLabel = screen.queryByText(
'label.custom-property-plural'
);
const entityVersionTimeLine = screen.queryByText('EntityVersionTimeLine');
const versionTable = screen.queryByText('VersionTable');
expect(errorPlaceHolder).toBeInTheDocument();
expect(loader).toBeNull();
expect(entityVersionTimeLine).toBeNull();
expect(dataAssetsVersionHeader).toBeNull();
expect(schemaTabLabel).toBeNull();
expect(customPropertyTabLabel).toBeNull();
expect(versionTable).toBeNull();
});
it('Should update url on click of tab', async () => {
await act(async () => {
render(<ContainerVersion {...containerVersionMockProps} />);
});
const customPropertyTabLabel = screen.getByText(
'label.custom-property-plural'
);
const versionTable = screen.getByText('VersionTable');
expect(customPropertyTabLabel).toBeInTheDocument();
expect(versionTable).toBeInTheDocument();
await act(async () => {
userEvent.click(customPropertyTabLabel);
});
expect(mockPush).toHaveBeenCalledWith(
'/container/sample_data.ecommerce_db.shopify.raw_product_catalog/versions/0.3/custom_properties'
);
});
it('Should display ErrorPlaceholder in Custom Property tab if no "viewAll" permission', async () => {
await act(async () => {
render(
<ContainerVersion
{...containerVersionMockProps}
entityPermissions={{ ...DEFAULT_ENTITY_PERMISSION, ViewBasic: true }}
/>
);
});
const customPropertyTabLabel = screen.getByText(
'label.custom-property-plural'
);
const versionTable = screen.getByText('VersionTable');
let errorPlaceHolder = screen.queryByText('ErrorPlaceHolder');
expect(customPropertyTabLabel).toBeInTheDocument();
expect(versionTable).toBeInTheDocument();
expect(errorPlaceHolder).toBeNull();
await act(async () => {
userEvent.click(customPropertyTabLabel);
});
errorPlaceHolder = screen.getByText('ErrorPlaceHolder');
expect(errorPlaceHolder).toBeInTheDocument();
});
});

View File

@ -19,7 +19,10 @@ import { CustomPropertyTable } from 'components/common/CustomPropertyTable/Custo
import { CustomPropertyProps } from 'components/common/CustomPropertyTable/CustomPropertyTable.interface';
import DescriptionV1 from 'components/common/description/DescriptionV1';
import ErrorPlaceHolder from 'components/common/error-with-placeholder/ErrorPlaceHolder';
import RichTextEditorPreviewer from 'components/common/rich-text-editor/RichTextEditorPreviewer';
import DataAssetsVersionHeader from 'components/DataAssets/DataAssetsVersionHeader/DataAssetsVersionHeader';
import EntityVersionTimeLine from 'components/EntityVersionTimeLine/EntityVersionTimeLine';
import Loader from 'components/Loader/Loader';
import TabsLabel from 'components/TabsLabel/TabsLabel.component';
import TagsContainerV2 from 'components/Tag/TagsContainerV2/TagsContainerV2';
import { getVersionPathWithTab } from 'constants/constants';
@ -41,9 +44,6 @@ import {
getEntityVersionByField,
getEntityVersionTags,
} from '../../utils/EntityVersionUtils';
import RichTextEditorPreviewer from '../common/rich-text-editor/RichTextEditorPreviewer';
import EntityVersionTimeLine from '../EntityVersionTimeLine/EntityVersionTimeLine';
import Loader from '../Loader/Loader';
import { DashboardVersionProp } from './DashboardVersion.interface';
const DashboardVersion: FC<DashboardVersionProp> = ({
@ -240,47 +240,43 @@ const DashboardVersion: FC<DashboardVersionProp> = ({
return (
<>
<div data-testid="dashboard-version-container">
{isVersionLoading ? (
<Loader />
) : (
<div
className={classNames('version-data')}
data-testid="version-data">
<Row gutter={[0, 12]}>
<Col span={24}>
<DataAssetsVersionHeader
breadcrumbLinks={slashedDashboardName}
currentVersionData={currentVersionData}
deleted={deleted}
displayName={displayName}
ownerDisplayName={ownerDisplayName}
ownerRef={ownerRef}
tierDisplayName={tierDisplayName}
version={version}
onVersionClick={backHandler}
/>
</Col>
<Col span={24}>
<Tabs
data-testid="tabs"
defaultActiveKey={tab ?? EntityTabs.DETAILS}
items={tabItems}
onChange={handleTabChange}
/>
</Col>
</Row>
</div>
)}
{isVersionLoading ? (
<Loader />
) : (
<div className={classNames('version-data')} data-testid="version-data">
<Row gutter={[0, 12]}>
<Col span={24}>
<DataAssetsVersionHeader
breadcrumbLinks={slashedDashboardName}
currentVersionData={currentVersionData}
deleted={deleted}
displayName={displayName}
ownerDisplayName={ownerDisplayName}
ownerRef={ownerRef}
tierDisplayName={tierDisplayName}
version={version}
onVersionClick={backHandler}
/>
</Col>
<Col span={24}>
<Tabs
data-testid="tabs"
defaultActiveKey={tab ?? EntityTabs.DETAILS}
items={tabItems}
onChange={handleTabChange}
/>
</Col>
</Row>
</div>
)}
<EntityVersionTimeLine
show
currentVersion={version}
versionHandler={versionHandler}
versionList={versionList}
onBack={backHandler}
/>
</div>
<EntityVersionTimeLine
show
currentVersion={version}
versionHandler={versionHandler}
versionList={versionList}
onBack={backHandler}
/>
</>
);
};

View File

@ -11,18 +11,23 @@
* limitations under the License.
*/
import { findByTestId, findByText, render } from '@testing-library/react';
import React, { ReactNode } from 'react';
import { act, render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { ENTITY_PERMISSIONS } from 'mocks/Permissions.mock';
import React from 'react';
import { MemoryRouter } from 'react-router-dom';
import DashboardVersion from './DashboardVersion.component';
import { DashboardVersionProp } from './DashboardVersion.interface';
import { DEFAULT_ENTITY_PERMISSION } from 'utils/PermissionsUtils';
import {
dashboardVersionProps,
mockNoChartData,
mockTagChangeVersion,
} from './dashboardVersion.mock';
} from '../../mocks/dashboardVersion.mock';
import DashboardVersion from './DashboardVersion.component';
import { DashboardVersionProp } from './DashboardVersion.interface';
jest.mock('../common/rich-text-editor/RichTextEditorPreviewer', () => {
const mockPush = jest.fn();
jest.mock('components/common/rich-text-editor/RichTextEditorPreviewer', () => {
return jest
.fn()
.mockImplementation(() => <div>RichTextEditorPreviewer.component</div>);
@ -32,54 +37,75 @@ jest.mock('components/common/description/DescriptionV1', () => {
return jest.fn().mockImplementation(() => <div>Description.component</div>);
});
jest.mock('../EntityVersionTimeLine/EntityVersionTimeLine', () => {
jest.mock('components/EntityVersionTimeLine/EntityVersionTimeLine', () => {
return jest
.fn()
.mockImplementation(() => <div>EntityVersionTimeLine.component</div>);
});
jest.mock('../Loader/Loader', () => {
jest.mock('components/Loader/Loader', () => {
return jest.fn().mockImplementation(() => <div>Loader.component</div>);
});
jest.mock('components/containers/PageLayoutV1', () => {
return jest
.fn()
.mockImplementation(({ children }: { children: ReactNode }) => (
<div data-testid="PageLayoutV1">{children}</div>
));
jest.mock('components/common/error-with-placeholder/ErrorPlaceHolder', () => {
return jest.fn().mockImplementation(() => <div>ErrorPlaceHolder</div>);
});
jest.mock(
'components/DataAssets/DataAssetsVersionHeader/DataAssetsVersionHeader',
() => jest.fn().mockImplementation(() => <div>DataAssetsVersionHeader</div>)
);
jest.mock('components/TabsLabel/TabsLabel.component', () =>
jest.fn().mockImplementation(({ name }) => <div>{name}</div>)
);
jest.mock('components/Tag/TagsContainerV2/TagsContainerV2', () =>
jest.fn().mockImplementation(() => <div>TagsContainerV2</div>)
);
jest.mock('components/Tag/TagsViewer/tags-viewer', () =>
jest.fn().mockImplementation(() => <div>TagsViewer</div>)
);
jest.mock('components/common/CustomPropertyTable/CustomPropertyTable', () => ({
CustomPropertyTable: jest
.fn()
.mockImplementation(() => <div>CustomPropertyTable</div>),
}));
jest.mock('react-router-dom', () => ({
useHistory: jest.fn().mockImplementation(() => ({
push: mockPush,
})),
useParams: jest.fn().mockReturnValue({
tab: 'dashboard',
}),
Link: jest.fn().mockImplementation(() => <div>Link</div>),
}));
JSON.parse = jest.fn().mockReturnValue([]);
describe('Test DashboardVersion page', () => {
it('Checks if the page has all the proper components rendered', async () => {
const { container } = render(
<DashboardVersion {...dashboardVersionProps} />,
{
describe('DashboardVersion tests', () => {
it('Should render component properly if not loading', async () => {
await act(async () => {
render(<DashboardVersion {...dashboardVersionProps} />, {
wrapper: MemoryRouter,
}
);
});
});
const dashboardVersionContainer = await findByTestId(
container,
'dashboard-version-container'
);
const versionData = await findByTestId(container, 'version-data');
const schemaTable = await findByTestId(container, 'schema-table');
const versionData = await screen.findByTestId('version-data');
const schemaTable = await screen.findByTestId('schema-table');
const entityVersionTimeLine = await findByText(
container,
const entityVersionTimeLine = await screen.findByText(
'EntityVersionTimeLine.component'
);
const tabs = await findByTestId(container, 'tabs');
const description = await findByText(container, 'Description.component');
const richTextEditorPreviewer = await findByText(
container,
const tabs = await screen.findByTestId('tabs');
const description = await screen.findByText('Description.component');
const richTextEditorPreviewer = await screen.findByText(
'RichTextEditorPreviewer.component'
);
expect(dashboardVersionContainer).toBeInTheDocument();
expect(versionData).toBeInTheDocument();
expect(schemaTable).toBeInTheDocument();
expect(entityVersionTimeLine).toBeInTheDocument();
@ -88,38 +114,33 @@ describe('Test DashboardVersion page', () => {
expect(richTextEditorPreviewer).toBeInTheDocument();
});
it('Checks if the page has all the proper components rendered, if change version is related to tags', async () => {
const { container } = render(
<DashboardVersion
{...dashboardVersionProps}
currentVersionData={
mockTagChangeVersion as DashboardVersionProp['currentVersionData']
it('Should render components properly if version changes are related to tags', async () => {
await act(async () => {
render(
<DashboardVersion
{...dashboardVersionProps}
currentVersionData={
mockTagChangeVersion as DashboardVersionProp['currentVersionData']
}
/>,
{
wrapper: MemoryRouter,
}
/>,
{
wrapper: MemoryRouter,
}
);
);
});
const dashboardVersionContainer = await findByTestId(
container,
'dashboard-version-container'
);
const versionData = await findByTestId(container, 'version-data');
const schemaTable = await findByTestId(container, 'schema-table');
const versionData = await screen.findByTestId('version-data');
const schemaTable = await screen.findByTestId('schema-table');
const entityVersionTimeLine = await findByText(
container,
const entityVersionTimeLine = await screen.findByText(
'EntityVersionTimeLine.component'
);
const tabs = await findByTestId(container, 'tabs');
const description = await findByText(container, 'Description.component');
const richTextEditorPreviewer = await findByText(
container,
const tabs = await screen.findByTestId('tabs');
const description = await screen.findByText('Description.component');
const richTextEditorPreviewer = await screen.findByText(
'RichTextEditorPreviewer.component'
);
expect(dashboardVersionContainer).toBeInTheDocument();
expect(versionData).toBeInTheDocument();
expect(schemaTable).toBeInTheDocument();
expect(entityVersionTimeLine).toBeInTheDocument();
@ -128,52 +149,109 @@ describe('Test DashboardVersion page', () => {
expect(richTextEditorPreviewer).toBeInTheDocument();
});
it('Checks if the page has all the proper components rendered, if the dashboard deleted is undefined', async () => {
const { container } = render(
<DashboardVersion
{...dashboardVersionProps}
currentVersionData={mockNoChartData}
deleted={undefined}
/>,
{
wrapper: MemoryRouter,
}
);
it('Should render component properly if "deleted" field for dashboard is undefined', async () => {
await act(async () => {
render(
<DashboardVersion
{...dashboardVersionProps}
currentVersionData={mockNoChartData}
deleted={undefined}
/>,
{
wrapper: MemoryRouter,
}
);
});
const dashboardVersionContainer = await findByTestId(
container,
'dashboard-version-container'
);
const versionData = await findByTestId(container, 'version-data');
const entityVersionTimeLine = await findByText(
container,
const versionData = await screen.findByTestId('version-data');
const entityVersionTimeLine = await screen.findByText(
'EntityVersionTimeLine.component'
);
const tabs = await findByTestId(container, 'tabs');
const description = await findByText(container, 'Description.component');
const tabs = await screen.findByTestId('tabs');
const description = await screen.findByText('Description.component');
expect(dashboardVersionContainer).toBeInTheDocument();
expect(versionData).toBeInTheDocument();
expect(entityVersionTimeLine).toBeInTheDocument();
expect(tabs).toBeInTheDocument();
expect(description).toBeInTheDocument();
});
it('If version is loading it should show loading component', async () => {
const { container } = render(
<DashboardVersion {...dashboardVersionProps} isVersionLoading />,
{
it('Should display Loader if isVersionLoading is true', async () => {
await act(async () => {
render(<DashboardVersion {...dashboardVersionProps} isVersionLoading />, {
wrapper: MemoryRouter,
}
);
});
});
const dashboardVersionContainer = await findByTestId(
container,
'dashboard-version-container'
const entityVersionTimeLine = await screen.findByText(
'EntityVersionTimeLine.component'
);
const loader = await findByText(container, 'Loader.component');
const loader = await screen.findByText('Loader.component');
expect(dashboardVersionContainer).toBeInTheDocument();
expect(entityVersionTimeLine).toBeInTheDocument();
expect(loader).toBeInTheDocument();
});
it('Should update url on click of tab', async () => {
await act(async () => {
render(
<DashboardVersion
{...dashboardVersionProps}
entityPermissions={ENTITY_PERMISSIONS}
/>,
{
wrapper: MemoryRouter,
}
);
});
const customPropertyTabLabel = screen.getByText(
'label.custom-property-plural'
);
expect(customPropertyTabLabel).toBeInTheDocument();
await act(async () => {
userEvent.click(customPropertyTabLabel);
});
expect(mockPush).toHaveBeenCalledWith(
'/dashboard/sample_superset.eta_predictions_performance/versions/0.3/custom_properties'
);
});
it('Should display ErrorPlaceholder if no viewing permission', async () => {
await act(async () => {
render(
<DashboardVersion
{...dashboardVersionProps}
entityPermissions={DEFAULT_ENTITY_PERMISSION}
/>,
{
wrapper: MemoryRouter,
}
);
});
const versionData = screen.queryByTestId('version-data');
const schemaTable = screen.queryByTestId('schema-table');
const tabs = screen.queryByTestId('tabs');
const description = screen.queryByText('Description.component');
const richTextEditorPreviewer = screen.queryByText(
'RichTextEditorPreviewer.component'
);
const entityVersionTimeLine = screen.queryByText(
'EntityVersionTimeLine.component'
);
const errorPlaceHolder = screen.getByText('ErrorPlaceHolder');
expect(entityVersionTimeLine).toBeNull();
expect(versionData).toBeNull();
expect(schemaTable).toBeNull();
expect(tabs).toBeNull();
expect(description).toBeNull();
expect(richTextEditorPreviewer).toBeNull();
expect(errorPlaceHolder).toBeInTheDocument();
});
});

View File

@ -361,6 +361,7 @@ export const DataAssetsHeader = ({
<Button
className="w-16 p-0"
data-testid="version-button"
icon={<Icon component={VersionIcon} />}
onClick={onVersionClick}>
<Typography.Text>{version}</Typography.Text>

View File

@ -86,6 +86,7 @@ function DataAssetsVersionHeader({
<Col>
<Button
className="w-16 p-0"
data-testid="version-button"
icon={<Icon component={VersionIcon} />}
onClick={onVersionClick}>
<Typography.Text>{version}</Typography.Text>

View File

@ -38,7 +38,7 @@ import { EntityTabs, EntityType } from 'enums/entity.enum';
import { MlFeature, Mlmodel } from 'generated/entity/data/mlmodel';
import { TagSource } from 'generated/type/tagLabel';
import { cloneDeep, isEqual } from 'lodash';
import React, { FC, Fragment, useEffect, useMemo, useState } from 'react';
import React, { FC, useEffect, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useHistory, useParams } from 'react-router-dom';
import { getFilterTags } from 'utils/TableTags/TableTags.utils';
@ -222,130 +222,128 @@ const MlModelVersion: FC<MlModelVersionProp> = ({
<Col span={24}>
{(currentVersionData as Mlmodel).mlFeatures &&
(currentVersionData as Mlmodel).mlFeatures?.length ? (
<Fragment>
<Row data-testid="feature-list">
<Col span={24}>
<Divider className="m-y-md" />
</Col>
<Col span={24}>
<Typography.Title level={5}>
{t('label.feature-plural-used')}
</Typography.Title>
</Col>
<Row data-testid="feature-list">
<Col span={24}>
<Divider className="m-y-md" />
</Col>
<Col span={24}>
<Typography.Title level={5}>
{t('label.feature-plural-used')}
</Typography.Title>
</Col>
{mlFeaturesData?.map((feature: MlFeature) => (
<Col key={feature.fullyQualifiedName} span={24}>
<Card
bordered
className="m-b-xlg"
data-testid="feature-card"
key={feature.fullyQualifiedName}>
<Row>
<Col className="m-b-xs" span={24}>
<Typography.Text className="font-semibold">
{feature.name}
</Typography.Text>
</Col>
<Col className="m-b-xs" span={24}>
<Space align="start">
<Space>
<Typography.Text className="text-grey-muted">
{`${t('label.type')}:`}
</Typography.Text>{' '}
<Typography.Text>
{feature.dataType || '--'}
</Typography.Text>
</Space>
<Divider
className="border-gray"
type="vertical"
/>
<Space>
<Typography.Text className="text-grey-muted">
{`${t('label.algorithm')}:`}
</Typography.Text>{' '}
<Typography.Text>
{feature.featureAlgorithm || '--'}
</Typography.Text>
</Space>
{mlFeaturesData?.map((feature: MlFeature) => (
<Col key={feature.fullyQualifiedName} span={24}>
<Card
bordered
className="m-b-xlg"
data-testid="feature-card"
key={feature.fullyQualifiedName}>
<Row>
<Col className="m-b-xs" span={24}>
<Typography.Text className="font-semibold">
{feature.name}
</Typography.Text>
</Col>
<Col className="m-b-xs" span={24}>
<Space align="start">
<Space>
<Typography.Text className="text-grey-muted">
{`${t('label.type')}:`}
</Typography.Text>{' '}
<Typography.Text>
{feature.dataType || '--'}
</Typography.Text>
</Space>
</Col>
<Col className="m-b-xs" span={24}>
<Row gutter={8} wrap={false}>
<Col flex="130px">
<Typography.Text className="text-grey-muted">
{`${t('label.glossary-term-plural')} :`}
</Typography.Text>
</Col>
<Divider
className="border-gray"
type="vertical"
/>
<Space>
<Typography.Text className="text-grey-muted">
{`${t('label.algorithm')}:`}
</Typography.Text>{' '}
<Typography.Text>
{feature.featureAlgorithm || '--'}
</Typography.Text>
</Space>
</Space>
</Col>
<Col className="m-b-xs" span={24}>
<Row gutter={8} wrap={false}>
<Col flex="130px">
<Typography.Text className="text-grey-muted">
{`${t('label.glossary-term-plural')} :`}
</Typography.Text>
</Col>
<Col flex="auto">
<TagsViewer
sizeCap={-1}
tags={
getFilterTags(feature.tags ?? [])
.Glossary
}
type="border"
/>
</Col>
</Row>
</Col>
<Col flex="auto">
<TagsViewer
sizeCap={-1}
tags={
getFilterTags(feature.tags ?? [])
.Glossary
}
type="border"
/>
</Col>
</Row>
</Col>
<Col className="m-b-xs" span={24}>
<Row gutter={8} wrap={false}>
<Col flex="130px">
<Typography.Text className="text-grey-muted">
{`${t('label.tag-plural')} :`}
</Typography.Text>
</Col>
<Col flex="auto">
<TagsViewer
sizeCap={-1}
tags={
getFilterTags(feature.tags ?? [])
.Classification
}
type="border"
/>
</Col>
</Row>
</Col>
<Col className="m-b-xs" span={24}>
<Row gutter={8} wrap={false}>
<Col flex="130px">
<Typography.Text className="text-grey-muted">
{`${t('label.tag-plural')} :`}
</Typography.Text>
</Col>
<Col flex="auto">
<TagsViewer
sizeCap={-1}
tags={
getFilterTags(feature.tags ?? [])
.Classification
}
type="border"
/>
</Col>
</Row>
</Col>
<Col className="m-b-xs" span={24}>
<Row gutter={8} wrap={false}>
<Col flex="120px">
<Typography.Text className="text-grey-muted">
{`${t('label.description')} :`}
</Typography.Text>
</Col>
<Col flex="auto">
<Space align="start">
{feature.description ? (
<RichTextEditorPreviewer
enableSeeMoreVariant={false}
markdown={feature.description}
/>
) : (
<Typography.Text className="text-grey-muted">
{t('label.no-entity', {
entity: t('label.description'),
})}
</Typography.Text>
)}
</Space>
</Col>
</Row>
</Col>
<Col className="m-b-xs" span={24}>
<Row gutter={8} wrap={false}>
<Col flex="120px">
<Typography.Text className="text-grey-muted">
{`${t('label.description')} :`}
</Typography.Text>
</Col>
<Col flex="auto">
<Space align="start">
{feature.description ? (
<RichTextEditorPreviewer
enableSeeMoreVariant={false}
markdown={feature.description}
/>
) : (
<Typography.Text className="text-grey-muted">
{t('label.no-entity', {
entity: t('label.description'),
})}
</Typography.Text>
)}
</Space>
</Col>
</Row>
</Col>
<Col span={24}>
<SourceList feature={feature} />
</Col>
</Row>
</Card>
</Col>
))}
</Row>
</Fragment>
<Col span={24}>
<SourceList feature={feature} />
</Col>
</Row>
</Card>
</Col>
))}
</Row>
) : (
<ErrorPlaceHolder />
)}

View File

@ -25,7 +25,6 @@ export interface MlModelVersionProp {
owner: Mlmodel['owner'];
tier: TagLabel;
slashedMlModelName: TitleBreadcrumbProps['titleLinks'];
topicFQN: string;
versionList: EntityHistory;
deleted?: boolean;
backHandler: () => void;

View File

@ -0,0 +1,209 @@
/*
* Copyright 2023 Collate.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { act, render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React from 'react';
import { DEFAULT_ENTITY_PERMISSION } from 'utils/PermissionsUtils';
import {
mlModelVersionMockProps,
mockMlModelDetails,
} from '../../mocks/MlModelVersion.mock';
import MlModelVersion from './MlModelVersion.component';
const mockPush = jest.fn();
jest.mock(
'components/DataAssets/DataAssetsVersionHeader/DataAssetsVersionHeader',
() => jest.fn().mockImplementation(() => <div>DataAssetsVersionHeader</div>)
);
jest.mock('components/TabsLabel/TabsLabel.component', () =>
jest.fn().mockImplementation(({ name }) => <div>{name}</div>)
);
jest.mock('components/Tag/TagsContainerV2/TagsContainerV2', () =>
jest.fn().mockImplementation(() => <div>TagsContainerV2</div>)
);
jest.mock('components/common/CustomPropertyTable/CustomPropertyTable', () => ({
CustomPropertyTable: jest
.fn()
.mockImplementation(() => <div>CustomPropertyTable</div>),
}));
jest.mock('components/common/description/DescriptionV1', () =>
jest.fn().mockImplementation(() => <div>DescriptionV1</div>)
);
jest.mock('components/common/error-with-placeholder/ErrorPlaceHolder', () =>
jest.fn().mockImplementation(() => <div>ErrorPlaceHolder</div>)
);
jest.mock('components/EntityVersionTimeLine/EntityVersionTimeLine', () =>
jest.fn().mockImplementation(() => <div>EntityVersionTimeLine</div>)
);
jest.mock('components/VersionTable/VersionTable.component', () =>
jest.fn().mockImplementation(() => <div>VersionTable</div>)
);
jest.mock('components/Loader/Loader', () =>
jest.fn().mockImplementation(() => <div>Loader</div>)
);
jest.mock('react-router-dom', () => ({
useHistory: jest.fn().mockImplementation(() => ({
push: mockPush,
})),
useParams: jest.fn().mockReturnValue({
tab: 'container',
}),
}));
describe('MlModelVersion tests', () => {
it('Should render component properly if not loading', async () => {
await act(async () => {
render(<MlModelVersion {...mlModelVersionMockProps} />);
});
const dataAssetsVersionHeader = screen.getByText('DataAssetsVersionHeader');
const description = screen.getByText('DescriptionV1');
const featureTabLabel = screen.getByText('label.feature-plural');
const customPropertyTabLabel = screen.getByText(
'label.custom-property-plural'
);
const entityVersionTimeLine = screen.getByText('EntityVersionTimeLine');
expect(dataAssetsVersionHeader).toBeInTheDocument();
expect(description).toBeInTheDocument();
expect(featureTabLabel).toBeInTheDocument();
expect(customPropertyTabLabel).toBeInTheDocument();
expect(entityVersionTimeLine).toBeInTheDocument();
});
it('Should display Loader if isVersionLoading is true', async () => {
await act(async () => {
render(<MlModelVersion {...mlModelVersionMockProps} isVersionLoading />);
});
const loader = screen.getByText('Loader');
const entityVersionTimeLine = screen.getByText('EntityVersionTimeLine');
const dataAssetsVersionHeader = screen.queryByText(
'DataAssetsVersionHeader'
);
const featureTabLabel = screen.queryByText('label.feature-plural');
const customPropertyTabLabel = screen.queryByText(
'label.custom-property-plural'
);
expect(loader).toBeInTheDocument();
expect(entityVersionTimeLine).toBeInTheDocument();
expect(dataAssetsVersionHeader).toBeNull();
expect(featureTabLabel).toBeNull();
expect(customPropertyTabLabel).toBeNull();
});
it('Should display ErrorPlaceholder if no viewing permission', async () => {
await act(async () => {
render(
<MlModelVersion
{...mlModelVersionMockProps}
entityPermissions={DEFAULT_ENTITY_PERMISSION}
/>
);
});
const errorPlaceHolder = screen.getByText('ErrorPlaceHolder');
const loader = screen.queryByText('Loader');
const dataAssetsVersionHeader = screen.queryByText(
'DataAssetsVersionHeader'
);
const featureTabLabel = screen.queryByText('label.feature-plural');
const customPropertyTabLabel = screen.queryByText(
'label.custom-property-plural'
);
const entityVersionTimeLine = screen.queryByText('EntityVersionTimeLine');
expect(errorPlaceHolder).toBeInTheDocument();
expect(loader).toBeNull();
expect(entityVersionTimeLine).toBeNull();
expect(dataAssetsVersionHeader).toBeNull();
expect(featureTabLabel).toBeNull();
expect(customPropertyTabLabel).toBeNull();
});
it('No data placeholder should be displayed if no mlFeatures are present in the mlModel data', async () => {
await act(async () => {
render(
<MlModelVersion
{...mlModelVersionMockProps}
currentVersionData={{ ...mockMlModelDetails, mlFeatures: undefined }}
/>
);
});
const featureTabLabel = screen.getByText('label.feature-plural');
const errorPlaceHolder = screen.getByText('ErrorPlaceHolder');
expect(featureTabLabel).toBeInTheDocument();
expect(errorPlaceHolder).toBeInTheDocument();
});
it('Should update url on click of tab', async () => {
await act(async () => {
render(<MlModelVersion {...mlModelVersionMockProps} />);
});
const customPropertyTabLabel = screen.getByText(
'label.custom-property-plural'
);
expect(customPropertyTabLabel).toBeInTheDocument();
await act(async () => {
userEvent.click(customPropertyTabLabel);
});
expect(mockPush).toHaveBeenCalledWith(
'/mlmodel/mlflow_svc.eta_predictions/versions/0.3/custom_properties'
);
});
it('Should display ErrorPlaceholder in Custom Property tab if no "viewAll" permission', async () => {
await act(async () => {
render(
<MlModelVersion
{...mlModelVersionMockProps}
entityPermissions={{ ...DEFAULT_ENTITY_PERMISSION, ViewBasic: true }}
/>
);
});
const customPropertyTabLabel = screen.getByText(
'label.custom-property-plural'
);
let errorPlaceHolder = screen.queryByText('ErrorPlaceHolder');
expect(customPropertyTabLabel).toBeInTheDocument();
expect(errorPlaceHolder).toBeNull();
await act(async () => {
userEvent.click(customPropertyTabLabel);
});
errorPlaceHolder = screen.getByText('ErrorPlaceHolder');
expect(errorPlaceHolder).toBeInTheDocument();
});
});

View File

@ -19,7 +19,10 @@ import { CustomPropertyTable } from 'components/common/CustomPropertyTable/Custo
import { CustomPropertyProps } from 'components/common/CustomPropertyTable/CustomPropertyTable.interface';
import DescriptionV1 from 'components/common/description/DescriptionV1';
import ErrorPlaceHolder from 'components/common/error-with-placeholder/ErrorPlaceHolder';
import RichTextEditorPreviewer from 'components/common/rich-text-editor/RichTextEditorPreviewer';
import DataAssetsVersionHeader from 'components/DataAssets/DataAssetsVersionHeader/DataAssetsVersionHeader';
import EntityVersionTimeLine from 'components/EntityVersionTimeLine/EntityVersionTimeLine';
import Loader from 'components/Loader/Loader';
import TabsLabel from 'components/TabsLabel/TabsLabel.component';
import TagsContainerV2 from 'components/Tag/TagsContainerV2/TagsContainerV2';
import TagsViewer from 'components/Tag/TagsViewer/tags-viewer';
@ -55,9 +58,6 @@ import {
getTextDiff,
} from '../../utils/EntityVersionUtils';
import { TagLabelWithStatus } from '../../utils/EntityVersionUtils.interface';
import RichTextEditorPreviewer from '../common/rich-text-editor/RichTextEditorPreviewer';
import EntityVersionTimeLine from '../EntityVersionTimeLine/EntityVersionTimeLine';
import Loader from '../Loader/Loader';
import { PipelineVersionProp } from './PipelineVersion.interface';
const PipelineVersion: FC<PipelineVersionProp> = ({

View File

@ -0,0 +1,218 @@
/*
* Copyright 2023 Collate.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { act, render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { EntityTabs } from 'enums/entity.enum';
import React from 'react';
import { MemoryRouter } from 'react-router-dom';
import { DEFAULT_ENTITY_PERMISSION } from 'utils/PermissionsUtils';
import {
mockColumnDiffPipelineVersionMockProps,
pipelineVersionMockProps,
} from '../../mocks/PipelineVersion.mock';
import PipelineVersion from './PipelineVersion.component';
const mockPush = jest.fn();
jest.mock(
'components/DataAssets/DataAssetsVersionHeader/DataAssetsVersionHeader',
() => jest.fn().mockImplementation(() => <div>DataAssetsVersionHeader</div>)
);
jest.mock('components/TabsLabel/TabsLabel.component', () =>
jest.fn().mockImplementation(({ name }) => <div>{name}</div>)
);
jest.mock('components/Tag/TagsContainerV2/TagsContainerV2', () =>
jest.fn().mockImplementation(() => <div>TagsContainerV2</div>)
);
jest.mock('components/common/rich-text-editor/RichTextEditorPreviewer', () =>
jest.fn().mockImplementation(() => <div>RichTextEditorPreviewer</div>)
);
jest.mock('components/Tag/TagsViewer/tags-viewer', () =>
jest.fn().mockImplementation(() => <div>TagsViewer</div>)
);
jest.mock('components/common/CustomPropertyTable/CustomPropertyTable', () => ({
CustomPropertyTable: jest
.fn()
.mockImplementation(() => <div>CustomPropertyTable</div>),
}));
jest.mock('components/common/description/DescriptionV1', () =>
jest.fn().mockImplementation(() => <div>DescriptionV1</div>)
);
jest.mock('components/common/error-with-placeholder/ErrorPlaceHolder', () =>
jest.fn().mockImplementation(() => <div>ErrorPlaceHolder</div>)
);
jest.mock('components/EntityVersionTimeLine/EntityVersionTimeLine', () =>
jest.fn().mockImplementation(() => <div>EntityVersionTimeLine</div>)
);
jest.mock('components/Loader/Loader', () =>
jest.fn().mockImplementation(() => <div>Loader</div>)
);
jest.mock('react-router-dom', () => ({
useHistory: jest.fn().mockImplementation(() => ({
push: mockPush,
})),
useParams: jest.fn().mockReturnValue({
tab: EntityTabs.PIPELINE,
}),
Link: jest.fn().mockImplementation(() => <div>Link</div>),
}));
JSON.parse = jest.fn().mockReturnValue([]);
describe('PipelineVersion tests', () => {
it('Should render component properly if not loading', async () => {
await act(async () => {
render(<PipelineVersion {...mockColumnDiffPipelineVersionMockProps} />, {
wrapper: MemoryRouter,
});
});
const dataAssetsVersionHeader = screen.getByText('DataAssetsVersionHeader');
const description = screen.getByText('DescriptionV1');
const schemaTabLabel = screen.getByText('label.task-plural');
const customPropertyTabLabel = screen.getByText(
'label.custom-property-plural'
);
const entityVersionTimeLine = screen.getByText('EntityVersionTimeLine');
const schemaTable = screen.getByTestId('schema-table');
expect(dataAssetsVersionHeader).toBeInTheDocument();
expect(description).toBeInTheDocument();
expect(schemaTabLabel).toBeInTheDocument();
expect(customPropertyTabLabel).toBeInTheDocument();
expect(entityVersionTimeLine).toBeInTheDocument();
expect(schemaTable).toBeInTheDocument();
});
it('Should display Loader if isVersionLoading is true', async () => {
await act(async () => {
render(
<PipelineVersion {...pipelineVersionMockProps} isVersionLoading />
);
});
const loader = screen.getByText('Loader');
const entityVersionTimeLine = screen.getByText('EntityVersionTimeLine');
const dataAssetsVersionHeader = screen.queryByText(
'DataAssetsVersionHeader'
);
const schemaTabLabel = screen.queryByText('label.schema');
const customPropertyTabLabel = screen.queryByText(
'label.custom-property-plural'
);
const schemaTable = screen.queryByTestId('schema-table');
expect(loader).toBeInTheDocument();
expect(entityVersionTimeLine).toBeInTheDocument();
expect(dataAssetsVersionHeader).toBeNull();
expect(schemaTabLabel).toBeNull();
expect(customPropertyTabLabel).toBeNull();
expect(schemaTable).toBeNull();
});
it('Should display ErrorPlaceholder if no viewing permission', async () => {
await act(async () => {
render(
<PipelineVersion
{...pipelineVersionMockProps}
entityPermissions={DEFAULT_ENTITY_PERMISSION}
/>
);
});
const errorPlaceHolder = screen.getByText('ErrorPlaceHolder');
const loader = screen.queryByText('Loader');
const dataAssetsVersionHeader = screen.queryByText(
'DataAssetsVersionHeader'
);
const schemaTabLabel = screen.queryByText('label.schema');
const customPropertyTabLabel = screen.queryByText(
'label.custom-property-plural'
);
const entityVersionTimeLine = screen.queryByText('EntityVersionTimeLine');
const schemaTable = screen.queryByTestId('schema-table');
expect(errorPlaceHolder).toBeInTheDocument();
expect(loader).toBeNull();
expect(entityVersionTimeLine).toBeNull();
expect(dataAssetsVersionHeader).toBeNull();
expect(schemaTabLabel).toBeNull();
expect(customPropertyTabLabel).toBeNull();
expect(schemaTable).toBeNull();
});
it('Should update url on click of tab', async () => {
await act(async () => {
render(<PipelineVersion {...pipelineVersionMockProps} />, {
wrapper: MemoryRouter,
});
});
const customPropertyTabLabel = screen.getByText(
'label.custom-property-plural'
);
expect(customPropertyTabLabel).toBeInTheDocument();
await act(async () => {
userEvent.click(customPropertyTabLabel);
});
expect(mockPush).toHaveBeenCalledWith(
'/pipeline/sample_airflow.snowflake_etl/versions/0.3/custom_properties'
);
});
it('Should display ErrorPlaceholder in Custom Property tab if no "viewAll" permission', async () => {
await act(async () => {
render(
<PipelineVersion
{...pipelineVersionMockProps}
entityPermissions={{ ...DEFAULT_ENTITY_PERMISSION, ViewBasic: true }}
/>,
{
wrapper: MemoryRouter,
}
);
});
const customPropertyTabLabel = screen.getByText(
'label.custom-property-plural'
);
const schemaTable = screen.getByTestId('schema-table');
let errorPlaceHolder = screen.queryByText('ErrorPlaceHolder');
expect(customPropertyTabLabel).toBeInTheDocument();
expect(schemaTable).toBeInTheDocument();
expect(errorPlaceHolder).toBeNull();
await act(async () => {
userEvent.click(customPropertyTabLabel);
});
errorPlaceHolder = screen.getByText('ErrorPlaceHolder');
expect(errorPlaceHolder).toBeInTheDocument();
});
});

View File

@ -18,8 +18,11 @@ import { CustomPropertyProps } from 'components/common/CustomPropertyTable/Custo
import DescriptionV1 from 'components/common/description/DescriptionV1';
import ErrorPlaceHolder from 'components/common/error-with-placeholder/ErrorPlaceHolder';
import DataAssetsVersionHeader from 'components/DataAssets/DataAssetsVersionHeader/DataAssetsVersionHeader';
import EntityVersionTimeLine from 'components/EntityVersionTimeLine/EntityVersionTimeLine';
import Loader from 'components/Loader/Loader';
import TabsLabel from 'components/TabsLabel/TabsLabel.component';
import TagsContainerV2 from 'components/Tag/TagsContainerV2/TagsContainerV2';
import VersionTable from 'components/VersionTable/VersionTable.component';
import { getVersionPathWithTab } from 'constants/constants';
import { EntityField } from 'constants/Feeds.constants';
import { ERROR_PLACEHOLDER_TYPE } from 'enums/common.enum';
@ -44,12 +47,9 @@ import {
getEntityVersionByField,
getEntityVersionTags,
} from '../../utils/EntityVersionUtils';
import EntityVersionTimeLine from '../EntityVersionTimeLine/EntityVersionTimeLine';
import Loader from '../Loader/Loader';
import VersionTable from '../VersionTable/VersionTable.component';
import { DatasetVersionProp } from './DatasetVersion.interface';
import { TableVersionProp } from './TableVersion.interface';
const DatasetVersion: React.FC<DatasetVersionProp> = ({
const TableVersion: React.FC<TableVersionProp> = ({
version,
currentVersionData,
isVersionLoading,
@ -62,7 +62,7 @@ const DatasetVersion: React.FC<DatasetVersionProp> = ({
backHandler,
versionHandler,
entityPermissions,
}: DatasetVersionProp) => {
}: TableVersionProp) => {
const { t } = useTranslation();
const history = useHistory();
const { tab } = useParams<{ tab: EntityTabs }>();
@ -265,4 +265,4 @@ const DatasetVersion: React.FC<DatasetVersionProp> = ({
);
};
export default DatasetVersion;
export default TableVersion;

View File

@ -18,7 +18,7 @@ import { EntityHistory } from '../../generated/type/entityHistory';
import { TagLabel } from '../../generated/type/tagLabel';
import { TitleBreadcrumbProps } from '../common/title-breadcrumb/title-breadcrumb.interface';
export interface DatasetVersionProp {
export interface TableVersionProp {
version: string;
currentVersionData: VersionData;
isVersionLoading: boolean;

View File

@ -0,0 +1,199 @@
/*
* Copyright 2023 Collate.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { act, render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React from 'react';
import { DEFAULT_ENTITY_PERMISSION } from 'utils/PermissionsUtils';
import { tableVersionMockProps } from '../../mocks/TableVersion.mock';
import TableVersion from './TableVersion.component';
const mockPush = jest.fn();
jest.mock(
'components/DataAssets/DataAssetsVersionHeader/DataAssetsVersionHeader',
() => jest.fn().mockImplementation(() => <div>DataAssetsVersionHeader</div>)
);
jest.mock('components/TabsLabel/TabsLabel.component', () =>
jest.fn().mockImplementation(({ name }) => <div>{name}</div>)
);
jest.mock('components/Tag/TagsContainerV2/TagsContainerV2', () =>
jest.fn().mockImplementation(() => <div>TagsContainerV2</div>)
);
jest.mock('components/common/CustomPropertyTable/CustomPropertyTable', () => ({
CustomPropertyTable: jest
.fn()
.mockImplementation(() => <div>CustomPropertyTable</div>),
}));
jest.mock('components/common/description/DescriptionV1', () =>
jest.fn().mockImplementation(() => <div>DescriptionV1</div>)
);
jest.mock('components/common/error-with-placeholder/ErrorPlaceHolder', () =>
jest.fn().mockImplementation(() => <div>ErrorPlaceHolder</div>)
);
jest.mock('components/EntityVersionTimeLine/EntityVersionTimeLine', () =>
jest.fn().mockImplementation(() => <div>EntityVersionTimeLine</div>)
);
jest.mock('components/VersionTable/VersionTable.component', () =>
jest.fn().mockImplementation(() => <div>VersionTable</div>)
);
jest.mock('components/Loader/Loader', () =>
jest.fn().mockImplementation(() => <div>Loader</div>)
);
jest.mock('react-router-dom', () => ({
useHistory: jest.fn().mockImplementation(() => ({
push: mockPush,
})),
useParams: jest.fn().mockReturnValue({
tab: 'tables',
}),
}));
describe('TableVersion tests', () => {
it('Should render component properly if not loading', async () => {
await act(async () => {
render(<TableVersion {...tableVersionMockProps} />);
});
const dataAssetsVersionHeader = screen.getByText('DataAssetsVersionHeader');
const description = screen.getByText('DescriptionV1');
const schemaTabLabel = screen.getByText('label.schema');
const customPropertyTabLabel = screen.getByText(
'label.custom-property-plural'
);
const entityVersionTimeLine = screen.getByText('EntityVersionTimeLine');
const versionTable = screen.getByText('VersionTable');
expect(dataAssetsVersionHeader).toBeInTheDocument();
expect(description).toBeInTheDocument();
expect(schemaTabLabel).toBeInTheDocument();
expect(customPropertyTabLabel).toBeInTheDocument();
expect(entityVersionTimeLine).toBeInTheDocument();
expect(versionTable).toBeInTheDocument();
});
it('Should display Loader if isVersionLoading is true', async () => {
await act(async () => {
render(<TableVersion {...tableVersionMockProps} isVersionLoading />);
});
const loader = screen.getByText('Loader');
const entityVersionTimeLine = screen.getByText('EntityVersionTimeLine');
const dataAssetsVersionHeader = screen.queryByText(
'DataAssetsVersionHeader'
);
const schemaTabLabel = screen.queryByText('label.schema');
const customPropertyTabLabel = screen.queryByText(
'label.custom-property-plural'
);
const versionTable = screen.queryByText('VersionTable');
expect(loader).toBeInTheDocument();
expect(entityVersionTimeLine).toBeInTheDocument();
expect(dataAssetsVersionHeader).toBeNull();
expect(schemaTabLabel).toBeNull();
expect(customPropertyTabLabel).toBeNull();
expect(versionTable).toBeNull();
});
it('Should display ErrorPlaceholder if no viewing permission', async () => {
await act(async () => {
render(
<TableVersion
{...tableVersionMockProps}
entityPermissions={DEFAULT_ENTITY_PERMISSION}
/>
);
});
const errorPlaceHolder = screen.getByText('ErrorPlaceHolder');
const loader = screen.queryByText('Loader');
const dataAssetsVersionHeader = screen.queryByText(
'DataAssetsVersionHeader'
);
const schemaTabLabel = screen.queryByText('label.schema');
const customPropertyTabLabel = screen.queryByText(
'label.custom-property-plural'
);
const entityVersionTimeLine = screen.queryByText('EntityVersionTimeLine');
const versionTable = screen.queryByText('VersionTable');
expect(errorPlaceHolder).toBeInTheDocument();
expect(loader).toBeNull();
expect(entityVersionTimeLine).toBeNull();
expect(dataAssetsVersionHeader).toBeNull();
expect(schemaTabLabel).toBeNull();
expect(customPropertyTabLabel).toBeNull();
expect(versionTable).toBeNull();
});
it('Should update url on click of tab', async () => {
await act(async () => {
render(<TableVersion {...tableVersionMockProps} />);
});
const customPropertyTabLabel = screen.getByText(
'label.custom-property-plural'
);
const versionTable = screen.getByText('VersionTable');
expect(customPropertyTabLabel).toBeInTheDocument();
expect(versionTable).toBeInTheDocument();
await act(async () => {
userEvent.click(customPropertyTabLabel);
});
expect(mockPush).toHaveBeenCalledWith(
'/table/sample_data.ecommerce_db.shopify.raw_product_catalog/versions/0.3/custom_properties'
);
});
it('Should display ErrorPlaceholder in Custom Property tab if no "viewAll" permission', async () => {
await act(async () => {
render(
<TableVersion
{...tableVersionMockProps}
entityPermissions={{ ...DEFAULT_ENTITY_PERMISSION, ViewBasic: true }}
/>
);
});
const customPropertyTabLabel = screen.getByText(
'label.custom-property-plural'
);
const versionTable = screen.getByText('VersionTable');
let errorPlaceHolder = screen.queryByText('ErrorPlaceHolder');
expect(customPropertyTabLabel).toBeInTheDocument();
expect(versionTable).toBeInTheDocument();
expect(errorPlaceHolder).toBeNull();
await act(async () => {
userEvent.click(customPropertyTabLabel);
});
errorPlaceHolder = screen.getByText('ErrorPlaceHolder');
expect(errorPlaceHolder).toBeInTheDocument();
});
});

View File

@ -18,6 +18,8 @@ import { CustomPropertyProps } from 'components/common/CustomPropertyTable/Custo
import DescriptionV1 from 'components/common/description/DescriptionV1';
import ErrorPlaceHolder from 'components/common/error-with-placeholder/ErrorPlaceHolder';
import DataAssetsVersionHeader from 'components/DataAssets/DataAssetsVersionHeader/DataAssetsVersionHeader';
import EntityVersionTimeLine from 'components/EntityVersionTimeLine/EntityVersionTimeLine';
import Loader from 'components/Loader/Loader';
import TabsLabel from 'components/TabsLabel/TabsLabel.component';
import TagsContainerV2 from 'components/Tag/TagsContainerV2/TagsContainerV2';
import TopicSchemaFields from 'components/TopicDetails/TopicSchema/TopicSchema';
@ -37,8 +39,6 @@ import {
getEntityVersionTags,
getUpdatedMessageSchema,
} from '../../utils/EntityVersionUtils';
import EntityVersionTimeLine from '../EntityVersionTimeLine/EntityVersionTimeLine';
import Loader from '../Loader/Loader';
import { TopicVersionProp } from './TopicVersion.interface';
const TopicVersion: FC<TopicVersionProp> = ({

View File

@ -0,0 +1,228 @@
/*
* Copyright 2023 Collate.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { act, render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { ENTITY_PERMISSIONS } from 'mocks/Permissions.mock';
import React from 'react';
import { DEFAULT_ENTITY_PERMISSION } from 'utils/PermissionsUtils';
import { topicVersionMockProps } from '../../mocks/TopicVersion.mock';
import TopicVersion from './TopicVersion.component';
const mockPush = jest.fn();
jest.mock(
'components/DataAssets/DataAssetsVersionHeader/DataAssetsVersionHeader',
() => jest.fn().mockImplementation(() => <div>DataAssetsVersionHeader</div>)
);
jest.mock('components/TabsLabel/TabsLabel.component', () =>
jest.fn().mockImplementation(({ name }) => <div>{name}</div>)
);
jest.mock('components/Tag/TagsContainerV2/TagsContainerV2', () =>
jest.fn().mockImplementation(() => <div>TagsContainerV2</div>)
);
jest.mock('components/common/CustomPropertyTable/CustomPropertyTable', () => ({
CustomPropertyTable: jest
.fn()
.mockImplementation(() => <div>CustomPropertyTable</div>),
}));
jest.mock('components/common/description/DescriptionV1', () =>
jest.fn().mockImplementation(() => <div>DescriptionV1</div>)
);
jest.mock('components/common/error-with-placeholder/ErrorPlaceHolder', () =>
jest.fn().mockImplementation(() => <div>ErrorPlaceHolder</div>)
);
jest.mock('components/EntityVersionTimeLine/EntityVersionTimeLine', () =>
jest.fn().mockImplementation(() => <div>EntityVersionTimeLine</div>)
);
jest.mock('components/TopicDetails/TopicSchema/TopicSchema', () =>
jest.fn().mockImplementation(() => <div>TopicSchema</div>)
);
jest.mock('components/Loader/Loader', () =>
jest.fn().mockImplementation(() => <div>Loader</div>)
);
jest.mock('react-router-dom', () => ({
useHistory: jest.fn().mockImplementation(() => ({
push: mockPush,
})),
useParams: jest.fn().mockReturnValue({
tab: 'topics',
}),
}));
describe('TopicVersion tests', () => {
it('Should render component properly if not loading', async () => {
await act(async () => {
render(<TopicVersion {...topicVersionMockProps} />);
});
const dataAssetsVersionHeader = screen.getByText('DataAssetsVersionHeader');
const description = screen.getByText('DescriptionV1');
const schemaTabLabel = screen.getByText('label.schema');
const customPropertyTabLabel = screen.getByText(
'label.custom-property-plural'
);
const entityVersionTimeLine = screen.getByText('EntityVersionTimeLine');
const topicSchema = screen.getByText('TopicSchema');
expect(dataAssetsVersionHeader).toBeInTheDocument();
expect(description).toBeInTheDocument();
expect(schemaTabLabel).toBeInTheDocument();
expect(customPropertyTabLabel).toBeInTheDocument();
expect(entityVersionTimeLine).toBeInTheDocument();
expect(topicSchema).toBeInTheDocument();
});
it('Should display Loader if isVersionLoading is true', async () => {
await act(async () => {
render(<TopicVersion {...topicVersionMockProps} isVersionLoading />);
});
const loader = screen.getByText('Loader');
const entityVersionTimeLine = screen.getByText('EntityVersionTimeLine');
const dataAssetsVersionHeader = screen.queryByText(
'DataAssetsVersionHeader'
);
const schemaTabLabel = screen.queryByText('label.schema');
const customPropertyTabLabel = screen.queryByText(
'label.custom-property-plural'
);
const topicSchema = screen.queryByText('TopicSchema');
expect(loader).toBeInTheDocument();
expect(entityVersionTimeLine).toBeInTheDocument();
expect(dataAssetsVersionHeader).toBeNull();
expect(schemaTabLabel).toBeNull();
expect(customPropertyTabLabel).toBeNull();
expect(topicSchema).toBeNull();
});
it('Should display ErrorPlaceholder if no viewing permission', async () => {
await act(async () => {
render(
<TopicVersion
{...topicVersionMockProps}
entityPermissions={DEFAULT_ENTITY_PERMISSION}
/>
);
});
const errorPlaceHolder = screen.getByText('ErrorPlaceHolder');
const loader = screen.queryByText('Loader');
const dataAssetsVersionHeader = screen.queryByText(
'DataAssetsVersionHeader'
);
const schemaTabLabel = screen.queryByText('label.schema');
const customPropertyTabLabel = screen.queryByText(
'label.custom-property-plural'
);
const entityVersionTimeLine = screen.queryByText('EntityVersionTimeLine');
const topicSchema = screen.queryByText('TopicSchema');
expect(errorPlaceHolder).toBeInTheDocument();
expect(loader).toBeNull();
expect(entityVersionTimeLine).toBeNull();
expect(dataAssetsVersionHeader).toBeNull();
expect(schemaTabLabel).toBeNull();
expect(customPropertyTabLabel).toBeNull();
expect(topicSchema).toBeNull();
});
it('Should display ErrorPlaceholder in Custom Property tab if no "viewAll" permission', async () => {
await act(async () => {
render(
<TopicVersion
{...topicVersionMockProps}
entityPermissions={{ ...DEFAULT_ENTITY_PERMISSION, ViewBasic: true }}
/>
);
});
const customPropertyTabLabel = screen.getByText(
'label.custom-property-plural'
);
const topicSchema = screen.getByText('TopicSchema');
let errorPlaceHolder = screen.queryByText('ErrorPlaceHolder');
expect(customPropertyTabLabel).toBeInTheDocument();
expect(topicSchema).toBeInTheDocument();
expect(errorPlaceHolder).toBeNull();
await act(async () => {
userEvent.click(customPropertyTabLabel);
});
errorPlaceHolder = screen.getByText('ErrorPlaceHolder');
expect(errorPlaceHolder).toBeInTheDocument();
});
it('Should update url on click of tab', async () => {
await act(async () => {
render(
<TopicVersion
{...topicVersionMockProps}
entityPermissions={ENTITY_PERMISSIONS}
/>
);
});
const customPropertyTabLabel = screen.getByText(
'label.custom-property-plural'
);
expect(customPropertyTabLabel).toBeInTheDocument();
await act(async () => {
userEvent.click(customPropertyTabLabel);
});
expect(mockPush).toHaveBeenCalledWith(
'/topic/sample_kafka.sales/versions/0.3/custom_properties'
);
});
it('ErrorPlaceholder should be displayed in case of no view permissions', async () => {
await act(async () => {
render(
<TopicVersion
{...topicVersionMockProps}
entityPermissions={DEFAULT_ENTITY_PERMISSION}
/>
);
});
const topicSchema = screen.queryByText('TopicSchema');
const description = screen.queryByText('Description.component');
const entityVersionTimeLine = screen.queryByText(
'EntityVersionTimeLine.component'
);
const errorPlaceHolder = screen.getByText('ErrorPlaceHolder');
expect(entityVersionTimeLine).toBeNull();
expect(topicSchema).toBeNull();
expect(description).toBeNull();
expect(errorPlaceHolder).toBeInTheDocument();
});
});

View File

@ -0,0 +1,96 @@
/*
* Copyright 2023 Collate.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {
Constraint,
DataType,
FileFormat,
StorageServiceType,
} from 'generated/entity/data/container';
import { ENTITY_PERMISSIONS } from 'mocks/Permissions.mock';
import {
mockBackHandler,
mockOwner,
mockTier,
mockVersionHandler,
mockVersionList,
} from 'mocks/VersionCommon.mock';
import { ContainerVersionProp } from '../components/ContainerVersion/ContainerVersion.interface';
export const mockContainerData = {
id: 'fa390cbe-63a3-4ebb-976b-4cc9cbe5a234',
name: 'expenditures',
fullyQualifiedName: 's3_storage_sample.departments.finance.expenditures',
displayName: 'Expenditures for the current year',
description: 'Bucket containing finance expenditures information updated',
version: 0.2,
updatedAt: 1689130888441,
updatedBy: 'admin',
service: {
id: '5bd7b845-adcd-4eb0-964f-c1c99c77bbb6',
type: 'storageService',
name: 's3_storage_sample',
fullyQualifiedName: 's3_storage_sample',
deleted: false,
},
dataModel: {
isPartitioned: false,
columns: [
{
name: 'department_id',
dataType: DataType.Numeric,
dataTypeDisplay: 'numeric',
description:
'The ID of the department. This column is the primary key for this table.',
fullyQualifiedName: 's3_storage_sample.expenditures.department_id',
tags: [],
constraint: Constraint.PrimaryKey,
ordinalPosition: 1,
},
],
},
prefix: '/departments/finance/expenditures-2023',
numberOfObjects: 10,
size: 65536,
fileFormats: [FileFormat.Zstd, FileFormat.Tsv],
serviceType: StorageServiceType.S3,
tags: [],
changeDescription: {
fieldsAdded: [],
fieldsUpdated: [
{
name: 'description',
oldValue: 'Bucket containing finance expenditures information',
newValue: 'Bucket containing finance expenditures information updated',
},
],
fieldsDeleted: [],
previousVersion: 0.1,
},
deleted: false,
};
export const containerVersionMockProps: ContainerVersionProp = {
version: '0.3',
currentVersionData: mockContainerData,
isVersionLoading: false,
owner: mockOwner,
tier: mockTier,
breadCrumbList: [],
containerFQN: 'sample_data.ecommerce_db.shopify.raw_product_catalog',
versionList: mockVersionList,
deleted: false,
backHandler: mockBackHandler,
versionHandler: mockVersionHandler,
entityPermissions: ENTITY_PERMISSIONS,
};

View File

@ -0,0 +1,181 @@
/*
* Copyright 2023 Collate.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {
FeatureSourceDataType,
FeatureType,
MlModelServiceType,
} from 'generated/entity/data/mlmodel';
import { ENTITY_PERMISSIONS } from 'mocks/Permissions.mock';
import {
mockBackHandler,
mockOwner,
mockTier,
mockVersionHandler,
mockVersionList,
} from 'mocks/VersionCommon.mock';
import { MlModelVersionProp } from '../components/MlModelVersion/MlModelVersion.interface';
export const mockMlModelDetails = {
id: '68b51299-dfd1-48ce-a00e-9611da93e7a3',
name: 'eta_predictions',
fullyQualifiedName: 'mlflow_svc.eta_predictions',
displayName: 'ETA Predictions',
description: 'ETA Predictions Model updated',
algorithm: 'Neural Network',
mlFeatures: [
{
name: 'sales',
dataType: FeatureType.Numerical,
description: 'Sales amount',
fullyQualifiedName: 'mlflow_svc.eta_predictions.sales',
featureSources: [
{
name: 'gross_sales',
dataType: FeatureSourceDataType.Integer,
fullyQualifiedName:
'sample_data.ecommerce_db.shopify.fact_sale.gross_sales',
dataSource: {
id: '655888c1-6a55-4730-a0a2-f1aa287301e2',
type: 'table',
fullyQualifiedName: 'sample_data.ecommerce_db.shopify.fact_sale',
description: '',
href: 'http://openmetadata-server:8585/api/v1/tables/655888c1-6a55-4730-a0a2-f1aa287301e2',
},
},
],
},
{
name: 'product',
dataType: FeatureType.Numerical,
fullyQualifiedName: 'mlflow_svc.eta_predictions.product',
featureSources: [
{
name: 'gross_sales',
dataType: FeatureSourceDataType.Integer,
fullyQualifiedName:
'sample_data.ecommerce_db.shopify.fact_sale.gross_sales',
dataSource: {
id: '655888c1-6a55-4730-a0a2-f1aa287301e2',
type: 'table',
fullyQualifiedName: 'sample_data.ecommerce_db.shopify.fact_sale',
description: '',
href: 'http://openmetadata-server:8585/api/v1/tables/655888c1-6a55-4730-a0a2-f1aa287301e2',
},
},
],
},
],
mlHyperParameters: [
{
name: 'regularisation',
value: '0.5',
},
{
name: 'random',
value: 'hello',
},
],
target: 'ETA_time',
dashboard: {
id: 'c995db51-0143-4561-9ea5-8cfabf401c48',
type: 'dashboard',
name: 'eta_predictions_performance',
fullyQualifiedName: 'sample_superset.eta_predictions_performance',
description: '',
displayName: 'ETA Predictions Performance',
deleted: false,
},
mlStore: {
storage: 's3://path-to-pickle',
imageRepository: 'https://docker.hub.com/image',
},
server: 'http://my-server.ai',
followers: [],
tags: [],
version: 0.2,
updatedAt: 1689140045807,
updatedBy: 'admin',
service: {
id: '0d546f38-8198-4444-9e84-9f203d598fe5',
type: 'mlmodelService',
name: 'mlflow_svc',
fullyQualifiedName: 'mlflow_svc',
deleted: false,
},
serviceType: MlModelServiceType.Mlflow,
changeDescription: {
fieldsAdded: [
{
name: 'mlFeatures',
newValue: `[{"name":"sales","dataType":"numerical","description":"Sales amount update","fullyQualifiedName":"mlflow_svc.eta_predictions.sales",
"featureSources":[{"name":"gross_sales","dataType":"integer","fullyQualifiedName":"sample_data.ecommerce_db.shopify.fact_sale.gross_sales",
"dataSource":{"id":"655888c1-6a55-4730-a0a2-f1aa287301e2","type":"table","fullyQualifiedName":"sample_data.ecommerce_db.shopify.fact_sale",
"description":"The.","href":"http://openmetadata-server:8585/api/v1/tables/655888c1-6a55-4730-a0a2-f1aa287301e2"}}]}]`,
},
{
name: 'mlFeatures',
newValue: `[{"name":"persona","dataType":"categorical","description":"type of buyer","fullyQualifiedName":"mlflow_svc.eta_predictions.persona",
"featureSources":[{"name":"membership","dataType":"string","fullyQualifiedName":"sample_data.ecommerce_db.shopify.raw_customer.membership",
"dataSource":{"id":"c37dae98-987b-4e09-b1d7-79155c83f0e4","type":"table","fullyQualifiedName":"sample_data.ecommerce_db.shopify.raw_customer",
"description":"This","href":"http://openmetadata-server:8585/api/v1/tables/c37dae98-987b-4e09-b1d7-79155c83f0e4"}},
{"name":"platform","dataType":"string","fullyQualifiedName":"sample_data.ecommerce_db.shopify.raw_customer.platform",
"dataSource":{"id":"c37dae98-987b-4e09-b1d7-79155c83f0e4","type":"table","fullyQualifiedName":"sample_data.ecommerce_db.shopify.raw_customer",
"description":"This","href":"http://openmetadata-server:8585/api/v1/tables/c37dae98-987b-4e09-b1d7-79155c83f0e4"}}],
"featureAlgorithm":"PCA","tags":[{"tagFQN":"PII.Sensitive","source":"Classification","labelType":"Manual","state":"Confirmed"}]}]`,
},
],
fieldsUpdated: [
{
name: 'description',
oldValue: 'ETA Predictions Model',
newValue: 'ETA Predictions Model updated',
},
],
fieldsDeleted: [
{
name: 'mlFeatures',
oldValue: `[{"name":"sales","dataType":"numerical","description":"Sales amount","fullyQualifiedName":"mlflow_svc.eta_predictions.sales",
"featureSources":[{"name":"gross_sales","dataType":"integer","fullyQualifiedName":"sample_data.ecommerce_db.shopify.fact_sale.gross_sales",
"dataSource":{"id":"655888c1-6a55-4730-a0a2-f1aa287301e2","type":"table","fullyQualifiedName":"sample_data.ecommerce_db.shopify.fact_sale",
"description":"The","href":"http://openmetadata-server:8585/api/v1/tables/655888c1-6a55-4730-a0a2-f1aa287301e2"}}]}]`,
},
{
name: 'mlFeatures',
oldValue: `[{"name":"persona","dataType":"categorical","description":"type of buyer","fullyQualifiedName":"mlflow_svc.eta_predictions.persona",
"featureSources":[{"name":"membership","dataType":"string","fullyQualifiedName":"sample_data.ecommerce_db.shopify.raw_customer.membership",
"dataSource":{"id":"c37dae98-987b-4e09-b1d7-79155c83f0e4","type":"table","fullyQualifiedName":"sample_data.ecommerce_db.shopify.raw_customer",
"description":"The","href":"http://openmetadata-server:8585/api/v1/tables/c37dae98-987b-4e09-b1d7-79155c83f0e4"}},
{"name":"platform","dataType":"string","fullyQualifiedName":"sample_data.ecommerce_db.shopify.raw_customer.platform",
"dataSource":{"id":"c37dae98-987b-4e09-b1d7-79155c83f0e4","type":"table","fullyQualifiedName":"sample_data.ecommerce_db.shopify.raw_customer",
"description":"The","href":"http://openmetadata-server:8585/api/v1/tables/c37dae98-987b-4e09-b1d7-79155c83f0e4"}}],"featureAlgorithm":"PCA"}]`,
},
],
previousVersion: 0.1,
},
deleted: false,
};
export const mlModelVersionMockProps: MlModelVersionProp = {
version: '0.3',
currentVersionData: mockMlModelDetails,
isVersionLoading: false,
owner: mockOwner,
tier: mockTier,
slashedMlModelName: [],
versionList: mockVersionList,
deleted: false,
backHandler: mockBackHandler,
versionHandler: mockVersionHandler,
entityPermissions: ENTITY_PERMISSIONS,
};

View File

@ -10,6 +10,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { OperationPermission } from 'components/PermissionProvider/PermissionProvider.interface';
export const PERMISSIONS = {
alert: {
Create: true,
@ -389,3 +391,27 @@ export const PERMISSIONS = {
EditCustomFields: true,
},
};
export const ENTITY_PERMISSIONS = {
Create: true,
Delete: true,
EditAll: true,
EditCustomFields: true,
EditDataProfile: true,
EditDescription: true,
EditDisplayName: true,
EditLineage: true,
EditOwner: true,
EditQueries: true,
EditSampleData: true,
EditTags: true,
EditTests: true,
EditTier: true,
ViewAll: true,
ViewBasic: true,
ViewDataProfile: true,
ViewQueries: true,
ViewSampleData: true,
ViewTests: true,
ViewUsage: true,
} as OperationPermission;

View File

@ -0,0 +1,144 @@
/*
* Copyright 2023 Collate.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { PipelineServiceType } from 'generated/entity/data/pipeline';
import { ENTITY_PERMISSIONS } from 'mocks/Permissions.mock';
import {
mockBackHandler,
mockOwner,
mockTier,
mockVersionHandler,
mockVersionList,
} from 'mocks/VersionCommon.mock';
import { PipelineVersionProp } from '../components/PipelineVersion/PipelineVersion.interface';
const mockDescriptionChangeDiff = {
fieldsAdded: [],
fieldsUpdated: [
{
name: 'tasks.snowflake_task.description',
oldValue: 'Airflow operator to perform ETL on snowflake tables',
newValue: 'Airflow operator to perform ETL on snowflake tables open',
},
],
fieldsDeleted: [],
previousVersion: 0.1,
};
const mockTagChangeDiff = {
fieldsAdded: [
{
name: 'tags',
newValue:
'[{"tagFQN":"PersonalData.Personal","source":"Classification","labelType":"Manual","state":"Confirmed"}]',
},
],
fieldsUpdated: [],
fieldsDeleted: [],
previousVersion: 0.2,
};
const mockColumnChangeDiff = {
fieldsAdded: [
{
name: 'tasks.snowflake_task.tags',
newValue:
'[{"tagFQN":"PII.Sensitive","source":"Classification","labelType":"Manual","state":"Confirmed"}]',
},
],
fieldsUpdated: [],
fieldsDeleted: [],
previousVersion: 0.3,
};
export const mockPipelineData = {
id: 'ea7d9b49-ef81-44a5-ac0d-cbe3d66e0a52',
name: 'snowflake_etl',
displayName: 'Snowflake ETL',
fullyQualifiedName: 'sample_airflow.snowflake_etl',
description: 'Snowflake ETL pipeline',
version: 0.2,
updatedAt: 1688625607758,
updatedBy: 'admin',
sourceUrl: 'http://localhost:8080/tree?dag_id=snowflake_etl',
tasks: [
{
name: 'snowflake_task',
displayName: 'Snowflake Task',
fullyQualifiedName: 'sample_airflow.snowflake_etl.snowflake_task',
sourceUrl:
'http://localhost:8080/taskinstance/list/?flt1_dag_id_equals=assert_table_exists',
downstreamTasks: ['assert_table_exists'],
taskType: 'SnowflakeOperator',
tags: [],
},
{
name: 'assert_table_exists',
displayName: 'Assert Table Exists',
fullyQualifiedName: 'sample_airflow.snowflake_etl.assert_table_exists',
description: 'Assert if a table exists',
sourceUrl:
'http://localhost:8080/taskinstance/list/?flt1_dag_id_equals=assert_table_exists',
downstreamTasks: [],
taskType: 'HiveOperator',
tags: [],
},
],
followers: [],
tags: [],
service: {
id: '53675b50-8121-4358-b590-8540ef70f2dd',
type: 'pipelineService',
name: 'sample_airflow',
fullyQualifiedName: 'sample_airflow',
deleted: false,
},
serviceType: PipelineServiceType.Airflow,
changeDescription: mockDescriptionChangeDiff,
deleted: false,
};
export const pipelineVersionMockProps: PipelineVersionProp = {
version: '0.3',
currentVersionData: mockPipelineData,
isVersionLoading: false,
owner: mockOwner,
tier: mockTier,
slashedPipelineName: [],
topicFQN: 'sample_data.ecommerce_db.shopify.raw_product_catalog',
versionList: mockVersionList,
deleted: false,
backHandler: mockBackHandler,
versionHandler: mockVersionHandler,
entityPermissions: ENTITY_PERMISSIONS,
};
const mockTagDiffPipelineData = {
...mockPipelineData,
changeDescription: mockTagChangeDiff,
};
const mockColumnDiffPipelineData = {
...mockPipelineData,
changeDescription: mockColumnChangeDiff,
};
export const mockTagDiffPipelineVersionMockProps = {
...pipelineVersionMockProps,
currentVersionData: mockTagDiffPipelineData,
};
export const mockColumnDiffPipelineVersionMockProps = {
...pipelineVersionMockProps,
currentVersionData: mockColumnDiffPipelineData,
};

View File

@ -0,0 +1,100 @@
/*
* Copyright 2023 Collate.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { DatabaseServiceType, TableType } from 'generated/entity/data/table';
import { ENTITY_PERMISSIONS } from 'mocks/Permissions.mock';
import {
mockBackHandler,
mockOwner,
mockTier,
mockVersionHandler,
mockVersionList,
} from 'mocks/VersionCommon.mock';
import { TableVersionProp } from '../components/TableVersion/TableVersion.interface';
export const mockTableData = {
id: 'ab4f893b-c303-43d9-9375-3e620a670b02',
name: 'raw_product_catalog',
fullyQualifiedName: 'sample_data.ecommerce_db.shopify.raw_product_catalog',
description:
'This is a raw product catalog table contains the product listing, price, seller etc.. represented in our online DB. ',
version: 0.2,
updatedAt: 1688442727895,
updatedBy: 'admin',
tableType: TableType.Regular,
columns: [],
owner: {
id: '38be030f-f817-4712-bc3b-ff7b9b9b805e',
type: 'user',
name: 'aaron_johnson0',
fullyQualifiedName: 'aaron_johnson0',
displayName: 'Aaron Johnson',
deleted: false,
},
databaseSchema: {
id: '3f0d9c39-0926-4028-8070-65b0c03556cb',
type: 'databaseSchema',
name: 'shopify',
fullyQualifiedName: 'sample_data.ecommerce_db.shopify',
description:
'This **mock** database contains schema related to shopify sales and orders with related dimension tables.',
deleted: false,
},
database: {
id: 'f085e133-e184-47c8-ada5-d7e005d3153b',
type: 'database',
name: 'ecommerce_db',
fullyQualifiedName: 'sample_data.ecommerce_db',
description:
'This **mock** database contains schemas related to shopify sales and orders with related dimension tables.',
deleted: false,
},
service: {
id: 'e61069a9-29e3-49fa-a7f4-f5227ae50b72',
type: 'databaseService',
name: 'sample_data',
fullyQualifiedName: 'sample_data',
deleted: false,
},
serviceType: DatabaseServiceType.BigQuery,
tags: [],
followers: [],
changeDescription: {
fieldsAdded: [
{
name: 'owner',
newValue:
'{"id":"38be030f-f817-4712-bc3b-ff7b9b9b805e","type":"user","name":"aaron_johnson0","fullyQualifiedName":"aaron_johnson0","displayName":"Aaron Johnson","deleted":false}',
},
],
fieldsUpdated: [],
fieldsDeleted: [],
previousVersion: 0.1,
},
deleted: false,
};
export const tableVersionMockProps: TableVersionProp = {
version: '0.3',
currentVersionData: mockTableData,
isVersionLoading: false,
owner: mockOwner,
tier: mockTier,
slashedTableName: [],
datasetFQN: 'sample_data.ecommerce_db.shopify.raw_product_catalog',
versionList: mockVersionList,
deleted: false,
backHandler: mockBackHandler,
versionHandler: mockVersionHandler,
entityPermissions: ENTITY_PERMISSIONS,
};

View File

@ -0,0 +1,102 @@
/*
* Copyright 2023 Collate.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Copyright 2023 Collate.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {
CleanupPolicy,
MessagingServiceType,
} from 'generated/entity/data/topic';
import { DataTypeTopic, SchemaType } from 'generated/type/schema';
import { ENTITY_PERMISSIONS } from 'mocks/Permissions.mock';
import {
mockBackHandler,
mockOwner,
mockTier,
mockVersionHandler,
mockVersionList,
} from 'mocks/VersionCommon.mock';
import { TopicVersionProp } from '../components/TopicVersion/TopicVersion.interface';
export const mockTopicData = {
id: 'eccc8f43-abd8-494b-9173-6b18644d5336',
name: 'sales',
fullyQualifiedName: 'sample_kafka.sales',
description: 'All sales related events gets captured in this topic',
version: 0.1,
updatedAt: 1688382358829,
updatedBy: 'admin',
service: {
id: 'c43ab59d-d96d-40af-9a63-6afcfa23baff',
type: 'messagingService',
name: 'sample_kafka',
fullyQualifiedName: 'sample_kafka',
deleted: false,
},
serviceType: MessagingServiceType.Kafka,
messageSchema: {
schemaText:
'{namespace: "openmetadata.kafka",type: "record",name: "Order",fields: [ { name: "sale_id", type: "int" },],}',
schemaType: SchemaType.Avro,
schemaFields: [
{
name: 'Order',
dataType: DataTypeTopic.Record,
fullyQualifiedName: 'sample_kafka.sales.Order',
tags: [],
children: [
{
name: 'sale_id',
dataType: DataTypeTopic.Int,
dataTypeDisplay: 'int',
fullyQualifiedName: 'sample_kafka.sales.Order.sale_id',
tags: [],
},
],
},
],
},
partitions: 128,
cleanupPolicies: [CleanupPolicy.Compact, CleanupPolicy.Delete],
replicationFactor: 4,
maximumMessageSize: 249,
retentionSize: 1931232624,
followers: [],
tags: [],
deleted: false,
};
export const topicVersionMockProps: TopicVersionProp = {
version: '0.3',
currentVersionData: mockTopicData,
isVersionLoading: false,
owner: mockOwner,
tier: mockTier,
slashedTopicName: [],
topicFQN: 'sample_data.ecommerce_db.shopify.raw_product_catalog',
versionList: mockVersionList,
deleted: false,
backHandler: mockBackHandler,
versionHandler: mockVersionHandler,
entityPermissions: ENTITY_PERMISSIONS,
};

View File

@ -0,0 +1,39 @@
/*
* Copyright 2023 Collate.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { LabelType, State, TagSource } from 'generated/type/tagLabel';
export const mockOwner = {
id: '38be030f-f817-4712-bc3b-ff7b9b9b805e',
type: 'user',
name: 'aaron_johnson0',
fullyQualifiedName: 'aaron_johnson0',
displayName: 'Aaron Johnson',
deleted: false,
};
export const mockTier = {
tagFQN: 'Tier.Tier3',
description: 'description',
source: TagSource.Classification,
labelType: LabelType.Manual,
state: State.Confirmed,
};
export const mockVersionList = {
entityType: 'table',
versions: [''],
};
export const mockBackHandler = jest.fn();
export const mockVersionHandler = jest.fn();

View File

@ -13,7 +13,7 @@
/* eslint-disable max-len */
import { VersionData } from 'pages/EntityVersionPage/EntityVersionPage.component';
import { DashboardVersionProp } from './DashboardVersion.interface';
import { DashboardVersionProp } from '../components/DashboardVersion/DashboardVersion.interface';
export const dashboardVersionProps = {
version: '0.3',

View File

@ -15,10 +15,10 @@ import { TitleBreadcrumbProps } from 'components/common/title-breadcrumb/title-b
import ContainerVersion from 'components/ContainerVersion/ContainerVersion.component';
import DashboardVersion from 'components/DashboardVersion/DashboardVersion.component';
import DataModelVersion from 'components/DataModelVersion/DataModelVersion.component';
import DatasetVersion from 'components/DatasetVersion/DatasetVersion.component';
import Loader from 'components/Loader/Loader';
import MlModelVersion from 'components/MlModelVersion/MlModelVersion.component';
import PipelineVersion from 'components/PipelineVersion/PipelineVersion.component';
import TableVersion from 'components/TableVersion/TableVersion.component';
import TopicVersion from 'components/TopicVersion/TopicVersion.component';
import { Container } from 'generated/entity/data/container';
import { DashboardDataModel } from 'generated/entity/data/dashboardDataModel';
@ -639,7 +639,7 @@ const EntityVersionPage: FunctionComponent = () => {
switch (entityType) {
case EntityType.TABLE: {
return (
<DatasetVersion
<TableVersion
backHandler={backHandler}
currentVersionData={currentVersionData}
datasetFQN={entityFQN}
@ -723,7 +723,6 @@ const EntityVersionPage: FunctionComponent = () => {
owner={owner}
slashedMlModelName={slashedEntityName}
tier={tier as TagLabel}
topicFQN={entityFQN}
version={version}
versionHandler={versionHandler}
versionList={versionList}

View File

@ -27,8 +27,8 @@ jest.mock('react-router-dom', () => ({
useParams: jest.fn().mockImplementation(() => mockParams),
}));
jest.mock('components/DatasetVersion/DatasetVersion.component', () => {
return jest.fn().mockReturnValue(<div>DatasetVersion component</div>);
jest.mock('components/TableVersion/TableVersion.component', () => {
return jest.fn().mockReturnValue(<div>TableVersion component</div>);
});
jest.mock('components/DashboardVersion/DashboardVersion.component', () => {
return jest.fn().mockReturnValue(<div>DashboardVersion component</div>);
@ -98,16 +98,14 @@ jest.mock('rest/dataModelsAPI', () => ({
}));
describe('Test EntityVersionPage component', () => {
it('Checks if the DatasetVersion component renderst if respective data pass', async () => {
it('Checks if the TableVersion component renderst if respective data pass', async () => {
await act(async () => {
render(<EntityVersionPage />, {
wrapper: MemoryRouter,
});
const datasetVersion = await screen.findByText(
/DatasetVersion component/i
);
const tableVersion = await screen.findByText(/TableVersion component/i);
expect(datasetVersion).toBeInTheDocument();
expect(tableVersion).toBeInTheDocument();
});
});