From 4cd9f13d97f40b0325379d7351270a93ef4622cf Mon Sep 17 00:00:00 2001 From: Aniket Katkar Date: Sat, 30 Aug 2025 18:37:45 +0530 Subject: [PATCH] Chore(UI): Fix flaky playwright tests (#23156) * Fix playwright tests - AdvancedSearch.spec.ts - Autopilot.spec.ts - DescriptionSuggestion.spec.ts - ObservabilityAlert.spec.ts - EntityVersionPages.spec.ts * Fix the flakiness while logging in * Update config to run only required tests * Revert description suggestion spec changes * Revert playwright config changes * Update playwright config * improve observability alert page navigation * Fix Description suggestion * Standardize left nav bar to close for all tests * Fix failing playwright tests --- .../AdvanceSearchCustomProperty.spec.ts | 9 ++-- .../e2e/Features/AdvancedSearch.spec.ts | 45 ++++++++++------ .../e2e/Features/CuratedAssets.spec.ts | 13 +++-- .../Features/DescriptionSuggestion.spec.ts | 27 +++++----- .../e2e/Features/NavigationBlocker.spec.ts | 24 +++++++-- .../CustomPropertyAdvanceSeach.spec.ts | 6 ++- .../e2e/Flow/CustomizeLandingPage.spec.ts | 1 - .../e2e/Flow/CustomizeWidgets.spec.ts | 31 ++--------- .../ui/playwright/e2e/Flow/Tour.spec.ts | 2 - .../e2e/Pages/DataContracts.spec.ts | 18 ++++--- .../VersionPages/EntityVersionPages.spec.ts | 51 ++++++++++++++----- .../resources/ui/playwright/e2e/auth.setup.ts | 3 -- .../entity/ingestion/ServiceBaseClass.ts | 9 +--- .../ui/playwright/support/user/UserClass.ts | 13 +++++ .../ui/playwright/utils/advancedSearch.ts | 28 ++++++++-- .../ui/playwright/utils/customProperty.ts | 9 ++-- .../ui/playwright/utils/observabilityAlert.ts | 13 ++++- .../resources/ui/playwright/utils/sidebar.ts | 10 ++-- 18 files changed, 196 insertions(+), 116 deletions(-) diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/AdvanceSearchCustomProperty.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/AdvanceSearchCustomProperty.spec.ts index 680b7bb88c8..9e37643cb54 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/AdvanceSearchCustomProperty.spec.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/AdvanceSearchCustomProperty.spec.ts @@ -98,20 +98,23 @@ test.describe('Advanced Search Custom Property', () => { await selectOption( page, ruleLocator.locator('.rule--field .ant-select'), - 'Custom Properties' + 'Custom Properties', + true ); await selectOption( page, ruleLocator.locator('.rule--field .ant-select'), - 'Table' + 'Table', + true ); // Perform click on custom property type to filter await selectOption( page, ruleLocator.locator('.rule--field .ant-select'), - durationPropertyName + durationPropertyName, + true ); // Perform click on operator diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/AdvancedSearch.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/AdvancedSearch.spec.ts index 73eb3c83c25..d01229d3a42 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/AdvancedSearch.spec.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/AdvancedSearch.spec.ts @@ -27,7 +27,6 @@ import { verifyAllConditions, } from '../../utils/advancedSearch'; import { createNewPage, redirectToHomePage } from '../../utils/common'; -import { assignTier } from '../../utils/entity'; import { sidebarClick } from '../../utils/sidebar'; const creationConfig: EntityDataClassCreationConfig = { @@ -74,7 +73,6 @@ test.describe('Advanced Search', { tag: '@advanced-search' }, () => { await table.create(apiContext); // Add Owner & Tag to the table - await EntityDataClass.table1.visitEntityPage(page); await EntityDataClass.table1.patch({ apiContext, patchData: [ @@ -106,7 +104,6 @@ test.describe('Advanced Search', { tag: '@advanced-search' }, () => { ], }); - await EntityDataClass.table2.visitEntityPage(page); await EntityDataClass.table2.patch({ apiContext, patchData: [ @@ -139,20 +136,38 @@ test.describe('Advanced Search', { tag: '@advanced-search' }, () => { }); // Add Tier To the topic 1 - await EntityDataClass.topic1.visitEntityPage(page); - await assignTier( - page, - COMMON_TIER_TAG[0].name, - EntityDataClass.topic1.endpoint - ); + await EntityDataClass.topic1.patch({ + apiContext, + patchData: [ + { + op: 'add', + path: '/tags/0', + value: { + name: COMMON_TIER_TAG[0].name, + tagFQN: COMMON_TIER_TAG[0].fullyQualifiedName, + labelType: 'Manual', + state: 'Confirmed', + }, + }, + ], + }); // Add Tier To the topic 2 - await EntityDataClass.topic2.visitEntityPage(page); - await assignTier( - page, - COMMON_TIER_TAG[1].name, - EntityDataClass.topic2.endpoint - ); + await EntityDataClass.topic2.patch({ + apiContext, + patchData: [ + { + op: 'add', + path: '/tags/0', + value: { + name: COMMON_TIER_TAG[1].name, + tagFQN: COMMON_TIER_TAG[1].fullyQualifiedName, + labelType: 'Manual', + state: 'Confirmed', + }, + }, + ], + }); // Update Search Criteria here searchCriteria = { diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/CuratedAssets.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/CuratedAssets.spec.ts index 10b74527500..918b83a2ee3 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/CuratedAssets.spec.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/CuratedAssets.spec.ts @@ -58,7 +58,6 @@ test.describe('Curated Assets', () => { test.slow(true); await redirectToHomePage(page); - await page.getByTestId('sidebar-toggle').click(); await setUserDefaultPersona(page, persona.responseData.displayName); }); @@ -91,7 +90,8 @@ test.describe('Curated Assets', () => { await selectOption( page, ruleLocator.locator('.rule--field .ant-select'), - 'Owners' + 'Owners', + true ); await selectOption( @@ -103,7 +103,8 @@ test.describe('Curated Assets', () => { await selectOption( page, ruleLocator.locator('.rule--value .ant-select'), - 'admin' + 'admin', + true ); await page.getByRole('button', { name: 'Add Condition' }).click(); @@ -112,7 +113,8 @@ test.describe('Curated Assets', () => { await selectOption( page, ruleLocator2.locator('.rule--field .ant-select'), - 'Display Name' + 'Display Name', + true ); await selectOption( @@ -124,7 +126,8 @@ test.describe('Curated Assets', () => { await selectOption( page, ruleLocator2.locator('.rule--value .ant-select'), - 'arcs' + 'arcs', + true ); await expect(page.locator('[data-testid="saveButton"]')).toBeEnabled(); diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/DescriptionSuggestion.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/DescriptionSuggestion.spec.ts index ce9b512b8f5..64996a95d7a 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/DescriptionSuggestion.spec.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/DescriptionSuggestion.spec.ts @@ -25,7 +25,7 @@ const user2 = new UserClass(); const user3 = new UserClass(); let entityLinkList: string[]; -test.describe('Description Suggestions Table Entity', () => { +test.describe.serial('Description Suggestions Table Entity', () => { test.slow(true); test.beforeAll('Setup pre-requests', async ({ browser }) => { @@ -49,16 +49,6 @@ test.describe('Description Suggestions Table Entity', () => { await afterAction(); }); - test.afterAll('Cleanup', async ({ browser }) => { - const { afterAction, apiContext } = await performAdminLogin(browser); - await table.delete(apiContext); - await table2.delete(apiContext); - await user1.delete(apiContext); - await user2.delete(apiContext); - await user3.delete(apiContext); - await afterAction(); - }); - test('View, Close, Reject and Accept the Suggestions', async ({ browser, }) => { @@ -233,6 +223,12 @@ test.describe('Description Suggestions Table Entity', () => { test('Reject All Suggestions', async ({ browser }) => { const { page, afterAction } = await performAdminLogin(browser); + const { afterAction: afterAction2, apiContext: apiContext2 } = + await performUserLogin(browser, user1); + + for (const entityLink of entityLinkList) { + await createTableDescriptionSuggestions(apiContext2, entityLink); + } await redirectToHomePage(page); await table.visitEntityPage(page); @@ -244,13 +240,13 @@ test.describe('Description Suggestions Table Entity', () => { // Click the first avatar await allAvatarSuggestion.nth(0).click(); - const acceptResponse = page.waitForResponse( + const rejectResponse = page.waitForResponse( '/api/v1/suggestions/reject-all?userId=*&entityFQN=*&suggestionType=SuggestDescription' ); await page.click(`[data-testid="reject-all-suggestions"]`); - await acceptResponse; + await rejectResponse; // check the last column description await expect( @@ -265,6 +261,7 @@ test.describe('Description Suggestions Table Entity', () => { await expect(page.getByTestId('close-suggestion')).not.toBeVisible(); await afterAction(); + await afterAction2(); }); test('Fetch on avatar click and then all Pending Suggestions button click', async ({ @@ -311,7 +308,7 @@ test.describe('Description Suggestions Table Entity', () => { .getByTestId('profile-avatar'); // Click the first avatar - await expect(allAvatarSuggestion).toHaveCount(4); + await expect(allAvatarSuggestion).toHaveCount(3); await afterAction(); await afterAction2(); @@ -375,7 +372,7 @@ test.describe('Description Suggestions Table Entity', () => { page.getByTestId('more-suggestion-button') ).not.toBeVisible(); - await expect(allAvatarSuggestion).toHaveCount(1); + await expect(allAvatarSuggestion).toHaveCount(0); } } diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/NavigationBlocker.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/NavigationBlocker.spec.ts index cf728cad8a2..654ef8522c8 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/NavigationBlocker.spec.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/NavigationBlocker.spec.ts @@ -78,7 +78,11 @@ test.describe('Navigation Blocker Tests', () => { ).toBeEnabled(); // Try to navigate to another page by clicking a sidebar link - await adminPage.locator('[data-testid="app-bar-item-settings"]').click(); + await adminPage + .locator( + '[data-menu-id*="settings"] [data-testid="app-bar-item-settings"]' + ) + .click(); // Navigation blocker modal should appear await expect(adminPage.locator('.ant-modal')).toBeVisible(); @@ -114,7 +118,11 @@ test.describe('Navigation Blocker Tests', () => { }); // Try to navigate away - await adminPage.locator('[data-testid="app-bar-item-settings"]').click(); + await adminPage + .locator( + '[data-menu-id*="settings"] [data-testid="app-bar-item-settings"]' + ) + .click(); // Modal should appear await expect(adminPage.locator('.ant-modal')).toBeVisible(); @@ -153,7 +161,11 @@ test.describe('Navigation Blocker Tests', () => { }); // Try to navigate to settings page - await adminPage.locator('[data-testid="app-bar-item-settings"]').click(); + await adminPage + .locator( + '[data-menu-id*="settings"] [data-testid="app-bar-item-settings"]' + ) + .click(); // Modal should appear await expect(adminPage.locator('.ant-modal')).toBeVisible(); @@ -204,7 +216,11 @@ test.describe('Navigation Blocker Tests', () => { ).toBeDisabled(); // Try to navigate away after saving - await adminPage.locator('[data-testid="app-bar-item-settings"]').click(); + await adminPage + .locator( + '[data-menu-id*="settings"] [data-testid="app-bar-item-settings"]' + ) + .click(); // Navigation should happen immediately without modal await adminPage.waitForLoadState('networkidle'); diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Flow/AdvanceSearchFilter/CustomPropertyAdvanceSeach.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Flow/AdvanceSearchFilter/CustomPropertyAdvanceSeach.spec.ts index 2a8431a1d6c..eb924f913f1 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Flow/AdvanceSearchFilter/CustomPropertyAdvanceSeach.spec.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Flow/AdvanceSearchFilter/CustomPropertyAdvanceSeach.spec.ts @@ -129,13 +129,15 @@ test('CustomProperty Dashboard Filter', async ({ page }) => { await selectOption( page, ruleLocator.locator('.rule--field .ant-select'), - 'Custom Properties' + 'Custom Properties', + true ); await selectOption( page, ruleLocator.locator('.rule--field .ant-select'), - 'Dashboard' + 'Dashboard', + true ); // Select Custom Property Field when we want filter diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Flow/CustomizeLandingPage.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Flow/CustomizeLandingPage.spec.ts index f08f5429145..0fb19eae9e4 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Flow/CustomizeLandingPage.spec.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Flow/CustomizeLandingPage.spec.ts @@ -57,7 +57,6 @@ base.afterAll('Cleanup', async ({ browser }) => { test.describe('Customize Landing Page Flow', () => { test('Check all default widget present', async ({ adminPage }) => { await redirectToHomePage(adminPage); - await adminPage.getByTestId('welcome-screen-close-btn').click(); await checkAllDefaultWidgets(adminPage); }); diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Flow/CustomizeWidgets.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Flow/CustomizeWidgets.spec.ts index e847c7c91d3..2ced4129c6c 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Flow/CustomizeWidgets.spec.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Flow/CustomizeWidgets.spec.ts @@ -40,7 +40,7 @@ const test = base.extend<{ page: Page }>({ }, }); -base.beforeAll('Setup pre-requests', async ({ browser, page }) => { +base.beforeAll('Setup pre-requests', async ({ browser }) => { const { afterAction, apiContext } = await performAdminLogin(browser); await adminUser.create(apiContext); await adminUser.setAdminRole(apiContext); @@ -60,16 +60,16 @@ test.describe('Widgets', () => { test.slow(true); await redirectToHomePage(page); - await page.getByTestId('sidebar-toggle').click(); await setUserDefaultPersona(page, persona.responseData.displayName); }); + test.beforeEach(async ({ page }) => { + await redirectToHomePage(page); + }); + test('Activity Feed', async ({ page }) => { test.slow(true); - await redirectToHomePage(page); - await page.getByTestId('welcome-screen-close-btn').click(); - await expect(page.getByTestId('KnowledgePanel.ActivityFeed')).toBeVisible(); await verifyActivityFeedFilters(page, 'KnowledgePanel.ActivityFeed'); @@ -90,9 +90,6 @@ test.describe('Widgets', () => { test('Data Assets', async ({ page }) => { test.slow(true); - await redirectToHomePage(page); - await page.getByTestId('welcome-screen-close-btn').click(); - await expect(page.getByTestId('KnowledgePanel.DataAssets')).toBeVisible(); await removeAndVerifyWidget( @@ -111,9 +108,6 @@ test.describe('Widgets', () => { test('My Data', async ({ page }) => { test.slow(true); - await redirectToHomePage(page); - await page.getByTestId('welcome-screen-close-btn').click(); - await expect(page.getByTestId('KnowledgePanel.MyData')).toBeVisible(); await verifyDataFilters(page, 'KnowledgePanel.MyData'); @@ -134,9 +128,6 @@ test.describe('Widgets', () => { test('KPI', async ({ page }) => { test.slow(true); - await redirectToHomePage(page); - await page.getByTestId('welcome-screen-close-btn').click(); - await expect(page.getByTestId('KnowledgePanel.KPI')).toBeVisible(); await removeAndVerifyWidget( @@ -155,9 +146,6 @@ test.describe('Widgets', () => { test('Total Data Assets', async ({ page }) => { test.slow(true); - await redirectToHomePage(page); - await page.getByTestId('welcome-screen-close-btn').click(); - await expect(page.getByTestId('KnowledgePanel.TotalAssets')).toBeVisible(); await verifyTotalDataAssetsFilters(page, 'KnowledgePanel.TotalAssets'); @@ -178,9 +166,6 @@ test.describe('Widgets', () => { test('Following Assets', async ({ page }) => { test.slow(true); - await redirectToHomePage(page); - await page.getByTestId('welcome-screen-close-btn').click(); - await expect(page.getByTestId('KnowledgePanel.Following')).toBeVisible(); await verifyDataFilters(page, 'KnowledgePanel.Following'); @@ -201,9 +186,6 @@ test.describe('Widgets', () => { test('Domains', async ({ page }) => { test.slow(true); - await redirectToHomePage(page); - await page.getByTestId('welcome-screen-close-btn').click(); - await expect(page.getByTestId('KnowledgePanel.Domains')).not.toBeVisible(); await addAndVerifyWidget( @@ -224,9 +206,6 @@ test.describe('Widgets', () => { test('My Tasks', async ({ page }) => { test.slow(true); - await redirectToHomePage(page); - await page.getByTestId('welcome-screen-close-btn').click(); - await expect(page.getByTestId('KnowledgePanel.MyTask')).not.toBeVisible(); await addAndVerifyWidget( diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Flow/Tour.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Flow/Tour.spec.ts index 37492c91e37..b443055b017 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Flow/Tour.spec.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Flow/Tour.spec.ts @@ -13,7 +13,6 @@ import { expect, Page, test } from '@playwright/test'; import { UserClass } from '../../support/user/UserClass'; import { performAdminLogin } from '../../utils/admin'; -import { redirectToHomePage } from '../../utils/common'; import { waitForAllLoadersToDisappear } from '../../utils/entity'; const user = new UserClass(); @@ -157,7 +156,6 @@ test.describe('Tour should work properly', () => { test.beforeEach('Visit entity details page', async ({ page }) => { await user.login(page); - await redirectToHomePage(page); }); test('Tour should work from help section', async ({ page }) => { diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/DataContracts.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/DataContracts.spec.ts index 23b28f662f3..d3ce4be3754 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/DataContracts.spec.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/DataContracts.spec.ts @@ -183,7 +183,8 @@ test.describe('Data Contracts', () => { await selectOption( page, ruleLocator.locator('.group--field .ant-select'), - DATA_CONTRACT_SEMANTICS1.rules[0].field + DATA_CONTRACT_SEMANTICS1.rules[0].field, + true ); await selectOption( page, @@ -193,7 +194,8 @@ test.describe('Data Contracts', () => { await selectOption( page, ruleLocator.locator('.rule--value .ant-select'), - 'admin' + 'admin', + true ); await page.getByRole('button', { name: 'Add New Rule' }).click(); @@ -203,7 +205,8 @@ test.describe('Data Contracts', () => { await selectOption( page, ruleLocator2.locator('.rule--field .ant-select'), - DATA_CONTRACT_SEMANTICS1.rules[1].field + DATA_CONTRACT_SEMANTICS1.rules[1].field, + true ); await selectOption( page, @@ -241,7 +244,8 @@ test.describe('Data Contracts', () => { await selectOption( page, ruleLocator3.locator('.group--field .ant-select'), - DATA_CONTRACT_SEMANTICS2.rules[0].field + DATA_CONTRACT_SEMANTICS2.rules[0].field, + true ); await selectOption( page, @@ -596,7 +600,8 @@ test.describe('Data Contracts', () => { await selectOption( page, ruleLocator.locator('.group--field .ant-select'), - DATA_CONTRACT_SEMANTICS1.rules[0].field + DATA_CONTRACT_SEMANTICS1.rules[0].field, + true ); await selectOption( page, @@ -606,7 +611,8 @@ test.describe('Data Contracts', () => { await selectOption( page, ruleLocator.locator('.rule--value .ant-select'), - 'admin' + 'admin', + true ); await page.getByTestId('save-semantic-button').click(); diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/VersionPages/EntityVersionPages.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/VersionPages/EntityVersionPages.spec.ts index 4998ee511a7..67d98bfc0b2 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/VersionPages/EntityVersionPages.spec.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/VersionPages/EntityVersionPages.spec.ts @@ -11,6 +11,7 @@ * limitations under the License. */ import { expect, Page, test as base } from '@playwright/test'; +import { COMMON_TIER_TAG } from '../../constant/common'; import { BIG_ENTITY_DELETE_TIMEOUT } from '../../constant/delete'; import { EntityDataClass } from '../../support/entity/EntityDataClass'; import { EntityDataClassCreationConfig } from '../../support/entity/EntityDataClass.interface'; @@ -19,14 +20,12 @@ import { UserClass } from '../../support/user/UserClass'; import { performAdminLogin } from '../../utils/admin'; import { descriptionBoxReadOnly, + getApiContext, redirectToHomePage, + reloadAndWaitForNetworkIdle, toastNotification, } from '../../utils/common'; -import { - addMultiOwner, - assignTier, - getEntityDataTypeDisplayPatch, -} from '../../utils/entity'; +import { getEntityDataTypeDisplayPatch } from '../../utils/entity'; const entityCreationConfig: EntityDataClassCreationConfig = { apiEndpoint: true, @@ -158,6 +157,7 @@ test.describe('Entity Version pages', () => { test(`${entity.getType()}`, async ({ page }) => { test.slow(); + const { apiContext } = await getApiContext(page); await entity.visitEntityPage(page); const versionDetailResponse = page.waitForResponse(`**/versions/0.2`); await page.locator('[data-testid="version-button"]').click(); @@ -194,17 +194,24 @@ test.describe('Entity Version pages', () => { await test.step('should show owner changes', async () => { await page.locator('[data-testid="version-button"]').click(); - const OWNER1 = EntityDataClass.user1.getUserName(); + const OWNER1 = EntityDataClass.user1.responseData; - await addMultiOwner({ - page, - ownerNames: [OWNER1], - activatorBtnDataTestId: 'edit-owner', - resultTestId: 'data-assets-header', - endpoint: entity.endpoint, - type: 'Users', + await entity.patch({ + apiContext, + patchData: [ + { + op: 'add', + path: '/owners/0', + value: { + id: OWNER1.id, + type: 'user', + }, + }, + ], }); + await reloadAndWaitForNetworkIdle(page); + const versionDetailResponse = page.waitForResponse(`**/versions/0.3`); await page.locator('[data-testid="version-button"]').click(); await versionDetailResponse; @@ -255,7 +262,23 @@ test.describe('Entity Version pages', () => { await test.step('should show tier changes', async () => { await page.locator('[data-testid="version-button"]').click(); - await assignTier(page, 'Tier1', entity.endpoint); + await entity.patch({ + apiContext, + patchData: [ + { + op: 'add', + path: '/tags/0', + value: { + name: COMMON_TIER_TAG[0].name, + tagFQN: COMMON_TIER_TAG[0].fullyQualifiedName, + labelType: 'Manual', + state: 'Confirmed', + }, + }, + ], + }); + + await reloadAndWaitForNetworkIdle(page); const versionDetailResponse = page.waitForResponse(`**/versions/0.3`); await page.locator('[data-testid="version-button"]').click(); diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/auth.setup.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/auth.setup.ts index 24a584f7b6c..99df32c3753 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/auth.setup.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/auth.setup.ts @@ -101,9 +101,6 @@ setup('authenticate all users', async ({ browser }) => { const newAdminPage = await browser.newPage(); await admin.login(newAdminPage); - // Close the leftside bar to run tests smoothly - await newAdminPage.getByTestId('sidebar-toggle').click(); - await newAdminPage.waitForURL('**/my-data'); const { apiContext, afterAction } = await getApiContext(adminPage); diff --git a/openmetadata-ui/src/main/resources/ui/playwright/support/entity/ingestion/ServiceBaseClass.ts b/openmetadata-ui/src/main/resources/ui/playwright/support/entity/ingestion/ServiceBaseClass.ts index 2a2ea79e676..f7daabbcfde 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/support/entity/ingestion/ServiceBaseClass.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/support/entity/ingestion/ServiceBaseClass.ts @@ -126,20 +126,15 @@ class ServiceBaseClass { await page.click('[data-testid="next-button"]'); await page.waitForSelector('#name_help'); - const nameHelp = await page.$eval('#name_help', (el) => el.textContent); - expect(nameHelp).toContain('Name is required'); + await expect(page.locator('#name_help')).toHaveText('Name is required'); // invalid name validation should work await page .locator('[data-testid="service-name"]') .fill(INVALID_NAMES.WITH_SPECIAL_CHARS); - const nameHelpError = await page.$eval( - '#name_help', - (el) => el.textContent - ); - expect(nameHelpError).toContain(NAME_VALIDATION_ERROR); + await expect(page.locator('#name_help')).toHaveText(NAME_VALIDATION_ERROR); await page.fill('[data-testid="service-name"]', serviceName); diff --git a/openmetadata-ui/src/main/resources/ui/playwright/support/user/UserClass.ts b/openmetadata-ui/src/main/resources/ui/playwright/support/user/UserClass.ts index 6b2e7c17838..7f879b059a2 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/support/user/UserClass.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/support/user/UserClass.ts @@ -203,6 +203,8 @@ export class UserClass { password = this.data.password ) { await page.goto('/'); + await page.waitForURL('**/signin'); + await page.waitForLoadState('networkidle'); await page.fill('input[id="email"]', userName); await page.locator('#email').press('Tab'); await page.fill('input[id="password"]', password); @@ -220,6 +222,17 @@ export class UserClass { if (modal) { await page.getByRole('dialog').getByRole('img').first().click(); } + + // Collapse the left side bar after logging in if it's open + const leftNavBar = page.locator('[data-testid="left-sidebar"]'); + + const hasOpenClass = await leftNavBar.evaluate((el) => + el.classList.contains('sidebar-open') + ); + + if (hasOpenClass) { + await page.getByTestId('sidebar-toggle').click(); + } } async logout(page: Page) { diff --git a/openmetadata-ui/src/main/resources/ui/playwright/utils/advancedSearch.ts b/openmetadata-ui/src/main/resources/ui/playwright/utils/advancedSearch.ts index 9c8cbbe9fe2..628b5447f70 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/utils/advancedSearch.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/utils/advancedSearch.ts @@ -197,13 +197,29 @@ export const showAdvancedSearchDialog = async (page: Page) => { export const selectOption = async ( page: Page, dropdownLocator: Locator, - optionTitle: string + optionTitle: string, + isSearchable = false ) => { - await dropdownLocator.click(); - await page.keyboard.type(optionTitle); + if (isSearchable) { + // Force click on the selector to ensure it opens even if there's an existing selection + await dropdownLocator + .locator('.ant-select-selector') + .click({ force: true }); + + // Clear any existing input and type the new value + const combobox = dropdownLocator.getByRole('combobox'); + await combobox.clear(); + await combobox.fill(optionTitle); + } else { + await dropdownLocator.click(); + } + + await expect(dropdownLocator).toHaveClass(/(^|\s)ant-select-focused(\s|$)/); + await page.waitForSelector(`.ant-select-dropdown:visible`, { state: 'visible', }); + await page.click(`.ant-select-dropdown:visible [title="${optionTitle}"]`); }; @@ -227,7 +243,8 @@ export const fillRule = async ( await selectOption( page, ruleLocator.locator('.rule--field .ant-select'), - field.id + field.id, + true ); // Perform click on operator @@ -595,7 +612,8 @@ export const runRuleGroupTestsWithNonExistingValue = async (page: Page) => { await selectOption( page, ruleLocator.locator('.rule--field .ant-select'), - 'Database' + 'Database', + true ); await selectOption( page, diff --git a/openmetadata-ui/src/main/resources/ui/playwright/utils/customProperty.ts b/openmetadata-ui/src/main/resources/ui/playwright/utils/customProperty.ts index 0f3433a8362..e5d656b7ddd 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/utils/customProperty.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/utils/customProperty.ts @@ -835,19 +835,22 @@ export const verifyCustomPropertyInAdvancedSearch = async ( await selectOption( page, ruleLocator.locator('.rule--field .ant-select'), - 'Custom Properties' + 'Custom Properties', + true ); await selectOption( page, ruleLocator.locator('.rule--field .ant-select'), - entityType + entityType, + true ); await selectOption( page, ruleLocator.locator('.rule--field .ant-select'), - propertyName + propertyName, + true ); await page.getByTestId('cancel-btn').click(); diff --git a/openmetadata-ui/src/main/resources/ui/playwright/utils/observabilityAlert.ts b/openmetadata-ui/src/main/resources/ui/playwright/utils/observabilityAlert.ts index 469364b73d9..3efaa14a359 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/utils/observabilityAlert.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/utils/observabilityAlert.ts @@ -45,12 +45,21 @@ export const visitObservabilityAlertPage = async (page: Page) => { await page.waitForSelector('[data-testid="loader"]', { state: 'detached', }); + + // Set up the response promise before navigation const getAlerts = page.waitForResponse( '/api/v1/events/subscriptions?*alertType=Observability*' ); + + // Set up navigation promise before clicking + const navigationPromise = page.waitForURL('**/observability/alerts', { + waitUntil: 'networkidle', + }); + await sidebarClick(page, SidebarItem.OBSERVABILITY_ALERT); - await page.waitForURL('**/observability/alerts'); - await getAlerts; + + // Wait for both navigation and API response + await Promise.all([navigationPromise, getAlerts]); }; export const addExternalDestination = async ({ diff --git a/openmetadata-ui/src/main/resources/ui/playwright/utils/sidebar.ts b/openmetadata-ui/src/main/resources/ui/playwright/utils/sidebar.ts index 201ae46dda1..2c7c6092aaf 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/utils/sidebar.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/utils/sidebar.ts @@ -29,9 +29,13 @@ export const clickOnLogo = async (page: Page) => { export const sidebarClick = async (page: Page, id: string) => { const items = SIDEBAR_LIST_ITEMS[id as keyof typeof SIDEBAR_LIST_ITEMS]; if (items) { - await page.hover('[data-testid="left-sidebar"]'); - await page.waitForTimeout(300); - await page.click(`[data-testid="${items[0]}"]`); + await page.hover( + `[data-testid="left-sidebar"] [data-menu-id*="${items[0]}"]` + ); + await page.waitForSelector(`[data-testid="app-bar-item-${items[1]}"]`, { + state: 'visible', + }); + await page.click(`[data-testid="app-bar-item-${items[1]}"]`); } else { await page.click(`[data-testid="app-bar-item-${id}"]`);