mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-10-04 21:32:16 +00:00
PLAYWRIGHT: fix the bulk action playwright flaky playwright test (#21582)
* fix the bulk action playwright flaky playwright test * change entity and service.spec user to custom user and add a retry mechnaism for checking soft deleted entity * try to fix alert not found issue * remove failure * fix bot playwright failure (cherry picked from commit 78f523fd1327a992a39c429281f057b4f770f9fa)
This commit is contained in:
parent
393d869e93
commit
e37b794248
@ -512,7 +512,7 @@ test.describe('Bulk Edit Entity', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('Table', async ({ page }) => {
|
test('Table', async ({ page }) => {
|
||||||
test.slow();
|
test.slow(true);
|
||||||
|
|
||||||
const tableEntity = new TableClass();
|
const tableEntity = new TableClass();
|
||||||
|
|
||||||
|
@ -794,6 +794,8 @@ test.describe('Bulk Import Export', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('Table', async ({ page }) => {
|
test('Table', async ({ page }) => {
|
||||||
|
test.slow(true);
|
||||||
|
|
||||||
const tableEntity = new TableClass();
|
const tableEntity = new TableClass();
|
||||||
|
|
||||||
const { apiContext, afterAction } = await getApiContext(page);
|
const { apiContext, afterAction } = await getApiContext(page);
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import { expect, test } from '@playwright/test';
|
import { expect, Page, test as base } from '@playwright/test';
|
||||||
import { isUndefined } from 'lodash';
|
import { isUndefined } from 'lodash';
|
||||||
import { CustomPropertySupportedEntityList } from '../../constant/customProperty';
|
import { CustomPropertySupportedEntityList } from '../../constant/customProperty';
|
||||||
import { ApiEndpointClass } from '../../support/entity/ApiEndpointClass';
|
import { ApiEndpointClass } from '../../support/entity/ApiEndpointClass';
|
||||||
@ -26,9 +26,9 @@ import { StoredProcedureClass } from '../../support/entity/StoredProcedureClass'
|
|||||||
import { TableClass } from '../../support/entity/TableClass';
|
import { TableClass } from '../../support/entity/TableClass';
|
||||||
import { TopicClass } from '../../support/entity/TopicClass';
|
import { TopicClass } from '../../support/entity/TopicClass';
|
||||||
import { UserClass } from '../../support/user/UserClass';
|
import { UserClass } from '../../support/user/UserClass';
|
||||||
|
import { performAdminLogin } from '../../utils/admin';
|
||||||
import {
|
import {
|
||||||
assignDomain,
|
assignDomain,
|
||||||
createNewPage,
|
|
||||||
generateRandomUsername,
|
generateRandomUsername,
|
||||||
getApiContext,
|
getApiContext,
|
||||||
getAuthContext,
|
getAuthContext,
|
||||||
@ -59,8 +59,23 @@ const entities = [
|
|||||||
MetricClass,
|
MetricClass,
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
// use the admin user to login
|
const adminUser = new UserClass();
|
||||||
test.use({ storageState: 'playwright/.auth/admin.json' });
|
|
||||||
|
const test = base.extend<{ page: Page }>({
|
||||||
|
page: async ({ browser }, use) => {
|
||||||
|
const adminPage = await browser.newPage();
|
||||||
|
await adminUser.login(adminPage);
|
||||||
|
await use(adminPage);
|
||||||
|
await adminPage.close();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
test.beforeAll('Setup pre-requests', async ({ browser }) => {
|
||||||
|
const { apiContext, afterAction } = await performAdminLogin(browser);
|
||||||
|
await adminUser.create(apiContext);
|
||||||
|
await adminUser.setAdminRole(apiContext);
|
||||||
|
await afterAction();
|
||||||
|
});
|
||||||
|
|
||||||
entities.forEach((EntityClass) => {
|
entities.forEach((EntityClass) => {
|
||||||
const entity = new EntityClass();
|
const entity = new EntityClass();
|
||||||
@ -72,7 +87,7 @@ entities.forEach((EntityClass) => {
|
|||||||
entity.type === 'MlModel' ? 'data-testid' : 'data-row-key';
|
entity.type === 'MlModel' ? 'data-testid' : 'data-row-key';
|
||||||
|
|
||||||
test.beforeAll('Setup pre-requests', async ({ browser }) => {
|
test.beforeAll('Setup pre-requests', async ({ browser }) => {
|
||||||
const { apiContext, afterAction } = await createNewPage(browser);
|
const { apiContext, afterAction } = await performAdminLogin(browser);
|
||||||
|
|
||||||
await EntityDataClass.preRequisitesForTests(apiContext);
|
await EntityDataClass.preRequisitesForTests(apiContext);
|
||||||
await entity.create(apiContext);
|
await entity.create(apiContext);
|
||||||
@ -380,7 +395,7 @@ entities.forEach((EntityClass) => {
|
|||||||
test.afterAll('Cleanup', async ({ browser }) => {
|
test.afterAll('Cleanup', async ({ browser }) => {
|
||||||
test.slow();
|
test.slow();
|
||||||
|
|
||||||
const { apiContext, afterAction } = await createNewPage(browser);
|
const { apiContext, afterAction } = await performAdminLogin(browser);
|
||||||
await entity.delete(apiContext);
|
await entity.delete(apiContext);
|
||||||
await EntityDataClass.postRequisitesForTests(apiContext);
|
await EntityDataClass.postRequisitesForTests(apiContext);
|
||||||
await afterAction();
|
await afterAction();
|
||||||
@ -418,3 +433,9 @@ entities.forEach((EntityClass) => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test.afterAll('Cleanup', async ({ browser }) => {
|
||||||
|
const { apiContext, afterAction } = await performAdminLogin(browser);
|
||||||
|
await adminUser.delete(apiContext);
|
||||||
|
await afterAction();
|
||||||
|
});
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import { test } from '@playwright/test';
|
import { Page, test as base } from '@playwright/test';
|
||||||
import { CustomPropertySupportedEntityList } from '../../constant/customProperty';
|
import { CustomPropertySupportedEntityList } from '../../constant/customProperty';
|
||||||
import { FollowSupportedServices } from '../../constant/service';
|
import { FollowSupportedServices } from '../../constant/service';
|
||||||
import { ApiCollectionClass } from '../../support/entity/ApiCollectionClass';
|
import { ApiCollectionClass } from '../../support/entity/ApiCollectionClass';
|
||||||
@ -25,8 +25,9 @@ import { MlmodelServiceClass } from '../../support/entity/service/MlmodelService
|
|||||||
import { PipelineServiceClass } from '../../support/entity/service/PipelineServiceClass';
|
import { PipelineServiceClass } from '../../support/entity/service/PipelineServiceClass';
|
||||||
import { SearchIndexServiceClass } from '../../support/entity/service/SearchIndexServiceClass';
|
import { SearchIndexServiceClass } from '../../support/entity/service/SearchIndexServiceClass';
|
||||||
import { StorageServiceClass } from '../../support/entity/service/StorageServiceClass';
|
import { StorageServiceClass } from '../../support/entity/service/StorageServiceClass';
|
||||||
|
import { UserClass } from '../../support/user/UserClass';
|
||||||
|
import { performAdminLogin } from '../../utils/admin';
|
||||||
import {
|
import {
|
||||||
createNewPage,
|
|
||||||
getApiContext,
|
getApiContext,
|
||||||
getAuthContext,
|
getAuthContext,
|
||||||
getToken,
|
getToken,
|
||||||
@ -48,8 +49,23 @@ const entities = [
|
|||||||
DatabaseSchemaClass,
|
DatabaseSchemaClass,
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
// use the admin user to login
|
const adminUser = new UserClass();
|
||||||
test.use({ storageState: 'playwright/.auth/admin.json' });
|
|
||||||
|
const test = base.extend<{ page: Page }>({
|
||||||
|
page: async ({ browser }, use) => {
|
||||||
|
const adminPage = await browser.newPage();
|
||||||
|
await adminUser.login(adminPage);
|
||||||
|
await use(adminPage);
|
||||||
|
await adminPage.close();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
test.beforeAll('Setup pre-requests', async ({ browser }) => {
|
||||||
|
const { apiContext, afterAction } = await performAdminLogin(browser);
|
||||||
|
await adminUser.create(apiContext);
|
||||||
|
await adminUser.setAdminRole(apiContext);
|
||||||
|
await afterAction();
|
||||||
|
});
|
||||||
|
|
||||||
entities.forEach((EntityClass) => {
|
entities.forEach((EntityClass) => {
|
||||||
const entity = new EntityClass();
|
const entity = new EntityClass();
|
||||||
@ -57,7 +73,7 @@ entities.forEach((EntityClass) => {
|
|||||||
|
|
||||||
test.describe(entity.getType(), () => {
|
test.describe(entity.getType(), () => {
|
||||||
test.beforeAll('Setup pre-requests', async ({ browser }) => {
|
test.beforeAll('Setup pre-requests', async ({ browser }) => {
|
||||||
const { apiContext, afterAction } = await createNewPage(browser);
|
const { apiContext, afterAction } = await performAdminLogin(browser);
|
||||||
|
|
||||||
await EntityDataClass.preRequisitesForTests(apiContext);
|
await EntityDataClass.preRequisitesForTests(apiContext);
|
||||||
await entity.create(apiContext);
|
await entity.create(apiContext);
|
||||||
@ -173,7 +189,7 @@ entities.forEach((EntityClass) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test.afterAll('Cleanup', async ({ browser }) => {
|
test.afterAll('Cleanup', async ({ browser }) => {
|
||||||
const { apiContext, afterAction } = await createNewPage(browser);
|
const { apiContext, afterAction } = await performAdminLogin(browser);
|
||||||
await entity.delete(apiContext);
|
await entity.delete(apiContext);
|
||||||
await EntityDataClass.postRequisitesForTests(apiContext);
|
await EntityDataClass.postRequisitesForTests(apiContext);
|
||||||
await afterAction();
|
await afterAction();
|
||||||
@ -211,3 +227,9 @@ entities.forEach((EntityClass) => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test.afterAll('Cleanup', async ({ browser }) => {
|
||||||
|
const { apiContext, afterAction } = await performAdminLogin(browser);
|
||||||
|
await adminUser.delete(apiContext);
|
||||||
|
await afterAction();
|
||||||
|
});
|
||||||
|
@ -114,7 +114,7 @@ export const deleteBot = async (page: Page) => {
|
|||||||
|
|
||||||
await toastNotification(page, /deleted successfully!/);
|
await toastNotification(page, /deleted successfully!/);
|
||||||
|
|
||||||
await expect(page.getByTestId('page-layout-v1')).not.toContainText(botName);
|
await expect(page.locator('.ant-table-tbody')).not.toContainText(botName);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const updateBotDetails = async (page: Page) => {
|
export const updateBotDetails = async (page: Page) => {
|
||||||
|
@ -1280,7 +1280,27 @@ export const softDeleteEntity = async (
|
|||||||
await page.reload();
|
await page.reload();
|
||||||
await page.waitForLoadState('networkidle');
|
await page.waitForLoadState('networkidle');
|
||||||
await page.waitForSelector('[data-testid="loader"]', { state: 'detached' });
|
await page.waitForSelector('[data-testid="loader"]', { state: 'detached' });
|
||||||
const deletedBadge = page.locator('[data-testid="deleted-badge"]');
|
// Retry mechanism for checking deleted badge
|
||||||
|
let deletedBadge = page.locator('[data-testid="deleted-badge"]');
|
||||||
|
let attempts = 0;
|
||||||
|
const maxAttempts = 5;
|
||||||
|
|
||||||
|
while (attempts < maxAttempts) {
|
||||||
|
const isVisible = await deletedBadge.isVisible();
|
||||||
|
if (isVisible) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
attempts++;
|
||||||
|
if (attempts < maxAttempts) {
|
||||||
|
await page.reload();
|
||||||
|
await page.waitForLoadState('networkidle');
|
||||||
|
await page.waitForSelector('[data-testid="loader"]', {
|
||||||
|
state: 'detached',
|
||||||
|
});
|
||||||
|
deletedBadge = page.locator('[data-testid="deleted-badge"]');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
await expect(deletedBadge).toHaveText('Deleted');
|
await expect(deletedBadge).toHaveText('Deleted');
|
||||||
|
|
||||||
|
@ -713,16 +713,46 @@ export const pressKeyXTimes = async (
|
|||||||
length: number,
|
length: number,
|
||||||
key: string
|
key: string
|
||||||
) => {
|
) => {
|
||||||
for (let i = 0; i < length; i++) {
|
const maxRetries = 3;
|
||||||
const activeCell = page.locator('.InovuaReactDataGrid__cell--cell-active');
|
const retryDelay = 1000; // 1 second delay between retries
|
||||||
const isActive = await activeCell.isVisible();
|
|
||||||
|
|
||||||
if (!isActive) {
|
for (let i = 0; i < length; i++) {
|
||||||
await page.click('.InovuaReactDataGrid__cell--cell-active');
|
let retryCount = 0;
|
||||||
|
let success = false;
|
||||||
|
|
||||||
|
while (!success && retryCount < maxRetries) {
|
||||||
|
try {
|
||||||
|
// Wait for the active cell to be visible
|
||||||
|
const activeCell = page.locator(
|
||||||
|
'.InovuaReactDataGrid__cell--cell-active'
|
||||||
|
);
|
||||||
|
await activeCell.waitFor({ state: 'visible', timeout: 5000 });
|
||||||
|
|
||||||
|
// Ensure the cell is focused
|
||||||
|
if (!(await activeCell.isVisible())) {
|
||||||
|
await activeCell.click({ timeout: 5000 });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perform the key press with a longer delay
|
||||||
|
await activeCell.press(key, { delay: 200 });
|
||||||
|
|
||||||
|
// Verify the key press was successful by checking if the cell is still active
|
||||||
|
await page.waitForTimeout(100); // Small delay to allow for state updates
|
||||||
|
const isStillActive = await activeCell.isVisible();
|
||||||
|
|
||||||
|
if (isStillActive) {
|
||||||
|
success = true;
|
||||||
|
} else {
|
||||||
|
// If cell lost focus, try to regain it
|
||||||
|
await activeCell.click({ timeout: 5000 });
|
||||||
|
retryCount++;
|
||||||
|
await page.waitForTimeout(retryDelay);
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
retryCount++;
|
||||||
|
await page.waitForTimeout(retryDelay);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
await page
|
|
||||||
.locator('.InovuaReactDataGrid__cell--cell-active')
|
|
||||||
.press(key, { delay: 100 });
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user