diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Flow/ObservabilityAlerts.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Flow/ObservabilityAlerts.spec.ts index 2f6fe3383fd1..6f3ae788d2c9 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Flow/ObservabilityAlerts.spec.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Flow/ObservabilityAlerts.spec.ts @@ -131,6 +131,7 @@ test.beforeEach(async ({ page }) => { }); test('Pipeline Alert', async ({ page }) => { + test.slow(); const ALERT_NAME = generateAlertName(); await test.step('Create alert', async () => { @@ -256,6 +257,7 @@ test('Alert operations for a user with and without permissions', async ({ process.env.PLAYWRIGHT_IS_OSS !== 'true', 'Skipping in AUT environment' ); + test.slow(); const ALERT_NAME = generateAlertName(); const { apiContext } = await getApiContext(page); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/AddToBundleSuiteModal/AddToBundleSuiteModal.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/AddToBundleSuiteModal/AddToBundleSuiteModal.component.tsx index 288e8803e6e7..534763738ae8 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/AddToBundleSuiteModal/AddToBundleSuiteModal.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/AddToBundleSuiteModal/AddToBundleSuiteModal.component.tsx @@ -33,7 +33,7 @@ import { } from '../../../rest/testAPI'; import { getEntityName } from '../../../utils/EntityUtils'; import { getPopupContainer } from '../../../utils/formUtils'; -import { getTestSuitePath } from '../../../utils/RouterUtils'; +import observabilityRouterClassBase from '../../../utils/ObservabilityRouterClassBase'; import { showErrorToast, showSuccessToast } from '../../../utils/ToastUtils'; import { AddToBundleSuiteModalProps } from './AddToBundleSuiteModal.interface'; @@ -128,7 +128,11 @@ const AddToBundleSuiteModal: React.FC = ({ (opt) => opt.value === selectedSuiteId ); if (selectedSuite?.suite.fullyQualifiedName) { - navigate(getTestSuitePath(selectedSuite.suite.fullyQualifiedName)); + navigate( + observabilityRouterClassBase.getTestSuitePath( + selectedSuite.suite.fullyQualifiedName + ) + ); } onAddedToExisting(); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/AddToBundleSuiteModal/AddToBundleSuiteModal.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/AddToBundleSuiteModal/AddToBundleSuiteModal.test.tsx index 12357f3d0abb..6b22577ed0ec 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/AddToBundleSuiteModal/AddToBundleSuiteModal.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/AddToBundleSuiteModal/AddToBundleSuiteModal.test.tsx @@ -21,6 +21,7 @@ import { addTestCasesToLogicalTestSuiteBulk, getListTestSuitesBySearch, } from '../../../rest/testAPI'; +import observabilityRouterClassBase from '../../../utils/ObservabilityRouterClassBase'; import AddToBundleSuiteModal from './AddToBundleSuiteModal.component'; import { AddToBundleSuiteModalProps } from './AddToBundleSuiteModal.interface'; @@ -339,4 +340,23 @@ describe('AddToBundleSuiteModal', () => { expect(mockOnCancel).toHaveBeenCalled(); expect(addTestCasesToLogicalTestSuiteBulk).not.toHaveBeenCalled(); }); + + describe('observabilityRouterClassBase migration', () => { + it('should navigate using observabilityRouterClassBase.getTestSuitePath after success', async () => { + render(); + + fireEvent.click(await screen.findByTestId('bundle-suite-select-option')); + + await act(async () => { + fireEvent.click(screen.getByTestId('add-button')); + }); + + const expectedFqn = mockTestSuites[0].fullyQualifiedName ?? ''; + const expectedPath = + observabilityRouterClassBase.getTestSuitePath(expectedFqn); + + expect(mockNavigate).toHaveBeenCalledWith(expectedPath); + expect(mockNavigate).toHaveBeenCalledWith(`/test-suites/${expectedFqn}`); + }); + }); }); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/DataQualityDashboard/DataQualityDashboard.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/DataQualityDashboard/DataQualityDashboard.component.tsx index 33903f942a3c..5e42b9904563 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/DataQualityDashboard/DataQualityDashboard.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/DataQualityDashboard/DataQualityDashboard.component.tsx @@ -34,7 +34,7 @@ import { FAILED_CHART_COLOR_SCHEME, SUCCESS_CHART_COLOR_SCHEME, } from '../../../constants/Chart.constants'; -import { PAGE_SIZE_BASE, ROUTES } from '../../../constants/constants'; +import { PAGE_SIZE_BASE } from '../../../constants/constants'; import { DATA_QUALITY_DASHBOARD_HEADER, DQ_FILTER_KEYS, @@ -861,7 +861,8 @@ const DataQualityDashboard = ({ incidentStatusType={TestCaseResolutionStatusTypes.New} name="open-incident" redirectPath={{ - pathname: ROUTES.INCIDENT_MANAGER, + pathname: + observabilityRouterClassBase.getIncidentManagerPath(), search: QueryString.stringify({ testCaseResolutionStatusType: TestCaseResolutionStatusTypes.New, @@ -878,7 +879,8 @@ const DataQualityDashboard = ({ incidentStatusType={TestCaseResolutionStatusTypes.Resolved} name="resolved-incident" redirectPath={{ - pathname: ROUTES.INCIDENT_MANAGER, + pathname: + observabilityRouterClassBase.getIncidentManagerPath(), search: QueryString.stringify({ testCaseResolutionStatusType: TestCaseResolutionStatusTypes.Resolved, diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/DataQualityDashboard/DataQualityDashboard.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/DataQualityDashboard/DataQualityDashboard.test.tsx index 92ca192a1e6c..a5f317e8e55d 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/DataQualityDashboard/DataQualityDashboard.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/DataQualityDashboard/DataQualityDashboard.test.tsx @@ -21,6 +21,7 @@ import { import { TestCaseStatus } from '../../../generated/tests/testCase'; import { TestCaseResolutionStatusTypes } from '../../../generated/tests/testCaseResolutionStatus'; import { DataQualityPageTabs } from '../../../pages/DataQuality/DataQualityPage.interface'; +import observabilityRouterClassBase from '../../../utils/ObservabilityRouterClassBase'; import { getDataQualityPagePath } from '../../../utils/RouterUtils'; import { IncidentTimeMetricsType } from '../DataQuality.interface'; import DataQualityDashboard from './DataQualityDashboard.component'; @@ -531,6 +532,48 @@ describe('DataQualityDashboard', () => { }) ); }); + + describe('observabilityRouterClassBase migration', () => { + it('open-incident widget redirectPath.pathname should be observabilityRouterClassBase.getIncidentManagerPath()', async () => { + render(, { wrapper: MemoryRouter }); + + await waitFor(() => { + expect(mockIncidentTypeAreaChartWidget).toHaveBeenCalled(); + }); + + const expectedPath = + observabilityRouterClassBase.getIncidentManagerPath(); + + expect(mockIncidentTypeAreaChartWidget).toHaveBeenCalledWith( + expect.objectContaining({ + name: 'open-incident', + redirectPath: expect.objectContaining({ + pathname: expectedPath, + }), + }) + ); + }); + + it('resolved-incident widget redirectPath.pathname should be observabilityRouterClassBase.getIncidentManagerPath()', async () => { + render(, { wrapper: MemoryRouter }); + + await waitFor(() => { + expect(mockIncidentTypeAreaChartWidget).toHaveBeenCalled(); + }); + + const expectedPath = + observabilityRouterClassBase.getIncidentManagerPath(); + + expect(mockIncidentTypeAreaChartWidget).toHaveBeenCalledWith( + expect.objectContaining({ + name: 'resolved-incident', + redirectPath: expect.objectContaining({ + pathname: expectedPath, + }), + }) + ); + }); + }); }); describe('Filter State Management', () => { diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/IncidentManager/FailedTestCaseSampleData/FailedTestCaseSampleData.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/IncidentManager/FailedTestCaseSampleData/FailedTestCaseSampleData.component.tsx index 7e97e80cdecf..8eeafdd27250 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/IncidentManager/FailedTestCaseSampleData/FailedTestCaseSampleData.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/IncidentManager/FailedTestCaseSampleData/FailedTestCaseSampleData.component.tsx @@ -34,8 +34,8 @@ import { } from '../../../../rest/testAPI'; import { getEntityDeleteMessage } from '../../../../utils/CommonUtils'; import { getColumnNameFromEntityLink } from '../../../../utils/EntityUtils'; +import observabilityRouterClassBase from '../../../../utils/ObservabilityRouterClassBase'; import { checkPermission } from '../../../../utils/PermissionsUtils'; -import { getTestCaseDetailPagePath } from '../../../../utils/RouterUtils'; import { showErrorToast } from '../../../../utils/ToastUtils'; import Loader from '../../../common/Loader/Loader'; import { ManageButtonItemLabel } from '../../../common/ManageButtonContentItem/ManageButtonContentItem.component'; @@ -222,7 +222,7 @@ const FailedTestCaseSampleData = ({
{testCaseData?.inspectionQuery && !isVersionPage && ( diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/IncidentManager/FailedTestCaseSampleData/FailedTestCaseSampleData.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/IncidentManager/FailedTestCaseSampleData/FailedTestCaseSampleData.test.tsx new file mode 100644 index 000000000000..7fa84ccee747 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/IncidentManager/FailedTestCaseSampleData/FailedTestCaseSampleData.test.tsx @@ -0,0 +1,131 @@ +/* + * Copyright 2026 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 { TestCase } from '../../../../generated/tests/testCase'; +import { TestCasePageTabs } from '../../../../pages/IncidentManager/IncidentManager.interface'; +import { getTestCaseFailedSampleData } from '../../../../rest/testAPI'; +import observabilityRouterClassBase from '../../../../utils/ObservabilityRouterClassBase'; +import FailedTestCaseSampleData from './FailedTestCaseSampleData.component'; + +jest.mock('react-router-dom', () => ({ + ...jest.requireActual('react-router-dom'), + Link: jest.fn().mockImplementation(({ children, to, ...rest }) => ( + + {children} + + )), + useParams: jest.fn().mockReturnValue({}), +})); + +jest.mock('../../../../utils/RouterUtils', () => ({ + getTestCaseDetailPagePath: jest + .fn() + .mockImplementation( + (fqn: string, tab?: string) => + `/test-case/${fqn}/${tab ?? 'test-case-results'}` + ), +})); + +jest.mock('../../../../context/PermissionProvider/PermissionProvider', () => ({ + usePermissionProvider: jest.fn().mockReturnValue({ + permissions: {}, + }), +})); + +jest.mock('../../../../utils/PermissionsUtils', () => ({ + checkPermission: jest.fn().mockReturnValue(true), +})); + +jest.mock('../../../../utils/EntityUtils', () => ({ + getColumnNameFromEntityLink: jest.fn().mockReturnValue('column_x'), +})); + +jest.mock('../../../../utils/CommonUtils', () => ({ + getEntityDeleteMessage: jest.fn().mockReturnValue('delete-message'), +})); + +jest.mock('../../../../utils/ToastUtils', () => ({ + showErrorToast: jest.fn(), +})); + +jest.mock('../../../../rest/testAPI', () => ({ + getTestCaseFailedSampleData: jest.fn(), + deleteTestCaseFailedSampleData: jest.fn(), +})); + +jest.mock('../../../common/Loader/Loader', () => + jest.fn().mockImplementation(() =>
Loader
) +); + +jest.mock( + '../../../common/ManageButtonContentItem/ManageButtonContentItem.component', + () => ({ + ManageButtonItemLabel: jest + .fn() + .mockImplementation(() =>
ManageButtonItemLabel
), + }) +); + +jest.mock('../../../Database/SampleDataTable/RowData', () => ({ + RowData: jest.fn().mockImplementation(() =>
RowData
), +})); + +jest.mock('../../../Modals/EntityDeleteModal/EntityDeleteModal', () => + jest.fn().mockImplementation(() =>
EntityDeleteModal
) +); + +const FQN = 'svc.db.schema.table.failing_test_case'; + +const mockTestCase: TestCase = { + id: 'tc-1', + name: 'failing_test_case', + fullyQualifiedName: FQN, + inspectionQuery: 'SELECT * FROM t', + entityLink: '<#E::table::svc.db.schema.table>', +} as TestCase; + +describe('FailedTestCaseSampleData - observabilityRouterClassBase migration', () => { + beforeEach(() => { + jest.clearAllMocks(); + (getTestCaseFailedSampleData as jest.Mock).mockResolvedValue({ + columns: ['c1'], + rows: [['r1']], + }); + }); + + it('explore-with-query Link should use observabilityRouterClassBase.getTestCaseDetailPagePath with SQL_QUERY tab', async () => { + const { getTestCaseDetailPagePath } = jest.requireMock( + '../../../../utils/RouterUtils' + ); + + await act(async () => { + render(); + }); + + const exploreBtn = await screen.findByTestId('explore-with-query'); + const link = exploreBtn.closest('a'); + + expect(link).not.toBeNull(); + expect(link?.getAttribute('data-to')).toBe( + observabilityRouterClassBase.getTestCaseDetailPagePath( + FQN, + TestCasePageTabs.SQL_QUERY + ) + ); + expect(getTestCaseDetailPagePath).toHaveBeenCalledWith( + FQN, + TestCasePageTabs.SQL_QUERY + ); + }); +}); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/TestSuite/TestSuiteList/TestSuites.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/TestSuite/TestSuiteList/TestSuites.component.tsx index ed25b5912e75..b70e76bdbf2c 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/TestSuite/TestSuiteList/TestSuites.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/TestSuite/TestSuiteList/TestSuites.component.tsx @@ -55,10 +55,7 @@ import { getEntityName } from '../../../../utils/EntityUtils'; import { getPopupContainer } from '../../../../utils/formUtils'; import observabilityRouterClassBase from '../../../../utils/ObservabilityRouterClassBase'; import { getPrioritizedViewPermission } from '../../../../utils/PermissionsUtils'; -import { - getEntityDetailsPath, - getTestSuitePath, -} from '../../../../utils/RouterUtils'; +import { getEntityDetailsPath } from '../../../../utils/RouterUtils'; import { showErrorToast } from '../../../../utils/ToastUtils'; import ErrorPlaceHolder from '../../../common/ErrorWithPlaceholder/ErrorPlaceHolder'; import FilterTablePlaceHolder from '../../../common/ErrorWithPlaceholder/FilterTablePlaceHolder'; @@ -245,7 +242,9 @@ export const TestSuites = () => { + to={observabilityRouterClassBase.getTestSuitePath( + record.fullyQualifiedName ?? record.name + )}> {getEntityName(record)} ); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/TestSuite/TestSuiteList/TestSuites.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/TestSuite/TestSuiteList/TestSuites.test.tsx index ff09e54c5ec4..cac85581b557 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/TestSuite/TestSuiteList/TestSuites.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/TestSuite/TestSuiteList/TestSuites.test.tsx @@ -14,6 +14,7 @@ import { act, fireEvent, render, screen } from '@testing-library/react'; import { MemoryRouter, useNavigate } from 'react-router-dom'; import { DataQualityPageTabs } from '../../../../pages/DataQuality/DataQualityPage.interface'; import { getListTestSuitesBySearch } from '../../../../rest/testAPI'; +import observabilityRouterClassBase from '../../../../utils/ObservabilityRouterClassBase'; import { TestSuites } from './TestSuites.component'; const testSuitePermission = { @@ -264,6 +265,9 @@ jest.mock('../../../../utils/ObservabilityRouterClassBase', () => ({ .mockImplementation( (tab: string, subTab: string) => `/data-quality/${tab}/${subTab}` ), + getTestSuitePath: jest + .fn() + .mockImplementation((fqn: string) => `/test-suites/${fqn}`), }, })); @@ -537,4 +541,41 @@ describe('TestSuites component', () => { screen.queryByText('NextPrevious.component') ).not.toBeInTheDocument(); }); + + describe('observabilityRouterClassBase migration', () => { + it('logical test suite name link should use observabilityRouterClassBase.getTestSuitePath', async () => { + // Restore permission for this test + testSuitePermission.ViewAll = true; + mockLocation.search = ''; + + const logicalSuiteName = 'svc.suite'; + (getListTestSuitesBySearch as jest.Mock).mockImplementationOnce(() => + Promise.resolve({ + data: [ + { + id: 'logical-id', + name: logicalSuiteName, + fullyQualifiedName: logicalSuiteName, + description: 'logical suite', + serviceType: 'TestSuite', + href: 'href', + deleted: false, + basic: false, + testCaseResultSummary: [], + }, + ], + paging: { offset: 0, limit: 15, total: 1 }, + }) + ); + + render(, { wrapper: MemoryRouter }); + + const link = await screen.findByTestId(logicalSuiteName); + + expect(link.getAttribute('to')).toBe( + observabilityRouterClassBase.getTestSuitePath(logicalSuiteName) + ); + expect(link.getAttribute('to')).toBe(`/test-suites/${logicalSuiteName}`); + }); + }); }); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Database/Profiler/DataQualityTab/DataQualityTab.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Database/Profiler/DataQualityTab/DataQualityTab.tsx index 4549cf6f5944..ad8939fd8bf5 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Database/Profiler/DataQualityTab/DataQualityTab.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Database/Profiler/DataQualityTab/DataQualityTab.tsx @@ -54,11 +54,8 @@ import { } from '../../../../utils/EntityUtils'; import { getEntityFQN } from '../../../../utils/FeedUtils'; import { Transi18next } from '../../../../utils/i18next/LocalUtil'; -import { - getEntityDetailsPath, - getTestCaseDetailPagePath, - getTestSuitePath, -} from '../../../../utils/RouterUtils'; +import observabilityRouterClassBase from '../../../../utils/ObservabilityRouterClassBase'; +import { getEntityDetailsPath } from '../../../../utils/RouterUtils'; import { replacePlus } from '../../../../utils/StringsUtils'; import { showErrorToast } from '../../../../utils/ToastUtils'; import DateTimeDisplay from '../../../common/DateTimeDisplay/DateTimeDisplay'; @@ -374,7 +371,11 @@ const DataQualityTab: React.FC = ({ setBundleSuiteFormInitialCases([]); setSelectedKeys(new Set()); if (testSuite.fullyQualifiedName) { - navigate(getTestSuitePath(testSuite.fullyQualifiedName)); + navigate( + observabilityRouterClassBase.getTestSuitePath( + testSuite.fullyQualifiedName + ) + ); } }; @@ -508,7 +509,7 @@ const DataQualityTab: React.FC = ({ } )}> @@ -583,9 +584,10 @@ const DataQualityTab: React.FC = ({ className="break-word" state={{ breadcrumbData }} to={{ - pathname: getTestCaseDetailPagePath( - record.fullyQualifiedName ?? '' - ), + pathname: + observabilityRouterClassBase.getTestCaseDetailPagePath( + record.fullyQualifiedName ?? '' + ), }}> {getEntityName(record)} diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Database/Profiler/TestSummaryCustomTooltip/TestSummaryCustomTooltip.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Database/Profiler/TestSummaryCustomTooltip/TestSummaryCustomTooltip.component.tsx index 6181502ebc24..aec60744f35c 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Database/Profiler/TestSummaryCustomTooltip/TestSummaryCustomTooltip.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Database/Profiler/TestSummaryCustomTooltip/TestSummaryCustomTooltip.component.tsx @@ -30,7 +30,7 @@ import { convertSecondsToHumanReadableFormat, formatDateTime, } from '../../../../utils/date-time/DateTimeUtils'; -import { getTestCaseDetailPagePath } from '../../../../utils/RouterUtils'; +import observabilityRouterClassBase from '../../../../utils/ObservabilityRouterClassBase'; import { getTaskDetailPath } from '../../../../utils/TasksUtils'; import { OwnerLabel } from '../../../common/OwnerLabel/OwnerLabel.component'; import './test-summary-custom-tooltip.less'; @@ -202,7 +202,7 @@ const TestSummaryCustomTooltip = (props: TestSummaryCustomTooltipProps) => { {testCaseFqnProp ? ( diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Database/Profiler/TestSummaryCustomTooltip/TestSummaryCustomTooltip.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Database/Profiler/TestSummaryCustomTooltip/TestSummaryCustomTooltip.test.tsx index c8b3dcb87f2d..f702dcf27e23 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Database/Profiler/TestSummaryCustomTooltip/TestSummaryCustomTooltip.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Database/Profiler/TestSummaryCustomTooltip/TestSummaryCustomTooltip.test.tsx @@ -11,6 +11,8 @@ * limitations under the License. */ import { render, screen } from '@testing-library/react'; +import { TestCasePageTabs } from '../../../../pages/IncidentManager/IncidentManager.interface'; +import observabilityRouterClassBase from '../../../../utils/ObservabilityRouterClassBase'; import TestSummaryCustomTooltip from './TestSummaryCustomTooltip.component'; const mockProps = { @@ -75,6 +77,15 @@ jest.mock('../../../../utils/TasksUtils', () => ({ getTaskDetailPath: jest.fn(), })); +jest.mock('react-router-dom', () => ({ + ...jest.requireActual('react-router-dom'), + Link: jest.fn().mockImplementation(({ children, to, ...rest }) => ( + + {children} + + )), +})); + jest.mock('../../../common/OwnerLabel/OwnerLabel.component', () => ({ OwnerLabel: jest.fn().mockReturnValue(
OwnerLabel
), })); @@ -119,4 +130,36 @@ describe('Test TestSummaryCustomTooltip component', () => { '7Y 2M 22d 9m 24s' ); }); + + describe('observabilityRouterClassBase migration', () => { + it('incident link should use observabilityRouterClassBase.getTestCaseDetailPagePath with ISSUES tab', async () => { + const fqn = 'svc.db.schema.table.test_case_freshness'; + const propsWithIncident = { + active: true, + testCaseFqn: fqn, + payload: [ + { + payload: { + name: 1748045364386, + status: 'Failed', + incidentId: 'incident-123', + }, + }, + ], + }; + + render(); + + const incidentCell = await screen.findByTestId('incident'); + const link = incidentCell.querySelector('a'); + + expect(link).not.toBeNull(); + expect(link?.getAttribute('data-to')).toBe( + observabilityRouterClassBase.getTestCaseDetailPagePath( + fqn, + TestCasePageTabs.ISSUES + ) + ); + }); + }); }); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/DataQualityTab/DataQualityTab.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/DataQualityTab/DataQualityTab.tsx index 8d11246e13dc..7d37c280a7b1 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/DataQualityTab/DataQualityTab.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/DataQualityTab/DataQualityTab.tsx @@ -40,7 +40,7 @@ import { } from '../../../../utils/date-time/DateTimeUtils'; import { getColumnNameFromEntityLink } from '../../../../utils/EntityUtils'; import { Transi18next } from '../../../../utils/i18next/LocalUtil'; -import { getTestCaseDetailPagePath } from '../../../../utils/RouterUtils'; +import observabilityRouterClassBase from '../../../../utils/ObservabilityRouterClassBase'; import { generateEntityLink } from '../../../../utils/TableUtils'; import { showErrorToast } from '../../../../utils/ToastUtils'; import DataQualitySection from '../../../common/DataQualitySection'; @@ -222,7 +222,9 @@ const TestCaseCard: React.FC = ({ testCase, incident }) => { + to={observabilityRouterClassBase.getTestCaseDetailPagePath( + testCase.fullyQualifiedName ?? '' + )}> {testCaseName}
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/IncidentManager/IncidentManager.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/IncidentManager/IncidentManager.component.tsx index 2cd348a5f2bd..a765deb0e878 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/IncidentManager/IncidentManager.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/IncidentManager/IncidentManager.component.tsx @@ -69,10 +69,8 @@ import { getPartialNameFromTableFQN, } from '../../utils/CommonUtils'; import { getEntityName } from '../../utils/EntityUtils'; -import { - getEntityDetailsPath, - getTestCaseDetailPagePath, -} from '../../utils/RouterUtils'; +import observabilityRouterClassBase from '../../utils/ObservabilityRouterClassBase'; +import { getEntityDetailsPath } from '../../utils/RouterUtils'; import { showErrorToast } from '../../utils/ToastUtils'; import { AsyncSelect } from '../common/AsyncSelect/AsyncSelect'; import DateTimeDisplay from '../common/DateTimeDisplay/DateTimeDisplay'; @@ -640,7 +638,9 @@ const IncidentManager = ({ + to={observabilityRouterClassBase.getTestCaseDetailPagePath( + ref?.fullyQualifiedName ?? '' + )}> {getEntityName(ref)} diff --git a/openmetadata-ui/src/main/resources/ui/src/components/IncidentManager/IncidentManager.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/IncidentManager/IncidentManager.test.tsx index 2d2e8aee35a3..37280aab8098 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/IncidentManager/IncidentManager.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/IncidentManager/IncidentManager.test.tsx @@ -14,8 +14,10 @@ import { fireEvent, render, screen } from '@testing-library/react'; import QueryString from 'qs'; import React, { act } from 'react'; import { Table } from '../../generated/entity/data/table'; +import { TestCasePageTabs } from '../../pages/IncidentManager/IncidentManager.interface'; import { getListTestCaseIncidentStatusFromSearch } from '../../rest/incidentManagerAPI'; import '../../test/unit/mocks/mui.mock'; +import observabilityRouterClassBase from '../../utils/ObservabilityRouterClassBase'; import IncidentManager from './IncidentManager.component'; jest.mock('../common/NextPrevious/NextPrevious', () => { @@ -261,7 +263,11 @@ jest.mock('../common/AsyncSelect/AsyncSelect', () => ({ })); jest.mock('react-router-dom', () => ({ ...jest.requireActual('react-router-dom'), - Link: jest.fn().mockImplementation(() =>
Link
), + Link: jest.fn().mockImplementation(({ children, to, ...rest }) => ( + + {children} + + )), useNavigate: jest.fn().mockReturnValue(jest.fn()), })); @@ -887,4 +893,44 @@ describe('IncidentManagerPage', () => { expect(mockHandlePageSizeChange).toHaveBeenCalledWith(25); }); }); + + describe('observabilityRouterClassBase migration', () => { + it('test case name link should use observabilityRouterClassBase.getTestCaseDetailPagePath', async () => { + const fqn = 'svc.db.schema.table.test_case_1'; + const { getTestCaseDetailPagePath } = require('../../utils/RouterUtils'); + (getTestCaseDetailPagePath as jest.Mock).mockClear(); + + (getListTestCaseIncidentStatusFromSearch as jest.Mock).mockResolvedValue({ + data: [ + { + id: 'tcr-1', + testCaseReference: { + fullyQualifiedName: fqn, + name: 'test_case_1', + }, + testCaseResolutionStatusType: 'New', + }, + ], + paging: { total: 1 }, + }); + + await act(async () => { + render(); + }); + + const link = await screen.findByTestId('test-case-test_case_1'); + + expect(link.tagName).toBe('A'); + expect(link.getAttribute('data-to')).toBe( + observabilityRouterClassBase.getTestCaseDetailPagePath( + fqn, + TestCasePageTabs.TEST_CASE_RESULTS + ) + ); + expect(getTestCaseDetailPagePath).toHaveBeenCalledWith( + fqn, + TestCasePageTabs.TEST_CASE_RESULTS + ); + }); + }); }); diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/AddObservabilityPage/AddObservabilityPage.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/AddObservabilityPage/AddObservabilityPage.tsx index 452b13c2cbaa..c8f64c8527c1 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/AddObservabilityPage/AddObservabilityPage.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/AddObservabilityPage/AddObservabilityPage.tsx @@ -28,7 +28,6 @@ import RichTextEditor from '../../components/common/RichTextEditor/RichTextEdito import TitleBreadcrumb from '../../components/common/TitleBreadcrumb/TitleBreadcrumb.component'; import { PAGE_SIZE_LARGE, - ROUTES, VALIDATION_MESSAGES, } from '../../constants/constants'; import { NAME_FIELD_RULES } from '../../constants/Form.constants'; @@ -61,11 +60,11 @@ import { } from '../../rest/observabilityAPI'; import alertsClassBase from '../../utils/AlertsClassBase'; import { getEntityName } from '../../utils/EntityUtils'; +import observabilityRouterClassBase from '../../utils/ObservabilityRouterClassBase'; import { DEFAULT_ENTITY_PERMISSION, getPrioritizedViewPermission, } from '../../utils/PermissionsUtils'; -import { getObservabilityAlertDetailsPath } from '../../utils/RouterUtils'; import { showErrorToast } from '../../utils/ToastUtils'; import { AddAlertPageLoadingState } from '../AddNotificationPage/AddNotificationPage.interface'; import { @@ -149,7 +148,7 @@ function AddObservabilityPage() { }, { name: t('label.alert-plural'), - url: ROUTES.OBSERVABILITY_ALERTS, + url: observabilityRouterClassBase.getObservabilityAlertsListPath(), }, { name: fqn @@ -175,7 +174,9 @@ function AddObservabilityPage() { updateAlertAPI: updateObservabilityAlert, afterSaveAction: async (fqn: string) => { !fqn && (await getResourceLimit('eventsubscription', true, true)); - navigate(getObservabilityAlertDetailsPath(fqn)); + navigate( + observabilityRouterClassBase.getObservabilityAlertDetailsPath(fqn) + ); }, setInlineAlertDetails, }); diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/AlertDetailsPage/AlertDetailsPage.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/AlertDetailsPage/AlertDetailsPage.tsx index 00793c0018d8..d21f437e9a49 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/AlertDetailsPage/AlertDetailsPage.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/AlertDetailsPage/AlertDetailsPage.tsx @@ -194,7 +194,7 @@ function AlertDetailsPage({ }, { name: t('label.alert-plural'), - url: ROUTES.OBSERVABILITY_ALERTS, + url: observabilityRouterClassBase.getObservabilityAlertsListPath(), }, { name: getEntityName(alertDetails), @@ -207,7 +207,7 @@ function AlertDetailsPage({ const handleAlertDelete = useCallback(async () => { isNotificationAlert ? navigate(ROUTES.NOTIFICATION_ALERT_LIST) - : navigate(ROUTES.OBSERVABILITY_ALERTS); + : navigate(observabilityRouterClassBase.getObservabilityAlertsListPath()); }, [navigate, isNotificationAlert]); const handleAlertEdit = useCallback(async () => { 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 35ab0b853c11..6a377a2c14ee 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 @@ -27,10 +27,6 @@ import { TestCase } from '../../generated/tests/testCase'; import { TestSuite } from '../../generated/tests/testSuite'; import { withPageLayout } from '../../hoc/withPageLayout'; import observabilityRouterClassBase from '../../utils/ObservabilityRouterClassBase'; -import { - getTestCaseDetailPagePath, - getTestSuitePath, -} from '../../utils/RouterUtils'; import './data-quality-page.less'; import DataQualityClassBase from './DataQualityClassBase'; import { DataQualityPageTabs } from './DataQualityPage.interface'; @@ -66,7 +62,11 @@ const DataQualityPage = () => { const handleBundleSuiteSuccess = (testSuite: TestSuite) => { if (testSuite.fullyQualifiedName) { - navigate(getTestSuitePath(testSuite.fullyQualifiedName)); + navigate( + observabilityRouterClassBase.getTestSuitePath( + testSuite.fullyQualifiedName + ) + ); } }; @@ -123,7 +123,11 @@ const DataQualityPage = () => { const handleFormSubmit = (testCase: TestCase) => { if (testCase.fullyQualifiedName) { - navigate(getTestCaseDetailPagePath(testCase.fullyQualifiedName)); + navigate( + observabilityRouterClassBase.getTestCaseDetailPagePath( + testCase.fullyQualifiedName + ) + ); } }; diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/IncidentManager/IncidentManagerDetailPage/IncidentManagerDetailPage.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/IncidentManager/IncidentManagerDetailPage/IncidentManagerDetailPage.tsx index 70a027926a8d..78d6116105b4 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/IncidentManager/IncidentManagerDetailPage/IncidentManagerDetailPage.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/IncidentManager/IncidentManagerDetailPage/IncidentManagerDetailPage.tsx @@ -38,7 +38,6 @@ import EntityHeaderTitle from '../../../components/Entity/EntityHeaderTitle/Enti import EntityVersionTimeLine from '../../../components/Entity/EntityVersionTimeLine/EntityVersionTimeLine'; import { EntityName } from '../../../components/Modals/EntityNameModal/EntityNameModal.interface'; import PageLayoutV1 from '../../../components/PageLayoutV1/PageLayoutV1'; -import { ROUTES } from '../../../constants/constants'; import { FEED_COUNT_INITIAL_DATA } from '../../../constants/entity.constants'; import { EntityField } from '../../../constants/Feeds.constants'; import { usePermissionProvider } from '../../../context/PermissionProvider/PermissionProvider'; @@ -61,11 +60,7 @@ import { import { getFeedCounts } from '../../../utils/CommonUtils'; import { getEntityName } from '../../../utils/EntityUtils'; import { getEntityVersionByField } from '../../../utils/EntityVersionUtils'; -import { - getTestCaseDetailPagePath, - getTestCaseDimensionsDetailPagePath, - getTestCaseVersionPath, -} from '../../../utils/RouterUtils'; +import observabilityRouterClassBase from '../../../utils/ObservabilityRouterClassBase'; import { showErrorToast } from '../../../utils/ToastUtils'; import { useRequiredParams } from '../../../utils/useRequiredParams'; import { TestCasePageTabs } from '../IncidentManager.interface'; @@ -215,7 +210,7 @@ const IncidentManagerDetailPage = ({ : [ { name: t('label.incident-manager'), - url: ROUTES.INCIDENT_MANAGER, + url: observabilityRouterClassBase.getIncidentManagerPath(), }, ]; @@ -224,7 +219,7 @@ const IncidentManagerDetailPage = ({ ...data, { name: testCase?.name ?? '', - url: getTestCaseDetailPagePath( + url: observabilityRouterClassBase.getTestCaseDetailPagePath( testCaseFQN, activeTab as TestCasePageTabs ), @@ -251,16 +246,19 @@ const IncidentManagerDetailPage = ({ const handleTabChange = (activeKey: string) => { if (activeKey !== activeTab) { const testCaseDetailsPath = isDimensionPage - ? getTestCaseDimensionsDetailPagePath( + ? observabilityRouterClassBase.getTestCaseDimensionsDetailPagePath( testCaseFQN, dimensionKey || '', activeKey as TestCasePageTabs ) - : getTestCaseDetailPagePath(testCaseFQN, activeKey as TestCasePageTabs); + : observabilityRouterClassBase.getTestCaseDetailPagePath( + testCaseFQN, + activeKey as TestCasePageTabs + ); navigate( isVersionPage - ? getTestCaseVersionPath( + ? observabilityRouterClassBase.getTestCaseVersionPath( testCaseFQN, version, activeKey as TestCasePageTabs @@ -325,8 +323,8 @@ const IncidentManagerDetailPage = ({ const onVersionClick = () => { navigate( isVersionPage - ? getTestCaseDetailPagePath(testCaseFQN) - : getTestCaseVersionPath( + ? observabilityRouterClassBase.getTestCaseDetailPagePath(testCaseFQN) + : observabilityRouterClassBase.getTestCaseVersionPath( testCaseFQN, toString(testCase?.version) ?? '', activeTab @@ -338,7 +336,11 @@ const IncidentManagerDetailPage = ({ const versionHandler = useCallback( (newVersion = version) => { navigate( - getTestCaseVersionPath(testCaseFQN, toString(newVersion), activeTab) + observabilityRouterClassBase.getTestCaseVersionPath( + testCaseFQN, + toString(newVersion), + activeTab + ) ); }, [testCaseFQN, activeTab] @@ -482,7 +484,11 @@ const IncidentManagerDetailPage = ({ {!isVersionPage && ( navigate(ROUTES.INCIDENT_MANAGER)} + afterDeleteAction={() => + navigate( + observabilityRouterClassBase.getIncidentManagerPath() + ) + } allowSoftDelete={false} canDelete={hasDeletePermission} displayName={testCase.displayName} diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/TestSuiteDetailsPage/TestSuiteDetailsPage.component.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/TestSuiteDetailsPage/TestSuiteDetailsPage.component.tsx index 746b28e3b5dc..d8fa7c607400 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/TestSuiteDetailsPage/TestSuiteDetailsPage.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/TestSuiteDetailsPage/TestSuiteDetailsPage.component.tsx @@ -87,7 +87,6 @@ import { checkPermission, DEFAULT_ENTITY_PERMISSION, } from '../../utils/PermissionsUtils'; -import { getTestSuitePath } from '../../utils/RouterUtils'; import { ExtraTestCaseDropdownOptions } from '../../utils/TestCaseUtils'; import { showErrorToast } from '../../utils/ToastUtils'; import './test-suite-details-page.less'; @@ -195,7 +194,9 @@ const TestSuiteDetailsPage = () => { }, { name: getEntityName(testSuite), - url: getTestSuitePath(testSuite?.fullyQualifiedName ?? ''), + url: observabilityRouterClassBase.getTestSuitePath( + testSuite?.fullyQualifiedName ?? '' + ), }, ]; }, [testSuite]); diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/TestSuiteDetailsPage/TestSuiteDetailsPage.test.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/TestSuiteDetailsPage/TestSuiteDetailsPage.test.tsx index f2093d08f61e..40a8ee56823e 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/TestSuiteDetailsPage/TestSuiteDetailsPage.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/TestSuiteDetailsPage/TestSuiteDetailsPage.test.tsx @@ -27,6 +27,7 @@ import { getTestSuiteByName, updateTestSuiteById, } from '../../rest/testAPI'; +import observabilityRouterClassBase from '../../utils/ObservabilityRouterClassBase'; import TestSuiteDetailsPage from './TestSuiteDetailsPage.component'; jest.mock('@openmetadata/ui-core-components', () => { @@ -307,21 +308,27 @@ jest.mock('../../components/common/EntityDescription/DescriptionV1', () => { )); }); +const mockDataQualityTab = jest.fn(); jest.mock( '../../components/Database/Profiler/DataQualityTab/DataQualityTab', () => { - return jest.fn().mockImplementation(({ onTestUpdate }) => ( -
- DataQualityTab.component - -
- )); + return jest.fn().mockImplementation((props) => { + const { onTestUpdate } = props; + mockDataQualityTab(props); + + return ( +
+ DataQualityTab.component + +
+ ); + }); } ); jest.mock('../../hooks/useApplicationStore', () => { @@ -627,6 +634,34 @@ describe('TestSuiteDetailsPage component', () => { expect(mockGetEntityPermissionByFqn).toHaveBeenCalled(); }); + + describe('observabilityRouterClassBase migration', () => { + it('DataQualityTab breadcrumbData should include test suite item with url from observabilityRouterClassBase.getTestSuitePath', async () => { + await act(async () => { + render(); + }); + + await waitFor(() => { + expect(mockDataQualityTab).toHaveBeenCalled(); + }); + + const fqn = mockTestSuite.fullyQualifiedName; + const expectedUrl = observabilityRouterClassBase.getTestSuitePath(fqn); + + const lastCallProps = + mockDataQualityTab.mock.calls[ + mockDataQualityTab.mock.calls.length - 1 + ][0]; + + expect(lastCallProps.breadcrumbData).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + url: expectedUrl, + }), + ]) + ); + }); + }); }); describe('Props Validation & Permissions', () => { diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/ObservabilityRouterClassBase.test.ts b/openmetadata-ui/src/main/resources/ui/src/utils/ObservabilityRouterClassBase.test.ts index 850d13a2263a..44c70d0ba7d9 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/ObservabilityRouterClassBase.test.ts +++ b/openmetadata-ui/src/main/resources/ui/src/utils/ObservabilityRouterClassBase.test.ts @@ -12,6 +12,7 @@ */ import { DataQualityPageTabs } from '../pages/DataQuality/DataQualityPage.interface'; +import { TestCasePageTabs } from '../pages/IncidentManager/IncidentManager.interface'; import observabilityRouterClassBase, { ObservabilityRouterClassBase, } from './ObservabilityRouterClassBase'; @@ -32,11 +33,28 @@ jest.mock('./RouterUtils', () => ({ `/observability/alerts/edit/${fqn}`, getObservabilityAlertDetailsPath: (fqn: string, tab?: string) => `/observability/alert/${fqn}/${tab ?? 'configuration'}`, + getTestSuitePath: (fqn: string) => `/test-suites/${fqn}`, + getTestCaseDetailPagePath: (fqn: string, tab?: string) => + `/test-case/${fqn}/${tab ?? 'test-case-results'}`, + getTestCaseVersionPath: (fqn: string, version: string, tab?: string) => + tab + ? `/test-case/${fqn}/versions/${version}/${tab}` + : `/test-case/${fqn}/versions/${version}`, + getTestCaseDimensionsDetailPagePath: ( + fqn: string, + dimensionKey: string, + tab?: string + ) => + `/test-case/${fqn}/dimensions/${dimensionKey}/${ + tab ?? 'test-case-results' + }`, })); jest.mock('../constants/constants', () => ({ ROUTES: { ADD_OBSERVABILITY_ALERTS: '/observability/alerts/add', + INCIDENT_MANAGER: '/incident-manager', + OBSERVABILITY_ALERTS: '/observability/alerts', }, })); @@ -110,6 +128,76 @@ describe('ObservabilityRouterClassBase', () => { }); }); + describe('getTestSuitePath', () => { + it('should delegate to RouterUtils helper with the test suite fqn', () => { + expect(router.getTestSuitePath('finance.suites.daily')).toBe( + '/test-suites/finance.suites.daily' + ); + }); + }); + + describe('getTestCaseDetailPagePath', () => { + it('should default the tab to TEST_CASE_RESULTS when omitted', () => { + expect(router.getTestCaseDetailPagePath('table.col')).toBe( + '/test-case/table.col/test-case-results' + ); + }); + + it('should pass through an explicit tab', () => { + expect( + router.getTestCaseDetailPagePath('table.col', TestCasePageTabs.ISSUES) + ).toBe(`/test-case/table.col/${TestCasePageTabs.ISSUES}`); + }); + }); + + describe('getTestCaseVersionPath', () => { + it('should return path without tab segment when tab is omitted', () => { + expect(router.getTestCaseVersionPath('table.col', '0.2')).toBe( + '/test-case/table.col/versions/0.2' + ); + }); + + it('should include tab segment when tab is provided', () => { + expect( + router.getTestCaseVersionPath('table.col', '0.2', 'incidents') + ).toBe('/test-case/table.col/versions/0.2/incidents'); + }); + }); + + describe('getTestCaseDimensionsDetailPagePath', () => { + it('should default the tab to TEST_CASE_RESULTS when omitted', () => { + expect( + router.getTestCaseDimensionsDetailPagePath('table.col', 'rowCount') + ).toBe('/test-case/table.col/dimensions/rowCount/test-case-results'); + }); + + it('should pass through an explicit tab', () => { + expect( + router.getTestCaseDimensionsDetailPagePath( + 'table.col', + 'rowCount', + TestCasePageTabs.ISSUES + ) + ).toBe( + `/test-case/table.col/dimensions/rowCount/${TestCasePageTabs.ISSUES}` + ); + }); + }); + + describe('getIncidentManagerPath', () => { + it('should return the incident manager route constant', () => { + expect(router.getIncidentManagerPath()).toBe('/incident-manager'); + }); + }); + + describe('getObservabilityAlertsListPath', () => { + it('should return the observability alerts route constant', () => { + expect(router.getObservabilityAlertsListPath()).toBe( + '/observability/alerts' + ); + }); + }); + describe('singleton default export', () => { it('default export should be an instance of ObservabilityRouterClassBase', () => { expect(observabilityRouterClassBase).toBeInstanceOf( diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/ObservabilityRouterClassBase.ts b/openmetadata-ui/src/main/resources/ui/src/utils/ObservabilityRouterClassBase.ts index b259f41e2132..4581e1d37b1f 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/ObservabilityRouterClassBase.ts +++ b/openmetadata-ui/src/main/resources/ui/src/utils/ObservabilityRouterClassBase.ts @@ -13,10 +13,15 @@ import { ROUTES } from '../constants/constants'; import { DataQualityPageTabs } from '../pages/DataQuality/DataQualityPage.interface'; +import { TestCasePageTabs } from '../pages/IncidentManager/IncidentManager.interface'; import { getDataQualityPagePath, getObservabilityAlertDetailsPath, getObservabilityAlertsEditPath, + getTestCaseDetailPagePath, + getTestCaseDimensionsDetailPagePath, + getTestCaseVersionPath, + getTestSuitePath, } from './RouterUtils'; class ObservabilityRouterClassBase { @@ -39,6 +44,14 @@ class ObservabilityRouterClassBase { return ROUTES.ADD_OBSERVABILITY_ALERTS; } + public getObservabilityAlertsListPath(): string { + return ROUTES.OBSERVABILITY_ALERTS; + } + + public getIncidentManagerPath(): string { + return ROUTES.INCIDENT_MANAGER; + } + public getObservabilityAlertsEditPath(fqn: string): string { return getObservabilityAlertsEditPath(fqn); } @@ -46,6 +59,33 @@ class ObservabilityRouterClassBase { public getObservabilityAlertDetailsPath(fqn: string, tab?: string): string { return getObservabilityAlertDetailsPath(fqn, tab); } + + public getTestSuitePath(testSuiteFqn: string): string { + return getTestSuitePath(testSuiteFqn); + } + + public getTestCaseDetailPagePath( + fqn: string, + tab: TestCasePageTabs = TestCasePageTabs.TEST_CASE_RESULTS + ): string { + return getTestCaseDetailPagePath(fqn, tab); + } + + public getTestCaseVersionPath( + fqn: string, + version: string, + tab?: string + ): string { + return getTestCaseVersionPath(fqn, version, tab); + } + + public getTestCaseDimensionsDetailPagePath( + fqn: string, + dimensionKey: string, + tab: TestCasePageTabs = TestCasePageTabs.TEST_CASE_RESULTS + ): string { + return getTestCaseDimensionsDetailPagePath(fqn, dimensionKey, tab); + } } const observabilityRouterClassBase = new ObservabilityRouterClassBase();