From c8b9af167435662abe521d71becaf4e8ccbe9b1a Mon Sep 17 00:00:00 2001 From: Rohit0301 Date: Sat, 15 Aug 2026 11:38:01 +0530 Subject: [PATCH 1/4] Fixed context center flaky test --- .../Features/ContextCenterArticles.spec.ts | 15 ++++--- .../Features/ContextCenterDocument.spec.ts | 40 ++++++++++++------- .../Features/ContextCenterMemories.spec.ts | 13 +++++- .../Flow/ServiceCreationPermissions.spec.ts | 20 +++++++++- .../ui/playwright/utils/ContextCenterUtil.ts | 33 +++++++++++++++ 5 files changed, 97 insertions(+), 24 deletions(-) diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/ContextCenterArticles.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/ContextCenterArticles.spec.ts index 55de2563f045..8cf10de53bc6 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/ContextCenterArticles.spec.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/ContextCenterArticles.spec.ts @@ -51,6 +51,7 @@ import { scrollListingToCard, verifyArticleSearch, waitForArticleInFollows, + waitForDraftPersisted, } from '../../utils/ContextCenterUtil'; import { addMultiOwner, @@ -275,7 +276,7 @@ test.describe('Context Center Articles', () => { await test.step('dashboard view all articles opens article list', async () => { await navigateToDashboard(page); - await page.getByTestId('article-detail-card').click(); + await page.getByTestId('article-detail-card').getByRole('button', { name: 'View All Articles' }).click(); await expect(page).toHaveURL(/\/context-center\/articles/); }); @@ -1520,7 +1521,7 @@ test.describe('Context Center Articles', () => { await navigateToArticle(page, draftArticleA.fullyQualifiedName); await page.fill('.om-block-editor', reloadDescription); await page.getByText('Unsaved').waitFor({ state: 'visible' }); - await page.waitForTimeout(400); + await waitForDraftPersisted(page, draftArticleA.id, reloadDescription); }); await test.step('Reload the page (simulates browser refresh before auto-save)', async () => { @@ -1599,7 +1600,11 @@ test.describe('Context Center Articles', () => { await navigateToArticle(page, articleToDelete.fullyQualifiedName); await page.fill('.om-block-editor', 'This draft should be deleted'); await page.getByText('Unsaved').waitFor({ state: 'visible' }); - await page.waitForTimeout(400); + await waitForDraftPersisted( + page, + articleToDelete.id, + 'This draft should be deleted' + ); }); await test.step('Navigate away to ensure draft is persisted in localStorage', async () => { @@ -1647,14 +1652,14 @@ test.describe('Context Center Articles', () => { await navigateToArticle(page, draftArticleA.fullyQualifiedName); await page.fill('.om-block-editor', contentA); await page.getByText('Unsaved').waitFor({ state: 'visible' }); - await page.waitForTimeout(400); + await waitForDraftPersisted(page, draftArticleA.id, contentA); }); await test.step('Navigate to draft article B and type without saving', async () => { await navigateToArticle(page, draftArticleB.fullyQualifiedName); await page.fill('.om-block-editor', contentB); await page.getByText('Unsaved').waitFor({ state: 'visible' }); - await page.waitForTimeout(400); + await waitForDraftPersisted(page, draftArticleB.id, contentB); }); await test.step('Reload Article B — its own draft should be synced', async () => { diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/ContextCenterDocument.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/ContextCenterDocument.spec.ts index 7de0d01af4ad..75c6d897cb48 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/ContextCenterDocument.spec.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/ContextCenterDocument.spec.ts @@ -20,6 +20,7 @@ import { expectBulkIdsRequest, expectSelectedCount, getDocumentRowByName, + getDocumentSearchInput, getFolderTreeItem, navigateToDocuments, parseResponseJson, @@ -291,7 +292,7 @@ test.describe('Context Center - Documents Page', () => { await navigateToDocuments(page); - const row = getDocumentRowByName(page, fileName); + const row = await searchAndGetDocumentRow(page, fileName); await expect(row).toBeVisible(); await row.scrollIntoViewIfNeeded(); @@ -381,7 +382,7 @@ test.describe('Context Center - Documents Page', () => { await navigateToDocuments(page); await selectFolderInSidebar(page, lastPageFolder.name); await expect( - getDocumentRowByName(page, fileName).getByTestId('document-folder-name') + (await searchAndGetDocumentRow(page, fileName)).getByTestId('document-folder-name') ).toHaveText(lastPageFolder.name); }); }); @@ -625,7 +626,7 @@ test.describe('Context Center - Documents Page', () => { await navigateToDocuments(page); - const row = getDocumentRowByName(page, fileName); + const row = await searchAndGetDocumentRow(page, fileName); await expect(row).toBeVisible(); await row.scrollIntoViewIfNeeded(); @@ -722,7 +723,7 @@ test.describe('Context Center - Documents Page', () => { await navigateToDocuments(page); - const row = getDocumentRowByName(page, fileName); + const row = await searchAndGetDocumentRow(page, fileName); await expect(row).toBeVisible(); await row.scrollIntoViewIfNeeded(); @@ -774,7 +775,7 @@ test.describe('Context Center - Documents Page', () => { await navigateToDocuments(page); - const row = getDocumentRowByName(page, fileName); + const row = await searchAndGetDocumentRow(page, fileName); await expect(row).toBeVisible(); await row.scrollIntoViewIfNeeded(); @@ -849,7 +850,7 @@ test.describe('Context Center - Documents Page', () => { await navigateToDocuments(page); - const row = getDocumentRowByName(page, fileName); + const row = await searchAndGetDocumentRow(page, fileName); await expect(row).toBeVisible(); await row.scrollIntoViewIfNeeded(); await row.click(); @@ -894,7 +895,7 @@ test.describe('Context Center - Documents Page', () => { await navigateToDocuments(page); - const row = getDocumentRowByName(page, fileName); + const row = await searchAndGetDocumentRow(page, fileName); await expect(row).toBeVisible(); await row.scrollIntoViewIfNeeded(); @@ -983,12 +984,12 @@ test.describe('Context Center - Documents Page', () => { await navigateToDocuments(page); await expect( - getDocumentRowByName(page, firstFileName).getByTestId( + (await searchAndGetDocumentRow(page, firstFileName)).getByTestId( 'document-folder-name' ) ).toHaveText(folderName); await expect( - getDocumentRowByName(page, secondFileName).getByTestId( + (await searchAndGetDocumentRow(page, secondFileName)).getByTestId( 'document-folder-name' ) ).toHaveText(folderName); @@ -1042,8 +1043,8 @@ test.describe('Context Center - Documents Page', () => { doc2.id, ]); - await expect(getDocumentRowByName(page, firstName)).not.toBeVisible(); - await expect(getDocumentRowByName(page, secondName)).not.toBeVisible(); + await expect(await searchAndGetDocumentRow(page, firstName)).not.toBeVisible(); + await expect(await searchAndGetDocumentRow(page, secondName)).not.toBeVisible(); await page.goto('/context-center/archive'); await page @@ -1082,7 +1083,7 @@ test.describe('Context Center - Documents Page', () => { await navigateToDocuments(page); - const row = getDocumentRowByName(page, fileName); + const row = await searchAndGetDocumentRow(page, fileName); await expect(row).toBeVisible(); await row.scrollIntoViewIfNeeded(); await row.getByTestId('manage-button').click(); @@ -1149,8 +1150,8 @@ test.describe('Context Center - Documents Page', () => { // Before selecting a folder: both documents should be visible and counts // reflect the global total (≥2 files; DocumentsView and DocumentFolderView // show the same number). - await expect(getDocumentRowByName(page, docInFolderName)).toBeVisible(); - await expect(getDocumentRowByName(page, docOutsideName)).toBeVisible(); + await expect(await searchAndGetDocumentRow(page, docInFolderName)).toBeVisible(); + await expect(await searchAndGetDocumentRow(page, docOutsideName)).toBeVisible(); const documentsViewCount = page.getByTestId('documents-view-file-count'); const folderViewCount = page.getByTestId('folder-view-file-count'); @@ -1171,6 +1172,15 @@ test.describe('Context Center - Documents Page', () => { // Click the folder — triggers a server-side refetch scoped to that folder. await selectFolderInSidebar(page, folderName); + const searchResPromise = page.waitForResponse( + (res) => + res.url().includes('/api/v1/search/query') && + res.url().includes('index=contextFile') && + res.request().method() === 'GET' + ); + await getDocumentSearchInput(page).fill(''); + await searchResPromise; + await waitForAllLoadersToDisappear(page); // After selecting folder: only in-folder document visible. await expect(getDocumentRowByName(page, docInFolderName)).toBeVisible(); @@ -1186,7 +1196,7 @@ test.describe('Context Center - Documents Page', () => { ); expect(folderCountAfter).toBeGreaterThanOrEqual(globalCount); - const inFolderRow = getDocumentRowByName(page, docInFolderName); + const inFolderRow = await searchAndGetDocumentRow(page, docInFolderName); await inFolderRow.scrollIntoViewIfNeeded(); await inFolderRow.getByTestId('manage-button').click(); await expect(page.getByTestId('move-btn')).toBeVisible(); diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/ContextCenterMemories.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/ContextCenterMemories.spec.ts index 22b3810f1280..62955b2baadf 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/ContextCenterMemories.spec.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/ContextCenterMemories.spec.ts @@ -75,6 +75,11 @@ test.describe( const { apiContext, afterAction } = await createNewPage(browser); await linkedTable.create(apiContext); + await waitForSearchIndexed( + apiContext, + linkedTable.entityResponseData.fullyQualifiedName, + 'table_search_index' + ); // dataConsumerPage is a per-test fixture and isn't resolved inside // beforeAll, so open a page against the same storage state directly. @@ -1280,7 +1285,9 @@ test.describe( await expect(dialog).toBeVisible(); await dialog.getByTestId('memory-type-select').click(); - await page.getByRole('option', { name: /faq/i }).click(); + const faqOption = page.getByRole('option', { name: /faq/i }); + await faqOption.click(); + await faqOption.waitFor({ state: 'detached' }); const updateResPromise = page.waitForResponse( new RegExp(`${MEMORIES_API}/${editMemoryId}`) @@ -1317,7 +1324,9 @@ test.describe( await editVisibilityBtn.click(); await dialog.getByTestId('memory-visibility-select').click(); - await page.getByRole('option', { name: /private/i }).click(); + const privateOption = page.getByRole('option', { name: /private/i }); + await privateOption.click(); + await privateOption.waitFor({ state: 'detached' }); const updateResPromise = page.waitForResponse( new RegExp(`${MEMORIES_API}/${editMemoryId}`) diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Flow/ServiceCreationPermissions.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Flow/ServiceCreationPermissions.spec.ts index 66b5dbc2ecb7..afb30bfd512d 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Flow/ServiceCreationPermissions.spec.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Flow/ServiceCreationPermissions.spec.ts @@ -110,8 +110,20 @@ const visitAgentCard = async (page: Page) => { }; const openPipelineActions = async (page: Page) => { + // The overflow menu's items are gated on an async permission fetch that + // resolves after the agent card mounts; without this wait the dropdown can + // open before permissions settle, so items like `re-deploy-button` are + // briefly absent and toBeVisible() assertions race. + const permissionResponse = page.waitForResponse( + `/api/v1/permissions/ingestionPipeline/name/${encodeURIComponent( + ingestionPipelineName + )}` + ); + const actionButton = (await visitAgentCard(page)).getByTestId('more-actions'); + await permissionResponse; + await actionButton.waitFor(); await actionButton.click(); @@ -645,8 +657,12 @@ test.describe( }) => { await openPipelineActions(page); - await expect(page.getByTestId('edit-button')).toBeVisible(); - await expect(page.getByTestId('re-deploy-button')).toBeVisible(); + const actionsDropdown = page.getByTestId('actions-dropdown'); + + await expect(actionsDropdown.getByTestId('edit-button')).toBeVisible(); + await expect( + actionsDropdown.getByTestId('re-deploy-button') + ).toBeVisible(); await expect( getAgentCard(page, ingestionPipelineName).getByTestId( 'run-agent-button' diff --git a/openmetadata-ui/src/main/resources/ui/playwright/utils/ContextCenterUtil.ts b/openmetadata-ui/src/main/resources/ui/playwright/utils/ContextCenterUtil.ts index f49004991e1e..2b465777e423 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/utils/ContextCenterUtil.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/utils/ContextCenterUtil.ts @@ -1050,6 +1050,39 @@ export const readDraftStore = async ( } }; +/** + * The draft-content debounce (300ms in KnowledgePageDetailComponent) means the + * "Unsaved" badge appearing gives no guarantee the debounced write to + * localStorage has actually landed yet. Polling the real draft store removes + * that race instead of guessing a fixed timeout that can lose the race under + * CI load. + * + * The timeout is deliberately kept well under the real-save debounce + * (SHORT_DELAY, 3000ms): once that autosave completes it clears the draft + * from localStorage (see endTrackedSave -> removeDraft), so a long poll here + * risks racing the autosave itself and observing the draft after it has + * already been removed rather than confirming it landed in time. + */ +export const waitForDraftPersisted = async ( + page: Page, + articleId: string, + expectedDescription: string +) => { + await expect + .poll( + async () => { + const drafts = await readDraftStore(page); + const draft = drafts[articleId] as { description?: string } | undefined; + + // The draft stores the editor's HTML output (e.g. wrapped in

...

), + // not the plain text that was typed, so match on substring containment. + return draft?.description?.includes(expectedDescription) ?? false; + }, + { timeout: 2000, intervals: [50, 100, 200] } + ) + .toBe(true); +}; + /** * A minimal valid 1x1 transparent PNG, used as an in-memory upload fixture * since this repo has no binary image fixtures under playwright/test-data/. From 6db39282ef26e2f036aa9448574344a144b0bbe3 Mon Sep 17 00:00:00 2001 From: Rohit0301 Date: Sat, 15 Aug 2026 11:40:29 +0530 Subject: [PATCH 2/4] lint fix --- .../Features/ContextCenterArticles.spec.ts | 5 ++- .../Features/ContextCenterDocument.spec.ts | 42 ++++++++++++------- 2 files changed, 30 insertions(+), 17 deletions(-) diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/ContextCenterArticles.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/ContextCenterArticles.spec.ts index 8cf10de53bc6..29051aed6d7b 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/ContextCenterArticles.spec.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/ContextCenterArticles.spec.ts @@ -276,7 +276,10 @@ test.describe('Context Center Articles', () => { await test.step('dashboard view all articles opens article list', async () => { await navigateToDashboard(page); - await page.getByTestId('article-detail-card').getByRole('button', { name: 'View All Articles' }).click(); + await page + .getByTestId('article-detail-card') + .getByRole('button', { name: 'View All Articles' }) + .click(); await expect(page).toHaveURL(/\/context-center\/articles/); }); diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/ContextCenterDocument.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/ContextCenterDocument.spec.ts index 75c6d897cb48..307ff729c90c 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/ContextCenterDocument.spec.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/ContextCenterDocument.spec.ts @@ -382,7 +382,9 @@ test.describe('Context Center - Documents Page', () => { await navigateToDocuments(page); await selectFolderInSidebar(page, lastPageFolder.name); await expect( - (await searchAndGetDocumentRow(page, fileName)).getByTestId('document-folder-name') + ( + await searchAndGetDocumentRow(page, fileName) + ).getByTestId('document-folder-name') ).toHaveText(lastPageFolder.name); }); }); @@ -984,14 +986,14 @@ test.describe('Context Center - Documents Page', () => { await navigateToDocuments(page); await expect( - (await searchAndGetDocumentRow(page, firstFileName)).getByTestId( - 'document-folder-name' - ) + ( + await searchAndGetDocumentRow(page, firstFileName) + ).getByTestId('document-folder-name') ).toHaveText(folderName); await expect( - (await searchAndGetDocumentRow(page, secondFileName)).getByTestId( - 'document-folder-name' - ) + ( + await searchAndGetDocumentRow(page, secondFileName) + ).getByTestId('document-folder-name') ).toHaveText(folderName); }); @@ -1043,8 +1045,12 @@ test.describe('Context Center - Documents Page', () => { doc2.id, ]); - await expect(await searchAndGetDocumentRow(page, firstName)).not.toBeVisible(); - await expect(await searchAndGetDocumentRow(page, secondName)).not.toBeVisible(); + await expect( + await searchAndGetDocumentRow(page, firstName) + ).not.toBeVisible(); + await expect( + await searchAndGetDocumentRow(page, secondName) + ).not.toBeVisible(); await page.goto('/context-center/archive'); await page @@ -1150,8 +1156,12 @@ test.describe('Context Center - Documents Page', () => { // Before selecting a folder: both documents should be visible and counts // reflect the global total (≥2 files; DocumentsView and DocumentFolderView // show the same number). - await expect(await searchAndGetDocumentRow(page, docInFolderName)).toBeVisible(); - await expect(await searchAndGetDocumentRow(page, docOutsideName)).toBeVisible(); + await expect( + await searchAndGetDocumentRow(page, docInFolderName) + ).toBeVisible(); + await expect( + await searchAndGetDocumentRow(page, docOutsideName) + ).toBeVisible(); const documentsViewCount = page.getByTestId('documents-view-file-count'); const folderViewCount = page.getByTestId('folder-view-file-count'); @@ -1173,11 +1183,11 @@ test.describe('Context Center - Documents Page', () => { // Click the folder — triggers a server-side refetch scoped to that folder. await selectFolderInSidebar(page, folderName); const searchResPromise = page.waitForResponse( - (res) => - res.url().includes('/api/v1/search/query') && - res.url().includes('index=contextFile') && - res.request().method() === 'GET' - ); + (res) => + res.url().includes('/api/v1/search/query') && + res.url().includes('index=contextFile') && + res.request().method() === 'GET' + ); await getDocumentSearchInput(page).fill(''); await searchResPromise; await waitForAllLoadersToDisappear(page); From 54b2bc82b6f2ae758e321b25e922ec3a435a46eb Mon Sep 17 00:00:00 2001 From: Rohit0301 Date: Sat, 15 Aug 2026 16:58:12 +0530 Subject: [PATCH 3/4] minor fix --- .../Features/ContextCenterDocument.spec.ts | 53 ++++++++------- .../Flow/ServiceCreationPermissions.spec.ts | 64 +++++++++++-------- 2 files changed, 64 insertions(+), 53 deletions(-) diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/ContextCenterDocument.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/ContextCenterDocument.spec.ts index 307ff729c90c..0873799cb6da 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/ContextCenterDocument.spec.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/ContextCenterDocument.spec.ts @@ -14,26 +14,26 @@ import { APIRequestContext, expect, Response } from '@playwright/test'; import { createNewPage, redirectToHomePage, uuid } from '../../utils/common'; import { - BulkOperationResult, - ContextCenterDocument, - ContextCenterFolder, - expectBulkIdsRequest, - expectSelectedCount, - getDocumentRowByName, - getDocumentSearchInput, - getFolderTreeItem, - navigateToDocuments, - parseResponseJson, - revealFolderRow, - scrollUntilResponse, - searchAndGetDocumentRow, - selectDocumentByName, - selectFolderInSidebar, - uploadDocument as uploadDocumentToApi, + BulkOperationResult, + ContextCenterDocument, + ContextCenterFolder, + expectBulkIdsRequest, + expectSelectedCount, + getDocumentRowByName, + getDocumentSearchInput, + getFolderTreeItem, + navigateToDocuments, + parseResponseJson, + revealFolderRow, + scrollUntilResponse, + searchAndGetDocumentRow, + selectDocumentByName, + selectFolderInSidebar, + uploadDocument as uploadDocumentToApi } from '../../utils/ContextCenterUtil'; import { - copyAndGetClipboardText, - waitForAllLoadersToDisappear, + copyAndGetClipboardText, + waitForAllLoadersToDisappear } from '../../utils/entity'; import { test } from '../fixtures/pages'; @@ -1179,19 +1179,18 @@ test.describe('Context Center - Documents Page', () => { 10 ); expect(folderCount).toBeGreaterThanOrEqual(globalCount); - - // Click the folder — triggers a server-side refetch scoped to that folder. - await selectFolderInSidebar(page, folderName); - const searchResPromise = page.waitForResponse( + const browseResPromise = page.waitForResponse( (res) => - res.url().includes('/api/v1/search/query') && - res.url().includes('index=contextFile') && - res.request().method() === 'GET' + res.url().includes('/api/v1/contextCenter/drive/files') && + !res.url().includes('search') ); - await getDocumentSearchInput(page).fill(''); - await searchResPromise; + await getDocumentSearchInput(page).clear(); + await browseResPromise; await waitForAllLoadersToDisappear(page); + // Click the folder — triggers a server-side refetch scoped to that folder. + await selectFolderInSidebar(page, folderName); + // After selecting folder: only in-folder document visible. await expect(getDocumentRowByName(page, docInFolderName)).toBeVisible(); await expect(getDocumentRowByName(page, docOutsideName)).not.toBeVisible(); diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Flow/ServiceCreationPermissions.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Flow/ServiceCreationPermissions.spec.ts index afb30bfd512d..3a2871c5f846 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Flow/ServiceCreationPermissions.spec.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Flow/ServiceCreationPermissions.spec.ts @@ -14,8 +14,8 @@ import { expect, Page, test as base } from '@playwright/test'; import { PLAYWRIGHT_INGESTION_TAG_OBJ } from '../../constant/config'; import { - SERVICE_CREATOR_RULES, - SERVICE_VIEWER_RULES, + SERVICE_CREATOR_RULES, + SERVICE_VIEWER_RULES } from '../../constant/permission'; import { GlobalSettingOptions } from '../../constant/settings'; import { PolicyClass } from '../../support/access-control/PoliciesClass'; @@ -24,19 +24,19 @@ import { DatabaseServiceClass } from '../../support/entity/service/DatabaseServi import { UserClass } from '../../support/user/UserClass'; import { performAdminLogin } from '../../utils/admin'; import { - redirectToHomePage, - toastNotification, - uuid, + redirectToHomePage, + toastNotification, + uuid } from '../../utils/common'; import { updateDescription } from '../../utils/entity'; import { visitServiceDetailsPage } from '../../utils/service'; import { - advanceToServiceConnectionStep, - getAgentCard, - mockSuccessfulTestConnection, - selectServiceConnector, - testConnectionIfRequired, - waitForServiceConnectionForm, + advanceToServiceConnectionStep, + getAgentCard, + mockSuccessfulTestConnection, + selectServiceConnector, + testConnectionIfRequired, + waitForServiceConnectionForm } from '../../utils/serviceIngestion'; import { settingClick } from '../../utils/sidebar'; @@ -110,24 +110,36 @@ const visitAgentCard = async (page: Page) => { }; const openPipelineActions = async (page: Page) => { - // The overflow menu's items are gated on an async permission fetch that - // resolves after the agent card mounts; without this wait the dropdown can - // open before permissions settle, so items like `re-deploy-button` are - // briefly absent and toBeVisible() assertions race. - const permissionResponse = page.waitForResponse( - `/api/v1/permissions/ingestionPipeline/name/${encodeURIComponent( - ingestionPipelineName - )}` - ); - const actionButton = (await visitAgentCard(page)).getByTestId('more-actions'); - - await permissionResponse; - await actionButton.waitFor(); - await actionButton.click(); - await page.getByTestId('actions-dropdown').waitFor(); + const actionsDropdown = page.getByTestId('actions-dropdown'); + + // AgentOverflowMenu recomputes its item list from the `permissions` prop on + // every render, but the async per-FQN permission fetch can still be in + // flight when the menu is first opened — some items (edit-gated: redeploy, + // edit, pause/resume) are briefly absent. Close and reopen until the + // permission-gated items are present instead of polling a single stale + // open instance. + await expect + .poll( + async () => { + await actionButton.click(); + await actionsDropdown.waitFor(); + const hasReDeploy = await actionsDropdown + .getByTestId('re-deploy-button') + .isVisible() + .catch(() => false); + if (!hasReDeploy) { + await page.keyboard.press('Escape'); + await actionsDropdown.waitFor({ state: 'hidden' }); + } + + return hasReDeploy; + }, + { intervals: [1_000, 2_000, 3_000], timeout: 30_000 } + ) + .toBe(true); }; test.describe( From 4a7e6706ccfb9305563afdb3af06795847238870 Mon Sep 17 00:00:00 2001 From: Rohit0301 Date: Sat, 15 Aug 2026 17:33:07 +0530 Subject: [PATCH 4/4] lint fix --- .../Features/ContextCenterDocument.spec.ts | 36 +++++++++---------- .../Flow/ServiceCreationPermissions.spec.ts | 22 ++++++------ 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/ContextCenterDocument.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/ContextCenterDocument.spec.ts index 0873799cb6da..2a99e5fd8eec 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/ContextCenterDocument.spec.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/ContextCenterDocument.spec.ts @@ -14,26 +14,26 @@ import { APIRequestContext, expect, Response } from '@playwright/test'; import { createNewPage, redirectToHomePage, uuid } from '../../utils/common'; import { - BulkOperationResult, - ContextCenterDocument, - ContextCenterFolder, - expectBulkIdsRequest, - expectSelectedCount, - getDocumentRowByName, - getDocumentSearchInput, - getFolderTreeItem, - navigateToDocuments, - parseResponseJson, - revealFolderRow, - scrollUntilResponse, - searchAndGetDocumentRow, - selectDocumentByName, - selectFolderInSidebar, - uploadDocument as uploadDocumentToApi + BulkOperationResult, + ContextCenterDocument, + ContextCenterFolder, + expectBulkIdsRequest, + expectSelectedCount, + getDocumentRowByName, + getDocumentSearchInput, + getFolderTreeItem, + navigateToDocuments, + parseResponseJson, + revealFolderRow, + scrollUntilResponse, + searchAndGetDocumentRow, + selectDocumentByName, + selectFolderInSidebar, + uploadDocument as uploadDocumentToApi, } from '../../utils/ContextCenterUtil'; import { - copyAndGetClipboardText, - waitForAllLoadersToDisappear + copyAndGetClipboardText, + waitForAllLoadersToDisappear, } from '../../utils/entity'; import { test } from '../fixtures/pages'; diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Flow/ServiceCreationPermissions.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Flow/ServiceCreationPermissions.spec.ts index 3a2871c5f846..d84452effb53 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Flow/ServiceCreationPermissions.spec.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Flow/ServiceCreationPermissions.spec.ts @@ -14,8 +14,8 @@ import { expect, Page, test as base } from '@playwright/test'; import { PLAYWRIGHT_INGESTION_TAG_OBJ } from '../../constant/config'; import { - SERVICE_CREATOR_RULES, - SERVICE_VIEWER_RULES + SERVICE_CREATOR_RULES, + SERVICE_VIEWER_RULES, } from '../../constant/permission'; import { GlobalSettingOptions } from '../../constant/settings'; import { PolicyClass } from '../../support/access-control/PoliciesClass'; @@ -24,19 +24,19 @@ import { DatabaseServiceClass } from '../../support/entity/service/DatabaseServi import { UserClass } from '../../support/user/UserClass'; import { performAdminLogin } from '../../utils/admin'; import { - redirectToHomePage, - toastNotification, - uuid + redirectToHomePage, + toastNotification, + uuid, } from '../../utils/common'; import { updateDescription } from '../../utils/entity'; import { visitServiceDetailsPage } from '../../utils/service'; import { - advanceToServiceConnectionStep, - getAgentCard, - mockSuccessfulTestConnection, - selectServiceConnector, - testConnectionIfRequired, - waitForServiceConnectionForm + advanceToServiceConnectionStep, + getAgentCard, + mockSuccessfulTestConnection, + selectServiceConnector, + testConnectionIfRequired, + waitForServiceConnectionForm, } from '../../utils/serviceIngestion'; import { settingClick } from '../../utils/sidebar';