({
+ usePermissionProvider: jest.fn().mockImplementation(() => ({
+ permissions: {
+ testCase: testCasePermission,
+ },
+ })),
+}));
+jest.mock('rest/testAPI', () => {
+ return {
+ ...jest.requireActual('rest/testAPI'),
+ getListTestCase: jest
+ .fn()
+ .mockImplementation(() =>
+ Promise.resolve({ data: [], paging: { total: 0 } })
+ ),
+ getTestCaseById: jest.fn().mockImplementation(() => Promise.resolve()),
+ };
+});
+jest.mock('rest/searchAPI', () => {
+ return {
+ ...jest.requireActual('rest/searchAPI'),
+ searchQuery: jest
+ .fn()
+ .mockImplementation(() =>
+ Promise.resolve({ hits: { hits: [], total: { value: 0 } } })
+ ),
+ };
+});
+jest.mock('react-router-dom', () => {
+ return {
+ ...jest.requireActual('react-router-dom'),
+ useParams: jest.fn().mockImplementation(() => mockUseParam),
+ useHistory: jest.fn().mockImplementation(() => mockUseHistory),
+ useLocation: jest.fn().mockImplementation(() => mockLocation),
+ };
+});
+jest.mock('../SummaryPannel/SummaryPanel.component', () => {
+ return {
+ SummaryPanel: jest
+ .fn()
+ .mockImplementation(() => SummaryPanel.component
),
+ };
+});
+jest.mock('components/common/next-previous/NextPrevious', () => {
+ return jest.fn().mockImplementation(() => NextPrevious.component
);
+});
+jest.mock('components/common/searchbar/Searchbar', () => {
+ return jest.fn().mockImplementation(() => Searchbar.component
);
+});
+jest.mock('components/ProfilerDashboard/component/DataQualityTab', () => {
+ return jest
+ .fn()
+ .mockImplementation(() => DataQualityTab.component
);
+});
+jest.mock('components/common/error-with-placeholder/ErrorPlaceHolder', () => {
+ return jest
+ .fn()
+ .mockImplementation(({ type }) => (
+
+ ErrorPlaceHolder.component
+
+ ));
+});
+
+describe('TestCases component', () => {
+ it('component should render', async () => {
+ render();
+
+ expect(
+ await screen.findByTestId('test-case-container')
+ ).toBeInTheDocument();
+ expect(await screen.findByText('Searchbar.component')).toBeInTheDocument();
+ expect(
+ await screen.findByText('SummaryPanel.component')
+ ).toBeInTheDocument();
+ expect(
+ await screen.findByText('DataQualityTab.component')
+ ).toBeInTheDocument();
+ });
+
+ it('on page load getListTestCase API should call', async () => {
+ const mockGetListTestCase = getListTestCase as jest.Mock;
+
+ render();
+
+ expect(mockGetListTestCase).toHaveBeenCalledWith({
+ fields: 'testDefinition,testCaseResult,testSuite',
+ });
+ });
+
+ it('should call searchQuery api, if there is search term in URL', async () => {
+ const mockSearchQuery = searchQuery as jest.Mock;
+ mockLocation.search = '?searchValue=sale';
+
+ render();
+
+ expect(mockSearchQuery).toHaveBeenCalledWith({
+ fetchSource: false,
+ pageNumber: 1,
+ pageSize: 10,
+ query: 'sale',
+ searchIndex: 'test_case_search_index',
+ });
+ });
+});
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/TestSuites/TestSuites.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/TestSuites/TestSuites.component.tsx
index 923544a260d..17cfe2131c8 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/TestSuites/TestSuites.component.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/TestSuites/TestSuites.component.tsx
@@ -13,6 +13,7 @@
import { Button, Col, Row, Table } from 'antd';
import { ColumnsType } from 'antd/lib/table';
import { AxiosError } from 'axios';
+import ErrorPlaceHolder from 'components/common/error-with-placeholder/ErrorPlaceHolder';
import FilterTablePlaceHolder from 'components/common/error-with-placeholder/FilterTablePlaceHolder';
import NextPrevious from 'components/common/next-previous/NextPrevious';
import { OwnerLabel } from 'components/common/OwnerLabel/OwnerLabel.component';
@@ -26,6 +27,7 @@ import {
ROUTES,
} from 'constants/constants';
import { PROGRESS_BAR_COLOR } from 'constants/TestSuite.constant';
+import { ERROR_PLACEHOLDER_TYPE } from 'enums/common.enum';
import { EntityTabs } from 'enums/entity.enum';
import { TestSummary } from 'generated/entity/data/table';
import { TestSuite } from 'generated/tests/testSuite';
@@ -159,17 +161,28 @@ export const TestSuites = () => {
useEffect(() => {
if (testSuitePermission?.ViewAll || testSuitePermission?.ViewBasic) {
fetchTestSuites();
+ } else {
+ setIsLoading(false);
}
}, [tab, testSuitePermission]);
+ if (!testSuitePermission?.ViewAll && !testSuitePermission?.ViewBasic) {
+ return ;
+ }
+
return (
-
+
{tab === DataQualityPageTabs.TEST_SUITES &&
testSuitePermission?.Create && (
-
+
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/TestSuites/TestSuites.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/TestSuites/TestSuites.test.tsx
new file mode 100644
index 00000000000..2b48724f67d
--- /dev/null
+++ b/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/TestSuites/TestSuites.test.tsx
@@ -0,0 +1,153 @@
+/*
+ * 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 { render, screen } from '@testing-library/react';
+import { DataQualityPageTabs } from 'pages/DataQuality/DataQualityPage.interface';
+import React from 'react';
+import { MemoryRouter } from 'react-router-dom';
+import { getListTestSuites } from 'rest/testAPI';
+import { TestSuites } from './TestSuites.component';
+
+const testSuitePermission = {
+ Create: true,
+ Delete: true,
+ ViewAll: true,
+ EditAll: true,
+ EditDescription: true,
+ EditDisplayName: true,
+ EditCustomFields: true,
+};
+const mockUseParam = { tab: DataQualityPageTabs.TABLES } as {
+ tab?: DataQualityPageTabs;
+};
+
+jest.mock('components/PermissionProvider/PermissionProvider', () => ({
+ usePermissionProvider: jest.fn().mockImplementation(() => ({
+ permissions: {
+ testSuite: testSuitePermission,
+ },
+ })),
+}));
+jest.mock('rest/testAPI', () => {
+ return {
+ ...jest.requireActual('rest/testAPI'),
+ getListTestSuites: jest
+ .fn()
+ .mockImplementation(() =>
+ Promise.resolve({ data: [], paging: { total: 0 } })
+ ),
+ };
+});
+jest.mock('react-router-dom', () => {
+ return {
+ ...jest.requireActual('react-router-dom'),
+ useParams: jest.fn().mockImplementation(() => mockUseParam),
+ };
+});
+jest.mock('../SummaryPannel/SummaryPanel.component', () => {
+ return {
+ SummaryPanel: jest
+ .fn()
+ .mockImplementation(() => SummaryPanel.component
),
+ };
+});
+jest.mock('components/common/next-previous/NextPrevious', () => {
+ return jest.fn().mockImplementation(() => NextPrevious.component
);
+});
+jest.mock('components/common/error-with-placeholder/ErrorPlaceHolder', () => {
+ return jest
+ .fn()
+ .mockImplementation(({ type }) => (
+
+ ErrorPlaceHolder.component
+
+ ));
+});
+
+describe('TestSuites component', () => {
+ it('component should render', async () => {
+ render();
+ const tableHeader = await screen.findAllByRole('columnheader');
+ const labels = tableHeader.map((header) => header.textContent);
+
+ expect(tableHeader).toHaveLength(4);
+ expect(labels).toStrictEqual([
+ 'label.name',
+ 'label.test-plural',
+ 'label.success %',
+ 'label.owner',
+ ]);
+ expect(await screen.findByTestId('test-suite-table')).toBeInTheDocument();
+ expect(
+ await screen.findByText('SummaryPanel.component')
+ ).toBeInTheDocument();
+ });
+
+ it('should send testSuiteType executable in api, if active tab is tables', async () => {
+ const mockGetListTestSuites = getListTestSuites as jest.Mock;
+
+ render();
+
+ expect(
+ await screen.findByTestId('test-suite-container')
+ ).toBeInTheDocument();
+ expect(mockGetListTestSuites).toHaveBeenCalledWith({
+ fields: 'owner,summary',
+ testSuiteType: 'executable',
+ });
+ });
+
+ it('pagination should visible if total is grater than 10', async () => {
+ (getListTestSuites as jest.Mock).mockImplementationOnce(() =>
+ Promise.resolve({ data: [], paging: { total: 15 } })
+ );
+
+ render();
+
+ expect(
+ await screen.findByText('NextPrevious.component')
+ ).toBeInTheDocument();
+ });
+
+ // TestSuite type test
+ it('add test suite button should be visible, if type is testSuite', async () => {
+ mockUseParam.tab = DataQualityPageTabs.TEST_SUITES;
+ render(, { wrapper: MemoryRouter });
+
+ expect(await screen.findByTestId('add-test-suite-btn')).toBeInTheDocument();
+ });
+
+ it('should send testSuiteType logical in api, if active tab is tables', async () => {
+ mockUseParam.tab = DataQualityPageTabs.TEST_SUITES;
+ const mockGetListTestSuites = getListTestSuites as jest.Mock;
+
+ render(, { wrapper: MemoryRouter });
+
+ expect(
+ await screen.findByTestId('test-suite-container')
+ ).toBeInTheDocument();
+ expect(mockGetListTestSuites).toHaveBeenCalledWith({
+ fields: 'owner,summary',
+ testSuiteType: 'logical',
+ });
+ });
+
+ it('should render no data placeholder, if there is no permission', async () => {
+ mockUseParam.tab = DataQualityPageTabs.TEST_SUITES;
+ testSuitePermission.ViewAll = false;
+ render(, { wrapper: MemoryRouter });
+
+ expect(
+ await screen.findByTestId('error-placeholder-type-PERMISSION')
+ ).toBeInTheDocument();
+ });
+});
diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/DataQuality/DataQualityPage.test.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/DataQuality/DataQualityPage.test.tsx
new file mode 100644
index 00000000000..4c8e16f22f8
--- /dev/null
+++ b/openmetadata-ui/src/main/resources/ui/src/pages/DataQuality/DataQualityPage.test.tsx
@@ -0,0 +1,98 @@
+/*
+ * 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, fireEvent, render, screen } from '@testing-library/react';
+import React from 'react';
+import { getDataQualityPagePath } from 'utils/RouterUtils';
+import DataQualityPage from './DataQualityPage';
+import { DataQualityPageTabs } from './DataQualityPage.interface';
+
+const mockUseParam = { tab: DataQualityPageTabs.TABLES } as {
+ tab?: DataQualityPageTabs;
+};
+const mockUseHistory = {
+ push: jest.fn(),
+};
+
+// mock components
+jest.mock('components/containers/PageLayoutV1', () => {
+ return jest.fn().mockImplementation(({ children }) => {children}
);
+});
+jest.mock('components/DataQuality/TestSuites/TestSuites.component', () => {
+ return {
+ TestSuites: jest
+ .fn()
+ .mockImplementation(() => TestSuites.component
),
+ };
+});
+jest.mock('components/DataQuality/TestCases/TestCases.component', () => {
+ return {
+ TestCases: jest
+ .fn()
+ .mockImplementation(() => TestCases.component
),
+ };
+});
+jest.mock('react-router-dom', () => {
+ return {
+ useParams: jest.fn().mockImplementation(() => mockUseParam),
+ useHistory: jest.fn().mockImplementation(() => mockUseHistory),
+ };
+});
+
+describe('DataQualityPage', () => {
+ it('component should render', async () => {
+ render();
+
+ expect(await screen.findByTestId('page-title')).toBeInTheDocument();
+ expect(await screen.findByTestId('page-sub-title')).toBeInTheDocument();
+ expect(await screen.findByTestId('tabs')).toBeInTheDocument();
+ expect(await screen.findByText('TestSuites.component')).toBeInTheDocument();
+ });
+
+ it('should render 3 tabs', async () => {
+ render();
+
+ const tabs = await screen.findAllByRole('tab');
+
+ expect(tabs).toHaveLength(3);
+ });
+
+ it('should change the tab, onClick of tab', async () => {
+ render();
+
+ const tabs = await screen.findAllByRole('tab');
+
+ expect(await screen.findByText('TestSuites.component')).toBeInTheDocument();
+
+ await act(async () => {
+ fireEvent.click(tabs[1]);
+ });
+
+ expect(mockUseHistory.push).toHaveBeenCalledWith(
+ getDataQualityPagePath(DataQualityPageTabs.TEST_CASES)
+ );
+ });
+
+ it('should render tables tab by default', async () => {
+ mockUseParam.tab = undefined;
+ render();
+
+ expect(await screen.findByText('TestSuites.component')).toBeInTheDocument();
+ });
+
+ it('should render testCase tab, if active tab is testCase', async () => {
+ mockUseParam.tab = DataQualityPageTabs.TEST_CASES;
+ render();
+
+ expect(await screen.findByText('TestCases.component')).toBeInTheDocument();
+ });
+});
diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/DataQuality/DataQualityPage.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/DataQuality/DataQualityPage.tsx
index a1c65824e1b..4b9da18c675 100644
--- a/openmetadata-ui/src/main/resources/ui/src/pages/DataQuality/DataQualityPage.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/pages/DataQuality/DataQualityPage.tsx
@@ -58,10 +58,15 @@ const DataQualityPage = () => {
-
+
{t('label.data-quality')}
-
+
{t('message.page-sub-header-for-data-quality')}