From a5bf6755ba9e79f602313d0f41afb1c07ae4a4bc Mon Sep 17 00:00:00 2001 From: Pranita Date: Sun, 15 Dec 2024 15:44:42 +0530 Subject: [PATCH 1/4] test: add e2e tests --- .../playwright/e2e/Pages/ExploreTree.spec.ts | 83 +++++++++++++++++++ .../resources/ui/playwright/utils/explore.ts | 49 +++++++++++ 2 files changed, 132 insertions(+) diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/ExploreTree.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/ExploreTree.spec.ts index 346131401181..39a8797641a9 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/ExploreTree.spec.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/ExploreTree.spec.ts @@ -13,10 +13,19 @@ import test, { expect } from '@playwright/test'; import { get } from 'lodash'; import { SidebarItem } from '../../constant/sidebar'; +import { ApiEndpointClass } from '../../support/entity/ApiEndpointClass'; +import { ContainerClass } from '../../support/entity/ContainerClass'; +import { DashboardClass } from '../../support/entity/DashboardClass'; import { EntityTypeEndpoint } from '../../support/entity/Entity.interface'; +import { MlModelClass } from '../../support/entity/MlModelClass'; +import { PipelineClass } from '../../support/entity/PipelineClass'; +import { SearchIndexClass } from '../../support/entity/SearchIndexClass'; +import { StoredProcedureClass } from '../../support/entity/StoredProcedureClass'; import { TableClass } from '../../support/entity/TableClass'; +import { TopicClass } from '../../support/entity/TopicClass'; import { getApiContext, redirectToHomePage } from '../../utils/common'; import { updateDisplayNameForEntity } from '../../utils/entity'; +import { validateBuckets } from '../../utils/explore'; import { sidebarClick } from '../../utils/sidebar'; // use the admin user to login @@ -192,6 +201,44 @@ test.describe('Explore Tree scenarios ', () => { }); test.describe('Explore page', () => { + const table = new TableClass(); + const dashboard = new DashboardClass(); + const storedProcedure = new StoredProcedureClass(); + const pipeline = new PipelineClass(); + const container = new ContainerClass(); + const apiEndpoint = new ApiEndpointClass(); + const topic = new TopicClass(); + const searchIndex = new SearchIndexClass(); + const mlModel = new MlModelClass(); + + test.beforeEach('Setup pre-requisits', async ({ page }) => { + const { apiContext, afterAction } = await getApiContext(page); + await table.create(apiContext); + await dashboard.create(apiContext); + await storedProcedure.create(apiContext); + await pipeline.create(apiContext); + await container.create(apiContext); + await apiEndpoint.create(apiContext); + await topic.create(apiContext); + await searchIndex.create(apiContext); + await mlModel.create(apiContext); + await afterAction(); + }); + + test.afterEach('Cleanup', async ({ page }) => { + const { apiContext, afterAction } = await getApiContext(page); + await table.delete(apiContext); + await dashboard.delete(apiContext); + await storedProcedure.delete(apiContext); + await pipeline.delete(apiContext); + await container.delete(apiContext); + await apiEndpoint.delete(apiContext); + await topic.delete(apiContext); + await searchIndex.delete(apiContext); + await mlModel.delete(apiContext); + await afterAction(); + }); + test('Check the listing of tags', async ({ page }) => { await page .locator('div') @@ -214,4 +261,40 @@ test.describe('Explore page', () => { expect(jsonResponse.hits.hits.length).toBeGreaterThan(0); }); + + test('Check listing of entities when index is dataAsset', async ({ + page, + }) => { + const index = 'dataAsset'; + + page.on('response', async (response) => { + if ( + response + .url() + .includes( + `/api/v1/search/query?q=&index=${index}&from=0&size=10&deleted=false&query_filter=%7B%22query%22:%7B%22bool%22:%7B%7D%7D%7D&sort_field=totalVotes&sort_order=desc` + ) && + response.status() === 200 + ) { + await validateBuckets(response); + } + }); + }); + + test('Check listing of entities when index is all', async ({ page }) => { + const index = 'all'; + + page.on('response', async (response) => { + if ( + response + .url() + .includes( + `/api/v1/search/query?q=&index=${index}&from=0&size=10&deleted=false&query_filter=%7B%22query%22:%7B%22bool%22:%7B%7D%7D%7D&sort_field=totalVotes&sort_order=desc` + ) && + response.status() === 200 + ) { + await validateBuckets(response); + } + }); + }); }); diff --git a/openmetadata-ui/src/main/resources/ui/playwright/utils/explore.ts b/openmetadata-ui/src/main/resources/ui/playwright/utils/explore.ts index fbe65243af9d..47a4d5239710 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/utils/explore.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/utils/explore.ts @@ -13,6 +13,11 @@ import { expect } from '@playwright/test'; import { Page } from 'playwright'; +export interface Bucket { + key: string; + doc_count: number; +} + export const searchAndClickOnOption = async ( page: Page, filter: { key: string; label: string; value?: string }, @@ -116,3 +121,47 @@ export const selectDataAssetFilter = async ( await page.getByTestId(`${filterValue}-checkbox`).check(); await page.getByTestId('update-btn').click(); }; + +export const checkBucket = (bucket: Bucket | undefined) => { + expect(bucket).toBeDefined(); + + if (bucket) { + expect(bucket.doc_count).toBeGreaterThan(0); + } +}; + +export const findBucket = (buckets: Bucket[], key: string) => + buckets.find((bucket) => bucket.key === key); + +export const validateBuckets = async (response: any) => { + const jsonResponse = await response.json(); + + const buckets = jsonResponse.aggregations?.['sterms#entityType']?.buckets; + + expect(buckets).toBeDefined(); + expect(Array.isArray(buckets)).toBeTruthy(); + expect(buckets.length).toBeGreaterThan(0); + + const keys = [ + 'table', + 'databaseSchema', + 'chart', + 'storedProcedure', + 'database', + 'pipeline', + 'dashboard', + 'container', + 'tag', + 'dashboardDataModel', + 'apiEndpoint', + 'topic', + 'apiCollection', + 'searchIndex', + 'mlModel', + ]; + + keys.forEach((key) => { + const bucket = findBucket(buckets, key); + checkBucket(bucket); + }); +}; From 3718032e6afe7d798851db7f1772f6af50285140 Mon Sep 17 00:00:00 2001 From: Pranita Date: Sun, 15 Dec 2024 17:00:34 +0530 Subject: [PATCH 2/4] test for listing of entities when index is dataAsset and all --- .../ui/playwright/constant/explore.ts | 29 ++++++++ .../playwright/e2e/Pages/ExploreTree.spec.ts | 36 ++-------- .../resources/ui/playwright/utils/explore.ts | 71 ++++++++----------- 3 files changed, 67 insertions(+), 69 deletions(-) create mode 100644 openmetadata-ui/src/main/resources/ui/playwright/constant/explore.ts diff --git a/openmetadata-ui/src/main/resources/ui/playwright/constant/explore.ts b/openmetadata-ui/src/main/resources/ui/playwright/constant/explore.ts new file mode 100644 index 000000000000..b72852d647f9 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/playwright/constant/explore.ts @@ -0,0 +1,29 @@ +/* + * 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. + */ +export const EXPECTED_BUCKETS = [ + 'table', + 'databaseSchema', + 'chart', + 'storedProcedure', + 'database', + 'pipeline', + 'dashboard', + 'container', + 'tag', + 'dashboardDataModel', + 'apiEndpoint', + 'topic', + 'apiCollection', + 'searchIndex', + 'mlmodel', +]; diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/ExploreTree.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/ExploreTree.spec.ts index 39a8797641a9..187db9913403 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/ExploreTree.spec.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/ExploreTree.spec.ts @@ -16,6 +16,7 @@ import { SidebarItem } from '../../constant/sidebar'; import { ApiEndpointClass } from '../../support/entity/ApiEndpointClass'; import { ContainerClass } from '../../support/entity/ContainerClass'; import { DashboardClass } from '../../support/entity/DashboardClass'; +import { DashboardDataModelClass } from '../../support/entity/DashboardDataModelClass'; import { EntityTypeEndpoint } from '../../support/entity/Entity.interface'; import { MlModelClass } from '../../support/entity/MlModelClass'; import { PipelineClass } from '../../support/entity/PipelineClass'; @@ -25,7 +26,7 @@ import { TableClass } from '../../support/entity/TableClass'; import { TopicClass } from '../../support/entity/TopicClass'; import { getApiContext, redirectToHomePage } from '../../utils/common'; import { updateDisplayNameForEntity } from '../../utils/entity'; -import { validateBuckets } from '../../utils/explore'; +import { validateBucketsForIndex } from '../../utils/explore'; import { sidebarClick } from '../../utils/sidebar'; // use the admin user to login @@ -209,6 +210,7 @@ test.describe('Explore page', () => { const apiEndpoint = new ApiEndpointClass(); const topic = new TopicClass(); const searchIndex = new SearchIndexClass(); + const dashboardDataModel = new DashboardDataModelClass(); const mlModel = new MlModelClass(); test.beforeEach('Setup pre-requisits', async ({ page }) => { @@ -221,6 +223,7 @@ test.describe('Explore page', () => { await apiEndpoint.create(apiContext); await topic.create(apiContext); await searchIndex.create(apiContext); + await dashboardDataModel.create(apiContext); await mlModel.create(apiContext); await afterAction(); }); @@ -235,6 +238,7 @@ test.describe('Explore page', () => { await apiEndpoint.delete(apiContext); await topic.delete(apiContext); await searchIndex.delete(apiContext); + await dashboardDataModel.delete(apiContext); await mlModel.delete(apiContext); await afterAction(); }); @@ -265,36 +269,10 @@ test.describe('Explore page', () => { test('Check listing of entities when index is dataAsset', async ({ page, }) => { - const index = 'dataAsset'; - - page.on('response', async (response) => { - if ( - response - .url() - .includes( - `/api/v1/search/query?q=&index=${index}&from=0&size=10&deleted=false&query_filter=%7B%22query%22:%7B%22bool%22:%7B%7D%7D%7D&sort_field=totalVotes&sort_order=desc` - ) && - response.status() === 200 - ) { - await validateBuckets(response); - } - }); + await validateBucketsForIndex(page, 'dataAsset'); }); test('Check listing of entities when index is all', async ({ page }) => { - const index = 'all'; - - page.on('response', async (response) => { - if ( - response - .url() - .includes( - `/api/v1/search/query?q=&index=${index}&from=0&size=10&deleted=false&query_filter=%7B%22query%22:%7B%22bool%22:%7B%7D%7D%7D&sort_field=totalVotes&sort_order=desc` - ) && - response.status() === 200 - ) { - await validateBuckets(response); - } - }); + await validateBucketsForIndex(page, 'all'); }); }); diff --git a/openmetadata-ui/src/main/resources/ui/playwright/utils/explore.ts b/openmetadata-ui/src/main/resources/ui/playwright/utils/explore.ts index 47a4d5239710..03ff9d1e344c 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/utils/explore.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/utils/explore.ts @@ -12,6 +12,8 @@ */ import { expect } from '@playwright/test'; import { Page } from 'playwright'; +import { EXPECTED_BUCKETS } from '../constant/explore'; +import { getApiContext } from './common'; export interface Bucket { key: string; @@ -122,46 +124,35 @@ export const selectDataAssetFilter = async ( await page.getByTestId('update-btn').click(); }; -export const checkBucket = (bucket: Bucket | undefined) => { - expect(bucket).toBeDefined(); +export const validateBucketsForIndex = async (page: Page, index: string) => { + const { apiContext } = await getApiContext(page); - if (bucket) { - expect(bucket.doc_count).toBeGreaterThan(0); - } -}; + return await expect + .poll( + async () => { + const response = await apiContext + .get( + `/api/v1/search/query?q=&index=${index}&from=0&size=10&deleted=false&query_filter=%7B%22query%22:%7B%22bool%22:%7B%7D%7D%7D&sort_field=totalVotes&sort_order=desc` + ) + .then((res) => res.json()); -export const findBucket = (buckets: Bucket[], key: string) => - buckets.find((bucket) => bucket.key === key); - -export const validateBuckets = async (response: any) => { - const jsonResponse = await response.json(); - - const buckets = jsonResponse.aggregations?.['sterms#entityType']?.buckets; - - expect(buckets).toBeDefined(); - expect(Array.isArray(buckets)).toBeTruthy(); - expect(buckets.length).toBeGreaterThan(0); - - const keys = [ - 'table', - 'databaseSchema', - 'chart', - 'storedProcedure', - 'database', - 'pipeline', - 'dashboard', - 'container', - 'tag', - 'dashboardDataModel', - 'apiEndpoint', - 'topic', - 'apiCollection', - 'searchIndex', - 'mlModel', - ]; - - keys.forEach((key) => { - const bucket = findBucket(buckets, key); - checkBucket(bucket); - }); + const buckets = + response.aggregations?.['sterms#entityType']?.buckets || []; + + // Check if each key in expectedBuckets has a doc_count > 0 + const validBuckets = EXPECTED_BUCKETS.every((expectedKey) => { + const bucket = buckets.find((b: Bucket) => b.key === expectedKey); + + return bucket ? bucket.doc_count > 0 : false; + }); + + return validBuckets; + }, + { + message: `Waiting for the expected buckets with doc_count > 0 for index "${index}"`, + timeout: 350_000, + intervals: [40_000, 30_000], + } + ) + .toBe(true); }; From 55131e6d466266f7d1cf27d39add7434ccec2c76 Mon Sep 17 00:00:00 2001 From: Pranita Date: Mon, 16 Dec 2024 10:49:14 +0530 Subject: [PATCH 3/4] fix: validateBucketsForIndex function --- .../resources/ui/playwright/utils/explore.ts | 46 ++++++++----------- 1 file changed, 19 insertions(+), 27 deletions(-) diff --git a/openmetadata-ui/src/main/resources/ui/playwright/utils/explore.ts b/openmetadata-ui/src/main/resources/ui/playwright/utils/explore.ts index 03ff9d1e344c..839be6a6208b 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/utils/explore.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/utils/explore.ts @@ -127,32 +127,24 @@ export const selectDataAssetFilter = async ( export const validateBucketsForIndex = async (page: Page, index: string) => { const { apiContext } = await getApiContext(page); - return await expect - .poll( - async () => { - const response = await apiContext - .get( - `/api/v1/search/query?q=&index=${index}&from=0&size=10&deleted=false&query_filter=%7B%22query%22:%7B%22bool%22:%7B%7D%7D%7D&sort_field=totalVotes&sort_order=desc` - ) - .then((res) => res.json()); - - const buckets = - response.aggregations?.['sterms#entityType']?.buckets || []; - - // Check if each key in expectedBuckets has a doc_count > 0 - const validBuckets = EXPECTED_BUCKETS.every((expectedKey) => { - const bucket = buckets.find((b: Bucket) => b.key === expectedKey); - - return bucket ? bucket.doc_count > 0 : false; - }); - - return validBuckets; - }, - { - message: `Waiting for the expected buckets with doc_count > 0 for index "${index}"`, - timeout: 350_000, - intervals: [40_000, 30_000], - } + const response = await apiContext + .get( + `/api/v1/search/query?q=&index=${index}&from=0&size=10&deleted=false&query_filter=%7B%22query%22:%7B%22bool%22:%7B%7D%7D%7D&sort_field=totalVotes&sort_order=desc` ) - .toBe(true); + .then((res) => res.json()); + + const buckets = response.aggregations?.['sterms#entityType']?.buckets ?? []; + + // Verify all expected buckets exist and have doc_count > 0 + const invalidBuckets = EXPECTED_BUCKETS.filter((expectedKey) => { + const bucket = buckets.find((b: Bucket) => b.key === expectedKey); + + return !bucket || bucket.doc_count <= 0; + }); + + if (invalidBuckets.length > 0) { + throw new Error( + `Buckets missing or with doc_count <= 0: ${invalidBuckets.join(', ')}` + ); + } }; From 364504030f805cb0079319ee3434c65aa2b492bb Mon Sep 17 00:00:00 2001 From: Pranita Date: Mon, 16 Dec 2024 12:06:01 +0530 Subject: [PATCH 4/4] fix: add expect to bucket and its doc_count --- .../ui/playwright/constant/explore.ts | 1 + .../playwright/e2e/Pages/ExploreTree.spec.ts | 8 ++++++++ .../resources/ui/playwright/utils/explore.ts | 18 +++++++++--------- 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/openmetadata-ui/src/main/resources/ui/playwright/constant/explore.ts b/openmetadata-ui/src/main/resources/ui/playwright/constant/explore.ts index b72852d647f9..f971a6eacc94 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/constant/explore.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/constant/explore.ts @@ -12,6 +12,7 @@ */ export const EXPECTED_BUCKETS = [ 'table', + 'glossaryTerm', 'databaseSchema', 'chart', 'storedProcedure', diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/ExploreTree.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/ExploreTree.spec.ts index 187db9913403..0fe6fc1b7dfa 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/ExploreTree.spec.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/ExploreTree.spec.ts @@ -24,6 +24,8 @@ import { SearchIndexClass } from '../../support/entity/SearchIndexClass'; import { StoredProcedureClass } from '../../support/entity/StoredProcedureClass'; import { TableClass } from '../../support/entity/TableClass'; import { TopicClass } from '../../support/entity/TopicClass'; +import { Glossary } from '../../support/glossary/Glossary'; +import { GlossaryTerm } from '../../support/glossary/GlossaryTerm'; import { getApiContext, redirectToHomePage } from '../../utils/common'; import { updateDisplayNameForEntity } from '../../utils/entity'; import { validateBucketsForIndex } from '../../utils/explore'; @@ -203,6 +205,8 @@ test.describe('Explore Tree scenarios ', () => { test.describe('Explore page', () => { const table = new TableClass(); + const glossary = new Glossary(); + const glossaryTerm = new GlossaryTerm(glossary); const dashboard = new DashboardClass(); const storedProcedure = new StoredProcedureClass(); const pipeline = new PipelineClass(); @@ -216,6 +220,8 @@ test.describe('Explore page', () => { test.beforeEach('Setup pre-requisits', async ({ page }) => { const { apiContext, afterAction } = await getApiContext(page); await table.create(apiContext); + await glossary.create(apiContext); + await glossaryTerm.create(apiContext); await dashboard.create(apiContext); await storedProcedure.create(apiContext); await pipeline.create(apiContext); @@ -231,6 +237,8 @@ test.describe('Explore page', () => { test.afterEach('Cleanup', async ({ page }) => { const { apiContext, afterAction } = await getApiContext(page); await table.delete(apiContext); + await glossary.delete(apiContext); + await glossaryTerm.delete(apiContext); await dashboard.delete(apiContext); await storedProcedure.delete(apiContext); await pipeline.delete(apiContext); diff --git a/openmetadata-ui/src/main/resources/ui/playwright/utils/explore.ts b/openmetadata-ui/src/main/resources/ui/playwright/utils/explore.ts index 839be6a6208b..f3bb5b71ac0b 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/utils/explore.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/utils/explore.ts @@ -135,16 +135,16 @@ export const validateBucketsForIndex = async (page: Page, index: string) => { const buckets = response.aggregations?.['sterms#entityType']?.buckets ?? []; - // Verify all expected buckets exist and have doc_count > 0 - const invalidBuckets = EXPECTED_BUCKETS.filter((expectedKey) => { + EXPECTED_BUCKETS.forEach((expectedKey) => { const bucket = buckets.find((b: Bucket) => b.key === expectedKey); - return !bucket || bucket.doc_count <= 0; - }); + // Expect the bucket to exist + expect(bucket, `Bucket with key "${expectedKey}" is missing`).toBeDefined(); - if (invalidBuckets.length > 0) { - throw new Error( - `Buckets missing or with doc_count <= 0: ${invalidBuckets.join(', ')}` - ); - } + // Expect the bucket's doc_count to be greater than 0 + expect( + bucket?.doc_count, + `Bucket "${expectedKey}" has doc_count <= 0` + ).toBeGreaterThan(0); + }); };