diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/EntityRepository.java b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/EntityRepository.java index 631d2f4b934..af160b48a33 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/EntityRepository.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/EntityRepository.java @@ -1355,9 +1355,11 @@ public abstract class EntityRepository { SearchListFilter searchListFilter, int limit, int offset, - String q) + String q, + String queryString) throws IOException { - return listFromSearchWithOffset(uriInfo, fields, searchListFilter, limit, offset, null, q); + return listFromSearchWithOffset( + uriInfo, fields, searchListFilter, limit, offset, null, q, queryString); } public ResultList listFromSearchWithOffset( @@ -1367,7 +1369,8 @@ public abstract class EntityRepository { int limit, int offset, SearchSortFilter searchSortFilter, - String q) + String q, + String queryString) throws IOException { List entityList = new ArrayList<>(); Long total; @@ -1375,7 +1378,7 @@ public abstract class EntityRepository { if (limit > 0) { SearchResultListMapper results = searchRepository.listWithOffset( - searchListFilter, limit, offset, entityType, searchSortFilter, q); + searchListFilter, limit, offset, entityType, searchSortFilter, q, queryString); total = results.getTotal(); for (Map json : results.getResults()) { T entity = JsonUtils.readOrConvertValueLenient(json, entityClass); @@ -1385,7 +1388,7 @@ public abstract class EntityRepository { } else { SearchResultListMapper results = searchRepository.listWithOffset( - searchListFilter, limit, offset, entityType, searchSortFilter, q); + searchListFilter, limit, offset, entityType, searchSortFilter, q, queryString); total = results.getTotal(); return new ResultList<>(entityList, null, limit, total.intValue()); } diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/EntityTimeSeriesRepository.java b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/EntityTimeSeriesRepository.java index 0ba36be7c9d..ba4f174cd88 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/EntityTimeSeriesRepository.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/EntityTimeSeriesRepository.java @@ -367,7 +367,8 @@ public abstract class EntityTimeSeriesRepository entityList = new ArrayList<>(); long total; @@ -377,7 +378,7 @@ public abstract class EntityTimeSeriesRepository 0) { SearchResultListMapper results = searchRepository.listWithOffset( - searchListFilter, limit, offset, entityType, searchSortFilter, q); + searchListFilter, limit, offset, entityType, searchSortFilter, q, queryString); total = results.getTotal(); for (Map json : results.getResults()) { T entity = setFieldsInternal(JsonUtils.readOrConvertValue(json, entityClass), fields); @@ -389,7 +390,7 @@ public abstract class EntityTimeSeriesRepository(entityList, null, limit, (int) total); } diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/EntityResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/EntityResource.java index b20af2cdf79..e599eb3bc1f 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/EntityResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/EntityResource.java @@ -195,12 +195,13 @@ public abstract class EntityResource listInternalFromSearch( @@ -72,12 +73,13 @@ public abstract class EntityTimeSeriesResource< int offset, SearchSortFilter searchSortFilter, String q, + String queryString, List authRequests, AuthorizationLogic authorizationLogic) throws IOException { authorizer.authorizeRequests(securityContext, authRequests, authorizationLogic); return repository.listFromSearchWithOffset( - fields, searchListFilter, limit, offset, searchSortFilter, q); + fields, searchListFilter, limit, offset, searchSortFilter, q, queryString); } public ResultList listLatestFromSearch( diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/dqtests/TestCaseResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/dqtests/TestCaseResource.java index d3234e6760a..94a0e3c2261 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/dqtests/TestCaseResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/dqtests/TestCaseResource.java @@ -401,7 +401,12 @@ public class TestCaseResource extends EntityResource authRequests = getAuthRequestsForListOps(); authorizer.authorizeRequests(securityContext, authRequests, AuthorizationLogic.ANY); return repository.listFromSearchWithOffset( - uriInfo, fields, searchListFilter, limit, offset, searchSortFilter, q); + uriInfo, fields, searchListFilter, limit, offset, searchSortFilter, q, queryString); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/search/SearchClient.java b/openmetadata-service/src/main/java/org/openmetadata/service/search/SearchClient.java index b62eac8f640..26b730f5d00 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/search/SearchClient.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/search/SearchClient.java @@ -187,7 +187,8 @@ public interface SearchClient { int offset, String index, SearchSortFilter searchSortFilter, - String q) + String q, + String queryString) throws IOException; SearchResultListMapper listWithDeepPagination( diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/search/SearchRepository.java b/openmetadata-service/src/main/java/org/openmetadata/service/search/SearchRepository.java index 53f04fbbb43..d3a3447533a 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/search/SearchRepository.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/search/SearchRepository.java @@ -1019,6 +1019,18 @@ public class SearchRepository { SearchSortFilter searchSortFilter, String q) throws IOException { + return listWithOffset(filter, limit, offset, entityType, searchSortFilter, q, null); + } + + public SearchResultListMapper listWithOffset( + SearchListFilter filter, + int limit, + int offset, + String entityType, + SearchSortFilter searchSortFilter, + String q, + String queryString) + throws IOException { IndexMapping index = entityIndexMap.get(entityType); return searchClient.listWithOffset( filter.getCondition(entityType), @@ -1026,7 +1038,8 @@ public class SearchRepository { offset, index.getIndexName(clusterAlias), searchSortFilter, - q); + q, + queryString); } public SearchResultListMapper listWithDeepPagination( diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/search/elasticsearch/ElasticSearchClient.java b/openmetadata-service/src/main/java/org/openmetadata/service/search/elasticsearch/ElasticSearchClient.java index 6372ea557e7..54eeb70ca94 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/search/elasticsearch/ElasticSearchClient.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/search/elasticsearch/ElasticSearchClient.java @@ -733,16 +733,20 @@ public class ElasticSearchClient implements SearchClient { int offset, String index, SearchSortFilter searchSortFilter, - String q) + String q, + String queryString) throws IOException { SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); if (!nullOrEmpty(q)) { - XContentParser queryParser = createXContentParser(q); + searchSourceBuilder = getSearchSourceBuilder(index, q, offset, limit); + } + if (!nullOrEmpty(queryString)) { + XContentParser queryParser = createXContentParser(queryString); searchSourceBuilder = SearchSourceBuilder.fromXContent(queryParser); } List> results = new ArrayList<>(); - getSearchFilter(filter, searchSourceBuilder, !nullOrEmpty(q)); + getSearchFilter(filter, searchSourceBuilder, !nullOrEmpty(q) || !nullOrEmpty(queryString)); searchSourceBuilder.timeout(new TimeValue(30, TimeUnit.SECONDS)); searchSourceBuilder.from(offset); diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/search/opensearch/OpenSearchClient.java b/openmetadata-service/src/main/java/org/openmetadata/service/search/opensearch/OpenSearchClient.java index bc2a69d1acf..1691e223f64 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/search/opensearch/OpenSearchClient.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/search/opensearch/OpenSearchClient.java @@ -717,16 +717,20 @@ public class OpenSearchClient implements SearchClient { int offset, String index, SearchSortFilter searchSortFilter, - String q) + String q, + String queryString) throws IOException { SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); if (!nullOrEmpty(q)) { - XContentParser queryParser = createXContentParser(q); + searchSourceBuilder = getSearchSourceBuilder(index, q, offset, limit); + } + if (!nullOrEmpty(queryString)) { + XContentParser queryParser = createXContentParser(queryString); searchSourceBuilder = SearchSourceBuilder.fromXContent(queryParser); } List> results = new ArrayList<>(); - getSearchFilter(filter, searchSourceBuilder, !nullOrEmpty(q)); + getSearchFilter(filter, searchSourceBuilder, !nullOrEmpty(q) || !nullOrEmpty(queryString)); searchSourceBuilder.timeout(new TimeValue(30, TimeUnit.SECONDS)); searchSourceBuilder.from(offset); diff --git a/openmetadata-service/src/test/java/org/openmetadata/service/resources/dqtests/TestCaseResourceTest.java b/openmetadata-service/src/test/java/org/openmetadata/service/resources/dqtests/TestCaseResourceTest.java index 8f19460d9a8..6a204ef131a 100644 --- a/openmetadata-service/src/test/java/org/openmetadata/service/resources/dqtests/TestCaseResourceTest.java +++ b/openmetadata-service/src/test/java/org/openmetadata/service/resources/dqtests/TestCaseResourceTest.java @@ -953,7 +953,7 @@ public class TestCaseResourceTest extends EntityResourceTest ts.getFullyQualifiedName().equals(testCaseForEL.getFullyQualifiedName()))); + queryParams.clear(); + queryParams.put("q", "test_getSimpleListFromSearchb"); + allEntities = listEntitiesFromSearch(queryParams, testCasesNum, 0, ADMIN_AUTH_HEADERS); + // Note: Since the "name" field and its ngram variant are prioritized in the search query + // and the test case names are very similar, the fuzzy matching returns all test cases. + assertEquals(testCasesNum, allEntities.getData().size()); + queryParams.clear(); queryParams.put("entityLink", testCaseForEL.getEntityLink()); queryParams.put("includeAllTests", "true"); @@ -3664,7 +3671,7 @@ public class TestCaseResourceTest extends EntityResourceTest ts.getFullyQualifiedName().equals(logicalTestSuite.getFullyQualifiedName()))); + + queryParams.clear(); + queryParams.put("q", logicalTestSuite.getFullyQualifiedName()); + queryTestSuites = listEntitiesFromSearch(queryParams, 100, 0, ADMIN_AUTH_HEADERS); + Assertions.assertTrue( + queryTestSuites.getData().stream() + .allMatch( + ts -> ts.getFullyQualifiedName().equals(logicalTestSuite.getFullyQualifiedName()))); + // 6. List test suites with a nested sort queryParams.clear(); queryParams.put("fields", "tests"); diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/TestSuiteMultiPipeline.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/TestSuiteMultiPipeline.spec.ts index 613b74ed94d..8e6300c8a5c 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/TestSuiteMultiPipeline.spec.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/TestSuiteMultiPipeline.spec.ts @@ -18,7 +18,7 @@ import { getApiContext, redirectToHomePage, uuid } from '../../utils/common'; // use the admin user to login test.use({ storageState: 'playwright/.auth/admin.json' }); -test.skip( +test( 'TestSuite multi pipeline support', PLAYWRIGHT_INGESTION_TAG_OBJ, async ({ page }) => { @@ -185,7 +185,7 @@ test.skip( } ); -test.skip( +test( "Edit the pipeline's test case", PLAYWRIGHT_INGESTION_TAG_OBJ, async ({ page }) => { diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/DataQualityAndProfiler.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/DataQualityAndProfiler.spec.ts index 31ea619f407..a2f6c7a1ea1 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/DataQualityAndProfiler.spec.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/DataQualityAndProfiler.spec.ts @@ -322,7 +322,7 @@ test( } ); -test.skip( +test( 'TestCase with Array params value', PLAYWRIGHT_INGESTION_TAG_OBJ, async ({ page }) => { @@ -639,417 +639,406 @@ test( } ); -test.skip( - 'TestCase filters', - PLAYWRIGHT_INGESTION_TAG_OBJ, - async ({ page }) => { - test.setTimeout(360000); +test('TestCase filters', PLAYWRIGHT_INGESTION_TAG_OBJ, async ({ page }) => { + test.setTimeout(360000); - const { apiContext, afterAction } = await getApiContext(page); - const filterTable1 = new TableClass(); + const { apiContext, afterAction } = await getApiContext(page); + const filterTable1 = new TableClass(); - await filterTable1.create(apiContext); - const filterTable2 = { - ...filterTable1.entity, - name: `${filterTable1.entity.name}-model`, - }; - const filterTable2Response = await apiContext - .post('/api/v1/tables', { - data: filterTable2, - }) - .then((response) => response.json()); - const domain = new Domain(); - await domain.create(apiContext); + await filterTable1.create(apiContext); + const filterTable2 = { + ...filterTable1.entity, + name: `${filterTable1.entity.name}-model`, + }; + const filterTable2Response = await apiContext + .post('/api/v1/tables', { + data: filterTable2, + }) + .then((response) => response.json()); + const domain = new Domain(); + await domain.create(apiContext); - // Add domain to table - await filterTable1.visitEntityPage(page); - await assignDomain(page, domain.responseData); - const testCases = [ - `pw_first_table_column_count_to_be_between_${uuid()}`, - `pw_second_table_column_count_to_be_between_${uuid()}`, - `pw_third_table_column_count_to_be_between_${uuid()}`, - ]; - const smilerNameTestCase = testCases.map((test) => `${test}_version_2`); - await filterTable1.patch({ - apiContext, - patchData: [ - { - op: 'add', - path: '/tags/0', - value: { - tagFQN: 'PII.None', - name: 'None', - description: 'Non PII', - source: 'Classification', - labelType: 'Manual', - state: 'Confirmed', - }, + // Add domain to table + await filterTable1.visitEntityPage(page); + await assignDomain(page, domain.responseData); + const testCases = [ + `pw_first_table_column_count_to_be_between_${uuid()}`, + `pw_second_table_column_count_to_be_between_${uuid()}`, + `pw_third_table_column_count_to_be_between_${uuid()}`, + ]; + const smilerNameTestCase = testCases.map((test) => `${test}_version_2`); + await filterTable1.patch({ + apiContext, + patchData: [ + { + op: 'add', + path: '/tags/0', + value: { + tagFQN: 'PII.None', + name: 'None', + description: 'Non PII', + source: 'Classification', + labelType: 'Manual', + state: 'Confirmed', }, - { - op: 'add', - path: '/tags/1', - value: { - tagFQN: 'Tier.Tier2', - name: 'Tier2', - source: 'Classification', - labelType: 'Manual', - state: 'Confirmed', - }, + }, + { + op: 'add', + path: '/tags/1', + value: { + tagFQN: 'Tier.Tier2', + name: 'Tier2', + source: 'Classification', + labelType: 'Manual', + state: 'Confirmed', }, - ], + }, + ], + }); + await filterTable1.createTestSuiteAndPipelines(apiContext); + const { testSuiteData: testSuite2Response } = + await filterTable1.createTestSuiteAndPipelines(apiContext, { + basicEntityReference: filterTable2Response?.['fullyQualifiedName'], }); - await filterTable1.createTestSuiteAndPipelines(apiContext); - const { testSuiteData: testSuite2Response } = - await filterTable1.createTestSuiteAndPipelines(apiContext, { - basicEntityReference: filterTable2Response?.['fullyQualifiedName'], - }); - const testCaseResult = { - result: - 'Found min=10001, max=27809 vs. the expected min=90001, max=96162.', - testCaseStatus: 'Failed', - testResultValue: [ - { - name: 'minValueForMaxInCol', - value: '10001', - }, - { - name: 'maxValueForMaxInCol', - value: '27809', - }, - ], - timestamp: getCurrentMillis(), - }; - for (let i = 0; i < testCases.length; i++) { - const testCase1 = await filterTable1.createTestCase(apiContext, { - name: testCases[i], - }); - await filterTable1.addTestCaseResult( - apiContext, - testCase1?.['fullyQualifiedName'], - testCaseResult - ); - const testCase2 = await filterTable1.createTestCase(apiContext, { - name: smilerNameTestCase[i], - entityLink: `<#E::table::${filterTable2Response?.['fullyQualifiedName']}>`, - testSuite: testSuite2Response?.['fullyQualifiedName'], - }); - await filterTable1.addTestCaseResult( - apiContext, - testCase2?.['fullyQualifiedName'], - testCaseResult - ); + const testCaseResult = { + result: 'Found min=10001, max=27809 vs. the expected min=90001, max=96162.', + testCaseStatus: 'Failed', + testResultValue: [ + { + name: 'minValueForMaxInCol', + value: '10001', + }, + { + name: 'maxValueForMaxInCol', + value: '27809', + }, + ], + timestamp: getCurrentMillis(), + }; + for (let i = 0; i < testCases.length; i++) { + const testCase1 = await filterTable1.createTestCase(apiContext, { + name: testCases[i], + }); + await filterTable1.addTestCaseResult( + apiContext, + testCase1?.['fullyQualifiedName'], + testCaseResult + ); + const testCase2 = await filterTable1.createTestCase(apiContext, { + name: smilerNameTestCase[i], + entityLink: `<#E::table::${filterTable2Response?.['fullyQualifiedName']}>`, + testSuite: testSuite2Response?.['fullyQualifiedName'], + }); + await filterTable1.addTestCaseResult( + apiContext, + testCase2?.['fullyQualifiedName'], + testCaseResult + ); + } + + const verifyFilterTestCase = async (page: Page) => { + for (const testCase of testCases) { + const element = page.locator(`[data-testid="${testCase}"]`); + + await expect(element).toBeVisible(); } + }; - const verifyFilterTestCase = async (page: Page) => { - for (const testCase of testCases) { - const element = page.locator(`[data-testid="${testCase}"]`); - + const verifyFilter2TestCase = async (page: Page, negation = false) => { + for (const testCase of smilerNameTestCase) { + const element = page.locator(`[data-testid="${testCase}"]`); + if (negation) { + await expect(element).not.toBeVisible(); + } else { await expect(element).toBeVisible(); } - }; - - const verifyFilter2TestCase = async (page: Page, negation = false) => { - for (const testCase of smilerNameTestCase) { - const element = page.locator(`[data-testid="${testCase}"]`); - if (negation) { - await expect(element).not.toBeVisible(); - } else { - await expect(element).toBeVisible(); - } - } - }; - - try { - await sidebarClick(page, SidebarItem.DATA_QUALITY); - const getTestCaseListData = page.waitForResponse( - '/api/v1/dataQuality/testCases/search/list?*' - ); - await page.click('[data-testid="by-test-cases"]'); - await getTestCaseListData; - // get all the filters - await page.click('[data-testid="advanced-filter"]'); - await page.click('[value="tableFqn"]'); - await page.click('[data-testid="advanced-filter"]'); - await page.click('[value="testPlatforms"]'); - await page.click('[data-testid="advanced-filter"]'); - await page.click('[value="lastRunRange"]'); - await page.click('[data-testid="advanced-filter"]'); - await page.click('[value="serviceName"]'); - await page.click('[data-testid="advanced-filter"]'); - await page.click('[value="tags"]'); - await page.click('[data-testid="advanced-filter"]'); - await page.click('[value="tier"]'); - - // Test case search filter - const searchTestCaseResponse = page.waitForResponse( - (url) => - url.url().includes('/api/v1/dataQuality/testCases/search/list') && - url.url().includes(testCases[0]) - ); - await page.fill( - '[data-testid="test-case-container"] [data-testid="searchbar"]', - testCases[0] - ); - await searchTestCaseResponse; - - await expect( - page.locator(`[data-testid="${testCases[0]}"]`) - ).toBeVisible(); - - // clear the search filter - const getTestCaseResponse = page.waitForResponse( - '/api/v1/dataQuality/testCases/search/list?*' - ); - await page.locator('.ant-input-clear-icon').click(); - await getTestCaseResponse; - - // Test case filter by service name - const serviceResponse = page.waitForResponse( - '/api/v1/search/query?q=*index=database_service_search_index*' - ); - await page.fill('#serviceName', filterTable1.service.name); - await serviceResponse; - - const testCaseByServiceName = page.waitForResponse( - `/api/v1/dataQuality/testCases/search/list?*serviceName=${filterTable1.service.name}*` - ); - await page - .locator('.ant-select-dropdown') - .filter({ - hasNot: page.locator('.ant-select-dropdown-hidden'), - has: page.locator(`[data-testid="${filterTable1.service.name}"]`), - }) - .click(); - await testCaseByServiceName; - await verifyFilterTestCase(page); - await verifyFilter2TestCase(page); - - // remove service filter - await page.click('[data-testid="advanced-filter"]'); - const getTestCase = page.waitForResponse( - '/api/v1/dataQuality/testCases/search/list?*' - ); - await page.click('[value="serviceName"]'); - await getTestCase; - - // Test case filter by Tags - const tagResponse = page.waitForResponse( - '/api/v1/search/query?q=*index=tag_search_index*' - ); - await page - .getByTestId('tags-select-filter') - .locator('div') - .filter({ hasText: 'Tags' }) - .click(); - await page.fill('#tags', 'PII.None'); - await tagResponse; - - const getTestCaseByTag = page.waitForResponse( - '/api/v1/dataQuality/testCases/search/list?*tags=PII.None*' - ); - await page - .locator('.ant-select-dropdown') - .filter({ - hasNot: page.locator('.ant-select-dropdown-hidden'), - has: page.locator(`[data-testid="PII.None"]`), - }) - .click(); - await getTestCaseByTag; - await verifyFilterTestCase(page); - await verifyFilter2TestCase(page, true); - - // remove tags filter - await page.click('[data-testid="advanced-filter"]'); - const getTestCaseWithoutTag = page.waitForResponse( - '/api/v1/dataQuality/testCases/search/list?*' - ); - await page.click('[value="tags"]'); - await getTestCaseWithoutTag; - - // Test case filter by Tier - - await page.click('#tier'); - await page.fill('#tier', 'Tier2'); - await page.waitForLoadState('domcontentloaded'); - const getTestCaseByTier = page.waitForResponse( - '/api/v1/dataQuality/testCases/search/list?*tier=Tier.Tier2*' - ); - await page.getByTestId('Tier.Tier2').getByText('Tier.Tier2').click(); - await getTestCaseByTier; - await verifyFilterTestCase(page); - await verifyFilter2TestCase(page, true); - - // remove tier filter - await page.click('[data-testid="advanced-filter"]'); - const getTestCaseWithoutTier = page.waitForResponse( - '/api/v1/dataQuality/testCases/search/list?*' - ); - await page.click('[value="tier"]'); - await getTestCaseWithoutTier; - - // Test case filter by table name - const tableSearchResponse = page.waitForResponse( - `/api/v1/search/query?q=*index=table_search_index*` - ); - await page.fill('#tableFqn', filterTable1.entity.name); - await tableSearchResponse; - const getTestCaseByTable = page.waitForResponse( - `/api/v1/dataQuality/testCases/search/list?*entityLink=*${filterTable1.entity.name}*` - ); - - await page - .getByTestId(filterTable1.entityResponseData?.['fullyQualifiedName']) - .click(); - await getTestCaseByTable; - await verifyFilterTestCase(page); - await verifyFilter2TestCase(page, true); - - // Test case filter by test type column - const testCaseTypeByColumn = page.waitForResponse( - `/api/v1/dataQuality/testCases/search/list?*testCaseType=column*` - ); - await page.getByTestId('test-case-type-select-filter').click(); - await page.getByTitle('Column').click(); - await testCaseTypeByColumn; - - await expect( - page.locator('[data-testid="search-error-placeholder"]') - ).toBeVisible(); - - // Test case filter by test type table - const testCaseTypeByTable = page.waitForResponse( - `/api/v1/dataQuality/testCases/search/list?*testCaseType=table*` - ); - await page.getByTestId('test-case-type-select-filter').click(); - await page - .locator('.ant-select-dropdown') - .filter({ - hasNot: page.locator('.ant-select-dropdown-hidden'), - has: page.locator(`[title="Table"]`), - hasText: 'Table', - }) - .click(); - await testCaseTypeByTable; - await verifyFilterTestCase(page); - - // Test case filter by test type all - const testCaseTypeByAll = page.waitForResponse( - `/api/v1/dataQuality/testCases/search/list?*testCaseType=all*` - ); - await page.getByTestId('test-case-type-select-filter').click(); - await page.getByTitle('All').nth(1).click(); - await testCaseTypeByAll; - - // Test case filter by status - const testCaseStatusBySuccess = page.waitForResponse( - `/api/v1/dataQuality/testCases/search/list?*testCaseStatus=Success*` - ); - await page.getByTestId('status-select-filter').click(); - await page.getByTitle('Success').click(); - await testCaseStatusBySuccess; - - await expect( - page.locator('[data-testid="search-error-placeholder"]') - ).toBeVisible(); - - // Test case filter by status - const testCaseStatusByFailed = page.waitForResponse( - `/api/v1/dataQuality/testCases/search/list?*testCaseStatus=Failed*` - ); - await page.getByTestId('status-select-filter').click(); - await page.getByTitle('Failed').click(); - await testCaseStatusByFailed; - await verifyFilterTestCase(page); - await verifyFilter2TestCase(page, true); - - // Test case filter by platform - const testCasePlatformByDBT = page.waitForResponse( - `/api/v1/dataQuality/testCases/search/list?*testPlatforms=DBT*` - ); - await page.getByTestId('platform-select-filter').click(); - await page.getByTitle('DBT').click(); - await testCasePlatformByDBT; - - await expect( - page.locator('[data-testid="search-error-placeholder"]') - ).toBeVisible(); - - const getTestCaseWithoutPlatform = page.waitForResponse( - '/api/v1/dataQuality/testCases/search/list?*' - ); - await page - .getByTestId('platform-select-filter') - .locator('.ant-select-clear') - .click(); - await getTestCaseWithoutPlatform; - const testCasePlatformByOpenMetadata = page.waitForResponse( - `/api/v1/dataQuality/testCases/search/list?*testPlatforms=OpenMetadata*` - ); - await page.getByTestId('platform-select-filter').click(); - await page.getByTitle('OpenMetadata').click(); - await testCasePlatformByOpenMetadata; - await clickOutside(page); - await verifyFilterTestCase(page); - await verifyFilter2TestCase(page, true); - const url = page.url(); - await page.reload(); - - await expect(page.url()).toBe(url); - - await page.getByTestId('advanced-filter').click(); - await page.click('[value="testPlatforms"]'); - await page.waitForTimeout(200); - - await expect( - page.getByTestId('platform-select-filter') - ).not.toBeVisible(); - - await page.reload(); - - await expect(page.locator('[value="tier"]')).not.toBeVisible(); - - // Apply domain globally - await page.getByTestId('domain-dropdown').click(); - - await page - .getByTestId(`tag-${domain.responseData.fullyQualifiedName}`) - .click(); - await page.getByTestId('saveAssociatedTag').click(); - - await sidebarClick(page, SidebarItem.DATA_QUALITY); - const getTestCaseList = page.waitForResponse( - '/api/v1/dataQuality/testCases/search/list?*' - ); - await page.click('[data-testid="by-test-cases"]'); - await getTestCaseList; - await verifyFilterTestCase(page); - await verifyFilter2TestCase(page, true); - await visitDataQualityTab(page, filterTable1); - const searchTestCase = page.waitForResponse( - (url) => - url.url().includes('/api/v1/dataQuality/testCases/search/list') && - url.url().includes(testCases[0]) - ); - await page - .getByTestId('table-profiler-container') - .getByTestId('searchbar') - .fill(testCases[0]); - await searchTestCase; - - await expect( - page.locator(`[data-testid="${testCases[0]}"]`) - ).toBeVisible(); - await expect( - page.locator(`[data-testid="${testCases[1]}"]`) - ).not.toBeVisible(); - await expect( - page.locator(`[data-testid="${testCases[2]}"]`) - ).not.toBeVisible(); - } finally { - await filterTable1.delete(apiContext); - await domain.delete(apiContext); - await afterAction(); } + }; + + try { + await sidebarClick(page, SidebarItem.DATA_QUALITY); + const getTestCaseListData = page.waitForResponse( + '/api/v1/dataQuality/testCases/search/list?*' + ); + await page.click('[data-testid="by-test-cases"]'); + await getTestCaseListData; + // get all the filters + await page.click('[data-testid="advanced-filter"]'); + await page.click('[value="tableFqn"]'); + await page.click('[data-testid="advanced-filter"]'); + await page.click('[value="testPlatforms"]'); + await page.click('[data-testid="advanced-filter"]'); + await page.click('[value="lastRunRange"]'); + await page.click('[data-testid="advanced-filter"]'); + await page.click('[value="serviceName"]'); + await page.click('[data-testid="advanced-filter"]'); + await page.click('[value="tags"]'); + await page.click('[data-testid="advanced-filter"]'); + await page.click('[value="tier"]'); + + // Test case search filter + const searchTestCaseResponse = page.waitForResponse( + (url) => + url.url().includes('/api/v1/dataQuality/testCases/search/list') && + url.url().includes(testCases[0]) + ); + await page.fill( + '[data-testid="test-case-container"] [data-testid="searchbar"]', + testCases[0] + ); + await searchTestCaseResponse; + + await expect(page.locator(`[data-testid="${testCases[0]}"]`)).toBeVisible(); + + // clear the search filter + const getTestCaseResponse = page.waitForResponse( + '/api/v1/dataQuality/testCases/search/list?*' + ); + await page.locator('.ant-input-clear-icon').click(); + await getTestCaseResponse; + + // Test case filter by service name + const serviceResponse = page.waitForResponse( + '/api/v1/search/query?q=*index=database_service_search_index*' + ); + await page.fill('#serviceName', filterTable1.service.name); + await serviceResponse; + + const testCaseByServiceName = page.waitForResponse( + `/api/v1/dataQuality/testCases/search/list?*serviceName=${filterTable1.service.name}*` + ); + await page + .locator('.ant-select-dropdown') + .filter({ + hasNot: page.locator('.ant-select-dropdown-hidden'), + has: page.locator(`[data-testid="${filterTable1.service.name}"]`), + }) + .click(); + await testCaseByServiceName; + await verifyFilterTestCase(page); + await verifyFilter2TestCase(page); + + // remove service filter + await page.click('[data-testid="advanced-filter"]'); + const getTestCase = page.waitForResponse( + '/api/v1/dataQuality/testCases/search/list?*' + ); + await page.click('[value="serviceName"]'); + await getTestCase; + + // Test case filter by Tags + const tagResponse = page.waitForResponse( + '/api/v1/search/query?q=*index=tag_search_index*' + ); + await page + .getByTestId('tags-select-filter') + .locator('div') + .filter({ hasText: 'Tags' }) + .click(); + await page.fill('#tags', 'PII.None'); + await tagResponse; + + const getTestCaseByTag = page.waitForResponse( + '/api/v1/dataQuality/testCases/search/list?*tags=PII.None*' + ); + await page + .locator('.ant-select-dropdown') + .filter({ + hasNot: page.locator('.ant-select-dropdown-hidden'), + has: page.locator(`[data-testid="PII.None"]`), + }) + .click(); + await getTestCaseByTag; + await verifyFilterTestCase(page); + await verifyFilter2TestCase(page, true); + + // remove tags filter + await page.click('[data-testid="advanced-filter"]'); + const getTestCaseWithoutTag = page.waitForResponse( + '/api/v1/dataQuality/testCases/search/list?*' + ); + await page.click('[value="tags"]'); + await getTestCaseWithoutTag; + + // Test case filter by Tier + + await page.click('#tier'); + await page.fill('#tier', 'Tier2'); + await page.waitForLoadState('domcontentloaded'); + const getTestCaseByTier = page.waitForResponse( + '/api/v1/dataQuality/testCases/search/list?*tier=Tier.Tier2*' + ); + await page.getByTestId('Tier.Tier2').getByText('Tier.Tier2').click(); + await getTestCaseByTier; + await verifyFilterTestCase(page); + await verifyFilter2TestCase(page, true); + + // remove tier filter + await page.click('[data-testid="advanced-filter"]'); + const getTestCaseWithoutTier = page.waitForResponse( + '/api/v1/dataQuality/testCases/search/list?*' + ); + await page.click('[value="tier"]'); + await getTestCaseWithoutTier; + + // Test case filter by table name + const tableSearchResponse = page.waitForResponse( + `/api/v1/search/query?q=*index=table_search_index*` + ); + await page.fill('#tableFqn', filterTable1.entity.name); + await tableSearchResponse; + const getTestCaseByTable = page.waitForResponse( + `/api/v1/dataQuality/testCases/search/list?*entityLink=*${filterTable1.entity.name}*` + ); + + await page + .getByTestId(filterTable1.entityResponseData?.['fullyQualifiedName']) + .click(); + await getTestCaseByTable; + await verifyFilterTestCase(page); + await verifyFilter2TestCase(page, true); + + // Test case filter by test type column + const testCaseTypeByColumn = page.waitForResponse( + `/api/v1/dataQuality/testCases/search/list?*testCaseType=column*` + ); + await page.getByTestId('test-case-type-select-filter').click(); + await page.getByTitle('Column').click(); + await testCaseTypeByColumn; + + await expect( + page.locator('[data-testid="search-error-placeholder"]') + ).toBeVisible(); + + // Test case filter by test type table + const testCaseTypeByTable = page.waitForResponse( + `/api/v1/dataQuality/testCases/search/list?*testCaseType=table*` + ); + await page.getByTestId('test-case-type-select-filter').click(); + await page + .locator('.ant-select-dropdown') + .filter({ + hasNot: page.locator('.ant-select-dropdown-hidden'), + has: page.locator(`[title="Table"]`), + hasText: 'Table', + }) + .click(); + await testCaseTypeByTable; + await verifyFilterTestCase(page); + + // Test case filter by test type all + const testCaseTypeByAll = page.waitForResponse( + `/api/v1/dataQuality/testCases/search/list?*testCaseType=all*` + ); + await page.getByTestId('test-case-type-select-filter').click(); + await page.getByTitle('All').nth(1).click(); + await testCaseTypeByAll; + + // Test case filter by status + const testCaseStatusBySuccess = page.waitForResponse( + `/api/v1/dataQuality/testCases/search/list?*testCaseStatus=Success*` + ); + await page.getByTestId('status-select-filter').click(); + await page.getByTitle('Success').click(); + await testCaseStatusBySuccess; + + await expect( + page.locator('[data-testid="search-error-placeholder"]') + ).toBeVisible(); + + // Test case filter by status + const testCaseStatusByFailed = page.waitForResponse( + `/api/v1/dataQuality/testCases/search/list?*testCaseStatus=Failed*` + ); + await page.getByTestId('status-select-filter').click(); + await page.getByTitle('Failed').click(); + await testCaseStatusByFailed; + await verifyFilterTestCase(page); + await verifyFilter2TestCase(page, true); + + // Test case filter by platform + const testCasePlatformByDBT = page.waitForResponse( + `/api/v1/dataQuality/testCases/search/list?*testPlatforms=DBT*` + ); + await page.getByTestId('platform-select-filter').click(); + await page.getByTitle('DBT').click(); + await testCasePlatformByDBT; + + await expect( + page.locator('[data-testid="search-error-placeholder"]') + ).toBeVisible(); + + const getTestCaseWithoutPlatform = page.waitForResponse( + '/api/v1/dataQuality/testCases/search/list?*' + ); + await page + .getByTestId('platform-select-filter') + .locator('.ant-select-clear') + .click(); + await getTestCaseWithoutPlatform; + const testCasePlatformByOpenMetadata = page.waitForResponse( + `/api/v1/dataQuality/testCases/search/list?*testPlatforms=OpenMetadata*` + ); + await page.getByTestId('platform-select-filter').click(); + await page.getByTitle('OpenMetadata').click(); + await testCasePlatformByOpenMetadata; + await clickOutside(page); + await verifyFilterTestCase(page); + await verifyFilter2TestCase(page, true); + const url = page.url(); + await page.reload(); + + await expect(page.url()).toBe(url); + + await page.getByTestId('advanced-filter').click(); + await page.click('[value="testPlatforms"]'); + await page.waitForTimeout(200); + + await expect(page.getByTestId('platform-select-filter')).not.toBeVisible(); + + await page.reload(); + + await expect(page.locator('[value="tier"]')).not.toBeVisible(); + + // Apply domain globally + await page.getByTestId('domain-dropdown').click(); + + await page + .getByTestId(`tag-${domain.responseData.fullyQualifiedName}`) + .click(); + await page.getByTestId('saveAssociatedTag').click(); + + await sidebarClick(page, SidebarItem.DATA_QUALITY); + const getTestCaseList = page.waitForResponse( + '/api/v1/dataQuality/testCases/search/list?*' + ); + await page.click('[data-testid="by-test-cases"]'); + await getTestCaseList; + await verifyFilterTestCase(page); + await verifyFilter2TestCase(page, true); + await visitDataQualityTab(page, filterTable1); + const searchTestCase = page.waitForResponse( + (url) => + url.url().includes('/api/v1/dataQuality/testCases/search/list') && + url.url().includes(testCases[0]) + ); + await page + .getByTestId('table-profiler-container') + .getByTestId('searchbar') + .fill(testCases[0]); + await searchTestCase; + + await expect(page.locator(`[data-testid="${testCases[0]}"]`)).toBeVisible(); + await expect( + page.locator(`[data-testid="${testCases[1]}"]`) + ).not.toBeVisible(); + await expect( + page.locator(`[data-testid="${testCases[2]}"]`) + ).not.toBeVisible(); + } finally { + await filterTable1.delete(apiContext); + await domain.delete(apiContext); + await afterAction(); } -); +}); diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/TestSuite.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/TestSuite.spec.ts index 28191c33139..cd4adcc1cb5 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/TestSuite.spec.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/TestSuite.spec.ts @@ -64,7 +64,7 @@ test.beforeEach(async ({ page }) => { await redirectToHomePage(page); }); -test.skip('Logical TestSuite', async ({ page }) => { +test('Logical TestSuite', async ({ page }) => { test.slow(); const NEW_TEST_SUITE = {