From 5f60bc0b2e1970742b1e7e724e51185643b790ef Mon Sep 17 00:00:00 2001 From: Ashish Gupta Date: Thu, 12 Jun 2025 15:10:49 +0530 Subject: [PATCH] PLAYWRIGHT: fix the flaky delete step failing in entity and serviceEntity around bar (#21671) --- .../e2e/Features/ActivityFeed.spec.ts | 34 +++++++++++++------ .../e2e/Features/BulkEditEntity.spec.ts | 30 ++++++++-------- .../ui/playwright/e2e/Pages/Tag.spec.ts | 11 ++---- .../ui/playwright/e2e/Pages/Users.spec.ts | 11 ++++-- .../VersionPages/GlossaryVersionPage.spec.ts | 9 ++--- .../ui/playwright/support/tag/TagClass.ts | 3 ++ .../resources/ui/playwright/utils/common.ts | 2 +- .../resources/ui/playwright/utils/entity.ts | 9 +++-- .../ui/playwright/utils/importUtils.ts | 10 ++++++ .../main/resources/ui/playwright/utils/tag.ts | 5 +-- .../resources/ui/playwright/utils/user.ts | 5 +++ 11 files changed, 76 insertions(+), 53 deletions(-) diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/ActivityFeed.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/ActivityFeed.spec.ts index da73dbc8d96..fa12daf93f8 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/ActivityFeed.spec.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/ActivityFeed.spec.ts @@ -52,8 +52,11 @@ const entity = new TableClass(); const entity2 = new TableClass(); const entity3 = new TableClass(); const entity4 = new TableClass(); +const entity5 = new TableClass(); const user1 = new UserClass(); const user2 = new UserClass(); +const user3 = new UserClass(); +const user4 = new UserClass(); const adminUser = new UserClass(); const test = base.extend<{ page: Page }>({ @@ -76,8 +79,11 @@ test.describe('Activity feed', () => { await entity2.create(apiContext); await entity3.create(apiContext); await entity4.create(apiContext); + await entity5.create(apiContext); await user1.create(apiContext); await user2.create(apiContext); + await user3.create(apiContext); + await user4.create(apiContext); await afterAction(); }); @@ -88,8 +94,11 @@ test.describe('Activity feed', () => { await entity2.delete(apiContext); await entity3.delete(apiContext); await entity4.delete(apiContext); + await entity5.delete(apiContext); await user1.delete(apiContext); await user2.delete(apiContext); + await user3.delete(apiContext); + await user4.delete(apiContext); await adminUser.delete(apiContext); await afterAction(); @@ -504,18 +513,18 @@ test.describe('Activity feed', () => { test('Check Task Filter in Landing Page Widget', async ({ browser }) => { const { page: page1, afterAction: afterActionUser1 } = await performUserLogin(browser, user1); - const { page: page2, afterAction: afterActionUser2 } = - await performUserLogin(browser, user2); + const { page: page2, afterAction: afterActionUser3 } = + await performUserLogin(browser, user3); - await base.step('Create and Assign Task to User 2', async () => { + await base.step('Create and Assign Task to User 3', async () => { await redirectToHomePage(page1); await entity.visitEntityPage(page1); - // Create task for the user 2 + // Create task for the user 3 await page1.getByTestId('request-description').click(); await createDescriptionTask(page1, { term: entity.entity.displayName, - assignee: user2.responseData.name, + assignee: user3.responseData.name, }); await afterActionUser1(); @@ -544,6 +553,7 @@ test.describe('Activity feed', () => { .click(); await taskResponse; + await page2.waitForLoadState('networkidle'); await expect( page2.locator( @@ -571,7 +581,7 @@ test.describe('Activity feed', () => { await page2.getByTestId('task-feed-card').locator('.ant-avatar').hover(); await expect( - page2.getByText(user2.responseData.displayName).first() + page2.getByText(user3.responseData.displayName).first() ).toBeVisible(); // Check the Task based on Created by me task filter @@ -592,21 +602,23 @@ test.describe('Activity feed', () => { await page2.getByTestId('task-feed-card').locator('.ant-avatar').hover(); await expect( - page2.getByText(user2.responseData.displayName).first() + page2.getByText(user3.responseData.displayName).first() ).toBeVisible(); - await afterActionUser2(); + await afterActionUser3(); }); }); test('Verify feed count', async ({ page }) => { await redirectToHomePage(page); - await entity.visitEntityPage(page); + await entity5.visitEntityPage(page); await page.getByTestId('request-description').click(); await createDescriptionTask(page, { - term: entity.entity.displayName, - assignee: user1.responseData.name, + term: entity5.entity.displayName, + assignee: user4.responseData.name, }); + await page.waitForSelector('[data-testid="loader"]', { state: 'detached' }); + await page.waitForLoadState('networkidle'); await expect(page.getByTestId('left-panel-task-count')).toHaveText('1'); }); diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/BulkEditEntity.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/BulkEditEntity.spec.ts index 487fffd0b01..951f89dd830 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/BulkEditEntity.spec.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/BulkEditEntity.spec.ts @@ -122,9 +122,6 @@ test.describe('Bulk Edit Entity', () => { ); await page.click('[data-testid="bulk-edit-table"]'); - // Adding manual wait for the file to load - await page.waitForTimeout(500); - await page.waitForSelector('[data-testid="loader"]', { state: 'detached', }); @@ -138,6 +135,9 @@ test.describe('Bulk Edit Entity', () => { page.getByRole('button', { name: 'Previous' }) ).not.toBeVisible(); + // Adding manual wait for the file to load + await page.waitForTimeout(500); + // Click on first cell and edit await page.click( @@ -260,9 +260,6 @@ test.describe('Bulk Edit Entity', () => { await page.click('[data-testid="bulk-edit-table"]'); - // Adding manual wait for the file to load - await page.waitForTimeout(500); - await page.waitForSelector('[data-testid="loader"]', { state: 'detached', }); @@ -276,6 +273,9 @@ test.describe('Bulk Edit Entity', () => { page.getByRole('button', { name: 'Previous' }) ).not.toBeVisible(); + // Adding manual wait for the file to load + await page.waitForTimeout(500); + // click on last row first cell await page.click( '.InovuaReactDataGrid__row--first > .InovuaReactDataGrid__row-cell-wrap > .InovuaReactDataGrid__cell--first' @@ -410,9 +410,6 @@ test.describe('Bulk Edit Entity', () => { await page.click('[data-testid="bulk-edit-table"]'); - // Adding manual wait for the file to load - await page.waitForTimeout(500); - // Adding some assertion to make sure that CSV loaded correctly await expect( page.locator('.InovuaReactDataGrid__header-layout') @@ -422,6 +419,9 @@ test.describe('Bulk Edit Entity', () => { page.getByRole('button', { name: 'Previous' }) ).not.toBeVisible(); + // Adding manual wait for the file to load + await page.waitForTimeout(500); + // Click on first cell and edit await page.click( '.InovuaReactDataGrid__row--first > .InovuaReactDataGrid__row-cell-wrap > .InovuaReactDataGrid__cell--first' @@ -524,9 +524,6 @@ test.describe('Bulk Edit Entity', () => { await page.click('[data-testid="bulk-edit-table"]'); - // Adding manual wait for the file to load - await page.waitForTimeout(500); - // Adding some assertion to make sure that CSV loaded correctly await expect( page.locator('.InovuaReactDataGrid__header-layout') @@ -536,6 +533,9 @@ test.describe('Bulk Edit Entity', () => { page.getByRole('button', { name: 'Previous' }) ).not.toBeVisible(); + // Adding manual wait for the file to load + await page.waitForTimeout(500); + // click on row first cell await page.click( '.InovuaReactDataGrid__row--first > .InovuaReactDataGrid__row-cell-wrap > .InovuaReactDataGrid__cell--first' @@ -623,9 +623,6 @@ test.describe('Bulk Edit Entity', () => { await page.click('[data-testid="bulk-edit-table"]'); - // Adding manual wait for the file to load - await page.waitForTimeout(500); - // Adding some assertion to make sure that CSV loaded correctly await expect( page.locator('.InovuaReactDataGrid__header-layout') @@ -635,6 +632,9 @@ test.describe('Bulk Edit Entity', () => { page.getByRole('button', { name: 'Previous' }) ).not.toBeVisible(); + // Adding manual wait for the file to load + await page.waitForTimeout(500); + // Click on first cell and edit await page.click( '.InovuaReactDataGrid__row--first > .InovuaReactDataGrid__row-cell-wrap > .InovuaReactDataGrid__cell--first' diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/Tag.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/Tag.spec.ts index 3ce57b09e42..c07af7d8416 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/Tag.spec.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/Tag.spec.ts @@ -137,9 +137,8 @@ test.describe('Tag Page with Admin Roles', () => { test('Rename Tag name', async ({ adminPage }) => { await redirectToHomePage(adminPage); - const res = adminPage.waitForResponse(`/api/v1/tags/name/*`); await tag.visitPage(adminPage); - await res; + await adminPage.getByTestId('manage-button').click(); await expect( @@ -163,9 +162,8 @@ test.describe('Tag Page with Admin Roles', () => { test('Restyle Tag', async ({ adminPage }) => { await redirectToHomePage(adminPage); - const res = adminPage.waitForResponse(`/api/v1/tags/name/*`); await tag.visitPage(adminPage); - await res; + await adminPage.getByTestId('manage-button').click(); await expect( @@ -189,9 +187,8 @@ test.describe('Tag Page with Admin Roles', () => { test('Edit Tag Description', async ({ adminPage }) => { await redirectToHomePage(adminPage); - const res = adminPage.waitForResponse(`/api/v1/tags/name/*`); await tag.visitPage(adminPage); - await res; + await adminPage.getByTestId('edit-description').click(); await expect(adminPage.getByRole('dialog')).toBeVisible(); @@ -212,9 +209,7 @@ test.describe('Tag Page with Admin Roles', () => { test('Delete a Tag', async ({ adminPage }) => { await redirectToHomePage(adminPage); - const res = adminPage.waitForResponse(`/api/v1/tags/name/*`); await tag.visitPage(adminPage); - await res; await adminPage.getByTestId('manage-button').click(); await expect( diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/Users.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/Users.spec.ts index 50e7dbe49a9..25b9e045c77 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/Users.spec.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/Users.spec.ts @@ -98,6 +98,8 @@ const test = base.extend<{ }); base.beforeAll('Setup pre-requests', async ({ browser }) => { + test.slow(true); + const { apiContext, afterAction } = await performAdminLogin(browser); await adminUser.create(apiContext); @@ -116,6 +118,8 @@ base.beforeAll('Setup pre-requests', async ({ browser }) => { }); base.afterAll('Cleanup', async ({ browser }) => { + test.slow(true); + const { apiContext, afterAction } = await performAdminLogin(browser); await adminUser.delete(apiContext); await dataConsumerUser.delete(apiContext); @@ -417,7 +421,7 @@ test.describe('User with Data Steward Roles', () => { await addOwner({ page: adminPage, - owner: user.responseData.displayName, + owner: user.responseData.displayName ?? user.responseData.name, type: 'Users', endpoint: EntityTypeEndpoint.Table, dataTestId: 'data-assets-header', @@ -471,9 +475,10 @@ test.describe('User Profile Feed Interactions', () => { ); const avatar = adminPage - .locator('[data-testid="message-container"]') + .locator('#feedData [data-testid="message-container"]') .first() - .locator('[data-testid="profile-avatar"]'); + .locator('[data-testid="profile-avatar"]') + .first(); await avatar.hover(); await adminPage.waitForSelector('.ant-popover-card'); diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/VersionPages/GlossaryVersionPage.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/VersionPages/GlossaryVersionPage.spec.ts index 07b1b1300c6..33e5d138540 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/VersionPages/GlossaryVersionPage.spec.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/VersionPages/GlossaryVersionPage.spec.ts @@ -187,6 +187,7 @@ test('GlossaryTerm', async ({ page }) => { }); await page.reload(); + await page.waitForLoadState('networkidle'); const versionPageResponse = page.waitForResponse( `/api/v1/glossaryTerms/${term2.responseData.id}/versions/0.2` ); @@ -199,14 +200,10 @@ test('GlossaryTerm', async ({ page }) => { ) ).toBeVisible(); - await page.waitForLoadState('networkidle'); - const glossaryTermsRes = page.waitForResponse( '/api/v1/glossaryTerms/name/**' ); await page.getByRole('dialog').getByRole('img').click(); - - await page.waitForLoadState('networkidle'); await glossaryTermsRes; await addMultiOwner({ @@ -219,11 +216,11 @@ test('GlossaryTerm', async ({ page }) => { }); await page.reload(); + await page.waitForLoadState('networkidle'); + await page.click('[data-testid="version-button"]'); await versionPageResponse; - await page.waitForLoadState('networkidle'); - const diffLocator = page.locator( '[data-testid="glossary-reviewer"] [data-testid="diff-added"]' ); diff --git a/openmetadata-ui/src/main/resources/ui/playwright/support/tag/TagClass.ts b/openmetadata-ui/src/main/resources/ui/playwright/support/tag/TagClass.ts index b0c86b70805..1957f65eb48 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/support/tag/TagClass.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/support/tag/TagClass.ts @@ -69,7 +69,10 @@ export class TagClass { this.responseData.classification.name, this.responseData.classification.displayName ); + const res = page.waitForResponse(`/api/v1/tags/name/*`); await page.getByTestId(this.data.name).click(); + await res; + await page.waitForLoadState('networkidle'); } async create(apiContext: APIRequestContext) { diff --git a/openmetadata-ui/src/main/resources/ui/playwright/utils/common.ts b/openmetadata-ui/src/main/resources/ui/playwright/utils/common.ts index 266ffb2d3f9..89a45ded80b 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/utils/common.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/utils/common.ts @@ -58,8 +58,8 @@ export const getAuthContext = async (token: string) => { export const redirectToHomePage = async (page: Page) => { await page.goto('/'); - await page.waitForLoadState('networkidle'); await page.waitForURL('**/my-data'); + await page.waitForLoadState('networkidle'); }; export const removeLandingBanner = async (page: Page) => { diff --git a/openmetadata-ui/src/main/resources/ui/playwright/utils/entity.ts b/openmetadata-ui/src/main/resources/ui/playwright/utils/entity.ts index 98fe8104033..5ec13b5648a 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/utils/entity.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/utils/entity.ts @@ -43,10 +43,10 @@ export const visitEntityPage = async (data: { dataTestId: string; }) => { const { page, searchTerm, dataTestId } = data; + await page.waitForLoadState('networkidle'); const waitForSearchResponse = page.waitForResponse( '/api/v1/search/query?q=*index=dataAsset*' ); - await page.waitForLoadState('networkidle'); await page.getByTestId('searchBox').fill(searchTerm); await waitForSearchResponse; await page.getByTestId(dataTestId).getByTestId('data-name').click(); @@ -1367,10 +1367,9 @@ export const hardDeleteEntity = async ( await page.click('[data-testid="confirm-button"]'); await deleteResponse; - await toastNotification( - page, - /deleted successfully!/, - BIG_ENTITY_DELETE_TIMEOUT + await expect(page.getByTestId('alert-bar')).toHaveText( + /(deleted successfully!|Delete operation initiated)/, + { timeout: BIG_ENTITY_DELETE_TIMEOUT } ); }; diff --git a/openmetadata-ui/src/main/resources/ui/playwright/utils/importUtils.ts b/openmetadata-ui/src/main/resources/ui/playwright/utils/importUtils.ts index 883515ce307..a2356306378 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/utils/importUtils.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/utils/importUtils.ts @@ -46,9 +46,19 @@ export const createGlossaryTermRowDetails = () => { export const fillTextInputDetails = async (page: Page, text: string) => { await page.keyboard.press('Enter', { delay: 100 }); + const isVisible = await page + .locator('.ant-layout-content') + .getByRole('textbox') + .isVisible(); + + if (!isVisible) { + await page.keyboard.press('Enter', { delay: 100 }); + } + const textboxLocator = page .locator('.ant-layout-content') .getByRole('textbox'); + await textboxLocator.fill(text); await textboxLocator.press('Enter', { delay: 100 }); }; diff --git a/openmetadata-ui/src/main/resources/ui/playwright/utils/tag.ts b/openmetadata-ui/src/main/resources/ui/playwright/utils/tag.ts index 4db44c4a2b6..9259ba94390 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/utils/tag.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/utils/tag.ts @@ -91,9 +91,7 @@ export const addAssetsToTag = async ( tag: TagClass, otherAsset?: EntityClass[] ) => { - const res = page.waitForResponse(`/api/v1/tags/name/*`); await tag.visitPage(page); - await res; await page.waitForSelector( '[data-testid="tags-container"] [data-testid="loader"]', @@ -334,9 +332,7 @@ export const verifyTagPageUI = async ( limitedAccess = false ) => { await redirectToHomePage(page); - const res = page.waitForResponse(`/api/v1/tags/name/*`); await tag.visitPage(page); - await res; await page.waitForSelector( '[data-testid="tags-container"] [data-testid="loader"]', @@ -363,6 +359,7 @@ export const verifyTagPageUI = async ( await page.getByRole('link', { name: classificationName }).click(); classificationTable; + const res = page.waitForResponse(`/api/v1/tags/name/*`); await page.getByTestId(tag.data.name).click(); await res; diff --git a/openmetadata-ui/src/main/resources/ui/playwright/utils/user.ts b/openmetadata-ui/src/main/resources/ui/playwright/utils/user.ts index 71e55af5163..87ea97f4851 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/utils/user.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/utils/user.ts @@ -578,6 +578,11 @@ export const checkStewardServicesPermissions = async (page: Page) => { await queryResponse; // Perform search actions await page.click('[data-testid="search-dropdown-Data Assets"]'); + + await page.getByTestId('drop-down-menu').getByTestId('loader').waitFor({ + state: 'detached', + }); + await page.locator('[data-testid="table-checkbox"]').scrollIntoViewIfNeeded(); await page.click('[data-testid="table-checkbox"]');