From b86f3d8ae73ca48c52dd7175fbf82d80e80d90df Mon Sep 17 00:00:00 2001 From: Chirag Madlani <12962843+chirag-madlani@users.noreply.github.com> Date: Wed, 4 Sep 2024 22:45:40 +0530 Subject: [PATCH] minor(test): migrate persona spec (#17701) * minor(test): migrate persona spec * remove persona cypress spec * address comments --- .../ui/cypress/e2e/Flow/PersonaFlow.spec.ts | 309 ------------------ .../ui/playwright/constant/common.ts | 2 + .../playwright/e2e/Flow/PersonaFlow.spec.ts | 259 +++++++++++++++ .../resources/ui/playwright/utils/persona.ts | 36 ++ .../Users/UsersTab/UsersTabs.component.tsx | 2 +- 5 files changed, 298 insertions(+), 310 deletions(-) delete mode 100644 openmetadata-ui/src/main/resources/ui/cypress/e2e/Flow/PersonaFlow.spec.ts create mode 100644 openmetadata-ui/src/main/resources/ui/playwright/e2e/Flow/PersonaFlow.spec.ts create mode 100644 openmetadata-ui/src/main/resources/ui/playwright/utils/persona.ts diff --git a/openmetadata-ui/src/main/resources/ui/cypress/e2e/Flow/PersonaFlow.spec.ts b/openmetadata-ui/src/main/resources/ui/cypress/e2e/Flow/PersonaFlow.spec.ts deleted file mode 100644 index ffd9ca0d040..00000000000 --- a/openmetadata-ui/src/main/resources/ui/cypress/e2e/Flow/PersonaFlow.spec.ts +++ /dev/null @@ -1,309 +0,0 @@ -/* - * Copyright 2022 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. - */ - -import { - descriptionBox, - interceptURL, - toastNotification, - verifyResponseStatusCode, -} from '../../common/common'; -import { validateFormNameFieldInput } from '../../common/Utils/Form'; -import { getToken } from '../../common/Utils/LocalStorage'; -import { DELETE_TERM } from '../../constants/constants'; -import { PERSONA_DETAILS, USER_DETAILS } from '../../constants/EntityConstant'; -import { GlobalSettingOptions } from '../../constants/settings.constant'; - -const updatePersonaDisplayName = (displayName) => { - interceptURL('PATCH', `/api/v1/personas/*`, 'updatePersona'); - - cy.get('[data-testid="manage-button"]').click(); - - cy.get( - '[data-testid="manage-dropdown-list-container"] [data-testid="rename-button"]' - ).click(); - - cy.get('#name').should('be.disabled'); - cy.get('#displayName').should('not.be.disabled').clear(); - - cy.get('#displayName').type(displayName); - - cy.get('[data-testid="save-button"]').click(); - verifyResponseStatusCode('@updatePersona', 200); -}; - -describe('Persona operations', { tags: 'Settings' }, () => { - const user = { - details: { - id: '', - name: '', - }, - }; - const userSearchText = `${USER_DETAILS.firstName}${USER_DETAILS.lastName}`; - before(() => { - cy.login(); - cy.getAllLocalStorage().then((data) => { - const token = getToken(data); - - // Create a new user - cy.request({ - method: 'POST', - url: `/api/v1/users/signup`, - headers: { Authorization: `Bearer ${token}` }, - body: USER_DETAILS, - }).then((response) => { - user.details = response.body; - }); - }); - }); - - after(() => { - cy.login(); - cy.getAllLocalStorage().then((data) => { - const token = getToken(data); - - // Delete created user - cy.request({ - method: 'DELETE', - url: `/api/v1/users/${user.details.id}?hardDelete=true&recursive=false`, - headers: { Authorization: `Bearer ${token}` }, - }); - }); - }); - - beforeEach(() => { - cy.login(); - - interceptURL('GET', '/api/v1/personas*', 'getPersonas'); - - cy.settingClick(GlobalSettingOptions.PERSONA); - - verifyResponseStatusCode('@getPersonas', 200); - }); - - it('Persona creation should work properly', () => { - interceptURL( - 'GET', - '/api/v1/users?limit=25&isBot=false', - 'getInitialUsers' - ); - - interceptURL( - 'GET', - '/api/v1/search/query?q=***%20AND%20isBot:false&from=0&size=25&index=user_search_index', - 'getUserSearch' - ); - - cy.get('[data-testid="add-persona-button"]').scrollIntoView().click(); - cy.get('[data-testid="name"]').clear().type(PERSONA_DETAILS.name); - validateFormNameFieldInput({ - value: PERSONA_DETAILS.name, - fieldName: 'Name', - fieldSelector: '[data-testid="name"]', - errorDivSelector: '#name_help', - }); - cy.get('[data-testid="displayName"]') - .clear() - .type(PERSONA_DETAILS.displayName); - cy.get(descriptionBox).type(PERSONA_DETAILS.description); - cy.get('[data-testid="add-users"]').scrollIntoView().click(); - - verifyResponseStatusCode('@getInitialUsers', 200); - - cy.get('[data-testid="searchbar"]').type(userSearchText); - - verifyResponseStatusCode('@getUserSearch', 200); - - cy.get(`.ant-popover [title="${userSearchText}"]`).click(); - cy.get('[data-testid="selectable-list-update-btn"]') - .scrollIntoView() - .click(); - - interceptURL('POST', '/api/v1/personas', 'createPersona'); - - cy.get('.ant-modal-footer > .ant-btn-primary') - .contains('Create') - .scrollIntoView() - .click(); - - verifyResponseStatusCode('@createPersona', 201); - - // Verify created persona details - - cy.get('[data-testid="persona-details-card"] .ant-card-meta-title').should( - 'contain', - PERSONA_DETAILS.displayName - ); - cy.get( - '[data-testid="persona-details-card"] .ant-card-meta-description' - ).should('contain', PERSONA_DETAILS.description); - - interceptURL( - 'GET', - `/api/v1/personas/name/${PERSONA_DETAILS.name}*`, - 'getPersonaDetails' - ); - - cy.get('[data-testid="persona-details-card"]') - .contains(PERSONA_DETAILS.displayName) - .scrollIntoView() - .click(); - - verifyResponseStatusCode('@getPersonaDetails', 200); - - cy.get('[data-testid="entity-header-name"]').should( - 'contain', - PERSONA_DETAILS.name - ); - cy.get('[data-testid="entity-header-display-name"]').should( - 'contain', - PERSONA_DETAILS.displayName - ); - cy.get( - '[data-testid="viewer-container"] [data-testid="markdown-parser"]' - ).should('contain', PERSONA_DETAILS.description); - - cy.get( - `[data-row-key="${user.details.name}"] [data-testid="${user.details.name}"]` - ).should('contain', user.details.name); - }); - - it('Persona update description flow should work properly', () => { - interceptURL( - 'GET', - `/api/v1/personas/name/${PERSONA_DETAILS.name}*`, - 'getPersonaDetails' - ); - - cy.get('[data-testid="persona-details-card"]') - .contains(PERSONA_DETAILS.displayName) - .scrollIntoView() - .click(); - - verifyResponseStatusCode('@getPersonaDetails', 200); - - cy.get('[data-testid="edit-description"]').click(); - - cy.get(`[data-testid="markdown-editor"] ${descriptionBox}`) - .clear() - .type('Updated description.'); - - interceptURL('PATCH', `/api/v1/personas/*`, 'updatePersona'); - - cy.get(`[data-testid="markdown-editor"] [data-testid="save"]`).click(); - - verifyResponseStatusCode('@updatePersona', 200); - - cy.get( - `[data-testid="viewer-container"] [data-testid="markdown-parser"]` - ).should('contain', 'Updated description.'); - }); - - it('Persona rename flow should work properly', () => { - interceptURL( - 'GET', - `/api/v1/personas/name/${PERSONA_DETAILS.name}*`, - 'getPersonaDetails' - ); - - cy.get('[data-testid="persona-details-card"]') - .contains(PERSONA_DETAILS.displayName) - .scrollIntoView() - .click(); - - verifyResponseStatusCode('@getPersonaDetails', 200); - - updatePersonaDisplayName('Test Persona'); - - cy.get('[data-testid="entity-header-display-name"]').should( - 'contain', - 'Test Persona' - ); - - updatePersonaDisplayName(PERSONA_DETAILS.displayName); - - cy.get('[data-testid="entity-header-display-name"]').should( - 'contain', - PERSONA_DETAILS.displayName - ); - }); - - it('Remove users in persona should work properly', () => { - // Remove user from the users tab - interceptURL( - 'GET', - `/api/v1/personas/name/${PERSONA_DETAILS.name}*`, - 'getPersonaDetails' - ); - - cy.get('[data-testid="persona-details-card"]') - .contains(PERSONA_DETAILS.displayName) - .scrollIntoView() - .click(); - - verifyResponseStatusCode('@getPersonaDetails', 200); - - cy.get( - `[data-row-key="${user.details.name}"] [data-testid="remove-user-btn"]` - ).click(); - - cy.get('[data-testid="remove-confirmation-modal"]').should( - 'contain', - `Are you sure you want to remove ${user.details.name}?` - ); - - interceptURL('PATCH', `/api/v1/personas/*`, 'updatePersona'); - - cy.get('[data-testid="remove-confirmation-modal"]') - .contains('Confirm') - .click(); - - verifyResponseStatusCode('@updatePersona', 200); - }); - - it('Delete persona should work properly', () => { - interceptURL( - 'GET', - `/api/v1/personas/name/${PERSONA_DETAILS.name}*`, - 'getPersonaDetails' - ); - - cy.get('[data-testid="persona-details-card"]') - .contains(PERSONA_DETAILS.displayName) - .scrollIntoView() - .click(); - - verifyResponseStatusCode('@getPersonaDetails', 200); - - cy.get('[data-testid="manage-button"]').click(); - - cy.get('[data-testid="delete-button-title"]').click(); - - cy.get('.ant-modal-header').should('contain', PERSONA_DETAILS.displayName); - - cy.get(`[data-testid="hard-delete-option"]`).click(); - - cy.get('[data-testid="confirm-button"]').should('be.disabled'); - cy.get('[data-testid="confirmation-text-input"]').type(DELETE_TERM); - - interceptURL( - 'DELETE', - `/api/v1/personas/*?hardDelete=true&recursive=false`, - `deletePersona` - ); - cy.get('[data-testid="confirm-button"]').should('not.be.disabled'); - cy.get('[data-testid="confirm-button"]').click(); - verifyResponseStatusCode(`@deletePersona`, 200); - - toastNotification(`"${PERSONA_DETAILS.displayName}" deleted successfully!`); - }); -}); diff --git a/openmetadata-ui/src/main/resources/ui/playwright/constant/common.ts b/openmetadata-ui/src/main/resources/ui/playwright/constant/common.ts index 2c84694b3e4..27796a5ac92 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/constant/common.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/constant/common.ts @@ -30,3 +30,5 @@ export const NAME_MIN_MAX_LENGTH_VALIDATION_ERROR = export const NAME_MAX_LENGTH_VALIDATION_ERROR = 'Name size must be between 1 and 128'; + +export const DELETE_TERM = 'DELETE'; diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Flow/PersonaFlow.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Flow/PersonaFlow.spec.ts new file mode 100644 index 00000000000..203d0bec0eb --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Flow/PersonaFlow.spec.ts @@ -0,0 +1,259 @@ +/* + * Copyright 2022 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. + */ + +import test, { expect } from '@playwright/test'; + +import { DELETE_TERM } from '../../constant/common'; +import { GlobalSettingOptions } from '../../constant/settings'; +import { UserClass } from '../../support/user/UserClass'; +import { + createNewPage, + descriptionBox, + redirectToHomePage, + toastNotification, + uuid, +} from '../../utils/common'; +import { validateFormNameFieldInput } from '../../utils/form'; +import { updatePersonaDisplayName } from '../../utils/persona'; +import { settingClick } from '../../utils/sidebar'; + +const PERSONA_DETAILS = { + name: `persona-with-%-${uuid()}`, + displayName: `persona ${uuid()}`, + description: `Persona description ${uuid()}.`, +}; + +// use the admin user to login +test.use({ + storageState: 'playwright/.auth/admin.json', +}); + +test.describe.serial('Persona operations', () => { + const user = new UserClass(); + + test.beforeAll('pre-requisites', async ({ browser }) => { + const { apiContext, afterAction } = await createNewPage(browser); + await user.create(apiContext); + await afterAction(); + }); + + test.afterAll('cleanup', async ({ browser }) => { + const { apiContext, afterAction } = await createNewPage(browser); + + await user.delete(apiContext); + await afterAction(); + }); + + test.beforeEach(async ({ page }) => { + await redirectToHomePage(page); + await settingClick(page, GlobalSettingOptions.PERSONA); + }); + + test('Persona creation should work properly', async ({ page }) => { + await page.getByTestId('add-persona-button').click(); + + await validateFormNameFieldInput({ + page, + value: PERSONA_DETAILS.name, + fieldName: 'Name', + fieldSelector: '[data-testid="name"]', + errorDivSelector: '#name_help', + }); + + await page.getByTestId('displayName').fill(PERSONA_DETAILS.displayName); + + await page.locator(descriptionBox).fill(PERSONA_DETAILS.description); + await page.getByTestId('add-users').click(); + + const searchUser = page.waitForResponse( + `/api/v1/search/query?q=*${encodeURIComponent( + user.responseData.displayName + )}*` + ); + await page.getByTestId('searchbar').fill(user.responseData.displayName); + + await searchUser; + await page.click(`.ant-popover [title="${user.responseData.displayName}"]`); + await page.getByTestId('selectable-list-update-btn').click(); + + await page.getByRole('button', { name: 'Create' }).click(); + + // Verify created persona details + + await expect( + page + .getByTestId('persona-details-card') + .getByText(PERSONA_DETAILS.displayName) + ).toBeVisible(); + + await expect( + page + .getByTestId('persona-details-card') + .getByText(PERSONA_DETAILS.description) + ).toBeVisible(); + + const personaResponse = page.waitForResponse( + `/api/v1/personas/name/${encodeURIComponent( + PERSONA_DETAILS.name + )}?fields=users` + ); + + await page + .locator('[data-testid="persona-details-card"]') + .getByText(PERSONA_DETAILS.displayName) + .click(); + + await personaResponse; + + await page.waitForSelector('[data-testid="entity-header-name"]', { + state: 'visible', + }); + + await expect(page.getByTestId('entity-header-name')).toContainText( + PERSONA_DETAILS.name + ); + + await expect(page.getByTestId('entity-header-display-name')).toContainText( + PERSONA_DETAILS.displayName + ); + + await expect( + page.locator( + '[data-testid="viewer-container"] [data-testid="markdown-parser"]' + ) + ).toContainText(PERSONA_DETAILS.description); + + await expect(page.getByTestId(user.responseData.name)).toContainText( + user.responseData.name + ); + }); + + test('Persona update description flow should work properly', async ({ + page, + }) => { + await page + .locator('[data-testid="persona-details-card"]') + .getByText(PERSONA_DETAILS.displayName) + .click(); + + await page.getByTestId('edit-description').click(); + + await page + .locator(`[data-testid="markdown-editor"] ${descriptionBox}`) + .clear(); + + await page + .locator(`[data-testid="markdown-editor"] ${descriptionBox}`) + .fill('Updated description.'); + + await page + .locator(`[data-testid="markdown-editor"] [data-testid="save"]`) + .click(); + + await expect( + page.locator( + `[data-testid="viewer-container"] [data-testid="markdown-parser"]` + ) + ).toContainText('Updated description.'); + }); + + test('Persona rename flow should work properly', async ({ page }) => { + await page + .locator('[data-testid="persona-details-card"]') + .getByText(PERSONA_DETAILS.displayName) + + .click(); + + await updatePersonaDisplayName({ page, displayName: 'Test Persona' }); + + await expect(page.getByTestId('entity-header-display-name')).toContainText( + 'Test Persona' + ); + + await updatePersonaDisplayName({ + page, + displayName: PERSONA_DETAILS.displayName, + }); + + await expect(page.getByTestId('entity-header-display-name')).toContainText( + PERSONA_DETAILS.displayName + ); + }); + + test('Remove users in persona should work properly', async ({ page }) => { + await page + .locator('[data-testid="persona-details-card"]') + .getByText(PERSONA_DETAILS.displayName) + .click(); + + await page + .locator( + `[data-row-key="${user.responseData.name}"] [data-testid="remove-user-btn"]` + ) + .click(); + + await expect( + page.locator('[data-testid="remove-confirmation-modal"]') + ).toContainText( + `Are you sure you want to remove ${user.responseData.name}?` + ); + + const updateResponse = page.waitForResponse(`/api/v1/personas/*`); + + await page + .locator('[data-testid="remove-confirmation-modal"]') + .getByText('Confirm') + .click(); + + await updateResponse; + }); + + test('Delete persona should work properly', async ({ page }) => { + await page + .locator('[data-testid="persona-details-card"]') + .getByText(PERSONA_DETAILS.displayName) + .click(); + + await page.click('[data-testid="manage-button"]'); + + await page.click('[data-testid="delete-button-title"]'); + + await expect(page.locator('.ant-modal-header')).toContainText( + PERSONA_DETAILS.displayName + ); + + await page.click(`[data-testid="hard-delete-option"]`); + + await expect(page.locator('[data-testid="confirm-button"]')).toBeDisabled(); + + await page + .locator('[data-testid="confirmation-text-input"]') + .fill(DELETE_TERM); + + const deleteResponse = page.waitForResponse( + `/api/v1/personas/*?hardDelete=true&recursive=false` + ); + + await expect( + page.locator('[data-testid="confirm-button"]') + ).not.toBeDisabled(); + + await page.click('[data-testid="confirm-button"]'); + await deleteResponse; + + await toastNotification( + page, + `"${PERSONA_DETAILS.displayName}" deleted successfully!` + ); + }); +}); diff --git a/openmetadata-ui/src/main/resources/ui/playwright/utils/persona.ts b/openmetadata-ui/src/main/resources/ui/playwright/utils/persona.ts new file mode 100644 index 00000000000..044a3746195 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/playwright/utils/persona.ts @@ -0,0 +1,36 @@ +/* + * 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. + */ +import { expect, Page } from '@playwright/test'; + +export const updatePersonaDisplayName = async ({ + page, + displayName, +}: { + page: Page; + displayName: string; +}) => { + await page.click('[data-testid="manage-button"]'); + + await page.click( + '[data-testid="manage-dropdown-list-container"] [data-testid="rename-button"]' + ); + + await page.waitForSelector('#name', { state: 'visible' }); + + await expect(page.locator('#name')).toBeDisabled(); + + await page.waitForSelector('#displayName', { state: 'visible' }); + await page.fill('#displayName', displayName); + + await page.click('[data-testid="save-button"]'); +}; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Settings/Users/UsersTab/UsersTabs.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Settings/Users/UsersTab/UsersTabs.component.tsx index 4851f6c334e..eb9db5e3621 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Settings/Users/UsersTab/UsersTabs.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Settings/Users/UsersTab/UsersTabs.component.tsx @@ -139,7 +139,7 @@ export const UsersTab = ({ users, onRemoveUser }: UsersTabProps) => { ), }} pagination={false} - rowKey="fullyQualifiedName" + rowKey="name" size="small" /> {Boolean(removeUserDetails?.state) && (