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 f971a6eacc9..8ef4d93118a 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/constant/explore.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/constant/explore.ts @@ -28,3 +28,22 @@ export const EXPECTED_BUCKETS = [ 'searchIndex', 'mlmodel', ]; + +export const DATA_ASSETS = [ + { name: 'Table', filter: 'table' }, + { name: 'Database', filter: 'database' }, + { name: 'Database Schema', filter: 'databaseSchema' }, + { name: 'Dashboard', filter: 'dashboard' }, + { name: 'Dashboard Data Model', filter: 'dashboardDataModel' }, + { name: 'Pipeline', filter: 'pipeline' }, + { name: 'Topic', filter: 'topic' }, + { name: 'ML Model', filter: 'mlmodel' }, + { name: 'Container', filter: 'container' }, + { name: 'Search Index', filter: 'searchIndex' }, + { name: 'API Endpoint', filter: 'apiEndpoint' }, + { name: 'API Collection', filter: 'apiCollection' }, + { name: 'Stored Procedure', filter: 'storedProcedure' }, + { name: 'Glossary Term', filter: 'glossaryTerm' }, + { name: 'Tags', filter: 'tag' }, + { name: 'Metrics', filter: 'metric' }, +]; diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/ExploreSortOrderFilter.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/ExploreSortOrderFilter.spec.ts new file mode 100644 index 00000000000..d88ac6a78f1 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/ExploreSortOrderFilter.spec.ts @@ -0,0 +1,68 @@ +/* + * Copyright 2025 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 { DATA_ASSETS } from '../../constant/explore'; +import { SidebarItem } from '../../constant/sidebar'; +import { EntityDataClass } from '../../support/entity/EntityDataClass'; +import { performAdminLogin } from '../../utils/admin'; +import { redirectToHomePage } from '../../utils/common'; +import { selectSortOrder, verifyEntitiesAreSorted } from '../../utils/explore'; +import { sidebarClick } from '../../utils/sidebar'; + +test.describe('Explore Sort Order Filter', () => { + test.beforeAll('Setup pre-requests', async ({ browser }) => { + test.slow(true); + + const { apiContext, afterAction } = await performAdminLogin(browser); + await EntityDataClass.preRequisitesForTests(apiContext); + await afterAction(); + }); + + test.afterAll('Cleanup', async ({ browser }) => { + test.slow(true); + + const { apiContext, afterAction } = await performAdminLogin(browser); + await EntityDataClass.postRequisitesForTests(apiContext); + await afterAction(); + }); + + DATA_ASSETS.forEach(({ name, filter }) => { + test(`${name}`, async ({ browser }) => { + test.slow(true); + + const { page, afterAction } = await performAdminLogin(browser); + + await redirectToHomePage(page); + await sidebarClick(page, SidebarItem.EXPLORE); + + await page.waitForLoadState('networkidle'); + + await page.getByRole('button', { name: 'Data Assets' }).click(); + + await page.waitForSelector( + 'data-testid="drop-down-menu" data-testid="loader"', + { + state: 'detached', + } + ); + + await page.getByTestId(`${filter}-checkbox`).check(); + await page.getByTestId('update-btn').click(); + + await selectSortOrder(page, 'Name'); + await verifyEntitiesAreSorted(page); + + await afterAction(); + }); + }); +}); diff --git a/openmetadata-ui/src/main/resources/ui/playwright/support/entity/EntityDataClass.ts b/openmetadata-ui/src/main/resources/ui/playwright/support/entity/EntityDataClass.ts index 43e73a50b2a..5c08b8038e6 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/support/entity/EntityDataClass.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/support/entity/EntityDataClass.ts @@ -27,6 +27,7 @@ import { DashboardDataModelClass } from './DashboardDataModelClass'; import { DatabaseClass } from './DatabaseClass'; import { DatabaseSchemaClass } from './DatabaseSchemaClass'; import { EntityDataClassCreationConfig } from './EntityDataClass.interface'; +import { MetricClass } from './MetricClass'; import { MlModelClass } from './MlModelClass'; import { PipelineClass } from './PipelineClass'; import { SearchIndexClass } from './SearchIndexClass'; @@ -104,6 +105,7 @@ export class EntityDataClass { static readonly dataProduct1 = new DataProduct(this.domain1); static readonly dataProduct2 = new DataProduct(this.domain1); static readonly dataProduct3 = new DataProduct(this.domain2); + static readonly metric1 = new MetricClass(); static async preRequisitesForTests( apiContext: APIRequestContext, @@ -129,6 +131,7 @@ export class EntityDataClass { this.certificationTag1.create(apiContext), this.certificationTag2.create(apiContext), this.classification1.create(apiContext), + this.metric1.create(apiContext), ] : []; @@ -249,6 +252,7 @@ export class EntityDataClass { this.dataProduct1.delete(apiContext), this.dataProduct2.delete(apiContext), this.dataProduct3.delete(apiContext), + this.metric1.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 5b16c7c9bed..8ee6817c2e6 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/utils/explore.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/utils/explore.ts @@ -154,3 +154,39 @@ export const validateBucketsForIndex = async (page: Page, index: string) => { ).toBeGreaterThan(0); }); }; + +export const selectSortOrder = async (page: Page, sortOrder: string) => { + await page.waitForSelector('[data-testid="loader"]', { state: 'detached' }); + await page.getByTestId('sorting-dropdown-label').click(); + await page.waitForSelector(`role=menuitem[name="${sortOrder}"]`, { + state: 'visible', + }); + await page.getByRole('menuitem', { name: sortOrder }).click(); + + await expect(page.getByTestId('sorting-dropdown-label')).toHaveText( + sortOrder + ); + + await page.getByTestId('sort-order-button').click(); + await page.waitForSelector('[data-testid="loader"]', { state: 'detached' }); +}; + +export const verifyEntitiesAreSorted = async (page: Page) => { + const entityNames = await page.$$eval( + '[data-testid="search-results"] .explore-search-card [data-testid="entity-link"]', + (elements) => elements.map((el) => el.textContent?.trim() ?? '') + ); + + // Normalize for case insensitivity, but retain punctuation + const normalize = (str: string) => str.toLowerCase().trim(); + + // Sort using ASCII-based string comparison (ES behavior) + const sortedEntityNames = [...entityNames].sort((a, b) => { + const normA = normalize(a); + const normB = normalize(b); + + return normA < normB ? -1 : normA > normB ? 1 : 0; + }); + + expect(entityNames).toEqual(sortedEntityNames); +}; diff --git a/openmetadata-ui/src/main/resources/ui/src/constants/explore.constants.ts b/openmetadata-ui/src/main/resources/ui/src/constants/explore.constants.ts index bc3bca39b46..533428f79f0 100644 --- a/openmetadata-ui/src/main/resources/ui/src/constants/explore.constants.ts +++ b/openmetadata-ui/src/main/resources/ui/src/constants/explore.constants.ts @@ -45,7 +45,7 @@ export const tableSortingFields: SortingField[] = [ }, { name: i18n.t('label.name'), - value: 'name.keyword', + value: 'displayName.keyword', }, { name: i18n.t('label.weekly-usage'), @@ -65,7 +65,7 @@ export const entitySortingFields = [ }, { name: i18n.t('label.name'), - value: 'name.keyword', + value: 'displayName.keyword', }, { name: i18n.t('label.relevance'), value: '_score' }, { @@ -77,7 +77,7 @@ export const entitySortingFields = [ export const tagSortingFields = [ { name: i18n.t('label.name'), - value: 'name.keyword', + value: 'displayName.keyword', }, { name: i18n.t('label.relevance'), value: '_score' }, {