Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
scrollListingToCard,
verifyArticleSearch,
waitForArticleInFollows,
waitForDraftPersisted,
} from '../../utils/ContextCenterUtil';
import {
addMultiOwner,
Expand Down Expand Up @@ -275,7 +276,10 @@

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/);
});
Expand Down Expand Up @@ -637,7 +641,7 @@
url.pathname.includes('/context-center/articles/')
);
await waitForAllLoadersToDisappear(page);
await page.waitForTimeout(500);

Check warning on line 644 in openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/ContextCenterArticles.spec.ts

View workflow job for this annotation

GitHub Actions / checkstyle

Unexpected use of page.waitForTimeout()

await navigateToArticles(page);
const rightPanel = page.getByTestId('knowledge-center-right-panel');
Expand Down Expand Up @@ -1200,7 +1204,7 @@
.click();
await page.getByTestId('save').click();

await page.waitForSelector(

Check warning on line 1207 in openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/ContextCenterArticles.spec.ts

View workflow job for this annotation

GitHub Actions / checkstyle

Unexpected use of page.waitForSelector()
'[role="dialog"].description-markdown-editor',
{ state: 'hidden' }
);
Expand Down Expand Up @@ -1455,7 +1459,7 @@
.getByTestId('entity-header-display-name')
.fill(newDisplayName);
await page.getByText('Unsaved').waitFor({ state: 'visible' });
await page.waitForTimeout(400);

Check warning on line 1462 in openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/ContextCenterArticles.spec.ts

View workflow job for this annotation

GitHub Actions / checkstyle

Unexpected use of page.waitForTimeout()
await page.getByRole('link', { name: 'Articles' }).click();
});

Expand Down Expand Up @@ -1520,7 +1524,7 @@
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 () => {
Expand Down Expand Up @@ -1599,7 +1603,11 @@
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 () => {
Expand Down Expand Up @@ -1647,14 +1655,14 @@
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 () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
expectBulkIdsRequest,
expectSelectedCount,
getDocumentRowByName,
getDocumentSearchInput,
getFolderTreeItem,
navigateToDocuments,
parseResponseJson,
Expand Down Expand Up @@ -291,7 +292,7 @@

await navigateToDocuments(page);

const row = getDocumentRowByName(page, fileName);
const row = await searchAndGetDocumentRow(page, fileName);
await expect(row).toBeVisible();
await row.scrollIntoViewIfNeeded();

Expand Down Expand Up @@ -381,7 +382,9 @@
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);
});
});
Expand Down Expand Up @@ -625,7 +628,7 @@

await navigateToDocuments(page);

const row = getDocumentRowByName(page, fileName);
const row = await searchAndGetDocumentRow(page, fileName);
await expect(row).toBeVisible();
await row.scrollIntoViewIfNeeded();

Expand Down Expand Up @@ -722,7 +725,7 @@

await navigateToDocuments(page);

const row = getDocumentRowByName(page, fileName);
const row = await searchAndGetDocumentRow(page, fileName);
await expect(row).toBeVisible();
await row.scrollIntoViewIfNeeded();

Expand Down Expand Up @@ -774,7 +777,7 @@

await navigateToDocuments(page);

const row = getDocumentRowByName(page, fileName);
const row = await searchAndGetDocumentRow(page, fileName);
await expect(row).toBeVisible();
await row.scrollIntoViewIfNeeded();

Expand Down Expand Up @@ -849,7 +852,7 @@

await navigateToDocuments(page);

const row = getDocumentRowByName(page, fileName);
const row = await searchAndGetDocumentRow(page, fileName);
await expect(row).toBeVisible();
await row.scrollIntoViewIfNeeded();
await row.click();
Expand Down Expand Up @@ -894,7 +897,7 @@

await navigateToDocuments(page);

const row = getDocumentRowByName(page, fileName);
const row = await searchAndGetDocumentRow(page, fileName);
await expect(row).toBeVisible();
await row.scrollIntoViewIfNeeded();

Expand All @@ -904,7 +907,7 @@
const clipboardText = await copyAndGetClipboardText(page, copyBtn);
expect(clipboardText).toContain(`document=${doc.id}`);

const newTab = await browser.newPage();

Check warning on line 910 in openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/ContextCenterDocument.spec.ts

View workflow job for this annotation

GitHub Actions / checkstyle

Prefer the `page` fixture (test.use({ storageState })) over browser.newPage() + manual login for single-user admin tests. For multi-user tests that need a second non-admin page, this warning is expected — no action needed
await newTab.goto(clipboardText);
await newTab
.getByTestId('context-center-documents-page')
Expand Down Expand Up @@ -983,14 +986,14 @@

