Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -128,7 +128,11 @@ const AddToBundleSuiteModal: React.FC<AddToBundleSuiteModalProps> = ({
(opt) => opt.value === selectedSuiteId
);
if (selectedSuite?.suite.fullyQualifiedName) {
navigate(getTestSuitePath(selectedSuite.suite.fullyQualifiedName));
navigate(
observabilityRouterClassBase.getTestSuitePath(
selectedSuite.suite.fullyQualifiedName
)
);
}

onAddedToExisting();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -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(<AddToBundleSuiteModal {...mockProps} />);

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}`);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand All @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -531,6 +532,48 @@ describe('DataQualityDashboard', () => {
})
);
});

describe('observabilityRouterClassBase migration', () => {
it('open-incident widget redirectPath.pathname should be observabilityRouterClassBase.getIncidentManagerPath()', async () => {
render(<DataQualityDashboard />, { 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(<DataQualityDashboard />, { 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', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -222,7 +222,7 @@ const FailedTestCaseSampleData = ({
<div className="d-flex gap-4">
{testCaseData?.inspectionQuery && !isVersionPage && (
<Link
to={getTestCaseDetailPagePath(
to={observabilityRouterClassBase.getTestCaseDetailPagePath(
testCaseData?.fullyQualifiedName ?? '',
TestCasePageTabs.SQL_QUERY
)}>
Expand Down
Original file line number Diff line number Diff line change
@@ -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 }) => (
<a data-to={typeof to === 'string' ? to : JSON.stringify(to)} {...rest}>
{children}
</a>
)),
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(() => <div data-testid="loader">Loader</div>)
);

jest.mock(
'../../../common/ManageButtonContentItem/ManageButtonContentItem.component',
() => ({
ManageButtonItemLabel: jest
.fn()
.mockImplementation(() => <div>ManageButtonItemLabel</div>),
})
);

jest.mock('../../../Database/SampleDataTable/RowData', () => ({
RowData: jest.fn().mockImplementation(() => <div>RowData</div>),
}));

jest.mock('../../../Modals/EntityDeleteModal/EntityDeleteModal', () =>
jest.fn().mockImplementation(() => <div>EntityDeleteModal</div>)
);

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(<FailedTestCaseSampleData testCaseData={mockTestCase} />);
});

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
);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -245,7 +242,9 @@ export const TestSuites = () => {
<Link
className="break-word"
data-testid={record.name}
to={getTestSuitePath(record.fullyQualifiedName ?? record.name)}>
to={observabilityRouterClassBase.getTestSuitePath(
record.fullyQualifiedName ?? record.name
)}>
{getEntityName(record)}
</Link>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand Down Expand Up @@ -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}`),
},
}));

Expand Down Expand Up @@ -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(<TestSuites />, { wrapper: MemoryRouter });

const link = await screen.findByTestId(logicalSuiteName);

expect(link.getAttribute('to')).toBe(
observabilityRouterClassBase.getTestSuitePath(logicalSuiteName)
);
expect(link.getAttribute('to')).toBe(`/test-suites/${logicalSuiteName}`);
});
});
});
Loading
Loading