fix(ui): Flaky ServiceEntity And Entity Playwright (#23162)

* fix flaky serviceEntity and entity

* added new testid

* fix domain list flakyness

* fix flaky user as owner in unsorted list

* addressed comments

* fixed flaky tests from entity spec

* removed unwanted code

* removed unwanted code

* added playwright config file for debugging cl issue

* removed playwright file

(cherry picked from commit 70d9a1182edf9cc9ac4a622eb54bf21f10827072)
This commit is contained in:
Dhruv Parmar 2025-09-04 20:20:05 +05:30 committed by OpenMetadata Release Bot
parent b4ba346d85
commit dd3998948f
5 changed files with 120 additions and 30 deletions

View File

@ -130,13 +130,22 @@ export class DatabaseSchemaClass extends EntityClass {
false
);
await page.waitForLoadState('networkidle');
// Wait for the database to be visible before clicking
await page.getByTestId(this.database.name).waitFor({ state: 'visible' });
const databaseResponse = page.waitForResponse(
`/api/v1/databases/name/*${this.database.name}?**`
);
await page.getByTestId(this.database.name).click();
await databaseResponse;
// Wait for database schema to be visible
await page.getByTestId(this.entity.name).waitFor({ state: 'visible' });
const databaseSchemaResponse = page.waitForResponse(
`/api/v1/databaseSchemas/name/*${this.entity}?*`
`/api/v1/databaseSchemas/name/*${this.entity.name}?*`
);
await page.getByTestId(this.entity.name).click();
await databaseSchemaResponse;

View File

@ -170,22 +170,31 @@ export const assignDomain = async (
) => {
await page.getByTestId('add-domain').click();
await page.waitForSelector('[data-testid="loader"]', { state: 'detached' });
const searchDomain = page.waitForResponse(
`/api/v1/search/query?q=*${encodeURIComponent(domain.name)}*`
);
await page
.getByTestId('domain-selectable-tree')
.getByTestId('searchbar')
.fill(domain.name);
await searchDomain;
await page.getByTestId(`tag-${domain.fullyQualifiedName}`).click();
// Wait for the tag element to be visible and ensure page is still valid
const tagSelector = page.getByTestId(`tag-${domain.fullyQualifiedName}`);
await tagSelector.waitFor({ state: 'visible' });
await tagSelector.click();
const patchReq = page.waitForResponse(
(req) => req.request().method() === 'PATCH'
);
await page.getByTestId('saveAssociatedTag').click();
await page
.getByTestId('domain-selectable-tree')
.getByTestId('saveAssociatedTag')
.click();
await patchReq;
await page.waitForSelector('[data-testid="loader"]', { state: 'detached' });
@ -221,7 +230,10 @@ export const updateDomain = async (
(req) => req.request().method() === 'PATCH'
);
await page.getByTestId('saveAssociatedTag').click();
await page
.getByTestId('domain-selectable-tree')
.getByTestId('saveAssociatedTag')
.click();
await patchReq;
await page.waitForSelector('[data-testid="loader"]', { state: 'detached' });
@ -247,7 +259,10 @@ export const removeDomain = async (
(req) => req.request().method() === 'PATCH'
);
await page.getByTestId('saveAssociatedTag').click();
await page
.getByTestId('domain-selectable-tree')
.getByTestId('saveAssociatedTag')
.click();
await patchReq;
await page.waitForSelector('[data-testid="loader"]', { state: 'detached' });
@ -281,13 +296,20 @@ export const assignDataProduct = async (
await searchDataProduct;
await page.getByTestId(`tag-${dataProduct.fullyQualifiedName}`).click();
await expect(page.getByTestId('saveAssociatedTag')).toBeEnabled();
await expect(
page
.getByTestId('data-product-dropdown-actions')
.getByTestId('saveAssociatedTag')
).toBeEnabled();
const patchReq = page.waitForResponse(
(req) => req.request().method() === 'PATCH'
);
await page.getByTestId('saveAssociatedTag').click();
await page
.getByTestId('data-product-dropdown-actions')
.getByTestId('saveAssociatedTag')
.click();
await patchReq;
await expect(
@ -320,13 +342,20 @@ export const removeDataProduct = async (
.locator('svg')
.click();
await expect(page.getByTestId('saveAssociatedTag')).toBeEnabled();
await expect(
page
.getByTestId('data-product-dropdown-actions')
.getByTestId('saveAssociatedTag')
).toBeEnabled();
const patchReq = page.waitForResponse(
(req) => req.request().method() === 'PATCH'
);
await page.getByTestId('saveAssociatedTag').click();
await page
.getByTestId('data-product-dropdown-actions')
.getByTestId('saveAssociatedTag')
.click();
await patchReq;
await expect(

View File

@ -94,7 +94,10 @@ export const visitEntityPage = async (data: {
await waitForSearchResponse;
await page.getByTestId(dataTestId).getByTestId('data-name').click();
await page.waitForLoadState('networkidle');
await page.waitForSelector('[data-testid="loader"]', {
state: 'detached',
});
await page.getByTestId('searchBox').clear();
};
@ -144,8 +147,13 @@ export const addOwner = async ({
await page.getByRole('listitem', { name: owner, exact: true }).click();
await patchRequest;
} else {
await page.getByRole('listitem', { name: owner, exact: true }).click();
const ownerItem = page.getByRole('listitem', {
name: owner,
exact: true,
});
await ownerItem.waitFor({ state: 'visible' });
await ownerItem.click();
const patchRequest = page.waitForResponse(`/api/v1/${endpoint}/*`);
await page.getByTestId('selectable-list-update-btn').click();
await patchRequest;
@ -260,9 +268,10 @@ export const removeOwner = async ({
await patchRequest;
await expect(
page.getByTestId(dataTestId ?? 'owner-link').getByTestId(ownerName)
).not.toBeVisible();
await page
.getByTestId(dataTestId ?? 'owner-link')
.getByTestId(ownerName)
.waitFor({ state: 'hidden' });
};
export const addMultiOwner = async (data: {
@ -345,6 +354,9 @@ export const addMultiOwner = async (data: {
name: ownerName,
exact: true,
});
await ownerItem.waitFor({ state: 'visible' });
// Wait for the item to exist and be visible before clicking
if (type === 'Teams') {
if (isSelectableInsideForm) {
@ -544,11 +556,19 @@ export const assignTag = async (
.getByTestId(action === 'Add' ? 'add-tag' : 'edit-button')
.click();
// Wait for the form to be visible and stable
await page.locator('#tagsForm_tags').waitFor({
state: 'visible',
});
const searchTags = page.waitForResponse(
`/api/v1/search/query?q=*${encodeURIComponent(tag)}*`
);
await page.locator('#tagsForm_tags').fill(tag);
await searchTags;
await page
.getByTestId(`tag-${tagFqn ? `${tagFqn}` : tag}`)
.first()
@ -742,13 +762,16 @@ export const assignGlossaryTerm = async (
.getByTestId('glossary-container')
.getByTestId(action === 'Add' ? 'add-tag' : 'edit-button')
.click();
const searchGlossaryTerm = page.waitForResponse(
`/api/v1/search/query?q=*${encodeURIComponent(glossaryTerm.displayName)}*`
);
// Wait for the form to be visible before proceeding
await page.locator('#tagsForm_tags').waitFor({ state: 'visible' });
// Fill the input first
await page.locator('#tagsForm_tags').fill(glossaryTerm.displayName);
await searchGlossaryTerm;
await page.getByTestId(`tag-${glossaryTerm.fullyQualifiedName}`).click();
await page.waitForSelector(
@ -756,11 +779,18 @@ export const assignGlossaryTerm = async (
{ state: 'visible' }
);
await expect(page.getByTestId('saveAssociatedTag')).toBeEnabled();
await expect(
page.getByTestId('custom-drop-down-menu').getByTestId('saveAssociatedTag')
).toBeEnabled();
await page.getByTestId('saveAssociatedTag').click();
await page
.getByTestId('custom-drop-down-menu')
.getByTestId('saveAssociatedTag')
.click();
await expect(page.getByTestId('saveAssociatedTag')).not.toBeVisible();
await expect(
page.getByTestId('custom-drop-down-menu').getByTestId('saveAssociatedTag')
).not.toBeVisible();
await expect(
page
@ -861,9 +891,14 @@ export const removeGlossaryTerm = async (
{ state: 'visible' }
);
await expect(page.getByTestId('saveAssociatedTag')).toBeEnabled();
await expect(
page.getByTestId('custom-drop-down-menu').getByTestId('saveAssociatedTag')
).toBeEnabled();
await page.getByTestId('saveAssociatedTag').click();
await page
.getByTestId('custom-drop-down-menu')
.getByTestId('saveAssociatedTag')
.click();
await patchRequest;
await expect(
@ -968,14 +1003,18 @@ export const unFollowEntity = async (
page: Page,
endpoint: EntityTypeEndpoint
) => {
await page.waitForLoadState('networkidle');
const followButton = page.getByTestId('entity-follow-button');
await followButton.waitFor({ state: 'visible' });
await expect(followButton).toContainText('Unfollow');
const unFollowResponse = page.waitForResponse(
`/api/v1/${endpoint}/*/followers/*`
);
await page.waitForLoadState('networkidle');
await page.waitForSelector('[data-testid="loader"]', {
state: 'detached',
});
await page.getByTestId('entity-follow-button').click();
await followButton.click();
await unFollowResponse;
await expect(page.getByTestId('entity-follow-button')).toContainText(
@ -1346,9 +1385,16 @@ export const removeDisplayNameForEntityChildren = async (
await page.locator('#displayName').fill('');
const updateRequest = page.waitForResponse((req) =>
['PUT', 'PATCH'].includes(req.request().method())
);
const updateRequest = page.waitForResponse((response) => {
const method = response.request().method();
const url = response.url();
// Check Analytics Api Does Not Intterupt With PUT CAll
return (
(method === 'PUT' || method === 'PATCH') &&
!url.includes('api/v1/analytics/web/events/collect')
);
});
await page.click('[data-testid="save-button"]');
await updateRequest;

View File

@ -128,7 +128,10 @@ const DataProductsSelectList = ({
<>
{menu}
{hasContentLoading ? <Loader size="small" /> : null}
<Space className="p-sm p-b-xss p-l-xs custom-dropdown-render" size={8}>
<Space
className="p-sm p-b-xss p-l-xs custom-dropdown-render"
data-testid="data-product-dropdown-actions"
size={8}>
<Button
className="update-btn"
data-testid="saveAssociatedTag"

View File

@ -141,7 +141,10 @@ const TreeAsyncSelectList: FC<TreeAsyncSelectListProps> = ({
<KeyDownStopPropagationWrapper>
<div ref={dropdownContainerRef}>
{isLoading ? <Loader size="small" /> : menu}
<Space className="p-sm p-b-xss p-l-xs custom-dropdown-render" size={8}>
<Space
className="p-sm p-b-xss p-l-xs custom-dropdown-render"
data-testid="custom-drop-down-menu"
size={8}>
<Button
className="update-btn"
data-testid="saveAssociatedTag"