mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-08-31 12:39:01 +00:00
Migrate advanced search tests (#17666)
* migrate advanced search tests * verify advanced search tests * fix minor config * push fixes * add before all timeout * increase test timeout * revert playwright config * add rule based search * migrate group tests (cherry picked from commit a63ad691000c5d49b251602f96a519d846a178da)
This commit is contained in:
parent
6fa662bfda
commit
32a85c0c96
@ -1,256 +0,0 @@
|
||||
/*
|
||||
* Copyright 2023 Collate.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {
|
||||
searchAndClickOnOption,
|
||||
selectNullOption,
|
||||
} from '../../common/advancedSearchQuickFilters';
|
||||
import { interceptURL, verifyResponseStatusCode } from '../../common/common';
|
||||
import { goToAdvanceSearch } from '../../common/Utils/AdvancedSearch';
|
||||
import { addDomainToEntity } from '../../common/Utils/Domain';
|
||||
import {
|
||||
createEntityViaREST,
|
||||
deleteEntityViaREST,
|
||||
visitEntityDetailsPage,
|
||||
} from '../../common/Utils/Entity';
|
||||
import { getToken } from '../../common/Utils/LocalStorage';
|
||||
import { addOwner, removeOwner } from '../../common/Utils/Owner';
|
||||
import { assignTags, removeTags } from '../../common/Utils/Tags';
|
||||
import { addTier, removeTier } from '../../common/Utils/Tier';
|
||||
import {
|
||||
FilterItem,
|
||||
QUICK_FILTERS_BY_ASSETS,
|
||||
SUPPORTED_EMPTY_FILTER_FIELDS,
|
||||
} from '../../constants/advancedSearchQuickFilters.constants';
|
||||
import { SEARCH_ENTITY_TABLE } from '../../constants/constants';
|
||||
import { EntityType, SidebarItem } from '../../constants/Entity.interface';
|
||||
import { DOMAIN_QUICK_FILTERS_DETAILS } from '../../constants/EntityConstant';
|
||||
const ownerName = 'Aaron Johnson';
|
||||
|
||||
const preRequisitesForTests = () => {
|
||||
cy.getAllLocalStorage().then((data) => {
|
||||
const token = getToken(data);
|
||||
|
||||
createEntityViaREST({
|
||||
body: DOMAIN_QUICK_FILTERS_DETAILS,
|
||||
endPoint: EntityType.Domain,
|
||||
token,
|
||||
});
|
||||
|
||||
visitEntityDetailsPage({
|
||||
term: SEARCH_ENTITY_TABLE.table_1.term,
|
||||
entity: SEARCH_ENTITY_TABLE.table_1.entity,
|
||||
serviceName: SEARCH_ENTITY_TABLE.table_1.serviceName,
|
||||
});
|
||||
addDomainToEntity(DOMAIN_QUICK_FILTERS_DETAILS.displayName);
|
||||
addOwner(ownerName);
|
||||
assignTags('PersonalData.Personal', EntityType.Table);
|
||||
addTier('Tier1');
|
||||
});
|
||||
};
|
||||
|
||||
const postRequisitesForTests = () => {
|
||||
cy.getAllLocalStorage().then((data) => {
|
||||
const token = getToken(data);
|
||||
// Domain 1 to test
|
||||
deleteEntityViaREST({
|
||||
entityName: DOMAIN_QUICK_FILTERS_DETAILS.name,
|
||||
endPoint: EntityType.Domain,
|
||||
token,
|
||||
});
|
||||
visitEntityDetailsPage({
|
||||
term: SEARCH_ENTITY_TABLE.table_1.term,
|
||||
entity: SEARCH_ENTITY_TABLE.table_1.entity,
|
||||
serviceName: SEARCH_ENTITY_TABLE.table_1.serviceName,
|
||||
});
|
||||
removeOwner(ownerName);
|
||||
removeTags('PersonalData.Personal', EntityType.Table);
|
||||
removeTier();
|
||||
});
|
||||
};
|
||||
|
||||
// migrated to playwright
|
||||
describe.skip(
|
||||
`Advanced search quick filters should work properly for assets`,
|
||||
{ tags: 'DataAssets' },
|
||||
() => {
|
||||
before(() => {
|
||||
cy.login();
|
||||
preRequisitesForTests();
|
||||
});
|
||||
|
||||
after(() => {
|
||||
cy.login();
|
||||
postRequisitesForTests();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
cy.login();
|
||||
});
|
||||
|
||||
it(`should show the quick filters for respective assets`, () => {
|
||||
// Navigate to explore page
|
||||
cy.sidebarClick(SidebarItem.EXPLORE);
|
||||
QUICK_FILTERS_BY_ASSETS.map((asset) => {
|
||||
cy.get(`[data-testid="${asset.tab}"]`).scrollIntoView().click();
|
||||
|
||||
asset.filters.map((filter) => {
|
||||
cy.get(`[data-testid="search-dropdown-${filter.label}"]`)
|
||||
.scrollIntoView()
|
||||
.should('be.visible');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('search dropdown should work properly for tables', () => {
|
||||
// Table
|
||||
const asset = QUICK_FILTERS_BY_ASSETS[0];
|
||||
|
||||
// Navigate to explore page
|
||||
cy.sidebarClick(SidebarItem.EXPLORE);
|
||||
cy.get(`[data-testid="${asset.tab}"]`).scrollIntoView().click();
|
||||
|
||||
asset.filters
|
||||
.filter((item: FilterItem) => item.select)
|
||||
.map((filter: FilterItem) => {
|
||||
cy.get(`[data-testid="search-dropdown-${filter.label}"]`).click();
|
||||
searchAndClickOnOption(asset, filter, true);
|
||||
|
||||
const querySearchURL = `/api/v1/search/query?*index=${
|
||||
asset.searchIndex
|
||||
}*query_filter=*should*${filter.key}*${encodeURI(
|
||||
Cypress._.toLower(filter.selectOption1).replace(' ', '+')
|
||||
)}*`;
|
||||
|
||||
interceptURL('GET', querySearchURL, 'querySearchAPI');
|
||||
|
||||
cy.get('[data-testid="update-btn"]').click();
|
||||
|
||||
verifyResponseStatusCode('@querySearchAPI', 200);
|
||||
});
|
||||
});
|
||||
|
||||
it('should search for empty or null filters', () => {
|
||||
const initialQuery = encodeURI(JSON.stringify({ query: { bool: {} } }));
|
||||
// Table
|
||||
interceptURL(
|
||||
'GET',
|
||||
`/api/v1/search/query?*index=table_search_index&*query_filter=${initialQuery}&*`,
|
||||
'initialQueryAPI'
|
||||
);
|
||||
|
||||
const asset = QUICK_FILTERS_BY_ASSETS[0];
|
||||
cy.sidebarClick(SidebarItem.EXPLORE);
|
||||
verifyResponseStatusCode('@initialQueryAPI', 200);
|
||||
cy.get(`[data-testid="${asset.tab}"]`).scrollIntoView().click();
|
||||
asset.filters
|
||||
.filter((item) => SUPPORTED_EMPTY_FILTER_FIELDS.includes(item.key))
|
||||
.map((filter) => {
|
||||
selectNullOption(asset, filter);
|
||||
});
|
||||
});
|
||||
|
||||
it('should search for multiple values alongwith null filters', () => {
|
||||
const initialQuery = encodeURI(JSON.stringify({ query: { bool: {} } }));
|
||||
// Table
|
||||
interceptURL(
|
||||
'GET',
|
||||
`/api/v1/search/query?*index=table_search_index&*query_filter=${initialQuery}&*`,
|
||||
'initialQueryAPI'
|
||||
);
|
||||
|
||||
const asset = QUICK_FILTERS_BY_ASSETS[0];
|
||||
cy.sidebarClick(SidebarItem.EXPLORE);
|
||||
verifyResponseStatusCode('@initialQueryAPI', 200);
|
||||
cy.get(`[data-testid="${asset.tab}"]`).scrollIntoView().click();
|
||||
// Checking Owner with multiple values
|
||||
asset.filters
|
||||
.filter((item) => SUPPORTED_EMPTY_FILTER_FIELDS.includes(item.key))
|
||||
.map((filter: FilterItem) => {
|
||||
selectNullOption(asset, filter, filter?.selectOptionTestId1);
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
const testIsNullAndIsNotNullFilters = (operatorTitle, queryFilter, alias) => {
|
||||
goToAdvanceSearch();
|
||||
|
||||
// Check Is Null or Is Not Null
|
||||
cy.get('.rule--operator > .ant-select > .ant-select-selector').eq(0).click();
|
||||
cy.get(`[title="${operatorTitle}"]`).click();
|
||||
|
||||
cy.intercept('GET', '/api/v1/search/query?*', (req) => {
|
||||
req.alias = alias;
|
||||
}).as(alias);
|
||||
|
||||
cy.get('[data-testid="apply-btn"]').click();
|
||||
|
||||
cy.wait(`@${alias}`).then((xhr) => {
|
||||
const actualQueryFilter = JSON.parse(xhr.request.query['query_filter']);
|
||||
|
||||
expect(actualQueryFilter).to.deep.equal(queryFilter);
|
||||
});
|
||||
};
|
||||
|
||||
describe(`Advanced Search Modal`, () => {
|
||||
beforeEach(() => {
|
||||
cy.login();
|
||||
});
|
||||
|
||||
it('should check isNull and isNotNull filters', () => {
|
||||
// Check Is Null
|
||||
const isNullQuery = {
|
||||
query: {
|
||||
bool: {
|
||||
must: [
|
||||
{
|
||||
bool: {
|
||||
must: [
|
||||
{
|
||||
bool: {
|
||||
must_not: {
|
||||
exists: { field: 'owners.displayName.keyword' },
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
testIsNullAndIsNotNullFilters('Is null', isNullQuery, 'searchAPI');
|
||||
|
||||
// Check Is Not Null
|
||||
const isNotNullQuery = {
|
||||
query: {
|
||||
bool: {
|
||||
must: [
|
||||
{
|
||||
bool: {
|
||||
must: [{ exists: { field: 'owners.displayName.keyword' } }],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
testIsNullAndIsNotNullFilters(
|
||||
'Is not null',
|
||||
isNotNullQuery,
|
||||
'newSearchAPI'
|
||||
);
|
||||
});
|
||||
});
|
@ -1,135 +0,0 @@
|
||||
/*
|
||||
* Copyright 2023 Collate.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// The spec is related to advance search feature
|
||||
|
||||
import {
|
||||
advancedSearchFlowCleanup,
|
||||
advanceSearchPreRequests,
|
||||
checkAddRuleWithOperator,
|
||||
CONDITIONS_MUST,
|
||||
CONDITIONS_MUST_NOT,
|
||||
FIELDS,
|
||||
OPERATOR,
|
||||
} from '../../../common/Utils/AdvancedSearch';
|
||||
import { getToken } from '../../../common/Utils/LocalStorage';
|
||||
|
||||
describe('Search with additional rule', () => {
|
||||
const testData = {
|
||||
user_1: {
|
||||
id: '',
|
||||
},
|
||||
user_2: {
|
||||
id: '',
|
||||
},
|
||||
};
|
||||
|
||||
before(() => {
|
||||
cy.login();
|
||||
cy.getAllLocalStorage().then((data) => {
|
||||
const token = getToken(data);
|
||||
advanceSearchPreRequests(testData, token);
|
||||
});
|
||||
});
|
||||
|
||||
after(() => {
|
||||
cy.login();
|
||||
cy.getAllLocalStorage().then((data) => {
|
||||
const token = getToken(data);
|
||||
|
||||
advancedSearchFlowCleanup(token);
|
||||
});
|
||||
Cypress.session.clearAllSavedSessions();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
cy.login();
|
||||
});
|
||||
|
||||
Object.values(OPERATOR).forEach((operator) => {
|
||||
it(`Verify Add Rule functionality for All with ${operator.name} operator & condition ${CONDITIONS_MUST.equalTo.name} and ${CONDITIONS_MUST_NOT.notEqualTo.name} `, () => {
|
||||
Object.values(FIELDS).forEach((field) => {
|
||||
let val = field.searchCriteriaSecondGroup;
|
||||
if (field.owner) {
|
||||
val = field.responseValueSecondGroup;
|
||||
}
|
||||
checkAddRuleWithOperator({
|
||||
condition_1: CONDITIONS_MUST.equalTo.name,
|
||||
condition_2: CONDITIONS_MUST_NOT.notEqualTo.name,
|
||||
fieldId: field.testId,
|
||||
searchCriteria_1: field.isLocalSearch
|
||||
? field.searchCriteriaFirstGroup
|
||||
: Cypress._.toLower(field.searchCriteriaFirstGroup),
|
||||
searchCriteria_2: field.isLocalSearch
|
||||
? field.searchCriteriaSecondGroup
|
||||
: Cypress._.toLower(field.searchCriteriaSecondGroup),
|
||||
index_1: 0,
|
||||
index_2: 1,
|
||||
operatorIndex: operator.index,
|
||||
filter_1: CONDITIONS_MUST.equalTo.filter,
|
||||
filter_2: CONDITIONS_MUST_NOT.notEqualTo.filter,
|
||||
response: field.isLocalSearch ? val : Cypress._.toLower(val),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it(`Verify Add Rule functionality for All with ${operator.name} operator & condition ${CONDITIONS_MUST.anyIn.name} and ${CONDITIONS_MUST_NOT.notIn.name} `, () => {
|
||||
Object.values(FIELDS).forEach((field) => {
|
||||
let val = field.searchCriteriaSecondGroup;
|
||||
if (field.owner) {
|
||||
val = field.responseValueSecondGroup;
|
||||
}
|
||||
checkAddRuleWithOperator({
|
||||
condition_1: CONDITIONS_MUST.anyIn.name,
|
||||
condition_2: CONDITIONS_MUST_NOT.notIn.name,
|
||||
fieldId: field.testId,
|
||||
searchCriteria_1: field.isLocalSearch
|
||||
? field.searchCriteriaFirstGroup
|
||||
: Cypress._.toLower(field.searchCriteriaFirstGroup),
|
||||
searchCriteria_2: field.isLocalSearch
|
||||
? field.searchCriteriaSecondGroup
|
||||
: Cypress._.toLower(field.searchCriteriaSecondGroup),
|
||||
index_1: 0,
|
||||
index_2: 1,
|
||||
operatorIndex: operator.index,
|
||||
filter_1: CONDITIONS_MUST.anyIn.filter,
|
||||
filter_2: CONDITIONS_MUST_NOT.notIn.filter,
|
||||
response: field.isLocalSearch ? val : Cypress._.toLower(val),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it(`Verify Add Rule functionality for All with ${operator.name} operator & condition ${CONDITIONS_MUST.contains.name} and ${CONDITIONS_MUST_NOT.notContains.name} `, () => {
|
||||
Object.values(FIELDS).forEach((field) => {
|
||||
const val = field.searchCriteriaSecondGroup;
|
||||
checkAddRuleWithOperator({
|
||||
condition_1: CONDITIONS_MUST.contains.name,
|
||||
condition_2: CONDITIONS_MUST_NOT.notContains.name,
|
||||
fieldId: field.testId,
|
||||
searchCriteria_1: field.isLocalSearch
|
||||
? field.searchCriteriaFirstGroup
|
||||
: Cypress._.toLower(field.searchCriteriaFirstGroup),
|
||||
searchCriteria_2: field.isLocalSearch
|
||||
? field.searchCriteriaSecondGroup
|
||||
: Cypress._.toLower(field.searchCriteriaSecondGroup),
|
||||
index_1: 0,
|
||||
index_2: 1,
|
||||
operatorIndex: operator.index,
|
||||
filter_1: CONDITIONS_MUST.contains.filter,
|
||||
filter_2: CONDITIONS_MUST_NOT.notContains.filter,
|
||||
response: field.isLocalSearch ? val : Cypress._.toLower(val),
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
@ -1,120 +0,0 @@
|
||||
/*
|
||||
* Copyright 2023 Collate.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// The spec is related to advance search feature
|
||||
|
||||
import {
|
||||
advancedSearchFlowCleanup,
|
||||
advanceSearchPreRequests,
|
||||
checkAddGroupWithOperator,
|
||||
CONDITIONS_MUST,
|
||||
CONDITIONS_MUST_NOT,
|
||||
FIELDS,
|
||||
OPERATOR,
|
||||
} from '../../../common/Utils/AdvancedSearch';
|
||||
import { getToken } from '../../../common/Utils/LocalStorage';
|
||||
|
||||
describe('Group search', () => {
|
||||
const testData = {
|
||||
user_1: {
|
||||
id: '',
|
||||
},
|
||||
user_2: {
|
||||
id: '',
|
||||
},
|
||||
};
|
||||
|
||||
before(() => {
|
||||
cy.login();
|
||||
cy.getAllLocalStorage().then((data) => {
|
||||
const token = getToken(data);
|
||||
advanceSearchPreRequests(testData, token);
|
||||
});
|
||||
});
|
||||
|
||||
after(() => {
|
||||
cy.login();
|
||||
cy.getAllLocalStorage().then((data) => {
|
||||
const token = getToken(data);
|
||||
|
||||
advancedSearchFlowCleanup(token);
|
||||
});
|
||||
Cypress.session.clearAllSavedSessions();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
cy.login();
|
||||
});
|
||||
|
||||
Object.values(OPERATOR).forEach((operator) => {
|
||||
it(`Verify Add group functionality for All with ${operator.name} operator & condition ${CONDITIONS_MUST.equalTo.name} and ${CONDITIONS_MUST_NOT.notEqualTo.name} `, () => {
|
||||
Object.values(FIELDS).forEach((field) => {
|
||||
checkAddGroupWithOperator({
|
||||
condition_1: CONDITIONS_MUST.equalTo.name,
|
||||
condition_2: CONDITIONS_MUST_NOT.notEqualTo.name,
|
||||
fieldId: field.testId,
|
||||
searchCriteria_1: field.isLocalSearch
|
||||
? field.searchCriteriaFirstGroup
|
||||
: Cypress._.toLower(field.searchCriteriaFirstGroup),
|
||||
searchCriteria_2: field.isLocalSearch
|
||||
? field.searchCriteriaSecondGroup
|
||||
: Cypress._.toLower(field.searchCriteriaSecondGroup),
|
||||
index_1: 0,
|
||||
index_2: 1,
|
||||
operatorIndex: operator.index,
|
||||
isLocalSearch: field.isLocalSearch,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it(`Verify Add group functionality for All with ${operator.name} operator & condition ${CONDITIONS_MUST.anyIn.name} and ${CONDITIONS_MUST_NOT.notIn.name} `, () => {
|
||||
Object.values(FIELDS).forEach((field) => {
|
||||
checkAddGroupWithOperator({
|
||||
condition_1: CONDITIONS_MUST.anyIn.name,
|
||||
condition_2: CONDITIONS_MUST_NOT.notIn.name,
|
||||
fieldId: field.testId,
|
||||
searchCriteria_1: field.isLocalSearch
|
||||
? field.searchCriteriaFirstGroup
|
||||
: Cypress._.toLower(field.searchCriteriaFirstGroup),
|
||||
searchCriteria_2: field.isLocalSearch
|
||||
? field.searchCriteriaSecondGroup
|
||||
: Cypress._.toLower(field.searchCriteriaSecondGroup),
|
||||
index_1: 0,
|
||||
index_2: 1,
|
||||
operatorIndex: operator.index,
|
||||
isLocalSearch: field.isLocalSearch,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it(`Verify Add group functionality for All with ${operator.name} operator & condition ${CONDITIONS_MUST.contains.name} and ${CONDITIONS_MUST_NOT.notContains.name} `, () => {
|
||||
Object.values(FIELDS).forEach((field) => {
|
||||
checkAddGroupWithOperator({
|
||||
condition_1: CONDITIONS_MUST.contains.name,
|
||||
condition_2: CONDITIONS_MUST_NOT.notContains.name,
|
||||
fieldId: field.testId,
|
||||
searchCriteria_1: field.isLocalSearch
|
||||
? field.searchCriteriaFirstGroup
|
||||
: Cypress._.toLower(field.searchCriteriaFirstGroup),
|
||||
searchCriteria_2: field.isLocalSearch
|
||||
? field.searchCriteriaSecondGroup
|
||||
: Cypress._.toLower(field.searchCriteriaSecondGroup),
|
||||
index_1: 0,
|
||||
index_2: 1,
|
||||
operatorIndex: operator.index,
|
||||
isLocalSearch: field.isLocalSearch,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
@ -1,88 +0,0 @@
|
||||
/*
|
||||
* Copyright 2023 Collate.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// The spec is related to advance search feature
|
||||
|
||||
import {
|
||||
advancedSearchFlowCleanup,
|
||||
advanceSearchPreRequests,
|
||||
checkMustPaths,
|
||||
checkMust_notPaths,
|
||||
CONDITIONS_MUST,
|
||||
CONDITIONS_MUST_NOT,
|
||||
FIELDS,
|
||||
} from '../../../common/Utils/AdvancedSearch';
|
||||
import { getToken } from '../../../common/Utils/LocalStorage';
|
||||
|
||||
describe('Single filed search', () => {
|
||||
const testData = {
|
||||
user_1: {
|
||||
id: '',
|
||||
},
|
||||
user_2: {
|
||||
id: '',
|
||||
},
|
||||
};
|
||||
|
||||
before(() => {
|
||||
cy.login();
|
||||
cy.getAllLocalStorage().then((data) => {
|
||||
const token = getToken(data);
|
||||
advanceSearchPreRequests(testData, token);
|
||||
});
|
||||
});
|
||||
|
||||
after(() => {
|
||||
cy.login();
|
||||
cy.getAllLocalStorage().then((data) => {
|
||||
const token = getToken(data);
|
||||
|
||||
advancedSearchFlowCleanup(token);
|
||||
});
|
||||
Cypress.session.clearAllSavedSessions();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
cy.login();
|
||||
});
|
||||
|
||||
Object.values(FIELDS).forEach((field) => {
|
||||
it(`Verify advance search results for ${field.name} field and all conditions`, () => {
|
||||
Object.values(CONDITIONS_MUST).forEach((condition) => {
|
||||
checkMustPaths(
|
||||
condition.name,
|
||||
field.testId,
|
||||
field.isLocalSearch
|
||||
? field.searchCriteriaFirstGroup
|
||||
: Cypress._.toLower(field.searchCriteriaFirstGroup),
|
||||
0,
|
||||
field.responseValueFirstGroup,
|
||||
field.isLocalSearch
|
||||
);
|
||||
});
|
||||
|
||||
Object.values(CONDITIONS_MUST_NOT).forEach((condition) => {
|
||||
checkMust_notPaths(
|
||||
condition.name,
|
||||
field.testId,
|
||||
field.isLocalSearch
|
||||
? field.searchCriteriaFirstGroup
|
||||
: Cypress._.toLower(field.searchCriteriaFirstGroup),
|
||||
0,
|
||||
field.responseValueFirstGroup,
|
||||
field.isLocalSearch
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
@ -0,0 +1,152 @@
|
||||
/*
|
||||
* Copyright 2024 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 test from '@playwright/test';
|
||||
import { SidebarItem } from '../../constant/sidebar';
|
||||
import { TableClass } from '../../support/entity/TableClass';
|
||||
import { TopicClass } from '../../support/entity/TopicClass';
|
||||
import { UserClass } from '../../support/user/UserClass';
|
||||
import {
|
||||
FIELDS,
|
||||
OPERATOR,
|
||||
runRuleGroupTests,
|
||||
verifyAllConditions,
|
||||
} from '../../utils/advancedSearch';
|
||||
import { createNewPage, redirectToHomePage } from '../../utils/common';
|
||||
import { addMultiOwner, assignTag, assignTier } from '../../utils/entity';
|
||||
import { sidebarClick } from '../../utils/sidebar';
|
||||
|
||||
test.describe('Advanced Search', { tag: '@advanced-search' }, () => {
|
||||
// use the admin user to login
|
||||
test.use({ storageState: 'playwright/.auth/admin.json' });
|
||||
|
||||
const user1 = new UserClass();
|
||||
const user2 = new UserClass();
|
||||
const table1 = new TableClass();
|
||||
const table2 = new TableClass();
|
||||
const topic1 = new TopicClass();
|
||||
const topic2 = new TopicClass();
|
||||
|
||||
let searchCriteria = {};
|
||||
|
||||
test.beforeAll('Setup pre-requests', async ({ browser }) => {
|
||||
test.setTimeout(150000);
|
||||
|
||||
const { page, apiContext, afterAction } = await createNewPage(browser);
|
||||
await Promise.all([
|
||||
user1.create(apiContext),
|
||||
user2.create(apiContext),
|
||||
table1.create(apiContext),
|
||||
table2.create(apiContext),
|
||||
topic1.create(apiContext),
|
||||
topic2.create(apiContext),
|
||||
]);
|
||||
|
||||
// Add Owner & Tag to the table
|
||||
await table1.visitEntityPage(page);
|
||||
await addMultiOwner({
|
||||
page,
|
||||
ownerNames: [user1.getUserName()],
|
||||
activatorBtnDataTestId: 'edit-owner',
|
||||
resultTestId: 'data-assets-header',
|
||||
endpoint: table1.endpoint,
|
||||
type: 'Users',
|
||||
});
|
||||
await assignTag(page, 'PersonalData.Personal');
|
||||
|
||||
await table2.visitEntityPage(page);
|
||||
await addMultiOwner({
|
||||
page,
|
||||
ownerNames: [user2.getUserName()],
|
||||
activatorBtnDataTestId: 'edit-owner',
|
||||
resultTestId: 'data-assets-header',
|
||||
endpoint: table1.endpoint,
|
||||
type: 'Users',
|
||||
});
|
||||
await assignTag(page, 'PII.None');
|
||||
|
||||
// Add Tier To the topic 1
|
||||
await topic1.visitEntityPage(page);
|
||||
await assignTier(page, 'Tier1', topic1.endpoint);
|
||||
|
||||
// Add Tier To the topic 2
|
||||
await topic2.visitEntityPage(page);
|
||||
await assignTier(page, 'Tier2', topic2.endpoint);
|
||||
|
||||
// Update Search Criteria here
|
||||
searchCriteria = {
|
||||
'owners.displayName.keyword': [user1.getUserName(), user2.getUserName()],
|
||||
'tags.tagFQN': ['PersonalData.Personal', 'PII.None'],
|
||||
'tier.tagFQN': ['Tier.Tier1', 'Tier.Tier2'],
|
||||
'service.displayName.keyword': [table1.service.name, table2.service.name],
|
||||
'database.displayName.keyword': [
|
||||
table1.database.name,
|
||||
table2.database.name,
|
||||
],
|
||||
'databaseSchema.displayName.keyword': [
|
||||
table1.schema.name,
|
||||
table2.schema.name,
|
||||
],
|
||||
'columns.name.keyword': ['email', 'shop_id'],
|
||||
};
|
||||
|
||||
await afterAction();
|
||||
});
|
||||
|
||||
test.afterAll('Cleanup', async ({ browser }) => {
|
||||
const { apiContext, afterAction } = await createNewPage(browser);
|
||||
await Promise.all([
|
||||
user1.delete(apiContext),
|
||||
user2.delete(apiContext),
|
||||
table1.delete(apiContext),
|
||||
table2.delete(apiContext),
|
||||
topic1.delete(apiContext),
|
||||
topic2.delete(apiContext),
|
||||
]);
|
||||
await afterAction();
|
||||
});
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await redirectToHomePage(page);
|
||||
await sidebarClick(page, SidebarItem.EXPLORE);
|
||||
});
|
||||
|
||||
FIELDS.forEach((field) => {
|
||||
test(`Verify All conditions for ${field.id} field`, async ({ page }) => {
|
||||
test.slow(true);
|
||||
|
||||
await verifyAllConditions(page, field, searchCriteria[field.name][0]);
|
||||
});
|
||||
});
|
||||
|
||||
Object.values(OPERATOR).forEach(({ name: operator }) => {
|
||||
FIELDS.forEach((field) => {
|
||||
// Rule based search
|
||||
test(`Verify Rule functionality for field ${field.id} with ${operator} operator`, async ({
|
||||
page,
|
||||
}) => {
|
||||
test.slow(true);
|
||||
|
||||
await runRuleGroupTests(page, field, operator, false, searchCriteria);
|
||||
});
|
||||
|
||||
// Group based search
|
||||
test(`Verify Group functionality for field ${field.id} with ${operator} operator`, async ({
|
||||
page,
|
||||
}) => {
|
||||
test.slow(true);
|
||||
|
||||
await runRuleGroupTests(page, field, operator, true, searchCriteria);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
@ -0,0 +1,471 @@
|
||||
/*
|
||||
* Copyright 2024 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 { expect, Locator, Page } from '@playwright/test';
|
||||
import { clickOutside } from './common';
|
||||
|
||||
type EntityFields = {
|
||||
id: string;
|
||||
name: string;
|
||||
localSearch: boolean;
|
||||
};
|
||||
|
||||
export const FIELDS: EntityFields[] = [
|
||||
{
|
||||
id: 'Owner',
|
||||
name: 'owners.displayName.keyword',
|
||||
localSearch: false,
|
||||
},
|
||||
{
|
||||
id: 'Tags',
|
||||
name: 'tags.tagFQN',
|
||||
localSearch: false,
|
||||
},
|
||||
{
|
||||
id: 'Tier',
|
||||
name: 'tier.tagFQN',
|
||||
localSearch: true,
|
||||
},
|
||||
{
|
||||
id: 'Service',
|
||||
name: 'service.displayName.keyword',
|
||||
localSearch: false,
|
||||
},
|
||||
{
|
||||
id: 'Database',
|
||||
name: 'database.displayName.keyword',
|
||||
localSearch: false,
|
||||
},
|
||||
{
|
||||
id: 'Database Schema',
|
||||
name: 'databaseSchema.displayName.keyword',
|
||||
localSearch: false,
|
||||
},
|
||||
{
|
||||
id: 'Column',
|
||||
name: 'columns.name.keyword',
|
||||
localSearch: false,
|
||||
},
|
||||
];
|
||||
|
||||
export const OPERATOR = {
|
||||
AND: {
|
||||
name: 'AND',
|
||||
index: 1,
|
||||
},
|
||||
OR: {
|
||||
name: 'OR',
|
||||
index: 2,
|
||||
},
|
||||
};
|
||||
|
||||
export const CONDITIONS_MUST = {
|
||||
equalTo: {
|
||||
name: '==',
|
||||
filter: 'must',
|
||||
},
|
||||
contains: {
|
||||
name: 'Contains',
|
||||
filter: 'must',
|
||||
},
|
||||
anyIn: {
|
||||
name: 'Any in',
|
||||
filter: 'must',
|
||||
},
|
||||
};
|
||||
|
||||
export const CONDITIONS_MUST_NOT = {
|
||||
notEqualTo: {
|
||||
name: '!=',
|
||||
filter: 'must_not',
|
||||
},
|
||||
notIn: {
|
||||
name: 'Not in',
|
||||
filter: 'must_not',
|
||||
},
|
||||
notContains: {
|
||||
name: 'Not contains',
|
||||
filter: 'must_not',
|
||||
},
|
||||
};
|
||||
|
||||
export const NULL_CONDITIONS = {
|
||||
isNull: {
|
||||
name: 'Is null',
|
||||
filter: 'empty',
|
||||
},
|
||||
isNotNull: {
|
||||
name: 'Is not null',
|
||||
filter: 'empty',
|
||||
},
|
||||
};
|
||||
|
||||
export const showAdvancedSearchDialog = async (page: Page) => {
|
||||
await page.getByTestId('advance-search-button').click();
|
||||
|
||||
await expect(page.locator('[role="dialog"].ant-modal')).toBeVisible();
|
||||
};
|
||||
|
||||
const selectOption = async (
|
||||
page: Page,
|
||||
dropdownLocator: Locator,
|
||||
optionTitle: string
|
||||
) => {
|
||||
await dropdownLocator.click();
|
||||
await page.click(`.ant-select-dropdown:visible [title="${optionTitle}"]`);
|
||||
};
|
||||
|
||||
export const fillRule = async (
|
||||
page: Page,
|
||||
{
|
||||
condition,
|
||||
field,
|
||||
searchCriteria,
|
||||
index,
|
||||
}: {
|
||||
condition: string;
|
||||
field: EntityFields;
|
||||
searchCriteria: string;
|
||||
index: number;
|
||||
}
|
||||
) => {
|
||||
const ruleLocator = page.locator('.rule').nth(index - 1);
|
||||
|
||||
// Perform click on rule field
|
||||
await selectOption(
|
||||
page,
|
||||
ruleLocator.locator('.rule--field .ant-select'),
|
||||
field.id
|
||||
);
|
||||
|
||||
// Perform click on operator
|
||||
await selectOption(
|
||||
page,
|
||||
ruleLocator.locator('.rule--operator .ant-select'),
|
||||
condition
|
||||
);
|
||||
|
||||
if (searchCriteria) {
|
||||
const inputElement = ruleLocator.locator(
|
||||
'.rule--widget--TEXT input[type="text"]'
|
||||
);
|
||||
const searchData = field.localSearch
|
||||
? searchCriteria
|
||||
: searchCriteria.toLowerCase();
|
||||
|
||||
if (await inputElement.isVisible()) {
|
||||
await inputElement.fill(searchData);
|
||||
} else {
|
||||
const dropdownInput = ruleLocator.locator(
|
||||
'.widget--widget > .ant-select > .ant-select-selector input'
|
||||
);
|
||||
let aggregateRes;
|
||||
|
||||
if (!field.localSearch) {
|
||||
aggregateRes = page.waitForResponse('/api/v1/search/aggregate?*');
|
||||
}
|
||||
|
||||
await dropdownInput.click();
|
||||
await dropdownInput.fill(searchData);
|
||||
|
||||
if (aggregateRes) {
|
||||
await aggregateRes;
|
||||
}
|
||||
|
||||
await page
|
||||
.locator(`.ant-select-dropdown [title="${searchData}"]`)
|
||||
.click();
|
||||
}
|
||||
|
||||
await clickOutside(page);
|
||||
}
|
||||
};
|
||||
|
||||
export const checkMustPaths = async (
|
||||
page: Page,
|
||||
{ condition, field, searchCriteria, index }
|
||||
) => {
|
||||
const searchData = field.localSearch
|
||||
? searchCriteria
|
||||
: searchCriteria.toLowerCase();
|
||||
|
||||
await fillRule(page, {
|
||||
condition,
|
||||
field,
|
||||
searchCriteria,
|
||||
index,
|
||||
});
|
||||
|
||||
const searchRes = page.waitForResponse(
|
||||
'/api/v1/search/query?*index=dataAsset&from=0&size=10*'
|
||||
);
|
||||
await page.getByTestId('apply-btn').click();
|
||||
await searchRes.then(async (res) => {
|
||||
await expect(res.request().url()).toContain(encodeURI(searchData));
|
||||
|
||||
await res.json().then(async (json) => {
|
||||
await expect(JSON.stringify(json.hits.hits)).toContain(searchCriteria);
|
||||
});
|
||||
});
|
||||
|
||||
await expect(
|
||||
page.getByTestId('advance-search-filter-container')
|
||||
).toContainText(searchData);
|
||||
};
|
||||
|
||||
export const checkMustNotPaths = async (
|
||||
page: Page,
|
||||
{ condition, field, searchCriteria, index }
|
||||
) => {
|
||||
const searchData = field.localSearch
|
||||
? searchCriteria
|
||||
: searchCriteria.toLowerCase();
|
||||
|
||||
await fillRule(page, {
|
||||
condition,
|
||||
field,
|
||||
searchCriteria,
|
||||
index,
|
||||
});
|
||||
|
||||
const searchRes = page.waitForResponse(
|
||||
'/api/v1/search/query?*index=dataAsset&from=0&size=10*'
|
||||
);
|
||||
await page.getByTestId('apply-btn').click();
|
||||
await searchRes.then(async (res) => {
|
||||
await expect(res.request().url()).toContain(encodeURI(searchData));
|
||||
|
||||
if (!['columns.name.keyword'].includes(field.name)) {
|
||||
await res.json().then(async (json) => {
|
||||
await expect(JSON.stringify(json.hits.hits)).not.toContain(
|
||||
searchCriteria
|
||||
);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
await expect(
|
||||
page.getByTestId('advance-search-filter-container')
|
||||
).toContainText(searchData);
|
||||
};
|
||||
|
||||
export const checkNullPaths = async (
|
||||
page: Page,
|
||||
{ condition, field, searchCriteria, index }
|
||||
) => {
|
||||
await fillRule(page, {
|
||||
condition,
|
||||
field,
|
||||
searchCriteria,
|
||||
index,
|
||||
});
|
||||
|
||||
const searchRes = page.waitForResponse(
|
||||
'/api/v1/search/query?*index=dataAsset&from=0&size=10*'
|
||||
);
|
||||
await page.getByTestId('apply-btn').click();
|
||||
await searchRes.then(async (res) => {
|
||||
const urlParams = new URLSearchParams(res.request().url());
|
||||
const queryFilter = JSON.parse(urlParams.get('query_filter') ?? '');
|
||||
|
||||
const resultQuery =
|
||||
condition === 'Is null'
|
||||
? {
|
||||
query: {
|
||||
bool: {
|
||||
must: [
|
||||
{
|
||||
bool: {
|
||||
must: [
|
||||
{
|
||||
bool: {
|
||||
must_not: {
|
||||
exists: { field: field.name },
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
}
|
||||
: {
|
||||
query: {
|
||||
bool: {
|
||||
must: [
|
||||
{
|
||||
bool: {
|
||||
must: [{ exists: { field: field.name } }],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
await expect(JSON.stringify(queryFilter)).toContain(
|
||||
JSON.stringify(resultQuery)
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
export const verifyAllConditions = async (
|
||||
page: Page,
|
||||
field: EntityFields,
|
||||
searchCriteria: string
|
||||
) => {
|
||||
// Check for Must conditions
|
||||
for (const condition of Object.values(CONDITIONS_MUST)) {
|
||||
await showAdvancedSearchDialog(page);
|
||||
await checkMustPaths(page, {
|
||||
condition: condition.name,
|
||||
field,
|
||||
searchCriteria: searchCriteria,
|
||||
index: 1,
|
||||
});
|
||||
await page.getByTestId('clear-filters').click();
|
||||
}
|
||||
|
||||
// Check for Must Not conditions
|
||||
for (const condition of Object.values(CONDITIONS_MUST_NOT)) {
|
||||
await showAdvancedSearchDialog(page);
|
||||
await checkMustNotPaths(page, {
|
||||
condition: condition.name,
|
||||
field,
|
||||
searchCriteria: searchCriteria,
|
||||
index: 1,
|
||||
});
|
||||
await page.getByTestId('clear-filters').click();
|
||||
}
|
||||
|
||||
// Check for Null and Not Null conditions
|
||||
for (const condition of Object.values(NULL_CONDITIONS)) {
|
||||
await showAdvancedSearchDialog(page);
|
||||
await checkNullPaths(page, {
|
||||
condition: condition.name,
|
||||
field,
|
||||
searchCriteria: undefined,
|
||||
index: 1,
|
||||
});
|
||||
await page.getByTestId('clear-filters').click();
|
||||
}
|
||||
};
|
||||
|
||||
export const checkAddRuleOrGroupWithOperator = async (
|
||||
page,
|
||||
{
|
||||
field,
|
||||
operator,
|
||||
condition1,
|
||||
condition2,
|
||||
searchCriteria1,
|
||||
searchCriteria2,
|
||||
}: {
|
||||
field: EntityFields;
|
||||
operator: string;
|
||||
condition1: string;
|
||||
condition2: string;
|
||||
searchCriteria1: string;
|
||||
searchCriteria2: string;
|
||||
},
|
||||
isGroupTest = false
|
||||
) => {
|
||||
await showAdvancedSearchDialog(page);
|
||||
await fillRule(page, {
|
||||
condition: condition1,
|
||||
field,
|
||||
searchCriteria: searchCriteria1,
|
||||
index: 1,
|
||||
});
|
||||
|
||||
if (!isGroupTest) {
|
||||
await page.getByTestId('advanced-search-add-rule').nth(1).click();
|
||||
} else {
|
||||
await page.getByTestId('advanced-search-add-group').first().click();
|
||||
}
|
||||
|
||||
await fillRule(page, {
|
||||
condition: condition2,
|
||||
field,
|
||||
searchCriteria: searchCriteria2,
|
||||
index: 2,
|
||||
});
|
||||
|
||||
if (operator === 'OR') {
|
||||
await page
|
||||
.getByTestId('advanced-search-modal')
|
||||
.getByRole('button', { name: 'Or' });
|
||||
}
|
||||
|
||||
const searchRes = page.waitForResponse(
|
||||
'/api/v1/search/query?*index=dataAsset&from=0&size=10*'
|
||||
);
|
||||
await page.getByTestId('apply-btn').click();
|
||||
await searchRes;
|
||||
await searchRes.then(async (res) => {
|
||||
await res.json().then(async (json) => {
|
||||
if (field.id !== 'Column') {
|
||||
if (operator === 'Or') {
|
||||
await expect(JSON.stringify(json)).toContain(searchCriteria1);
|
||||
await expect(JSON.stringify(json)).toContain(searchCriteria2);
|
||||
} else {
|
||||
await expect(JSON.stringify(json)).toContain(searchCriteria1);
|
||||
await expect(JSON.stringify(json)).not.toContain(searchCriteria2);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
export const runRuleGroupTests = async (
|
||||
page: Page,
|
||||
field: EntityFields,
|
||||
operator: string,
|
||||
isGroupTest: boolean,
|
||||
searchCriteria: Record<string, string[]>
|
||||
) => {
|
||||
const searchCriteria1 = searchCriteria[field.name][0];
|
||||
const searchCriteria2 = searchCriteria[field.name][1];
|
||||
|
||||
const testCases = [
|
||||
{
|
||||
condition1: CONDITIONS_MUST.equalTo.name,
|
||||
condition2: CONDITIONS_MUST_NOT.notEqualTo.name,
|
||||
},
|
||||
{
|
||||
condition1: CONDITIONS_MUST.contains.name,
|
||||
condition2: CONDITIONS_MUST_NOT.notContains.name,
|
||||
},
|
||||
{
|
||||
condition1: CONDITIONS_MUST.anyIn.name,
|
||||
condition2: CONDITIONS_MUST_NOT.notIn.name,
|
||||
},
|
||||
];
|
||||
|
||||
for (const { condition1, condition2 } of testCases) {
|
||||
await checkAddRuleOrGroupWithOperator(
|
||||
page,
|
||||
{
|
||||
field,
|
||||
operator,
|
||||
condition1,
|
||||
condition2,
|
||||
searchCriteria1,
|
||||
searchCriteria2,
|
||||
},
|
||||
isGroupTest
|
||||
);
|
||||
await page.getByTestId('clear-filters').click();
|
||||
}
|
||||
};
|
@ -115,6 +115,7 @@ export const renderAdvanceSearchButtons: RenderSettings['renderButton'] = (
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
CloseCircleOutlined as React.ForwardRefExoticComponent<any>
|
||||
}
|
||||
data-testid="advanced-search-delete-rule"
|
||||
onClick={props?.onClick}
|
||||
/>
|
||||
);
|
||||
@ -123,6 +124,7 @@ export const renderAdvanceSearchButtons: RenderSettings['renderButton'] = (
|
||||
<Button
|
||||
ghost
|
||||
className="action action--ADD-RULE"
|
||||
data-testid="advanced-search-add-rule"
|
||||
icon={<PlusOutlined />}
|
||||
type="primary"
|
||||
onClick={props?.onClick}>
|
||||
@ -133,6 +135,7 @@ export const renderAdvanceSearchButtons: RenderSettings['renderButton'] = (
|
||||
return (
|
||||
<Button
|
||||
className="action action--ADD-GROUP"
|
||||
data-testid="advanced-search-add-group"
|
||||
icon={<PlusOutlined />}
|
||||
type="primary"
|
||||
onClick={props?.onClick}>
|
||||
@ -147,6 +150,7 @@ export const renderAdvanceSearchButtons: RenderSettings['renderButton'] = (
|
||||
})}
|
||||
className="action action--DELETE cursor-pointer align-middle"
|
||||
component={IconDeleteColored}
|
||||
data-testid="advanced-search-delete-group"
|
||||
style={{ fontSize: '16px' }}
|
||||
onClick={props?.onClick as () => void}
|
||||
/>
|
||||
|
Loading…
x
Reference in New Issue
Block a user