From 107aeb03834bffe6e9839a1df2e940b03e959efd Mon Sep 17 00:00:00 2001 From: Satish Date: Tue, 30 Sep 2025 22:27:58 +0530 Subject: [PATCH] test: fix Playwright domain and subdomain test selectors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add data-testid to table rows for reliable subdomain selection - Fix race conditions using Promise.all() pattern - Update search box locator from getByRole to getByPlaceholder - Add skipDomainSelection parameter to prevent redundant navigation - Update IngestionBot test to skip domain selection when already on page 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../playwright/e2e/Flow/IngestionBot.spec.ts | 4 +- .../ui/playwright/e2e/Pages/Domains.spec.ts | 16 ++-- .../resources/ui/playwright/utils/domain.ts | 83 ++++++++++--------- .../common/atoms/table/useTableRow.tsx | 1 + 4 files changed, 54 insertions(+), 50 deletions(-) diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Flow/IngestionBot.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Flow/IngestionBot.spec.ts index c8b0012d2af..89e69512d52 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Flow/IngestionBot.spec.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Flow/IngestionBot.spec.ts @@ -101,7 +101,7 @@ test.describe('Ingestion Bot ', () => { state: 'detached', }); await selectDomain(page, domain1.data); - await addAssetsToDomain(page, domain1, domainAsset1); + await addAssetsToDomain(page, domain1, domainAsset1, true, true); // Add assets to domain 2 await sidebarClick(page, SidebarItem.DOMAIN); @@ -110,7 +110,7 @@ test.describe('Ingestion Bot ', () => { state: 'detached', }); await selectDomain(page, domain2.data); - await addAssetsToDomain(page, domain2, domainAsset2); + await addAssetsToDomain(page, domain2, domainAsset2, true, true); }); await test.step( diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/Domains.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/Domains.spec.ts index 4f5d1621d1d..2e5bfeca531 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/Domains.spec.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/Domains.spec.ts @@ -452,11 +452,10 @@ test.describe('Domains', () => { state: 'detached', }); - const domainApiRes = page.waitForResponse('/api/v1/domains/name/*'); - - await page.getByRole('row', { name: subDomain.data.displayName }).click(); - - await domainApiRes; + await Promise.all([ + page.getByTestId(subDomain.data.name).click(), + page.waitForResponse('/api/v1/domains/name/*'), + ]); await verifyDomain(page, subDomain.data, domain.data, false); // Follow domain @@ -516,9 +515,10 @@ test.describe('Domains', () => { state: 'detached', }); - const domainApiRes1 = page.waitForResponse('/api/v1/domains/name/*'); - await page.getByRole('row', { name: subDomain.data.displayName }).click(); - await domainApiRes1; + await Promise.all([ + page.getByTestId(subDomain.data.name).click(), + page.waitForResponse('/api/v1/domains/name/*'), + ]); await verifyDomain(page, nestedSubDomain.data, domain.data, false); } finally { await nestedSubDomain.delete(apiContext); diff --git a/openmetadata-ui/src/main/resources/ui/playwright/utils/domain.ts b/openmetadata-ui/src/main/resources/ui/playwright/utils/domain.ts index 3af65d78a79..87d97a2b5b3 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/utils/domain.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/utils/domain.ts @@ -121,25 +121,21 @@ export const validateDomainForm = async (page: Page) => { export const selectDomain = async (page: Page, domain: Domain['data']) => { const searchBox = page .getByTestId('page-layout-v1') - .getByRole('textbox', { name: 'Search' }); + .getByPlaceholder('Search'); - const domainRes = page.waitForResponse( - '/api/v1/search/query?q=*&index=domain_search_index*' - ); - - await searchBox.fill(domain.name); - - await domainRes; + await Promise.all([ + searchBox.fill(domain.name), + page.waitForResponse('/api/v1/search/query?q=*&index=domain_search_index*'), + ]); await page.waitForSelector('[data-testid="loader"]', { state: 'detached', }); - const domainApiRes = page.waitForResponse('/api/v1/domains/name/*'); - - await page.locator('td').filter({ hasText: domain.displayName }).click(); - - await domainApiRes; + await Promise.all([ + page.locator('td').filter({ hasText: domain.displayName }).click(), + page.waitForResponse('/api/v1/domains/name/*'), + ]); await page.waitForLoadState('networkidle'); @@ -197,13 +193,13 @@ export const selectDataProductFromTab = async ( await page.waitForSelector('[data-testid="loader"]', { state: 'detached' }); - const dpDataRes = page.waitForResponse('/api/v1/dataProducts/name/*'); - - await page - .getByTestId(`explore-card-${dataProduct.name}`) - .getByTestId('entity-link') - .click(); - await dpDataRes; + await Promise.all([ + page + .getByTestId(`explore-card-${dataProduct.name}`) + .getByTestId('entity-link') + .click(), + page.waitForResponse('/api/v1/dataProducts/name/*'), + ]); }; export const selectDataProduct = async ( @@ -212,38 +208,44 @@ export const selectDataProduct = async ( ) => { const searchBox = page .getByTestId('page-layout-v1') - .getByRole('textbox', { name: 'Search' }); + .getByPlaceholder('Search'); - const dpRes = page.waitForResponse( - '/api/v1/search/query?q=*&index=data_product_search_index*' - ); - - await searchBox.fill(dataProduct.name); - - await dpRes; + await Promise.all([ + searchBox.fill(dataProduct.name), + page.waitForResponse( + '/api/v1/search/query?q=*&index=data_product_search_index*' + ), + ]); await page.waitForSelector('[data-testid="loader"]', { state: 'detached', }); - const dpApiRes = page.waitForResponse('/api/v1/dataProducts/name/*'); - - await page.locator('td').filter({ hasText: dataProduct.displayName }).click(); - - await dpApiRes; + await Promise.all([ + page.locator('td').filter({ hasText: dataProduct.displayName }).click(), + page.waitForResponse('/api/v1/dataProducts/name/*'), + ]); await page.waitForSelector('[data-testid="loader"]', { state: 'detached', }); }; -const goToAssetsTab = async (page: Page, domain: Domain['data']) => { - await selectDomain(page, domain); +const goToAssetsTab = async ( + page: Page, + domain: Domain['data'], + skipDomainSelection = false +) => { + if (!skipDomainSelection) { + await selectDomain(page, domain); + } + await checkDomainDisplayName(page, domain.displayName); - const assetRes = page.waitForResponse('/api/v1/search/query?q=&index=all*'); - await page.getByTestId('assets').click({ timeout: 15000 }); - await assetRes; + await Promise.all([ + page.getByTestId('assets').click({ timeout: 15000 }), + page.waitForResponse('/api/v1/search/query?q=&index=all*'), + ]); await page.waitForLoadState('networkidle'); await page.waitForSelector('[data-testid="loader"]', { state: 'detached' }); @@ -388,10 +390,11 @@ export const addAssetsToDomain = async ( page: Page, domain: Domain, assets: EntityClass[], - navigateToAssetsTab = true + navigateToAssetsTab = true, + skipDomainSelection = false ) => { if (navigateToAssetsTab) { - await goToAssetsTab(page, domain.data); + await goToAssetsTab(page, domain.data, skipDomainSelection); } await checkAssetsCount(page, 0); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/atoms/table/useTableRow.tsx b/openmetadata-ui/src/main/resources/ui/src/components/common/atoms/table/useTableRow.tsx index fd2f8afc4ff..eb06248d18b 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/common/atoms/table/useTableRow.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/common/atoms/table/useTableRow.tsx @@ -50,6 +50,7 @@ export const useTableRow = ( () => ( config.onEntityClick(config.entity)}> {config.enableSelection && (