From c87b9cf7e0b163f51ab05721c670a15aa53f1733 Mon Sep 17 00:00:00 2001 From: Shailesh Parmar Date: Wed, 5 Jun 2024 16:41:01 +0530 Subject: [PATCH 1/3] playwright: migrate announcement, rename, soft delete and hard delete test --- .../ui/playwright/constant/delete.constant.ts | 68 +++ .../ui/playwright/e2e/Pages/Entity.spec.ts | 69 ++- .../playwright/support/entity/EntityClass.ts | 45 ++ .../ui/playwright/utils/entityUtils.ts | 535 +++++++++++++++++- 4 files changed, 706 insertions(+), 11 deletions(-) create mode 100644 openmetadata-ui/src/main/resources/ui/playwright/constant/delete.constant.ts diff --git a/openmetadata-ui/src/main/resources/ui/playwright/constant/delete.constant.ts b/openmetadata-ui/src/main/resources/ui/playwright/constant/delete.constant.ts new file mode 100644 index 000000000000..bbc742dc60c0 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/playwright/constant/delete.constant.ts @@ -0,0 +1,68 @@ +/* + * Copyright 2024 Collate. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export const LIST_OF_FIELDS_TO_EDIT_NOT_TO_BE_PRESENT = [ + { + containerSelector: '[data-testid="header-domain-container"]', + elementSelector: '[data-testid="add-domain"]', + }, + { + containerSelector: '[data-testid="owner-label"]', + elementSelector: '[data-testid="edit-owner"]', + }, + { + containerSelector: '[data-testid="header-tier-container"]', + elementSelector: '[data-testid="edit-tier"]', + }, + { + containerSelector: '[data-testid="asset-description-container"]', + elementSelector: '[data-testid="edit-description"]', + }, + { + containerSelector: + '[data-testid="entity-right-panel"] [data-testid="tags-container"]', + elementSelector: '[data-testid="add-tag"]', + }, + { + containerSelector: + '[data-testid="entity-right-panel"] [data-testid="glossary-container"]', + elementSelector: '[data-testid="add-tag"]', + }, +]; + +export const LIST_OF_FIELDS_TO_EDIT_TO_BE_DISABLED = [ + { + containerSelector: '[data-testid="asset-header-btn-group"]', + elementSelector: '[data-testid="up-vote-btn"]', + }, + { + containerSelector: '[data-testid="asset-header-btn-group"]', + elementSelector: '[data-testid="down-vote-btn"]', + }, + { + containerSelector: '[data-testid="asset-header-btn-group"]', + elementSelector: '[data-testid="entity-follow-button"]', + }, +]; + +export const ENTITIES_WITHOUT_FOLLOWING_BUTTON = [ + 'databases', + 'databaseSchemas', + 'services/databaseServices', + 'services/messagingServices', + 'services/pipelineServices', + 'services/dashboardServices', + 'services/mlmodelServices', + 'services/storageServices', + 'services/metadataServices', + 'services/searchServices', +]; diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/Entity.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/Entity.spec.ts index 3521ab2bc153..2752a09d1f99 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/Entity.spec.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/Entity.spec.ts @@ -19,22 +19,30 @@ import { MlModelClass } from '../../support/entity/MlModelClass'; import { PipelineClass } from '../../support/entity/PipelineClass'; import { SearchIndexClass } from '../../support/entity/SearchIndexClass'; import { TopicClass } from '../../support/entity/TopicClass'; -import { createNewPage, redirectToHomePage } from '../../utils/common'; +import { + createNewPage, + getAuthContext, + getToken, + redirectToHomePage, +} from '../../utils/common'; const entities = [ - new DashboardClass(), - new PipelineClass(), - new TopicClass(), - new MlModelClass(), - new ContainerClass(), - new SearchIndexClass(), - new DashboardDataModelClass(), + DashboardClass, + PipelineClass, + TopicClass, + MlModelClass, + ContainerClass, + SearchIndexClass, + DashboardDataModelClass, ] as const; // use the admin user to login test.use({ storageState: 'playwright/.auth/admin.json' }); -entities.forEach((entity) => { +entities.forEach((entityClass) => { + const entity = new entityClass(); + const deleteEntity = new entityClass(); + test.describe(entity.getType(), () => { test.beforeAll('Setup pre-requests', async ({ browser }) => { const { apiContext, afterAction } = await createNewPage(browser); @@ -89,6 +97,17 @@ entities.forEach((entity) => { ); }); + test(`Announcement create & delete`, async ({ page }) => { + await entity.announcement( + page, + entity.entityResponseData?.['fullyQualifiedName'] + ); + }); + + test(`Inactive Announcement create & delete`, async ({ page }) => { + await entity.inactiveAnnouncement(page); + }); + test(`UpVote & DownVote entity`, async ({ page }) => { await entity.upVote(page); await entity.downVote(page); @@ -99,6 +118,10 @@ entities.forEach((entity) => { await entity.followUnfollowEntity(page, entityName); }); + test(`Update displayName`, async ({ page }) => { + await entity.renameEntity(page, entity.entity.name); + }); + test.afterAll('Cleanup', async ({ browser }) => { const { apiContext, afterAction } = await createNewPage(browser); await entity.delete(apiContext); @@ -106,4 +129,32 @@ entities.forEach((entity) => { await afterAction(); }); }); + + test(`Delete ${deleteEntity.getType()}`, async ({ page }) => { + await redirectToHomePage(page); + // get the token from localStorage + const token = await getToken(page); + + // create a new context with the token + const apiContext = await getAuthContext(token); + await deleteEntity.create(apiContext); + await redirectToHomePage(page); + await deleteEntity.visitEntityPage(page); + + await test.step('Soft delete', async () => { + await deleteEntity.softDeleteEntity( + page, + deleteEntity.entity.name, + deleteEntity.entityResponseData?.['displayName'] + ); + }); + + await test.step('Hard delete', async () => { + await deleteEntity.hardDeleteEntity( + page, + deleteEntity.entity.name, + deleteEntity.entityResponseData?.['displayName'] + ); + }); + }); }); diff --git a/openmetadata-ui/src/main/resources/ui/playwright/support/entity/EntityClass.ts b/openmetadata-ui/src/main/resources/ui/playwright/support/entity/EntityClass.ts index 508fe7756c1f..d7232ed05d6c 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/support/entity/EntityClass.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/support/entity/EntityClass.ts @@ -21,14 +21,21 @@ import { assignGlossaryTerm, assignTag, assignTier, + createAnnouncement, + createInactiveAnnouncement, + deleteAnnouncement, downVote, followEntity, + hardDeleteEntity, removeGlossaryTerm, removeOwner, removeTag, removeTier, + replyAnnouncement, + softDeleteEntity, unFollowEntity, updateDescription, + updateDisplayNameForEntity, updateOwner, upVote, validateFollowedEntityToWidget, @@ -132,4 +139,42 @@ export class EntityClass { await unFollowEntity(page, this.endpoint); await validateFollowedEntityToWidget(page, entity, false); } + + async announcement(page: Page, entityFqn: string) { + await createAnnouncement(page, entityFqn, { + title: 'Playwright Test Announcement', + description: 'Playwright Test Announcement Description', + }); + await replyAnnouncement(page); + await deleteAnnouncement(page); + } + + async inactiveAnnouncement(page: Page) { + await createInactiveAnnouncement(page, { + title: 'Inactive Playwright announcement', + description: 'Inactive Playwright announcement description', + }); + await deleteAnnouncement(page); + } + + async renameEntity(page: Page, entityName: string) { + await updateDisplayNameForEntity( + page, + `Cypress ${entityName} updated`, + this.endpoint + ); + } + + async softDeleteEntity(page: Page, entityName: string, displayName?: string) { + await softDeleteEntity( + page, + entityName, + this.endpoint, + displayName ?? entityName + ); + } + + async hardDeleteEntity(page: Page, entityName: string, displayName?: string) { + await hardDeleteEntity(page, displayName ?? entityName, this.endpoint); + } } diff --git a/openmetadata-ui/src/main/resources/ui/playwright/utils/entityUtils.ts b/openmetadata-ui/src/main/resources/ui/playwright/utils/entityUtils.ts index a07c0a5bc2d0..2fd1b265c867 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/utils/entityUtils.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/utils/entityUtils.ts @@ -12,7 +12,20 @@ */ import { expect, Page } from '@playwright/test'; import { lowerCase } from 'lodash'; -import { EntityType } from '../support/entity/Entity.interface'; +import { + customFormatDateTime, + getCurrentMillis, + getEpochMillisForFutureDays, +} from '../../src/utils/date-time/DateTimeUtils'; +import { + ENTITIES_WITHOUT_FOLLOWING_BUTTON, + LIST_OF_FIELDS_TO_EDIT_NOT_TO_BE_PRESENT, + LIST_OF_FIELDS_TO_EDIT_TO_BE_DISABLED, +} from '../constant/delete.constant'; +import { + EntityType, + EntityTypeEndpoint, +} from '../support/entity/Entity.interface'; import { redirectToHomePage } from './common'; export const visitEntityPage = async (data: { @@ -35,6 +48,10 @@ export const addOwner = async ( ) => { await page.getByTestId('edit-owner').click(); await page.getByRole('tab', { name: type }).click(); + if (type === 'Users') { + await page.waitForResponse('/api/v1/users?limit=*&isBot=false*'); + } + await page.waitForSelector('[data-testid="loader"]', { state: 'detached' }); await page .getByTestId(`owner-select-${lowerCase(type)}-search-bar`) @@ -57,7 +74,7 @@ export const updateOwner = async ( ) => { await page.getByTestId('edit-owner').click(); await page.getByRole('tab', { name: type }).click(); - + await page.waitForSelector('[data-testid="loader"]', { state: 'detached' }); await page .getByTestId(`owner-select-${lowerCase(type)}-search-bar`) .fill(owner); @@ -282,3 +299,517 @@ export const validateFollowedEntityToWidget = async ( await expect(page.getByTestId(`following-${entity}`)).not.toBeVisible(); } }; + +const announcementForm = async ( + page: Page, + data: { + title: string; + startDate: string; + endDate: string; + description: string; + } +) => { + await page.fill('#title', data.title); + + await page.click('#startTime'); + await page.fill('#startTime', `${data.startDate}`); + await page.press('#startTime', 'Enter'); + + await page.click('#endTime'); + await page.fill('#endTime', `${data.endDate}`); + await page.press('#startTime', 'Enter'); + + await page.fill( + '.toastui-editor-md-container > .toastui-editor > .ProseMirror', + data.description + ); + + await page.locator('#announcement-submit').scrollIntoViewIfNeeded(); + await page.click('#announcement-submit'); + await page.waitForResponse('/api/v1/feed?entityLink=*type=Announcement*'); + await page.click('.Toastify__close-button'); +}; + +export const createAnnouncement = async ( + page: Page, + entityFqn: string, + data: { title: string; description: string } +) => { + await page.getByTestId('manage-button').click(); + await page.getByTestId('announcement-button').click(); + const startDate = customFormatDateTime(getCurrentMillis(), 'yyyy-MM-dd'); + const endDate = customFormatDateTime( + getEpochMillisForFutureDays(5), + 'yyyy-MM-dd' + ); + + await expect(page.getByTestId('announcement-error')).toContainText( + 'No Announcements, Click on add announcement to add one.' + ); + + await page.getByTestId('add-announcement').click(); + + await expect(page.locator('.ant-modal-header')).toContainText( + 'Make an announcement' + ); + + await announcementForm(page, { ...data, startDate, endDate }); + + await page.reload(); + await page.getByTestId('announcement-card').isVisible(); + + await expect(page.getByTestId('announcement-card')).toContainText(data.title); + + await redirectToHomePage(page); + + await page + .getByTestId('announcement-container') + .locator(`a[href*="${encodeURIComponent(entityFqn)}"]`) + .click(); + await page.getByTestId('announcement-card').isVisible(); + + await expect(page.getByTestId('announcement-card')).toContainText(data.title); +}; + +export const replyAnnouncement = async (page: Page) => { + await page.click('[data-testid="announcement-card"]'); + + await page.hover( + '[data-testid="announcement-card"] [data-testid="main-message"]' + ); + + await expect(page.locator('.ant-popover')).toBeVisible(); + await expect(page.getByTestId('add-reply').locator('svg')).toBeVisible(); + + await page.getByTestId('add-reply').locator('svg').click(); + + await expect(page.locator('.ql-editor')).toBeVisible(); + + const sendButtonIsDisabled = await page + .locator('[data-testid="send-button"]') + .isEnabled(); + + expect(sendButtonIsDisabled).toBe(false); + + await page.fill('[data-testid="editor-wrapper"] .ql-editor', 'Reply message'); + await page.click('[data-testid="send-button"]'); + + await expect( + page.locator('[data-testid="replies"] [data-testid="viewer-container"]') + ).toHaveText('Reply message'); + await expect(page.locator('[data-testid="show-reply-thread"]')).toHaveText( + '1 replies' + ); + + // Edit the reply message + await page.hover('[data-testid="replies"] > [data-testid="main-message"]'); + await page.click('[data-testid="edit-message"]'); + + await page.fill( + '[data-testid="editor-wrapper"] .ql-editor', + 'Reply message edited' + ); + + await page.click('[data-testid="save-button"]'); + + await expect( + page.locator('[data-testid="replies"] [data-testid="viewer-container"]') + ).toHaveText('Reply message edited'); + + await page.reload(); +}; + +export const deleteAnnouncement = async (page: Page) => { + await page.getByTestId('manage-button').click(); + await page.getByTestId('announcement-button').click(); + + await page.hover( + '[data-testid="announcement-card"] [data-testid="main-message"]' + ); + + await expect(page.locator('.ant-popover')).toBeVisible(); + + await page.click('[data-testid="delete-message"]'); + const modalText = await page.textContent('.ant-modal-body'); + + expect(modalText).toContain( + 'Are you sure you want to permanently delete this message?' + ); + + await page.click('[data-testid="save-button"]'); + await page.waitForResponse('/api/v1/feed/*'); +}; + +export const createInactiveAnnouncement = async ( + page: Page, + data: { title: string; description: string } +) => { + await page.getByTestId('manage-button').click(); + await page.getByTestId('announcement-button').click(); + const startDate = customFormatDateTime( + getEpochMillisForFutureDays(6), + 'yyyy-MM-dd' + ); + const endDate = customFormatDateTime( + getEpochMillisForFutureDays(11), + 'yyyy-MM-dd' + ); + + await page.getByTestId('add-announcement').click(); + + await expect(page.locator('.ant-modal-header')).toContainText( + 'Make an announcement' + ); + + await announcementForm(page, { ...data, startDate, endDate }); + await page.getByTestId('inActive-announcements').isVisible(); + await page.reload(); +}; + +export const updateDisplayNameForEntity = async ( + page: Page, + displayName: string, + endPoint: string +) => { + await page.click('[data-testid="manage-button"]'); + await page.click('[data-testid="rename-button"]'); + + const nameInputIsDisabled = await page.locator('#name').isEnabled(); + + expect(nameInputIsDisabled).toBe(false); + + await expect(page.locator('#displayName')).toBeVisible(); + + await page.locator('#displayName').clear(); + + await page.fill('#displayName', displayName); + await page.click('[data-testid="save-button"]'); + + // For the network request verification, you can use `page.waitForResponse`: + await page.waitForResponse(`/api/v1/${endPoint}/*`); + + await expect( + page.locator('[data-testid="entity-header-display-name"]') + ).toHaveText(displayName); +}; + +export const checkForEditActions = async ({ entityType, deleted, page }) => { + for (const { + containerSelector, + elementSelector, + } of LIST_OF_FIELDS_TO_EDIT_TO_BE_DISABLED) { + if ( + elementSelector === '[data-testid="entity-follow-button"]' && + ENTITIES_WITHOUT_FOLLOWING_BUTTON.includes(entityType) + ) { + continue; + } + + if (entityType.startsWith('services/')) { + continue; + } + + const isDisabled = await page + .locator(`${containerSelector} ${elementSelector}`) + .isEnabled(); + + expect(isDisabled).toBe(!deleted); + } + + for (const { + containerSelector, + elementSelector, + } of LIST_OF_FIELDS_TO_EDIT_NOT_TO_BE_PRESENT) { + if (!deleted) { + await expect( + page.locator(`${containerSelector} ${elementSelector}`) + ).toBeVisible(); + } else { + const exists = await page + .locator(`${containerSelector} ${elementSelector}`) + .isVisible(); + + expect(exists).toBe(false); + } + } +}; + +export const checkLineageTabActions = async (page: Page, deleted?: boolean) => { + // Click the lineage tab + await page.click('[data-testid="lineage"]'); + + // Ensure the response has been received and check the status code + await page.waitForResponse( + '/api/v1/lineage/getLineage?fqn=*&upstreamDepth=3&downstreamDepth=3&query_filter=*&includeDeleted=false' + ); + + // Check the presence or absence of the edit-lineage element based on the deleted flag + if (deleted) { + await expect( + page.locator('[data-testid="edit-lineage"]') + ).not.toBeVisible(); + } else { + await expect(page.locator('[data-testid="edit-lineage"]')).toBeVisible(); + } +}; + +export const checkForTableSpecificFields = async ( + page: Page, + deleted?: boolean +) => { + const queryDataUrl = `/api/v1/search/query?q=*&index=query_search_index*`; + const systemProfileUrl = `/api/v1/tables/*/systemProfile*`; + + // Click the table queries tab + await page.click('[data-testid="table_queries"]'); + + if (!deleted) { + await page.waitForResponse(queryDataUrl); + + // Check if the add-query button is enabled + const addQueryButton = page.locator('[data-testid="add-query-btn"]'); + + await expect(addQueryButton).toBeEnabled(); + } else { + // Check for the no data placeholder message + const noDataPlaceholder = page.locator( + '[data-testid="no-data-placeholder"]' + ); + + await expect(noDataPlaceholder).toContainText( + 'Queries data is not available for deleted entities.' + ); + } + + // Click the profiler tab + await page.click('[data-testid="profiler"]'); + await page.waitForResponse(systemProfileUrl); + + // Check the visibility of profiler buttons based on the deleted flag + const addTableTestButton = page.locator( + '[data-testid="profiler-add-table-test-btn"]' + ); + const settingButton = page.locator('[data-testid="profiler-setting-btn"]'); + + if (!deleted) { + await expect(addTableTestButton).toBeVisible(); + await expect(settingButton).toBeVisible(); + } else { + await expect(addTableTestButton).not.toBeVisible(); + await expect(settingButton).not.toBeVisible(); + } +}; + +export const deletedEntityCommonChecks = async ({ + page, + endPoint, + deleted, +}: { + page: Page; + endPoint: EntityTypeEndpoint; + deleted?: boolean; +}) => { + const isTableEntity = endPoint === EntityTypeEndpoint.Table; + + // Go to first tab before starts validating + await page.click('.ant-tabs-tab:nth-child(1)'); + + // Check if all the edit actions are available for the entity + await checkForEditActions({ + page, + entityType: endPoint, + deleted, + }); + + if (isTableEntity) { + await checkLineageTabActions(page, deleted); + } + + if (isTableEntity) { + await checkForTableSpecificFields(page, deleted); + } + + await page.click('[data-testid="manage-button"]'); + + if (deleted) { + // only two menu options (restore and delete) should be present + await expect( + page.locator( + '[data-testid="manage-dropdown-list-container"] [data-testid="announcement-button"]' + ) + ).toBeHidden(); + await expect( + page.locator( + '[data-testid="manage-dropdown-list-container"] [data-testid="rename-button"]' + ) + ).toBeHidden(); + await expect( + page.locator( + '[data-testid="manage-dropdown-list-container"] [data-testid="profiler-setting-button"]' + ) + ).toBeHidden(); + await expect( + page.locator( + '[data-testid="manage-dropdown-list-container"] [data-testid="restore-button"]' + ) + ).toBeVisible(); + await expect( + page.locator( + '[data-testid="manage-dropdown-list-container"] [data-testid="delete-button"]' + ) + ).toBeVisible(); + } else { + await expect( + page.locator( + '[data-testid="manage-dropdown-list-container"] [data-testid="announcement-button"]' + ) + ).toBeVisible(); + await expect( + page.locator( + '[data-testid="manage-dropdown-list-container"] [data-testid="rename-button"]' + ) + ).toBeVisible(); + + if ( + [EntityTypeEndpoint.Database, EntityTypeEndpoint.DatabaseSchema].includes( + endPoint + ) + ) { + await expect( + page.locator( + '[data-testid="manage-dropdown-list-container"] [data-testid="profiler-setting-button"]' + ) + ).toBeVisible(); + } + + await expect( + page.locator( + '[data-testid="manage-dropdown-list-container"] [data-testid="delete-button"]' + ) + ).toBeVisible(); + } + + await page.click('body'); +}; + +export const restoreEntity = async (page: Page) => { + await expect(page.locator('[data-testid="deleted-badge"]')).toBeVisible(); + + await page.click('[data-testid="manage-button"]'); + await page.click('[data-testid="restore-button"]'); + await page.click('button:has-text("Restore")'); + + await expect(page.locator('.Toastify__toast-body')).toHaveText( + /restored successfully/ + ); + + await page.click('.Toastify__close-button'); + + const exists = await page + .locator('[data-testid="deleted-badge"]') + .isVisible(); + + expect(exists).toBe(false); +}; + +export const softDeleteEntity = async ( + page: Page, + entityName: string, + endPoint: EntityTypeEndpoint, + displayName: string +) => { + await deletedEntityCommonChecks({ + page, + endPoint, + deleted: false, + }); + + await page.click('body'); // Equivalent to clicking outside + + await page.click('[data-testid="manage-button"]'); + await page.click('[data-testid="delete-button"]'); + + await page.waitForSelector('[role="dialog"]'); + + await expect(page.locator('[role="dialog"]')).toBeVisible(); + await expect(page.locator('.ant-modal-title')).toContainText(displayName); + + await page.fill('[data-testid="confirmation-text-input"]', 'DELETE'); + await page.click('[data-testid="confirm-button"]'); + + await page.waitForResponse( + `/api/v1/${endPoint}/*?hardDelete=false&recursive=true` + ); + + await expect(page.locator('.Toastify__toast-body')).toHaveText( + /deleted successfully!/ + ); + + await page.click('.Toastify__close-button'); + + await page.reload(); + + const deletedBadge = await page.locator('[data-testid="deleted-badge"]'); + + await expect(deletedBadge).toHaveText('Deleted'); + + await deletedEntityCommonChecks({ + page, + endPoint, + deleted: true, + }); + + await page.click('body'); // Equivalent to clicking outside + + if (endPoint === EntityTypeEndpoint.Table) { + await page.click('[data-testid="breadcrumb-link"]:last-child'); + await page.waitForResponse('/api/v1/databaseSchemas/name'); + await page.click('[data-testid="show-deleted"]'); + await page.waitForResponse('/api/v1/tables?databaseSchema=*'); + const tableCount = await page.locator( + '[data-testid="table"] [data-testid="count"]' + ); + + await expect(tableCount).toContainText('1'); + + await page.click(`[data-testid=${entityName}]`); + } + + await restoreEntity(page); + await page.reload(); + + await deletedEntityCommonChecks({ + page, + endPoint, + deleted: false, + }); +}; + +export const hardDeleteEntity = async ( + page: Page, + entityName: string, + endPoint: EntityType +) => { + await page.click('[data-testid="manage-button"]'); + await page.click('[data-testid="delete-button"]'); + + await expect(page.locator('[role="dialog"]')).toBeVisible(); + + await expect( + page.locator('[data-testid="delete-modal"] .ant-modal-title') + ).toHaveText(new RegExp(entityName)); + + await page.click('[data-testid="hard-delete-option"]'); + await page.check('[data-testid="hard-delete"]'); + await page.fill('[data-testid="confirmation-text-input"]', 'DELETE'); + await page.click('[data-testid="confirm-button"]'); + await page.waitForResponse( + `**/api/v1/${endPoint}/*?hardDelete=true&recursive=true` + ); + + await expect(page.locator('.Toastify__toast-body')).toHaveText( + /deleted successfully!/ + ); + + await page.click('.Toastify__close-button'); +}; From 42fc2f7ffc341a7914a146fbd49713f7444d1350 Mon Sep 17 00:00:00 2001 From: Shailesh Parmar Date: Wed, 5 Jun 2024 17:07:23 +0530 Subject: [PATCH 2/3] addressing comment --- .../main/resources/ui/playwright/e2e/Pages/Entity.spec.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/Entity.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/Entity.spec.ts index 2752a09d1f99..81a3bdbc62a3 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/Entity.spec.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/Entity.spec.ts @@ -39,9 +39,9 @@ const entities = [ // use the admin user to login test.use({ storageState: 'playwright/.auth/admin.json' }); -entities.forEach((entityClass) => { - const entity = new entityClass(); - const deleteEntity = new entityClass(); +entities.forEach((EntityClass) => { + const entity = new EntityClass(); + const deleteEntity = new EntityClass(); test.describe(entity.getType(), () => { test.beforeAll('Setup pre-requests', async ({ browser }) => { From dfa21b8cb6ed9e767fcc7f9f0abdfecf0b070210 Mon Sep 17 00:00:00 2001 From: Shailesh Parmar Date: Wed, 5 Jun 2024 17:24:41 +0530 Subject: [PATCH 3/3] minor fix for hover --- .../src/main/resources/ui/playwright/utils/entityUtils.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/openmetadata-ui/src/main/resources/ui/playwright/utils/entityUtils.ts b/openmetadata-ui/src/main/resources/ui/playwright/utils/entityUtils.ts index 2fd1b265c867..25a1a18c22aa 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/utils/entityUtils.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/utils/entityUtils.ts @@ -378,7 +378,8 @@ export const replyAnnouncement = async (page: Page) => { '[data-testid="announcement-card"] [data-testid="main-message"]' ); - await expect(page.locator('.ant-popover')).toBeVisible(); + await page.waitForSelector('.ant-popover', { state: 'visible' }); + await expect(page.getByTestId('add-reply').locator('svg')).toBeVisible(); await page.getByTestId('add-reply').locator('svg').click(); @@ -403,6 +404,7 @@ export const replyAnnouncement = async (page: Page) => { // Edit the reply message await page.hover('[data-testid="replies"] > [data-testid="main-message"]'); + await page.waitForSelector('.ant-popover', { state: 'visible' }); await page.click('[data-testid="edit-message"]'); await page.fill( @@ -427,7 +429,7 @@ export const deleteAnnouncement = async (page: Page) => { '[data-testid="announcement-card"] [data-testid="main-message"]' ); - await expect(page.locator('.ant-popover')).toBeVisible(); + await page.waitForSelector('.ant-popover', { state: 'visible' }); await page.click('[data-testid="delete-message"]'); const modalText = await page.textContent('.ant-modal-body');