mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-09-30 03:16:30 +00:00
fix: Search Filter removal doesn't restore full test case list in edit page of test suite pipeline (#23419)
* fix: Search Filter removal doesn't restore full test case list in edit page of test suite pipeline Fixes #23405 * fix: enhance search query to include wildcard when filters are applied
This commit is contained in:
parent
1ca4329483
commit
7ef792db59
@ -12,45 +12,45 @@
|
||||
*/
|
||||
import { act, fireEvent, render, screen } from '@testing-library/react';
|
||||
import { Form } from 'antd';
|
||||
import React from 'react';
|
||||
import { TestCase } from '../../../../generated/tests/testCase';
|
||||
import { TestSuite } from '../../../../generated/tests/testSuite';
|
||||
import { AddTestSuitePipelineProps } from '../AddDataQualityTest.interface';
|
||||
import AddTestSuitePipeline from './AddTestSuitePipeline';
|
||||
|
||||
const mockNavigate = jest.fn();
|
||||
const mockUseCustomLocation = jest.fn();
|
||||
const mockUseFqn = jest.fn();
|
||||
const mockScheduleInterval = jest.fn();
|
||||
|
||||
jest.mock('react-router-dom', () => ({
|
||||
useNavigate: () => mockNavigate,
|
||||
}));
|
||||
|
||||
jest.mock('../../../../hooks/useCustomLocation/useCustomLocation', () =>
|
||||
jest.fn().mockImplementation(() => mockUseCustomLocation())
|
||||
);
|
||||
|
||||
jest.mock('../../../../hooks/useCustomLocation/useCustomLocation', () => {
|
||||
return jest.fn().mockImplementation(() => ({
|
||||
search: `?testSuiteId=test-suite-id`,
|
||||
}));
|
||||
});
|
||||
jest.mock('../../../../hooks/useFqn', () => ({
|
||||
useFqn: jest.fn().mockReturnValue({ fqn: 'test-suite-fqn' }),
|
||||
useFqn: jest.fn().mockImplementation(() => mockUseFqn()),
|
||||
}));
|
||||
|
||||
jest.mock('../../AddTestCaseList/AddTestCaseList.component', () => ({
|
||||
AddTestCaseList: jest
|
||||
.fn()
|
||||
.mockImplementation(() => <div>AddTestCaseList.component</div>),
|
||||
AddTestCaseList: () => <div>AddTestCaseList.component</div>,
|
||||
}));
|
||||
|
||||
jest.mock(
|
||||
'../../../Settings/Services/AddIngestion/Steps/ScheduleInterval',
|
||||
() =>
|
||||
jest
|
||||
.fn()
|
||||
.mockImplementation(({ children, topChildren, onDeploy, onBack }) => (
|
||||
<div>
|
||||
ScheduleInterval
|
||||
{topChildren}
|
||||
{children}
|
||||
<div onClick={onDeploy}>submit</div>
|
||||
<div onClick={onBack}>cancel</div>
|
||||
</div>
|
||||
))
|
||||
() => jest.fn().mockImplementation((props) => mockScheduleInterval(props))
|
||||
);
|
||||
jest.mock('react-router-dom', () => ({
|
||||
useNavigate: jest.fn().mockImplementation(() => mockNavigate),
|
||||
}));
|
||||
|
||||
jest.mock('../../../../utils/SchedularUtils', () => ({
|
||||
getRaiseOnErrorFormField: jest.fn().mockReturnValue({}),
|
||||
getRaiseOnErrorFormField: () => ({
|
||||
name: 'raiseOnError',
|
||||
label: 'Raise On Error',
|
||||
type: 'switch',
|
||||
required: false,
|
||||
}),
|
||||
}));
|
||||
|
||||
const mockProps: AddTestSuitePipelineProps = {
|
||||
@ -59,6 +59,25 @@ const mockProps: AddTestSuitePipelineProps = {
|
||||
};
|
||||
|
||||
describe('AddTestSuitePipeline', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
mockUseCustomLocation.mockReturnValue({
|
||||
search: '?testSuiteId=test-suite-id',
|
||||
});
|
||||
mockUseFqn.mockReturnValue({ ingestionFQN: '' });
|
||||
mockScheduleInterval.mockImplementation(
|
||||
({ children, topChildren, onDeploy, onBack }) => (
|
||||
<div>
|
||||
ScheduleInterval
|
||||
{topChildren}
|
||||
{children}
|
||||
<div onClick={onDeploy}>submit</div>
|
||||
<div onClick={onBack}>cancel</div>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
it('renders form fields', () => {
|
||||
render(
|
||||
<Form>
|
||||
@ -66,7 +85,6 @@ describe('AddTestSuitePipeline', () => {
|
||||
</Form>
|
||||
);
|
||||
|
||||
// Assert that the form fields are rendered
|
||||
expect(screen.getByTestId('pipeline-name')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('select-all-test-cases')).toBeInTheDocument();
|
||||
expect(screen.getByText('submit')).toBeInTheDocument();
|
||||
@ -90,7 +108,6 @@ describe('AddTestSuitePipeline', () => {
|
||||
fireEvent.click(screen.getByText('submit'));
|
||||
});
|
||||
|
||||
// Assert that onSubmit is called with the correct values
|
||||
expect(mockProps.onSubmit).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@ -130,7 +147,7 @@ describe('AddTestSuitePipeline', () => {
|
||||
onClick={() =>
|
||||
onFormChange('', {
|
||||
forms: {
|
||||
['schedular-form']: {
|
||||
'schedular-form': {
|
||||
getFieldValue: jest.fn().mockImplementation(() => true),
|
||||
setFieldsValue: jest.fn(),
|
||||
},
|
||||
@ -147,15 +164,483 @@ describe('AddTestSuitePipeline', () => {
|
||||
</Form>
|
||||
);
|
||||
|
||||
// Assert that AddTestCaseList.component is now visible
|
||||
expect(screen.getByText('AddTestCaseList.component')).toBeInTheDocument();
|
||||
|
||||
// Click on the select-all-test-cases switch
|
||||
await act(async () => {
|
||||
fireEvent.click(screen.getByTestId('select-all-test-cases'));
|
||||
});
|
||||
|
||||
// Assert that AddTestCaseList.component is not initially visible
|
||||
expect(screen.queryByText('AddTestCaseList.component')).toBeNull();
|
||||
});
|
||||
|
||||
describe('raiseOnError functionality', () => {
|
||||
it('includes raiseOnError field in form submission', async () => {
|
||||
const mockOnSubmit = jest.fn();
|
||||
render(
|
||||
<Form>
|
||||
<AddTestSuitePipeline {...mockProps} onSubmit={mockOnSubmit} />
|
||||
</Form>
|
||||
);
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(screen.getByText('submit'));
|
||||
});
|
||||
|
||||
expect(mockOnSubmit).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
raiseOnError: undefined,
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('passes raiseOnError value from form to onSubmit', async () => {
|
||||
const mockOnSubmit = jest.fn();
|
||||
const initialData = {
|
||||
raiseOnError: true,
|
||||
selectAllTestCases: true,
|
||||
};
|
||||
|
||||
mockScheduleInterval.mockImplementationOnce(
|
||||
({
|
||||
children,
|
||||
onDeploy,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
onDeploy: (values: unknown) => void;
|
||||
}) => (
|
||||
<div>
|
||||
{children}
|
||||
<div
|
||||
onClick={() =>
|
||||
onDeploy({
|
||||
raiseOnError: true,
|
||||
selectAllTestCases: true,
|
||||
})
|
||||
}>
|
||||
submit
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
|
||||
render(
|
||||
<Form>
|
||||
<AddTestSuitePipeline
|
||||
{...mockProps}
|
||||
initialData={initialData}
|
||||
onSubmit={mockOnSubmit}
|
||||
/>
|
||||
</Form>
|
||||
);
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(screen.getByText('submit'));
|
||||
});
|
||||
|
||||
expect(mockOnSubmit).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
raiseOnError: true,
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('testCase mapping logic', () => {
|
||||
it('maps TestCase objects to string names', async () => {
|
||||
const mockOnSubmit = jest.fn();
|
||||
const testCaseObject: TestCase = {
|
||||
name: 'test-case-object',
|
||||
id: '123',
|
||||
fullyQualifiedName: 'test.case.object',
|
||||
} as TestCase;
|
||||
|
||||
mockScheduleInterval.mockImplementationOnce(
|
||||
({
|
||||
children,
|
||||
onDeploy,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
onDeploy: (values: unknown) => void;
|
||||
}) => (
|
||||
<div>
|
||||
{children}
|
||||
<div
|
||||
onClick={() =>
|
||||
onDeploy({
|
||||
testCases: [testCaseObject, 'test-case-string'],
|
||||
})
|
||||
}>
|
||||
submit
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
|
||||
render(
|
||||
<Form>
|
||||
<AddTestSuitePipeline {...mockProps} onSubmit={mockOnSubmit} />
|
||||
</Form>
|
||||
);
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(screen.getByText('submit'));
|
||||
});
|
||||
|
||||
expect(mockOnSubmit).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
testCases: ['test-case-object', 'test-case-string'],
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('handles undefined testCases array', async () => {
|
||||
const mockOnSubmit = jest.fn();
|
||||
|
||||
mockScheduleInterval.mockImplementationOnce(
|
||||
({
|
||||
children,
|
||||
onDeploy,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
onDeploy: (values: unknown) => void;
|
||||
}) => (
|
||||
<div>
|
||||
{children}
|
||||
<div
|
||||
onClick={() =>
|
||||
onDeploy({
|
||||
testCases: undefined,
|
||||
selectAllTestCases: true,
|
||||
})
|
||||
}>
|
||||
submit
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
|
||||
render(
|
||||
<Form>
|
||||
<AddTestSuitePipeline
|
||||
{...mockProps}
|
||||
initialData={{ selectAllTestCases: true }}
|
||||
onSubmit={mockOnSubmit}
|
||||
/>
|
||||
</Form>
|
||||
);
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(screen.getByText('submit'));
|
||||
});
|
||||
|
||||
expect(mockOnSubmit).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
testCases: undefined,
|
||||
selectAllTestCases: true,
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('handles mixed array of TestCase objects and strings', async () => {
|
||||
const mockOnSubmit = jest.fn();
|
||||
const testCase1: TestCase = {
|
||||
name: 'test-case-1',
|
||||
id: '1',
|
||||
fullyQualifiedName: 'test.case.1',
|
||||
} as TestCase;
|
||||
const testCase2: TestCase = {
|
||||
name: 'test-case-2',
|
||||
id: '2',
|
||||
fullyQualifiedName: 'test.case.2',
|
||||
} as TestCase;
|
||||
|
||||
mockScheduleInterval.mockImplementationOnce(
|
||||
({
|
||||
children,
|
||||
onDeploy,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
onDeploy: (values: unknown) => void;
|
||||
}) => (
|
||||
<div>
|
||||
{children}
|
||||
<div
|
||||
onClick={() =>
|
||||
onDeploy({
|
||||
testCases: [testCase1, 'string-test', testCase2],
|
||||
})
|
||||
}>
|
||||
submit
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
|
||||
render(
|
||||
<Form>
|
||||
<AddTestSuitePipeline {...mockProps} onSubmit={mockOnSubmit} />
|
||||
</Form>
|
||||
);
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(screen.getByText('submit'));
|
||||
});
|
||||
|
||||
expect(mockOnSubmit).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
testCases: ['test-case-1', 'string-test', 'test-case-2'],
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('testSuiteId extraction', () => {
|
||||
it('uses testSuiteId from testSuite prop when available', () => {
|
||||
const testSuite = { id: 'prop-test-suite-id' } as TestSuite;
|
||||
|
||||
render(
|
||||
<Form>
|
||||
<AddTestSuitePipeline {...mockProps} testSuite={testSuite} />
|
||||
</Form>
|
||||
);
|
||||
|
||||
expect(screen.getByText('AddTestCaseList.component')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('extracts testSuiteId from URL search params when testSuite prop is not provided', () => {
|
||||
mockUseCustomLocation.mockReturnValueOnce({
|
||||
search: '?testSuiteId=url-test-suite-id',
|
||||
});
|
||||
|
||||
render(
|
||||
<Form>
|
||||
<AddTestSuitePipeline {...mockProps} />
|
||||
</Form>
|
||||
);
|
||||
|
||||
expect(screen.getByText('AddTestCaseList.component')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('handles URL search params without question mark', () => {
|
||||
mockUseCustomLocation.mockReturnValueOnce({
|
||||
search: 'testSuiteId=no-question-mark-id',
|
||||
});
|
||||
|
||||
render(
|
||||
<Form>
|
||||
<AddTestSuitePipeline {...mockProps} />
|
||||
</Form>
|
||||
);
|
||||
|
||||
expect(screen.getByText('AddTestCaseList.component')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('prioritizes testSuite prop over URL params', () => {
|
||||
mockUseCustomLocation.mockReturnValueOnce({
|
||||
search: '?testSuiteId=url-id',
|
||||
});
|
||||
|
||||
const testSuite = { id: 'prop-id' } as TestSuite;
|
||||
|
||||
render(
|
||||
<Form>
|
||||
<AddTestSuitePipeline {...mockProps} testSuite={testSuite} />
|
||||
</Form>
|
||||
);
|
||||
|
||||
expect(screen.getByText('AddTestCaseList.component')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Form state management', () => {
|
||||
it('clears testCases field when selectAllTestCases is enabled', async () => {
|
||||
const mockSetFieldsValue = jest.fn();
|
||||
const mockGetFieldValue = jest.fn().mockReturnValue(true);
|
||||
|
||||
jest.spyOn(Form, 'Provider').mockImplementation(
|
||||
jest.fn().mockImplementation(({ onFormChange, children }) => (
|
||||
<div>
|
||||
{children}
|
||||
<button
|
||||
data-testid="trigger-form-change"
|
||||
onClick={() =>
|
||||
onFormChange('', {
|
||||
forms: {
|
||||
'schedular-form': {
|
||||
getFieldValue: mockGetFieldValue,
|
||||
setFieldsValue: mockSetFieldsValue,
|
||||
},
|
||||
},
|
||||
})
|
||||
}>
|
||||
Trigger Change
|
||||
</button>
|
||||
</div>
|
||||
))
|
||||
);
|
||||
|
||||
render(
|
||||
<Form>
|
||||
<AddTestSuitePipeline {...mockProps} />
|
||||
</Form>
|
||||
);
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(screen.getByTestId('trigger-form-change'));
|
||||
});
|
||||
|
||||
expect(mockGetFieldValue).toHaveBeenCalledWith('selectAllTestCases');
|
||||
expect(mockSetFieldsValue).toHaveBeenCalledWith({ testCases: undefined });
|
||||
});
|
||||
|
||||
it('does not clear testCases when selectAllTestCases is false', async () => {
|
||||
const mockSetFieldsValue = jest.fn();
|
||||
const mockGetFieldValue = jest.fn().mockReturnValue(false);
|
||||
|
||||
jest.spyOn(Form, 'Provider').mockImplementation(
|
||||
jest.fn().mockImplementation(({ onFormChange, children }) => (
|
||||
<div>
|
||||
{children}
|
||||
<button
|
||||
data-testid="trigger-form-change"
|
||||
onClick={() =>
|
||||
onFormChange('', {
|
||||
forms: {
|
||||
'schedular-form': {
|
||||
getFieldValue: mockGetFieldValue,
|
||||
setFieldsValue: mockSetFieldsValue,
|
||||
},
|
||||
},
|
||||
})
|
||||
}>
|
||||
Trigger Change
|
||||
</button>
|
||||
</div>
|
||||
))
|
||||
);
|
||||
|
||||
render(
|
||||
<Form>
|
||||
<AddTestSuitePipeline {...mockProps} />
|
||||
</Form>
|
||||
);
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(screen.getByTestId('trigger-form-change'));
|
||||
});
|
||||
|
||||
expect(mockGetFieldValue).toHaveBeenCalledWith('selectAllTestCases');
|
||||
expect(mockSetFieldsValue).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('updates selectAllTestCases state when form changes', async () => {
|
||||
const { rerender } = render(
|
||||
<Form>
|
||||
<AddTestSuitePipeline {...mockProps} />
|
||||
</Form>
|
||||
);
|
||||
|
||||
expect(screen.getByText('AddTestCaseList.component')).toBeInTheDocument();
|
||||
|
||||
const propsWithInitialData = {
|
||||
...mockProps,
|
||||
initialData: { selectAllTestCases: true },
|
||||
};
|
||||
|
||||
rerender(
|
||||
<Form>
|
||||
<AddTestSuitePipeline {...propsWithInitialData} />
|
||||
</Form>
|
||||
);
|
||||
|
||||
await act(async () => {
|
||||
// Form state should reflect the initial data
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Edit mode behavior', () => {
|
||||
it('displays Save button in edit mode', () => {
|
||||
mockUseFqn.mockReturnValueOnce({ ingestionFQN: 'test-ingestion-fqn' });
|
||||
|
||||
render(
|
||||
<Form>
|
||||
<AddTestSuitePipeline {...mockProps} />
|
||||
</Form>
|
||||
);
|
||||
|
||||
expect(screen.getByText('ScheduleInterval')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('displays Create button when not in edit mode', () => {
|
||||
mockUseFqn.mockReturnValueOnce({ ingestionFQN: '' });
|
||||
|
||||
render(
|
||||
<Form>
|
||||
<AddTestSuitePipeline {...mockProps} />
|
||||
</Form>
|
||||
);
|
||||
|
||||
expect(screen.getByText('ScheduleInterval')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Form submission with all fields', () => {
|
||||
it('submits form with all populated fields', async () => {
|
||||
const mockOnSubmit = jest.fn();
|
||||
const initialData = {
|
||||
name: 'Test Pipeline',
|
||||
cron: '0 0 * * *',
|
||||
enableDebugLog: true,
|
||||
selectAllTestCases: false,
|
||||
raiseOnError: true,
|
||||
};
|
||||
|
||||
mockScheduleInterval.mockImplementationOnce(
|
||||
({
|
||||
children,
|
||||
onDeploy,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
onDeploy: (values: unknown) => void;
|
||||
}) => (
|
||||
<div>
|
||||
{children}
|
||||
<div
|
||||
onClick={() =>
|
||||
onDeploy({
|
||||
...initialData,
|
||||
testCases: ['test-1', 'test-2'],
|
||||
})
|
||||
}>
|
||||
submit
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
|
||||
render(
|
||||
<Form>
|
||||
<AddTestSuitePipeline
|
||||
{...mockProps}
|
||||
initialData={initialData}
|
||||
onSubmit={mockOnSubmit}
|
||||
/>
|
||||
</Form>
|
||||
);
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(screen.getByText('submit'));
|
||||
});
|
||||
|
||||
expect(mockOnSubmit).toHaveBeenCalledWith({
|
||||
name: 'Test Pipeline',
|
||||
cron: '0 0 * * *',
|
||||
enableDebugLog: true,
|
||||
selectAllTestCases: false,
|
||||
testCases: ['test-1', 'test-2'],
|
||||
raiseOnError: true,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -11,14 +11,16 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
import {
|
||||
act,
|
||||
fireEvent,
|
||||
queryByAttribute,
|
||||
render,
|
||||
screen,
|
||||
waitFor,
|
||||
} from '@testing-library/react';
|
||||
import { EntityReference } from '../../../generated/tests/testCase';
|
||||
import { act } from 'react';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import { EntityReference, TestCase } from '../../../generated/tests/testCase';
|
||||
import { getListTestCaseBySearch } from '../../../rest/testAPI';
|
||||
import { AddTestCaseList } from './AddTestCaseList.component';
|
||||
import { AddTestCaseModalProps } from './AddTestCaseList.interface';
|
||||
|
||||
@ -31,7 +33,15 @@ jest.mock('../../common/Loader/Loader', () => {
|
||||
});
|
||||
|
||||
jest.mock('../../common/SearchBarComponent/SearchBar.component', () => {
|
||||
return jest.fn().mockImplementation(() => <div>Search Bar Mock</div>);
|
||||
return jest.fn().mockImplementation(({ onSearch, searchValue }) => (
|
||||
<div>
|
||||
<input
|
||||
data-testid="search-bar"
|
||||
value={searchValue}
|
||||
onChange={(e) => onSearch(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
));
|
||||
});
|
||||
jest.mock('../../../utils/StringsUtils', () => {
|
||||
return {
|
||||
@ -60,12 +70,7 @@ jest.mock('../../../utils/CommonUtils', () => {
|
||||
});
|
||||
jest.mock('../../../rest/testAPI', () => {
|
||||
return {
|
||||
getListTestCaseBySearch: jest.fn().mockResolvedValue({
|
||||
data: [],
|
||||
paging: {
|
||||
total: 0,
|
||||
},
|
||||
}),
|
||||
getListTestCaseBySearch: jest.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
@ -85,23 +90,116 @@ const mockProps: AddTestCaseModalProps = {
|
||||
};
|
||||
|
||||
jest.mock('../../../utils/RouterUtils', () => ({
|
||||
getEntityDetailsPath: jest.fn(),
|
||||
getEntityDetailsPath: jest.fn().mockReturnValue('/path/to/entity'),
|
||||
}));
|
||||
|
||||
const mockGetListTestCaseBySearch =
|
||||
getListTestCaseBySearch as jest.MockedFunction<
|
||||
typeof getListTestCaseBySearch
|
||||
>;
|
||||
|
||||
const mockTestCases: TestCase[] = [
|
||||
{
|
||||
id: 'test-case-1',
|
||||
name: 'test_case_1',
|
||||
displayName: 'Test Case 1',
|
||||
entityLink: '<#E::table::sample.table>',
|
||||
testDefinition: {
|
||||
id: 'test-def-1',
|
||||
name: 'table_column_count_to_equal',
|
||||
displayName: 'Table Column Count To Equal',
|
||||
},
|
||||
} as TestCase,
|
||||
{
|
||||
id: 'test-case-2',
|
||||
name: 'test_case_2',
|
||||
displayName: 'Test Case 2',
|
||||
entityLink: '<#E::table::sample.table::columns::id>',
|
||||
testDefinition: {
|
||||
id: 'test-def-2',
|
||||
name: 'column_values_to_be_unique',
|
||||
displayName: 'Column Values To Be Unique',
|
||||
},
|
||||
} as TestCase,
|
||||
{
|
||||
id: 'test-case-3',
|
||||
name: 'test_case_3',
|
||||
displayName: 'Test Case 3',
|
||||
entityLink: '<#E::table::another.table>',
|
||||
testDefinition: {
|
||||
id: 'test-def-3',
|
||||
name: 'table_row_count_to_be_between',
|
||||
displayName: 'Table Row Count To Be Between',
|
||||
},
|
||||
} as TestCase,
|
||||
];
|
||||
|
||||
const renderWithRouter = (props: AddTestCaseModalProps) => {
|
||||
return render(
|
||||
<MemoryRouter>
|
||||
<AddTestCaseList {...props} />
|
||||
</MemoryRouter>
|
||||
);
|
||||
};
|
||||
|
||||
describe('AddTestCaseList', () => {
|
||||
it('renders the component', async () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
mockGetListTestCaseBySearch.mockResolvedValue({
|
||||
data: [],
|
||||
paging: {
|
||||
total: 0,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('renders the component with initial state', async () => {
|
||||
await act(async () => {
|
||||
render(<AddTestCaseList {...mockProps} />);
|
||||
renderWithRouter(mockProps);
|
||||
});
|
||||
|
||||
expect(screen.getByText('Search Bar Mock')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('search-bar')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('cancel')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('submit')).toBeInTheDocument();
|
||||
expect(mockGetListTestCaseBySearch).toHaveBeenCalledWith({
|
||||
q: '*',
|
||||
limit: 25,
|
||||
offset: 0,
|
||||
});
|
||||
});
|
||||
|
||||
it('renders empty state when no test cases are found', async () => {
|
||||
await act(async () => {
|
||||
renderWithRouter(mockProps);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('Error Placeholder Mock')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it('renders test cases when data is available', async () => {
|
||||
mockGetListTestCaseBySearch.mockResolvedValue({
|
||||
data: mockTestCases,
|
||||
paging: {
|
||||
total: 3,
|
||||
},
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
renderWithRouter(mockProps);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('test_case_1')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('test_case_2')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('test_case_3')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it('calls onCancel when cancel button is clicked', async () => {
|
||||
await act(async () => {
|
||||
render(<AddTestCaseList {...mockProps} />);
|
||||
renderWithRouter(mockProps);
|
||||
});
|
||||
fireEvent.click(screen.getByTestId('cancel'));
|
||||
|
||||
@ -110,7 +208,7 @@ describe('AddTestCaseList', () => {
|
||||
|
||||
it('calls onSubmit when submit button is clicked', async () => {
|
||||
await act(async () => {
|
||||
render(<AddTestCaseList {...mockProps} />);
|
||||
renderWithRouter(mockProps);
|
||||
});
|
||||
const submitBtn = screen.getByTestId('submit');
|
||||
fireEvent.click(submitBtn);
|
||||
@ -125,10 +223,483 @@ describe('AddTestCaseList', () => {
|
||||
|
||||
it('does not render submit and cancel buttons when showButton is false', async () => {
|
||||
await act(async () => {
|
||||
render(<AddTestCaseList {...mockProps} showButton={false} />);
|
||||
renderWithRouter({ ...mockProps, showButton: false });
|
||||
});
|
||||
|
||||
expect(screen.queryByTestId('cancel')).toBeNull();
|
||||
expect(screen.queryByTestId('submit')).toBeNull();
|
||||
});
|
||||
|
||||
describe('Search functionality', () => {
|
||||
it('triggers search when search term is entered', async () => {
|
||||
await act(async () => {
|
||||
renderWithRouter(mockProps);
|
||||
});
|
||||
|
||||
const searchBar = screen.getByTestId('search-bar');
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.change(searchBar, { target: { value: 'test_search' } });
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockGetListTestCaseBySearch).toHaveBeenCalledWith({
|
||||
q: 'test_search',
|
||||
limit: 25,
|
||||
offset: 0,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('applies filters when provided', async () => {
|
||||
const filters = 'testSuiteFullyQualifiedName:sample.test.suite';
|
||||
|
||||
await act(async () => {
|
||||
renderWithRouter({ ...mockProps, filters });
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockGetListTestCaseBySearch).toHaveBeenCalledWith({
|
||||
q: `* && ${filters}`,
|
||||
limit: 25,
|
||||
offset: 0,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('combines search term with filters', async () => {
|
||||
const filters = 'testSuiteFullyQualifiedName:sample.test.suite';
|
||||
|
||||
await act(async () => {
|
||||
renderWithRouter({ ...mockProps, filters });
|
||||
});
|
||||
|
||||
const searchBar = screen.getByTestId('search-bar');
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.change(searchBar, { target: { value: 'column_test' } });
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockGetListTestCaseBySearch).toHaveBeenCalledWith({
|
||||
q: `column_test && ${filters}`,
|
||||
limit: 25,
|
||||
offset: 0,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('passes testCaseParams to API call', async () => {
|
||||
const testCaseParams = {
|
||||
testSuiteId: 'test-suite-123',
|
||||
includeFields: ['testDefinition', 'testSuite'],
|
||||
};
|
||||
|
||||
await act(async () => {
|
||||
render(
|
||||
<AddTestCaseList {...mockProps} testCaseParams={testCaseParams} />
|
||||
);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockGetListTestCaseBySearch).toHaveBeenCalledWith({
|
||||
q: '*',
|
||||
limit: 25,
|
||||
offset: 0,
|
||||
...testCaseParams,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Test case selection', () => {
|
||||
beforeEach(() => {
|
||||
mockGetListTestCaseBySearch.mockResolvedValue({
|
||||
data: mockTestCases,
|
||||
paging: {
|
||||
total: 3,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('selects a test case when clicked', async () => {
|
||||
const onChange = jest.fn();
|
||||
|
||||
await act(async () => {
|
||||
renderWithRouter({ ...mockProps, onChange });
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('test_case_1')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
const testCaseCard = screen
|
||||
.getByTestId('test_case_1')
|
||||
.closest('.cursor-pointer');
|
||||
|
||||
expect(testCaseCard).not.toBeNull();
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(testCaseCard as Element);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(onChange).toHaveBeenCalledWith([mockTestCases[0]]);
|
||||
});
|
||||
|
||||
const checkbox = screen.getByTestId('checkbox-test_case_1');
|
||||
|
||||
expect(checkbox).toHaveProperty('checked', true);
|
||||
});
|
||||
|
||||
it('deselects a test case when clicked again', async () => {
|
||||
const onChange = jest.fn();
|
||||
|
||||
await act(async () => {
|
||||
renderWithRouter({ ...mockProps, onChange });
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('test_case_1')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
const testCaseCard = screen
|
||||
.getByTestId('test_case_1')
|
||||
.closest('.cursor-pointer');
|
||||
|
||||
expect(testCaseCard).not.toBeNull();
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(testCaseCard as Element);
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(testCaseCard as Element);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(onChange).toHaveBeenLastCalledWith([]);
|
||||
});
|
||||
|
||||
const checkbox = screen.getByTestId('checkbox-test_case_1');
|
||||
|
||||
expect(checkbox).toHaveProperty('checked', false);
|
||||
});
|
||||
|
||||
it('handles multiple test case selections', async () => {
|
||||
const onChange = jest.fn();
|
||||
|
||||
await act(async () => {
|
||||
renderWithRouter({ ...mockProps, onChange });
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('test_case_1')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
const testCaseCard1 = screen
|
||||
.getByTestId('test_case_1')
|
||||
.closest('.cursor-pointer');
|
||||
const testCaseCard2 = screen
|
||||
.getByTestId('test_case_2')
|
||||
.closest('.cursor-pointer');
|
||||
|
||||
expect(testCaseCard1).not.toBeNull();
|
||||
expect(testCaseCard2).not.toBeNull();
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(testCaseCard1 as Element);
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(testCaseCard2 as Element);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(onChange).toHaveBeenLastCalledWith([
|
||||
mockTestCases[0],
|
||||
mockTestCases[1],
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
it('pre-selects test cases when selectedTest prop is provided', async () => {
|
||||
mockGetListTestCaseBySearch.mockResolvedValue({
|
||||
data: [mockTestCases[0], mockTestCases[1]],
|
||||
paging: {
|
||||
total: 2,
|
||||
},
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
renderWithRouter({
|
||||
...mockProps,
|
||||
selectedTest: ['test_case_1', 'test_case_2'],
|
||||
});
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
const checkbox1 = screen.getByTestId('checkbox-test_case_1');
|
||||
const checkbox2 = screen.getByTestId('checkbox-test_case_2');
|
||||
|
||||
expect(checkbox1).toHaveProperty('checked', true);
|
||||
expect(checkbox2).toHaveProperty('checked', true);
|
||||
});
|
||||
});
|
||||
|
||||
it('handles test cases without id gracefully', async () => {
|
||||
const testCasesWithoutId = [{ ...mockTestCases[0], id: undefined }];
|
||||
|
||||
mockGetListTestCaseBySearch.mockResolvedValue({
|
||||
data: testCasesWithoutId,
|
||||
paging: {
|
||||
total: 1,
|
||||
},
|
||||
});
|
||||
|
||||
const onChange = jest.fn();
|
||||
|
||||
await act(async () => {
|
||||
renderWithRouter({ ...mockProps, onChange });
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('test_case_1')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
const testCaseCard = screen
|
||||
.getByTestId('test_case_1')
|
||||
.closest('.cursor-pointer');
|
||||
|
||||
expect(testCaseCard).not.toBeNull();
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(testCaseCard as Element);
|
||||
});
|
||||
|
||||
expect(onChange).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Pagination and virtual list', () => {
|
||||
it('fetches data with correct pagination parameters', async () => {
|
||||
mockGetListTestCaseBySearch.mockResolvedValue({
|
||||
data: mockTestCases.slice(0, 2),
|
||||
paging: {
|
||||
total: 3,
|
||||
},
|
||||
});
|
||||
|
||||
renderWithRouter(mockProps);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockGetListTestCaseBySearch).toHaveBeenCalledWith({
|
||||
q: '*',
|
||||
limit: 25,
|
||||
offset: 0,
|
||||
});
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('test_case_1')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('test_case_2')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it('maintains search term with API calls', async () => {
|
||||
mockGetListTestCaseBySearch.mockResolvedValue({
|
||||
data: [mockTestCases[0]],
|
||||
paging: {
|
||||
total: 2,
|
||||
},
|
||||
});
|
||||
|
||||
renderWithRouter(mockProps);
|
||||
|
||||
const searchBar = screen.getByTestId('search-bar');
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.change(searchBar, { target: { value: 'specific_test' } });
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockGetListTestCaseBySearch).toHaveBeenCalledWith({
|
||||
q: 'specific_test',
|
||||
limit: 25,
|
||||
offset: 0,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('uses virtual list for performance optimization', async () => {
|
||||
mockGetListTestCaseBySearch.mockResolvedValue({
|
||||
data: mockTestCases,
|
||||
paging: {
|
||||
total: 100,
|
||||
},
|
||||
});
|
||||
|
||||
const { container } = renderWithRouter(mockProps);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('test_case_1')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
const virtualList = container.querySelector('.rc-virtual-list-holder');
|
||||
|
||||
expect(virtualList).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Submit functionality', () => {
|
||||
it('submits selected test cases', async () => {
|
||||
mockGetListTestCaseBySearch.mockResolvedValue({
|
||||
data: mockTestCases,
|
||||
paging: {
|
||||
total: 3,
|
||||
},
|
||||
});
|
||||
|
||||
const onSubmit = jest.fn();
|
||||
|
||||
await act(async () => {
|
||||
renderWithRouter({ ...mockProps, onSubmit });
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('test_case_1')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
const testCaseCard1 = screen
|
||||
.getByTestId('test_case_1')
|
||||
.closest('.cursor-pointer');
|
||||
const testCaseCard2 = screen
|
||||
.getByTestId('test_case_2')
|
||||
.closest('.cursor-pointer');
|
||||
|
||||
expect(testCaseCard1).not.toBeNull();
|
||||
expect(testCaseCard2).not.toBeNull();
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(testCaseCard1 as Element);
|
||||
fireEvent.click(testCaseCard2 as Element);
|
||||
});
|
||||
|
||||
const submitBtn = screen.getByTestId('submit');
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(submitBtn);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(onSubmit).toHaveBeenCalledWith([
|
||||
mockTestCases[0],
|
||||
mockTestCases[1],
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
it('handles async submit operations', async () => {
|
||||
mockGetListTestCaseBySearch.mockResolvedValue({
|
||||
data: mockTestCases,
|
||||
paging: {
|
||||
total: 3,
|
||||
},
|
||||
});
|
||||
|
||||
const onSubmit = jest
|
||||
.fn()
|
||||
.mockImplementation(
|
||||
() => new Promise((resolve) => setTimeout(resolve, 100))
|
||||
);
|
||||
|
||||
await act(async () => {
|
||||
renderWithRouter({ ...mockProps, onSubmit });
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('test_case_1')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
const submitBtn = screen.getByTestId('submit');
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(submitBtn);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
const loader = queryByAttribute('aria-label', submitBtn, 'loading');
|
||||
|
||||
expect(loader).toBeInTheDocument();
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(onSubmit).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Column test cases', () => {
|
||||
it('displays column information for column test cases', async () => {
|
||||
const columnTestCase: TestCase = {
|
||||
id: 'column-test',
|
||||
name: 'column_test',
|
||||
displayName: 'Column Test',
|
||||
entityLink: '<#E::table::sample.table::columns::user_id>',
|
||||
testDefinition: {
|
||||
id: 'test-def',
|
||||
name: 'column_values_to_be_unique',
|
||||
displayName: 'Column Values To Be Unique',
|
||||
},
|
||||
} as TestCase;
|
||||
|
||||
mockGetListTestCaseBySearch.mockResolvedValue({
|
||||
data: [columnTestCase],
|
||||
paging: {
|
||||
total: 1,
|
||||
},
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
renderWithRouter(mockProps);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('column_test')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
expect(screen.getByText('label.column:')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('does not display column information for table test cases', async () => {
|
||||
const tableTestCase: TestCase = {
|
||||
id: 'table-test',
|
||||
name: 'table_test',
|
||||
displayName: 'Table Test',
|
||||
entityLink: '<#E::table::sample.table>',
|
||||
testDefinition: {
|
||||
id: 'test-def',
|
||||
name: 'table_row_count_to_be_between',
|
||||
displayName: 'Table Row Count To Be Between',
|
||||
},
|
||||
} as TestCase;
|
||||
|
||||
mockGetListTestCaseBySearch.mockResolvedValue({
|
||||
data: [tableTestCase],
|
||||
paging: {
|
||||
total: 1,
|
||||
},
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
renderWithRouter(mockProps);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('table_test')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
expect(screen.queryByText('label.column:')).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -70,7 +70,9 @@ export const AddTestCaseList = ({
|
||||
setIsLoading(true);
|
||||
|
||||
const testCaseResponse = await getListTestCaseBySearch({
|
||||
q: filters ? `${searchText} && ${filters}` : searchText,
|
||||
q: filters
|
||||
? `${searchText || WILD_CARD_CHAR} && ${filters}`
|
||||
: searchText,
|
||||
limit: PAGE_SIZE_MEDIUM,
|
||||
offset: (page - 1) * PAGE_SIZE_MEDIUM,
|
||||
...(testCaseParams ?? {}),
|
||||
|
Loading…
x
Reference in New Issue
Block a user