mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-12-13 00:22:23 +00:00
Minor: added support for search param based test case filter (#17030)
* Minor: added support for search param based test case filter * fixed remove filter issue
This commit is contained in:
parent
f8beaa6c30
commit
c2db1bbf0f
@ -1057,6 +1057,7 @@ describe(
|
||||
});
|
||||
cy.get('[value="serviceName"]').click({ waitForAnimations: true });
|
||||
verifyResponseStatusCode('@getTestCase', 200);
|
||||
cy.get('#serviceName').should('not.exist');
|
||||
|
||||
// Test case filter by Tags
|
||||
interceptURL(
|
||||
@ -1073,12 +1074,13 @@ describe(
|
||||
verifyResponseStatusCode('@getTestCaseByTags', 200);
|
||||
verifyFilterTestCase();
|
||||
verifyFilter2TestCase(true);
|
||||
// remove service filter
|
||||
// remove tags filter
|
||||
cy.get('[data-testid="advanced-filter"]').click({
|
||||
waitForAnimations: true,
|
||||
});
|
||||
cy.get('[value="tags"]').click({ waitForAnimations: true });
|
||||
verifyResponseStatusCode('@getTestCase', 200);
|
||||
cy.get('#tags').should('not.exist');
|
||||
|
||||
// Test case filter by Tier
|
||||
interceptURL(
|
||||
@ -1094,12 +1096,13 @@ describe(
|
||||
verifyResponseStatusCode('@getTestCaseByTier', 200);
|
||||
verifyFilterTestCase();
|
||||
verifyFilter2TestCase(true);
|
||||
// remove service filter
|
||||
// remove tier filter
|
||||
cy.get('[data-testid="advanced-filter"]').click({
|
||||
waitForAnimations: true,
|
||||
});
|
||||
cy.get('[value="tier"]').click({ waitForAnimations: true });
|
||||
verifyResponseStatusCode('@getTestCase', 200);
|
||||
cy.get('#tier').should('not.exist');
|
||||
|
||||
// Test case filter by table name
|
||||
interceptURL(
|
||||
@ -1196,6 +1199,25 @@ describe(
|
||||
verifyResponseStatusCode('@testCasePlatformByOpenMetadata', 200);
|
||||
cy.clickOutside();
|
||||
verifyFilterTestCase();
|
||||
cy.url().then((url) => {
|
||||
cy.reload();
|
||||
verifyResponseStatusCode('@testCasePlatformByOpenMetadata', 200);
|
||||
cy.url().then((updatedUrl) => {
|
||||
expect(url).to.be.equal(updatedUrl);
|
||||
});
|
||||
});
|
||||
|
||||
cy.get('[data-testid="advanced-filter"]').click({
|
||||
waitForAnimations: true,
|
||||
});
|
||||
cy.get('[value="testPlatforms"]').click({
|
||||
waitForAnimations: true,
|
||||
});
|
||||
verifyResponseStatusCode('@getTestCase', 200);
|
||||
cy.get('[value="platform-select-filter"]').should('not.exist');
|
||||
cy.reload();
|
||||
verifyResponseStatusCode('@getTestCase', 200);
|
||||
cy.get('[value="tier"]').should('not.exist');
|
||||
});
|
||||
|
||||
it('Filter with domain', () => {
|
||||
|
||||
@ -1,3 +1,8 @@
|
||||
import { DateRangeObject } from 'Models';
|
||||
import { TestCaseStatus } from '../../generated/tests/testCase';
|
||||
import { TestPlatform } from '../../generated/tests/testDefinition';
|
||||
import { TestCaseType } from '../../rest/testAPI';
|
||||
|
||||
/*
|
||||
* Copyright 2023 Collate.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -10,9 +15,21 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
export type DataQualitySearchParams = {
|
||||
export type TestSuiteSearchParams = {
|
||||
searchValue: string;
|
||||
status: string;
|
||||
type: string;
|
||||
owner: string;
|
||||
};
|
||||
|
||||
export type TestCaseSearchParams = {
|
||||
searchValue?: string;
|
||||
tableFqn?: string;
|
||||
testPlatforms?: TestPlatform[];
|
||||
testCaseStatus?: TestCaseStatus;
|
||||
testCaseType?: TestCaseType;
|
||||
lastRunRange?: DateRangeObject;
|
||||
tier?: string;
|
||||
tags?: string;
|
||||
serviceName?: string;
|
||||
};
|
||||
|
||||
@ -30,11 +30,9 @@ import {
|
||||
debounce,
|
||||
entries,
|
||||
isEmpty,
|
||||
isEqual,
|
||||
isUndefined,
|
||||
omit,
|
||||
omitBy,
|
||||
startCase,
|
||||
uniq,
|
||||
} from 'lodash';
|
||||
import QueryString from 'qs';
|
||||
import React, {
|
||||
@ -67,14 +65,10 @@ import { usePaging } from '../../../hooks/paging/usePaging';
|
||||
import { DataQualityPageTabs } from '../../../pages/DataQuality/DataQualityPage.interface';
|
||||
import { searchQuery } from '../../../rest/searchAPI';
|
||||
import { getTags } from '../../../rest/tagAPI';
|
||||
import {
|
||||
getListTestCaseBySearch,
|
||||
ListTestCaseParamsBySearch,
|
||||
} from '../../../rest/testAPI';
|
||||
import { buildTestCaseParams } from '../../../utils/DataQuality/DataQualityUtils';
|
||||
import { getListTestCaseBySearch } from '../../../rest/testAPI';
|
||||
import { getTestCaseFiltersValue } from '../../../utils/DataQuality/DataQualityUtils';
|
||||
import { getEntityName } from '../../../utils/EntityUtils';
|
||||
import { getDataQualityPagePath } from '../../../utils/RouterUtils';
|
||||
import { generateEntityLink } from '../../../utils/TableUtils';
|
||||
import tagClassBase from '../../../utils/TagClassBase';
|
||||
import { showErrorToast } from '../../../utils/ToastUtils';
|
||||
import DatePickerMenu from '../../common/DatePickerMenu/DatePickerMenu.component';
|
||||
@ -82,7 +76,7 @@ import ErrorPlaceHolder from '../../common/ErrorWithPlaceholder/ErrorPlaceHolder
|
||||
import { PagingHandlerParams } from '../../common/NextPrevious/NextPrevious.interface';
|
||||
import Searchbar from '../../common/SearchBarComponent/SearchBar.component';
|
||||
import DataQualityTab from '../../Database/Profiler/DataQualityTab/DataQualityTab';
|
||||
import { DataQualitySearchParams } from '../DataQuality.interface';
|
||||
import { TestCaseSearchParams } from '../DataQuality.interface';
|
||||
|
||||
export const TestCases = ({ summaryPanel }: { summaryPanel: ReactNode }) => {
|
||||
const [form] = useForm();
|
||||
@ -105,13 +99,12 @@ export const TestCases = ({ summaryPanel }: { summaryPanel: ReactNode }) => {
|
||||
search.startsWith('?') ? search.substring(1) : search
|
||||
);
|
||||
|
||||
return params as DataQualitySearchParams;
|
||||
}, [location]);
|
||||
return params as TestCaseSearchParams;
|
||||
}, [location.search]);
|
||||
const { searchValue = '' } = params;
|
||||
|
||||
const [testCase, setTestCase] = useState<TestCase[]>([]);
|
||||
const [isLoading, setIsLoading] = useState<boolean>(true);
|
||||
const [filters, setFilters] = useState<ListTestCaseParamsBySearch>({});
|
||||
const [selectedFilter, setSelectedFilter] = useState<string[]>([
|
||||
TEST_CASE_FILTERS.status,
|
||||
TEST_CASE_FILTERS.type,
|
||||
@ -127,12 +120,12 @@ export const TestCases = ({ summaryPanel }: { summaryPanel: ReactNode }) => {
|
||||
showPagination,
|
||||
} = usePaging(PAGE_SIZE);
|
||||
|
||||
const handleSearchParam = (
|
||||
value: string | boolean,
|
||||
key: keyof DataQualitySearchParams
|
||||
const handleSearchParam = <K extends keyof TestCaseSearchParams>(
|
||||
key: K,
|
||||
value?: TestCaseSearchParams[K]
|
||||
) => {
|
||||
history.push({
|
||||
search: QueryString.stringify({ ...params, [key]: value }),
|
||||
search: QueryString.stringify({ ...params, [key]: value || undefined }),
|
||||
});
|
||||
};
|
||||
|
||||
@ -150,12 +143,17 @@ export const TestCases = ({ summaryPanel }: { summaryPanel: ReactNode }) => {
|
||||
|
||||
const fetchTestCases = async (
|
||||
currentPage = INITIAL_PAGING_VALUE,
|
||||
params?: ListTestCaseParamsBySearch
|
||||
filters?: string[]
|
||||
) => {
|
||||
const updatedParams = getTestCaseFiltersValue(
|
||||
params,
|
||||
filters ?? selectedFilter
|
||||
);
|
||||
|
||||
setIsLoading(true);
|
||||
try {
|
||||
const { data, paging } = await getListTestCaseBySearch({
|
||||
...params,
|
||||
...updatedParams,
|
||||
testCaseStatus: isEmpty(params?.testCaseStatus)
|
||||
? undefined
|
||||
: params?.testCaseStatus,
|
||||
@ -192,31 +190,16 @@ export const TestCases = ({ summaryPanel }: { summaryPanel: ReactNode }) => {
|
||||
};
|
||||
|
||||
const handlePagingClick = ({ currentPage }: PagingHandlerParams) => {
|
||||
fetchTestCases(currentPage, filters);
|
||||
fetchTestCases(currentPage);
|
||||
};
|
||||
|
||||
const handleFilterChange: FormProps['onValuesChange'] = (_, values) => {
|
||||
const { lastRunRange, tableFqn } = values;
|
||||
const startTimestamp = lastRunRange?.startTs;
|
||||
const endTimestamp = lastRunRange?.endTs;
|
||||
const entityLink = tableFqn ? generateEntityLink(tableFqn) : undefined;
|
||||
const params = {
|
||||
...omit(values, ['lastRunRange', 'tableFqn']),
|
||||
startTimestamp,
|
||||
endTimestamp,
|
||||
entityLink,
|
||||
const handleFilterChange: FormProps<TestCaseSearchParams>['onValuesChange'] =
|
||||
(value?: TestCaseSearchParams) => {
|
||||
if (!isUndefined(value)) {
|
||||
const [data] = Object.entries(value);
|
||||
handleSearchParam(data[0] as keyof TestCaseSearchParams, data[1]);
|
||||
}
|
||||
};
|
||||
const updatedParams = omitBy(
|
||||
buildTestCaseParams(params, selectedFilter),
|
||||
isUndefined
|
||||
);
|
||||
|
||||
if (!isEqual(filters, updatedParams)) {
|
||||
fetchTestCases(INITIAL_PAGING_VALUE, updatedParams);
|
||||
}
|
||||
|
||||
setFilters(updatedParams);
|
||||
};
|
||||
|
||||
const fetchTierOptions = async () => {
|
||||
try {
|
||||
@ -360,33 +343,46 @@ export const TestCases = ({ summaryPanel }: { summaryPanel: ReactNode }) => {
|
||||
}
|
||||
};
|
||||
|
||||
const getInitialOptions = (key: string, isLengthCheck = false) => {
|
||||
switch (key) {
|
||||
case TEST_CASE_FILTERS.tier:
|
||||
(isEmpty(tierOptions) || !isLengthCheck) && fetchTierOptions();
|
||||
|
||||
break;
|
||||
case TEST_CASE_FILTERS.table:
|
||||
(isEmpty(tableOptions) || !isLengthCheck) && fetchTableData();
|
||||
|
||||
break;
|
||||
case TEST_CASE_FILTERS.tags:
|
||||
(isEmpty(tagOptions) || !isLengthCheck) && fetchTagOptions();
|
||||
|
||||
break;
|
||||
case TEST_CASE_FILTERS.service:
|
||||
(isEmpty(serviceOptions) || !isLengthCheck) && fetchServiceOptions();
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
const handleMenuClick = ({ key }: { key: string }) => {
|
||||
setSelectedFilter((prevSelected) => {
|
||||
if (prevSelected.includes(key)) {
|
||||
const updatedValue = prevSelected.filter(
|
||||
(selected) => selected !== key
|
||||
);
|
||||
const updatedFilters = omitBy(
|
||||
buildTestCaseParams(filters, updatedValue),
|
||||
isUndefined
|
||||
);
|
||||
form.setFieldsValue({ [key]: undefined });
|
||||
if (!isEqual(filters, updatedFilters)) {
|
||||
fetchTestCases(INITIAL_PAGING_VALUE, updatedFilters);
|
||||
}
|
||||
setFilters(updatedFilters);
|
||||
|
||||
return updatedValue;
|
||||
}
|
||||
|
||||
return [...prevSelected, key];
|
||||
return uniq([...prevSelected, key]);
|
||||
});
|
||||
|
||||
// Fetch options based on the selected filter
|
||||
key === TEST_CASE_FILTERS.tier && fetchTierOptions();
|
||||
key === TEST_CASE_FILTERS.tags && fetchTagOptions();
|
||||
key === TEST_CASE_FILTERS.table && fetchTableData();
|
||||
key === TEST_CASE_FILTERS.service && fetchServiceOptions();
|
||||
getInitialOptions(key);
|
||||
handleSearchParam(key as keyof TestCaseSearchParams, undefined);
|
||||
};
|
||||
|
||||
const filterMenu: ItemType[] = useMemo(() => {
|
||||
@ -396,7 +392,7 @@ export const TestCases = ({ summaryPanel }: { summaryPanel: ReactNode }) => {
|
||||
value: filter,
|
||||
onClick: handleMenuClick,
|
||||
}));
|
||||
}, [filters]);
|
||||
}, []);
|
||||
|
||||
const debounceFetchTableData = useCallback(debounce(fetchTableData, 1000), [
|
||||
fetchTableData,
|
||||
@ -411,15 +407,30 @@ export const TestCases = ({ summaryPanel }: { summaryPanel: ReactNode }) => {
|
||||
[fetchServiceOptions]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (testCasePermission?.ViewAll || testCasePermission?.ViewBasic) {
|
||||
if (tab === DataQualityPageTabs.TEST_CASES) {
|
||||
fetchTestCases(INITIAL_PAGING_VALUE, filters);
|
||||
const getTestCases = () => {
|
||||
if (!isEmpty(params)) {
|
||||
const updatedValue = uniq([...selectedFilter, ...Object.keys(params)]);
|
||||
for (const key of updatedValue) {
|
||||
getInitialOptions(key, true);
|
||||
}
|
||||
setSelectedFilter(updatedValue);
|
||||
fetchTestCases(INITIAL_PAGING_VALUE, updatedValue);
|
||||
form.setFieldsValue(params);
|
||||
} else {
|
||||
fetchTestCases(INITIAL_PAGING_VALUE);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
(testCasePermission?.ViewAll || testCasePermission?.ViewBasic) &&
|
||||
tab === DataQualityPageTabs.TEST_CASES
|
||||
) {
|
||||
getTestCases();
|
||||
} else {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, [tab, searchValue, testCasePermission, pageSize]);
|
||||
}, [tab, testCasePermission, pageSize, params]);
|
||||
|
||||
const pagingData = useMemo(
|
||||
() => ({
|
||||
@ -443,9 +454,8 @@ export const TestCases = ({ summaryPanel }: { summaryPanel: ReactNode }) => {
|
||||
data-testid="test-case-container"
|
||||
gutter={[16, 16]}>
|
||||
<Col span={24}>
|
||||
<Form
|
||||
<Form<TestCaseSearchParams>
|
||||
form={form}
|
||||
initialValues={filters}
|
||||
layout="horizontal"
|
||||
onValuesChange={handleFilterChange}>
|
||||
<Space wrap align="center" className="w-full" size={16}>
|
||||
@ -456,7 +466,7 @@ export const TestCases = ({ summaryPanel }: { summaryPanel: ReactNode }) => {
|
||||
entity: t('label.test-case-lowercase'),
|
||||
})}
|
||||
searchValue={searchValue}
|
||||
onSearch={(value) => handleSearchParam(value, 'searchValue')}
|
||||
onSearch={(value) => handleSearchParam('searchValue', value)}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item noStyle name="selectedFilters">
|
||||
|
||||
@ -58,7 +58,7 @@ import Table from '../../../common/Table/Table';
|
||||
import { UserTeamSelectableList } from '../../../common/UserTeamSelectableList/UserTeamSelectableList.component';
|
||||
import { TableProfilerTab } from '../../../Database/Profiler/ProfilerDashboard/profilerDashboard.interface';
|
||||
import ProfilerProgressWidget from '../../../Database/Profiler/TableProfiler/ProfilerProgressWidget/ProfilerProgressWidget';
|
||||
import { DataQualitySearchParams } from '../../DataQuality.interface';
|
||||
import { TestSuiteSearchParams } from '../../DataQuality.interface';
|
||||
|
||||
export const TestSuites = ({ summaryPanel }: { summaryPanel: ReactNode }) => {
|
||||
const { t } = useTranslation();
|
||||
@ -74,7 +74,7 @@ export const TestSuites = ({ summaryPanel }: { summaryPanel: ReactNode }) => {
|
||||
search.startsWith('?') ? search.substring(1) : search
|
||||
);
|
||||
|
||||
return params as DataQualitySearchParams;
|
||||
return params as TestSuiteSearchParams;
|
||||
}, [location]);
|
||||
const { searchValue, owner } = params;
|
||||
const selectedOwner = useMemo(
|
||||
@ -228,7 +228,7 @@ export const TestSuites = ({ summaryPanel }: { summaryPanel: ReactNode }) => {
|
||||
|
||||
const handleSearchParam = (
|
||||
value: string,
|
||||
key: keyof DataQualitySearchParams
|
||||
key: keyof TestSuiteSearchParams
|
||||
) => {
|
||||
history.push({
|
||||
search: QueryString.stringify({
|
||||
|
||||
@ -14,6 +14,7 @@
|
||||
import { t } from 'i18next';
|
||||
import { capitalize, map, startCase, values } from 'lodash';
|
||||
import { DateFilterType, StepperStepType } from 'Models';
|
||||
import { TestCaseSearchParams } from '../components/DataQuality/DataQuality.interface';
|
||||
import { CSMode } from '../enums/codemirror.enum';
|
||||
import { DMLOperationType } from '../generated/api/data/createTableProfile';
|
||||
import {
|
||||
@ -417,7 +418,7 @@ export const TEST_CASE_STATUS_OPTION = [
|
||||
})),
|
||||
];
|
||||
|
||||
export const TEST_CASE_FILTERS = {
|
||||
export const TEST_CASE_FILTERS: Record<string, keyof TestCaseSearchParams> = {
|
||||
table: 'tableFqn',
|
||||
platform: 'testPlatforms',
|
||||
type: 'testCaseType',
|
||||
|
||||
@ -10,6 +10,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { TestCaseSearchParams } from '../../components/DataQuality/DataQuality.interface';
|
||||
import {
|
||||
TestDataType,
|
||||
TestDefinition,
|
||||
@ -18,162 +19,279 @@ import { ListTestCaseParamsBySearch } from '../../rest/testAPI';
|
||||
import {
|
||||
buildTestCaseParams,
|
||||
createTestCaseParameters,
|
||||
getTestCaseFiltersValue,
|
||||
} from './DataQualityUtils';
|
||||
|
||||
jest.mock('../../constants/profiler.constant', () => ({
|
||||
TEST_CASE_FILTERS: {
|
||||
lastRun: 'lastRun',
|
||||
table: 'table',
|
||||
platform: 'platform',
|
||||
type: 'type',
|
||||
status: 'status',
|
||||
table: 'tableFqn',
|
||||
platform: 'testPlatforms',
|
||||
type: 'testCaseType',
|
||||
status: 'testCaseStatus',
|
||||
lastRun: 'lastRunRange',
|
||||
tier: 'tier',
|
||||
tags: 'tags',
|
||||
service: 'serviceName',
|
||||
},
|
||||
}));
|
||||
|
||||
describe('buildTestCaseParams', () => {
|
||||
it('should return an empty object if params is undefined', () => {
|
||||
const params = undefined;
|
||||
const filters = ['lastRun', 'table'];
|
||||
jest.mock('../TableUtils', () => ({
|
||||
generateEntityLink: jest.fn().mockImplementation((fqn: string) => {
|
||||
return `<#E::table::${fqn}>`;
|
||||
}),
|
||||
}));
|
||||
|
||||
const result = buildTestCaseParams(params, filters);
|
||||
describe('DataQualityUtils', () => {
|
||||
describe('buildTestCaseParams', () => {
|
||||
it('should return an empty object if params is undefined', () => {
|
||||
const params = undefined;
|
||||
const filters = ['lastRun', 'table'];
|
||||
|
||||
expect(result).toEqual({});
|
||||
const result = buildTestCaseParams(params, filters);
|
||||
|
||||
expect(result).toEqual({});
|
||||
});
|
||||
|
||||
it('should return the updated test case parameters with the applied filters', () => {
|
||||
const params = {
|
||||
endTimestamp: 1234567890,
|
||||
startTimestamp: 1234567890,
|
||||
entityLink: 'table1',
|
||||
testPlatforms: ['DBT'],
|
||||
} as ListTestCaseParamsBySearch;
|
||||
const filters = ['lastRunRange', 'tableFqn'];
|
||||
|
||||
const result = buildTestCaseParams(params, filters);
|
||||
|
||||
expect(result).toEqual({
|
||||
endTimestamp: 1234567890,
|
||||
startTimestamp: 1234567890,
|
||||
entityLink: 'table1',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should return the updated test case parameters with the applied filters', () => {
|
||||
const params = {
|
||||
endTimestamp: 1234567890,
|
||||
startTimestamp: 1234567890,
|
||||
entityLink: 'table1',
|
||||
testPlatforms: ['DBT'],
|
||||
} as ListTestCaseParamsBySearch;
|
||||
const filters = ['lastRun', 'table'];
|
||||
describe('createTestCaseParameters', () => {
|
||||
const mockDefinition = {
|
||||
parameterDefinition: [
|
||||
{ name: 'arrayParam', dataType: TestDataType.Array },
|
||||
{ name: 'stringParam', dataType: TestDataType.String },
|
||||
],
|
||||
} as TestDefinition;
|
||||
|
||||
const result = buildTestCaseParams(params, filters);
|
||||
it('should return an empty array for empty parameters', () => {
|
||||
const result = createTestCaseParameters({}, mockDefinition);
|
||||
|
||||
expect(result).toEqual({
|
||||
endTimestamp: 1234567890,
|
||||
startTimestamp: 1234567890,
|
||||
entityLink: 'table1',
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
|
||||
it('should handle parameters not matching any definition', () => {
|
||||
const params = { unrelatedParam: 'value' };
|
||||
const result = createTestCaseParameters(params, mockDefinition);
|
||||
|
||||
expect(result).toEqual([{ name: 'unrelatedParam', value: 'value' }]);
|
||||
});
|
||||
|
||||
it('should handle a mix of string and array parameters', () => {
|
||||
const params = {
|
||||
stringParam: 'stringValue',
|
||||
arrayParam: [{ value: 'arrayValue1' }, { value: 'arrayValue2' }],
|
||||
};
|
||||
const expected = [
|
||||
{ name: 'stringParam', value: 'stringValue' },
|
||||
{
|
||||
name: 'arrayParam',
|
||||
value: JSON.stringify(['arrayValue1', 'arrayValue2']),
|
||||
},
|
||||
];
|
||||
const result = createTestCaseParameters(params, mockDefinition);
|
||||
|
||||
expect(result).toEqual(expected);
|
||||
});
|
||||
|
||||
it('should ignore array items without a value', () => {
|
||||
const params = {
|
||||
arrayParam: [{ value: '' }, { value: 'validValue' }],
|
||||
};
|
||||
const expected = [
|
||||
{ name: 'arrayParam', value: JSON.stringify(['validValue']) },
|
||||
];
|
||||
const result = createTestCaseParameters(params, mockDefinition);
|
||||
|
||||
expect(result).toEqual(expected);
|
||||
});
|
||||
|
||||
it('should return undefined if params not present', () => {
|
||||
const result = createTestCaseParameters(undefined, undefined);
|
||||
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should return params value for sqlExpression test defination type', () => {
|
||||
const data = {
|
||||
params: {
|
||||
sqlExpression: 'select * from dim_address',
|
||||
strategy: 'COUNT',
|
||||
threshold: '12',
|
||||
},
|
||||
selectedDefinition: {
|
||||
parameterDefinition: [
|
||||
{
|
||||
name: 'sqlExpression',
|
||||
displayName: 'SQL Expression',
|
||||
dataType: 'STRING',
|
||||
description: 'SQL expression to run against the table',
|
||||
required: true,
|
||||
optionValues: [],
|
||||
},
|
||||
{
|
||||
name: 'strategy',
|
||||
displayName: 'Strategy',
|
||||
dataType: 'ARRAY',
|
||||
description:
|
||||
'Strategy to use to run the custom SQL query (i.e. `SELECT COUNT(<col>)` or `SELECT <col> (defaults to ROWS)',
|
||||
required: false,
|
||||
optionValues: ['ROWS', 'COUNT'],
|
||||
},
|
||||
{
|
||||
name: 'threshold',
|
||||
displayName: 'Threshold',
|
||||
dataType: 'NUMBER',
|
||||
description:
|
||||
'Threshold to use to determine if the test passes or fails (defaults to 0).',
|
||||
required: false,
|
||||
optionValues: [],
|
||||
},
|
||||
],
|
||||
} as TestDefinition,
|
||||
};
|
||||
|
||||
const expected = [
|
||||
{
|
||||
name: 'sqlExpression',
|
||||
value: 'select * from dim_address',
|
||||
},
|
||||
{
|
||||
name: 'strategy',
|
||||
value: 'COUNT',
|
||||
},
|
||||
{
|
||||
name: 'threshold',
|
||||
value: '12',
|
||||
},
|
||||
];
|
||||
|
||||
const result = createTestCaseParameters(
|
||||
data.params,
|
||||
data.selectedDefinition
|
||||
);
|
||||
|
||||
expect(result).toEqual(expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getTestCaseFiltersValue', () => {
|
||||
const testCaseSearchParams = {
|
||||
testCaseType: 'column',
|
||||
testCaseStatus: 'Success',
|
||||
searchValue: 'between',
|
||||
testPlatforms: ['DBT', 'Deequ'],
|
||||
lastRunRange: {
|
||||
startTs: '1720417883445',
|
||||
endTs: '1721022683445',
|
||||
key: 'last7days',
|
||||
title: 'Last 7 days',
|
||||
},
|
||||
tags: ['PII.None'],
|
||||
tier: 'Tier.Tier1',
|
||||
serviceName: 'sample_data',
|
||||
tableFqn: 'sample_data.ecommerce_db.shopify.fact_sale',
|
||||
} as unknown as TestCaseSearchParams;
|
||||
|
||||
it('should correctly process values and selectedFilter', () => {
|
||||
const selectedFilter = [
|
||||
'testCaseStatus',
|
||||
'testCaseType',
|
||||
'searchValue',
|
||||
'testPlatforms',
|
||||
'lastRunRange',
|
||||
'tags',
|
||||
'tier',
|
||||
'serviceName',
|
||||
'tableFqn',
|
||||
];
|
||||
|
||||
const expected = {
|
||||
endTimestamp: '1721022683445',
|
||||
startTimestamp: '1720417883445',
|
||||
entityLink: '<#E::table::sample_data.ecommerce_db.shopify.fact_sale>',
|
||||
testPlatforms: ['DBT', 'Deequ'],
|
||||
testCaseType: 'column',
|
||||
testCaseStatus: 'Success',
|
||||
tags: ['PII.None'],
|
||||
tier: 'Tier.Tier1',
|
||||
serviceName: 'sample_data',
|
||||
};
|
||||
|
||||
const result = getTestCaseFiltersValue(
|
||||
testCaseSearchParams,
|
||||
selectedFilter
|
||||
);
|
||||
|
||||
expect(result).toEqual(expected);
|
||||
});
|
||||
|
||||
it('should only create params based on selected filters', () => {
|
||||
const selectedFilter = ['testPlatforms', 'lastRunRange'];
|
||||
|
||||
const expected = {
|
||||
testPlatforms: ['DBT', 'Deequ'],
|
||||
endTimestamp: '1721022683445',
|
||||
startTimestamp: '1720417883445',
|
||||
};
|
||||
|
||||
const result = getTestCaseFiltersValue(
|
||||
testCaseSearchParams,
|
||||
selectedFilter
|
||||
);
|
||||
|
||||
expect(result).toEqual(expected);
|
||||
});
|
||||
|
||||
it('should only create params based on selected filters and available data', () => {
|
||||
const testCaseSearchParams = {
|
||||
testCaseType: 'column',
|
||||
testCaseStatus: 'Success',
|
||||
searchValue: 'between',
|
||||
testPlatforms: ['DBT', 'Deequ'],
|
||||
tags: ['PII.None'],
|
||||
tier: 'Tier.Tier1',
|
||||
} as unknown as TestCaseSearchParams;
|
||||
const selectedFilter = ['testPlatforms', 'tags', 'testCaseStatus'];
|
||||
|
||||
const expected = {
|
||||
testPlatforms: ['DBT', 'Deequ'],
|
||||
tags: ['PII.None'],
|
||||
testCaseStatus: 'Success',
|
||||
};
|
||||
|
||||
const result = getTestCaseFiltersValue(
|
||||
testCaseSearchParams,
|
||||
selectedFilter
|
||||
);
|
||||
|
||||
expect(result).toEqual(expected);
|
||||
});
|
||||
|
||||
it('should not send params if selected filter is empty', () => {
|
||||
const selectedFilter: string[] = [];
|
||||
|
||||
const result = getTestCaseFiltersValue(
|
||||
testCaseSearchParams,
|
||||
selectedFilter
|
||||
);
|
||||
|
||||
expect(result).toEqual({});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('createTestCaseParameters', () => {
|
||||
const mockDefinition = {
|
||||
parameterDefinition: [
|
||||
{ name: 'arrayParam', dataType: TestDataType.Array },
|
||||
{ name: 'stringParam', dataType: TestDataType.String },
|
||||
],
|
||||
} as TestDefinition;
|
||||
|
||||
it('should return an empty array for empty parameters', () => {
|
||||
const result = createTestCaseParameters({}, mockDefinition);
|
||||
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
|
||||
it('should handle parameters not matching any definition', () => {
|
||||
const params = { unrelatedParam: 'value' };
|
||||
const result = createTestCaseParameters(params, mockDefinition);
|
||||
|
||||
expect(result).toEqual([{ name: 'unrelatedParam', value: 'value' }]);
|
||||
});
|
||||
|
||||
it('should handle a mix of string and array parameters', () => {
|
||||
const params = {
|
||||
stringParam: 'stringValue',
|
||||
arrayParam: [{ value: 'arrayValue1' }, { value: 'arrayValue2' }],
|
||||
};
|
||||
const expected = [
|
||||
{ name: 'stringParam', value: 'stringValue' },
|
||||
{
|
||||
name: 'arrayParam',
|
||||
value: JSON.stringify(['arrayValue1', 'arrayValue2']),
|
||||
},
|
||||
];
|
||||
const result = createTestCaseParameters(params, mockDefinition);
|
||||
|
||||
expect(result).toEqual(expected);
|
||||
});
|
||||
|
||||
it('should ignore array items without a value', () => {
|
||||
const params = {
|
||||
arrayParam: [{ value: '' }, { value: 'validValue' }],
|
||||
};
|
||||
const expected = [
|
||||
{ name: 'arrayParam', value: JSON.stringify(['validValue']) },
|
||||
];
|
||||
const result = createTestCaseParameters(params, mockDefinition);
|
||||
|
||||
expect(result).toEqual(expected);
|
||||
});
|
||||
|
||||
it('should return undefined if params not present', () => {
|
||||
const result = createTestCaseParameters(undefined, undefined);
|
||||
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should return params value for sqlExpression test defination type', () => {
|
||||
const data = {
|
||||
params: {
|
||||
sqlExpression: 'select * from dim_address',
|
||||
strategy: 'COUNT',
|
||||
threshold: '12',
|
||||
},
|
||||
selectedDefinition: {
|
||||
parameterDefinition: [
|
||||
{
|
||||
name: 'sqlExpression',
|
||||
displayName: 'SQL Expression',
|
||||
dataType: 'STRING',
|
||||
description: 'SQL expression to run against the table',
|
||||
required: true,
|
||||
optionValues: [],
|
||||
},
|
||||
{
|
||||
name: 'strategy',
|
||||
displayName: 'Strategy',
|
||||
dataType: 'ARRAY',
|
||||
description:
|
||||
'Strategy to use to run the custom SQL query (i.e. `SELECT COUNT(<col>)` or `SELECT <col> (defaults to ROWS)',
|
||||
required: false,
|
||||
optionValues: ['ROWS', 'COUNT'],
|
||||
},
|
||||
{
|
||||
name: 'threshold',
|
||||
displayName: 'Threshold',
|
||||
dataType: 'NUMBER',
|
||||
description:
|
||||
'Threshold to use to determine if the test passes or fails (defaults to 0).',
|
||||
required: false,
|
||||
optionValues: [],
|
||||
},
|
||||
],
|
||||
} as TestDefinition,
|
||||
};
|
||||
|
||||
const expected = [
|
||||
{
|
||||
name: 'sqlExpression',
|
||||
value: 'select * from dim_address',
|
||||
},
|
||||
{
|
||||
name: 'strategy',
|
||||
value: 'COUNT',
|
||||
},
|
||||
{
|
||||
name: 'threshold',
|
||||
value: '12',
|
||||
},
|
||||
];
|
||||
|
||||
const result = createTestCaseParameters(
|
||||
data.params,
|
||||
data.selectedDefinition
|
||||
);
|
||||
|
||||
expect(result).toEqual(expected);
|
||||
});
|
||||
});
|
||||
|
||||
@ -10,7 +10,8 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { isArray } from 'lodash';
|
||||
import { isArray, isUndefined, omit, omitBy } from 'lodash';
|
||||
import { TestCaseSearchParams } from '../../components/DataQuality/DataQuality.interface';
|
||||
import { TEST_CASE_FILTERS } from '../../constants/profiler.constant';
|
||||
import { TestCaseParameterValue } from '../../generated/tests/testCase';
|
||||
import {
|
||||
@ -18,6 +19,7 @@ import {
|
||||
TestDefinition,
|
||||
} from '../../generated/tests/testDefinition';
|
||||
import { ListTestCaseParamsBySearch } from '../../rest/testAPI';
|
||||
import { generateEntityLink } from '../TableUtils';
|
||||
|
||||
/**
|
||||
* Builds the parameters for a test case search based on the given filters.
|
||||
@ -74,3 +76,27 @@ export const createTestCaseParameters = (
|
||||
}, [] as TestCaseParameterValue[])
|
||||
: params;
|
||||
};
|
||||
|
||||
export const getTestCaseFiltersValue = (
|
||||
values: TestCaseSearchParams,
|
||||
selectedFilter: string[]
|
||||
) => {
|
||||
const { lastRunRange, tableFqn } = values;
|
||||
const startTimestamp = lastRunRange?.startTs;
|
||||
const endTimestamp = lastRunRange?.endTs;
|
||||
const entityLink = tableFqn ? generateEntityLink(tableFqn) : undefined;
|
||||
|
||||
const apiParams = {
|
||||
...omit(values, ['lastRunRange', 'tableFqn', 'searchValue']),
|
||||
startTimestamp,
|
||||
endTimestamp,
|
||||
entityLink,
|
||||
};
|
||||
|
||||
const updatedParams = omitBy(
|
||||
buildTestCaseParams(apiParams, selectedFilter),
|
||||
isUndefined
|
||||
);
|
||||
|
||||
return updatedParams;
|
||||
};
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user