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 9cebb02a1091..6badc570f924 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 @@ -17,6 +17,7 @@ import { SidebarItem } from '../../constant/sidebar'; import { DataProduct } from '../../support/domain/DataProduct'; import { Domain } from '../../support/domain/Domain'; import { ApiEndpointClass } from '../../support/entity/ApiEndpointClass'; +import { ChartClass } from '../../support/entity/ChartClass'; import { ContainerClass } from '../../support/entity/ContainerClass'; import { DashboardClass } from '../../support/entity/DashboardClass'; import { DashboardDataModelClass } from '../../support/entity/DashboardDataModelClass'; @@ -290,6 +291,7 @@ test.describe('Explore page', () => { const glossary = new Glossary(); const glossaryTerm = new GlossaryTerm(glossary); const dashboard = new DashboardClass(); + const chart = new ChartClass(); const storedProcedure = new StoredProcedureClass(); const pipeline = new PipelineClass(); const container = new ContainerClass(); @@ -319,6 +321,7 @@ test.describe('Explore page', () => { await glossary.create(apiContext); await glossaryTerm.create(apiContext); await dashboard.create(apiContext); + await chart.create(apiContext); await storedProcedure.create(apiContext); await pipeline.create(apiContext); await container.create(apiContext); @@ -348,6 +351,7 @@ test.describe('Explore page', () => { await glossaryTerm.delete(apiContext); await glossary.delete(apiContext); await dashboard.delete(apiContext); + await chart.delete(apiContext); await storedProcedure.delete(apiContext); await pipeline.delete(apiContext); await container.delete(apiContext); @@ -402,6 +406,79 @@ test.describe('Explore page', () => { await validateBucketsForIndex(page, 'all'); }); + test('Verify charts are visible in explore tree', async ({ page }) => { + await page.waitForSelector('[data-testid="loader"]', { + state: 'detached', + }); + + const dashboardNode = page.getByTestId('explore-tree-title-Dashboards'); + await expect(dashboardNode).toBeVisible(); + + const dashboardNodeClickResponse = page.waitForResponse( + (resp) => + resp.url().includes('/api/v1/search/query') && + resp.url().includes('index=dataAsset') && + resp.url().includes('query_filter') + ); + + await page.getByTestId('explore-tree-title-Dashboards').click(); + + await dashboardNodeClickResponse; + + await page + .locator('.ant-tree-treenode', { + has: page.getByTestId('explore-tree-title-Dashboards'), + }) + .locator('.ant-tree-switcher') + .click(); + + const supersetNode = page.getByTestId('explore-tree-title-superset'); + await expect(supersetNode).toBeVisible(); + + await page + .locator('.ant-tree-treenode', { + has: page.getByTestId('explore-tree-title-superset'), + }) + .locator('.ant-tree-switcher') + .click(); + + const sampleSupersetNode = page.getByTestId( + 'explore-tree-title-sample_superset' + ); + await expect(sampleSupersetNode).toBeVisible(); + + await page + .locator('.ant-tree-treenode', { + has: page.getByTestId('explore-tree-title-sample_superset'), + }) + .locator('.ant-tree-switcher') + .click(); + + const chartsNode = page.getByTestId('explore-tree-title-chart'); + await expect(chartsNode).toBeVisible(); + await expect(chartsNode).toContainText('Charts'); + + const searchInput = page.getByTestId('searchBox'); + await searchInput.click(); + await searchInput.clear(); + await searchInput.fill(chart.entityResponseData.name); + await searchInput.press('Enter'); + + const searchResults = page.getByTestId('search-results'); + const chartCard = searchResults.getByTestId( + `table-data-card_${chart.entityResponseData.fullyQualifiedName}` + ); + + await expect(chartCard).toBeVisible(); + + const exploreLeftPanel = page.getByTestId('explore-left-panel'); + await expect(exploreLeftPanel).toBeVisible(); + + const chartsTab = page.getByTestId('charts-tab'); + await expect(chartsTab).toBeVisible(); + await expect(chartsTab).toContainText('Charts'); + }); + DATA_ASSETS.forEach((asset) => { test.fixme( `Check listing of ${asset.key} when sort is descending`, @@ -505,7 +582,11 @@ test.describe('Explore page', () => { await expect(sidePanel).not.toBeVisible(); // Verify URL does not contain the column part - await expect(page).toHaveURL(new RegExp(`/searchIndex/${searchIndex.entityResponseData?.['fullyQualifiedName']}$`)); + await expect(page).toHaveURL( + new RegExp( + `/searchIndex/${searchIndex.entityResponseData?.['fullyQualifiedName']}$` + ) + ); }); test('Copy field link should have valid URL format for APIEndpoint', async ({ @@ -543,6 +624,10 @@ test.describe('Explore page', () => { await expect(sidePanel).not.toBeVisible(); // Verify URL does not contain the column part - await expect(page).toHaveURL(new RegExp(`/apiEndpoint/${apiEndpoint.entityResponseData?.['fullyQualifiedName']}$`)); + await expect(page).toHaveURL( + new RegExp( + `/apiEndpoint/${apiEndpoint.entityResponseData?.['fullyQualifiedName']}$` + ) + ); }); }); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Explore/ExplorePage.interface.ts b/openmetadata-ui/src/main/resources/ui/src/components/Explore/ExplorePage.interface.ts index 0ffd4d4be26f..2c8c3157ddbd 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Explore/ExplorePage.interface.ts +++ b/openmetadata-ui/src/main/resources/ui/src/components/Explore/ExplorePage.interface.ts @@ -64,6 +64,7 @@ export type ExploreSearchIndex = | SearchIndex.DASHBOARD | SearchIndex.DATABASE | SearchIndex.DATABASE_SCHEMA + | SearchIndex.CHART | SearchIndex.MLMODEL | SearchIndex.TOPIC | SearchIndex.CONTAINER diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/EntityUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/EntityUtils.tsx index 1dd734627b3e..28c4e1db488d 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/EntityUtils.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/utils/EntityUtils.tsx @@ -2719,6 +2719,7 @@ export const getPluralizeEntityName = (entityType?: string) => { [EntityType.PIPELINE]: t('label.pipeline-plural'), [EntityType.CONTAINER]: t('label.container-plural'), [EntityType.DASHBOARD]: t('label.dashboard-plural'), + [EntityType.CHART]: t('label.chart-plural'), [EntityType.STORED_PROCEDURE]: t('label.stored-procedure-plural'), [EntityType.MLMODEL]: t('label.ml-model-plural'), [EntityType.DASHBOARD_DATA_MODEL]: t('label.data-model-plural'), diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/SearchClassBase.ts b/openmetadata-ui/src/main/resources/ui/src/utils/SearchClassBase.ts index af65ad033ea5..48447da6c0f9 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/SearchClassBase.ts +++ b/openmetadata-ui/src/main/resources/ui/src/utils/SearchClassBase.ts @@ -12,6 +12,7 @@ */ import { SearchOutlined } from '@ant-design/icons'; import { ReactComponent as GovernIcon } from '../assets/svg/bank.svg'; +import { ReactComponent as ChartIcon } from '../assets/svg/chart.svg'; import { ReactComponent as ClassificationIcon } from '../assets/svg/classification.svg'; import { ReactComponent as IconDataModel } from '../assets/svg/data-model.svg'; import { ReactComponent as GlossaryIcon } from '../assets/svg/glossary.svg'; @@ -249,6 +250,7 @@ class SearchClassBase { childEntities: [ EntityType.DASHBOARD_DATA_MODEL, EntityType.DASHBOARD, + EntityType.CHART, ], }, icon: DashboardIcon, @@ -439,6 +441,13 @@ class SearchClassBase { path: ExplorePageTabs.DASHBOARD_DATA_MODEL, icon: IconDataModel, }, + [SearchIndex.CHART]: { + label: t('label.chart-plural'), + sortingFields: entitySortingFields, + sortField: INITIAL_SORT_FIELD, + path: ExplorePageTabs.CHARTS, + icon: ChartIcon, + }, [SearchIndex.PIPELINE]: { label: t('label.pipeline-plural'), sortingFields: entitySortingFields,