await navigateToDocuments(page);
await expect(
getDocumentRowByName(page, firstFileName).getByTestId(
'document-folder-name'
)
(
await searchAndGetDocumentRow(page, firstFileName)
).getByTestId('document-folder-name')
).toHaveText(folderName);
await expect(
getDocumentRowByName(page, secondFileName).getByTestId(
'document-folder-name'
)
(
await searchAndGetDocumentRow(page, secondFileName)
).getByTestId('document-folder-name')
).toHaveText(folderName);
});

Expand Down Expand Up @@ -1042,8 +1045,12 @@
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
Expand Down Expand Up @@ -1082,7 +1089,7 @@

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();
Expand Down Expand Up @@ -1149,8 +1156,12 @@
// 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');
Expand All @@ -1168,6 +1179,14 @@
10
);
expect(folderCount).toBeGreaterThanOrEqual(globalCount);
const browseResPromise = page.waitForResponse(
(res) =>
res.url().includes('/api/v1/contextCenter/drive/files') &&
!res.url().includes('search')
);
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);
Expand All @@ -1186,7 +1205,7 @@
);
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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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}`)
Expand Down Expand Up @@ -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}`)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,25 +66,25 @@
pipelineEditPage: Page;
}>({
serviceOwnerPage: async ({ browser }, use) => {
const page = await browser.newPage();

Check warning on line 69 in openmetadata-ui/src/main/resources/ui/playwright/e2e/Flow/ServiceCreationPermissions.spec.ts

View workflow job for this annotation

GitHub Actions / checkstyle

Prefer the `page` fixture (test.use({ storageState })) over browser.newPage() + manual login for single-user admin tests. For multi-user tests that need a second non-admin page, this warning is expected — no action needed
await serviceOwnerUser.login(page);
await use(page);
await page.close();
},
anotherUserPage: async ({ browser }, use) => {
const page = await browser.newPage();

Check warning on line 75 in openmetadata-ui/src/main/resources/ui/playwright/e2e/Flow/ServiceCreationPermissions.spec.ts

View workflow job for this annotation

GitHub Actions / checkstyle

Prefer the `page` fixture (test.use({ storageState })) over browser.newPage() + manual login for single-user admin tests. For multi-user tests that need a second non-admin page, this warning is expected — no action needed
await anotherUser.login(page);
await use(page);
await page.close();
},
pipelineTriggerPage: async ({ browser }, use) => {
const page = await browser.newPage();

Check warning on line 81 in openmetadata-ui/src/main/resources/ui/playwright/e2e/Flow/ServiceCreationPermissions.spec.ts

View workflow job for this annotation

GitHub Actions / checkstyle

Prefer the `page` fixture (test.use({ storageState })) over browser.newPage() + manual login for single-user admin tests. For multi-user tests that need a second non-admin page, this warning is expected — no action needed
await pipelineTriggerUser.login(page);
await use(page);
await page.close();
},
pipelineEditPage: async ({ browser }, use) => {
const page = await browser.newPage();

Check warning on line 87 in openmetadata-ui/src/main/resources/ui/playwright/e2e/Flow/ServiceCreationPermissions.spec.ts

View workflow job for this annotation

GitHub Actions / checkstyle

Prefer the `page` fixture (test.use({ storageState })) over browser.newPage() + manual login for single-user admin tests. For multi-user tests that need a second non-admin page, this warning is expected — no action needed
await pipelineEditUser.login(page);
await use(page);
await page.close();
Expand All @@ -111,11 +111,35 @@

const openPipelineActions = async (page: Page) => {
const actionButton = (await visitAgentCard(page)).getByTestId('more-actions');

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(
Expand Down Expand Up @@ -645,8 +669,12 @@
}) => {
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'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -752,7 +752,7 @@

if (lastNode === previousLastNode) {
staleCount += 1;
await page.waitForTimeout(1000);

Check warning on line 755 in openmetadata-ui/src/main/resources/ui/playwright/utils/ContextCenterUtil.ts

View workflow job for this annotation

GitHub Actions / checkstyle

Unexpected use of page.waitForTimeout()
if (staleCount >= 5) {
break;
}
Expand Down Expand Up @@ -860,7 +860,7 @@

if (lastCard === previousLastCard) {
staleCount += 1;
await page.waitForTimeout(1000);

Check warning on line 863 in openmetadata-ui/src/main/resources/ui/playwright/utils/ContextCenterUtil.ts

View workflow job for this annotation

GitHub Actions / checkstyle

Unexpected use of page.waitForTimeout()
if (staleCount >= 5) {
break;
}
Expand Down Expand Up @@ -1050,6 +1050,39 @@
}
};

/**
* 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 <p>...</p>),
// 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/.
Expand Down
Loading