diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/Users.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/Users.spec.ts index 940714edabc..ddfae2f8405 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/Users.spec.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/Users.spec.ts @@ -18,6 +18,7 @@ import { PolicyClass } from '../../support/access-control/PoliciesClass'; import { RolesClass } from '../../support/access-control/RolesClass'; import { EntityTypeEndpoint } from '../../support/entity/Entity.interface'; import { TableClass } from '../../support/entity/TableClass'; +import { PersonaClass } from '../../support/persona/PersonaClass'; import { UserClass } from '../../support/user/UserClass'; import { performAdminLogin } from '../../utils/admin'; import { @@ -25,7 +26,7 @@ import { uuid, visitOwnProfilePage, } from '../../utils/common'; -import { addOwner } from '../../utils/entity'; +import { addOwner, waitForAllLoadersToDisappear } from '../../utils/entity'; import { settingClick, sidebarClick } from '../../utils/sidebar'; import { addUser, @@ -72,6 +73,8 @@ const tableEntity = new TableClass(); const tableEntity2 = new TableClass(); const policy = new PolicyClass(); const role = new RolesClass(); +const persona1 = new PersonaClass(); +const persona2 = new PersonaClass(); const test = base.extend<{ adminPage: Page; @@ -114,6 +117,8 @@ base.beforeAll('Setup pre-requests', async ({ browser }) => { await tableEntity2.create(apiContext); await policy.create(apiContext, DATA_STEWARD_RULES); await role.create(apiContext, [policy.responseData.name]); + await persona1.create(apiContext); + await persona2.create(apiContext); await afterAction(); }); @@ -129,6 +134,8 @@ base.afterAll('Cleanup', async ({ browser }) => { await tableEntity2.delete(apiContext); await policy.delete(apiContext); await role.delete(apiContext); + await persona1.delete(apiContext); + await persona2.delete(apiContext); await afterAction(); }); @@ -538,3 +545,263 @@ test.describe('User Profile Feed Interactions', () => { ).not.toBeVisible(); }); }); + +test.describe('User Profile Persona Interactions', () => { + test('Should add, remove, and navigate to persona pages for Personas section', async ({ + adminPage, + }) => { + await redirectToHomePage(adminPage); + await visitOwnProfilePage(adminPage); + + // Wait for the persona card to be visible + await adminPage.waitForSelector('[data-testid="persona-details-card"]'); + + // Test adding personas + await test.step('Add personas to user profile', async () => { + // Click edit button for Personas section + await adminPage + .locator('[data-testid="edit-user-persona"]') + .first() + .click(); + + // Wait for persona popover to be visible + await adminPage.waitForSelector('[data-testid="persona-select-list"]'); + + // Open the persona select dropdown and wait for options to load + await adminPage.locator('[data-testid="persona-select-list"]').click(); + + // Wait for dropdown to open and options to be visible + await adminPage.waitForSelector('.ant-select-dropdown', { + state: 'visible', + }); + + // Select first available persona - try test ID first, fallback to role selector + const personaOptionTestId = adminPage.getByTestId( + `${persona1.data.displayName}-option` + ); + + await personaOptionTestId.click(); + + // Save the changes + await adminPage + .locator('[data-testid="user-profile-persona-edit-save"]') + .click(); + + // Wait for the API call to complete and persona to appear + await adminPage.waitForResponse('/api/v1/users/*'); + await adminPage.waitForSelector( + '[data-testid="chip-container"] [data-testid="tag-chip"]' + ); + }); + + // Test clicking on persona chip to navigate to persona page + await test.step( + 'Navigate to persona page by clicking on persona chip', + async () => { + const personaChip = adminPage + .locator('[data-testid="chip-container"] [data-testid="tag-chip"]') + .first(); + const personaLink = personaChip.locator('a').first(); + + // Get the persona name/link for verification + const personaText = await personaLink.textContent(); + + expect(personaText).toBeTruthy(); + + // Click the persona link to navigate + await personaLink.click(); + await adminPage.waitForLoadState('networkidle'); + + // Verify we're on the persona page + await expect(adminPage.url()).toContain('/persona/'); + } + ); + + // Navigate back to user profile for removal test + await test.step('Navigate back to user profile', async () => { + await visitOwnProfilePage(adminPage); + await adminPage.waitForSelector('[data-testid="persona-details-card"]'); + }); + + // Test removing personas + await test.step('Remove personas from user profile', async () => { + // Click edit button for Personas section + await adminPage + .locator('[data-testid="edit-user-persona"]') + .first() + .click(); + + // Wait for persona popover to be visible + await adminPage.waitForSelector('[data-testid="persona-select-list"]'); + + // Clear all selected personas + await adminPage + .locator('[data-testid="persona-select-list"] .ant-select-clear') + .click(); + + // Save the changes + await adminPage + .locator('[data-testid="user-profile-persona-edit-save"]') + .click(); + + // Wait for the API call to complete and verify no personas are shown + await adminPage.waitForResponse('/api/v1/users/*'); + + await expect( + adminPage + .getByTestId('persona-details-card') + .getByText('No persona assigned') + ).toBeVisible(); + }); + }); + + test('Should add, remove, and navigate to persona pages for Default Persona section', async ({ + adminPage, + }) => { + await redirectToHomePage(adminPage); + await visitOwnProfilePage(adminPage); + + // Wait for the persona card to be visible + await adminPage.waitForSelector('[data-testid="persona-details-card"]'); + + // First, add some personas to the user so we can select a default persona + await test.step('Add personas to user profile first', async () => { + // Click edit button for Personas section (regular personas, not default) + await adminPage + .locator('[data-testid="edit-user-persona"]') + .first() + .click(); + + // Wait for persona popover and select multiple personas + await adminPage.waitForSelector('[data-testid="persona-select-list"]'); + await adminPage.locator('[data-testid="persona-select-list"]').click(); + + // Wait for dropdown to open and options to be visible + await adminPage.waitForSelector('.ant-select-dropdown', { + state: 'visible', + }); + + // Select multiple personas - try test IDs first, fallback to role selectors + const persona1OptionTestId = adminPage.getByTestId( + `${persona1.data.displayName}-option` + ); + + await persona1OptionTestId.click(); + + const persona2OptionTestId = adminPage.getByTestId( + `${persona2.data.displayName}-option` + ); + + await persona2OptionTestId.click(); + + // Save the changes + await adminPage + .locator('[data-testid="user-profile-persona-edit-save"]') + .click(); + await adminPage.waitForResponse('/api/v1/users/*'); + }); + + // Test adding default persona + await test.step('Add default persona to user profile', async () => { + // Click edit button for Default Persona section using specific test ID + await adminPage + .locator('[data-testid="default-edit-user-persona"]') + .click(); + + // Wait for persona popover to be visible + await adminPage.waitForSelector( + '[data-testid="default-persona-select-list"]' + ); + + // Open the default persona select dropdown + await adminPage + .locator('[data-testid="default-persona-select-list"]') + .click(); + + // Wait for dropdown to open and options to load + await adminPage.waitForSelector('.ant-select-dropdown', { + state: 'visible', + }); + await adminPage.waitForLoadState('networkidle'); + + // Select specific persona for default - try test ID first, fallback to role selector + const defaultPersonaOptionTestId = adminPage.getByTestId( + `${persona1.data.displayName}-option` + ); + + await defaultPersonaOptionTestId.click(); + + // Save the changes + await adminPage + .locator('[data-testid="user-profile-default-persona-edit-save"]') + .click(); + + // Wait for the API call to complete and default persona to appear + await adminPage.waitForResponse('/api/v1/users/*'); + await adminPage.waitForSelector( + '.default-persona-text [data-testid="tag-chip"]' + ); + }); + + // Test clicking on default persona chip to navigate to persona page + await test.step( + 'Navigate to persona page by clicking on default persona chip', + async () => { + const defaultPersonaChip = adminPage + .locator('.default-persona-text [data-testid="tag-chip"]') + .first(); + const personaLink = defaultPersonaChip.locator('a').first(); + + // Get the persona name/link for verification + const personaText = await personaLink.textContent(); + + expect(personaText).toBeTruthy(); + + // Click the persona link to navigate + await personaLink.click(); + await adminPage.waitForLoadState('networkidle'); + + // Verify we're on the persona page + await expect(adminPage.url()).toContain('/persona/'); + } + ); + + // Navigate back to user profile for removal test + await test.step('Navigate back to user profile', async () => { + await visitOwnProfilePage(adminPage); + await adminPage.waitForSelector('[data-testid="persona-details-card"]'); + }); + + // Test removing default persona + await test.step('Remove default persona from user profile', async () => { + // Click edit button for Default Persona section using specific test ID + await adminPage + .locator('[data-testid="default-edit-user-persona"]') + .click(); + + await waitForAllLoadersToDisappear(adminPage); + + // Wait for persona popover to be visible + await adminPage.waitForSelector( + '[data-testid="default-persona-select-list"]' + ); + + // Clear the selected default persona + await adminPage + .locator( + '[data-testid="default-persona-select-list"] .ant-select-clear' + ) + .click(); + + // Save the changes + await adminPage + .locator('[data-testid="user-profile-default-persona-edit-save"]') + .click(); + + // Wait for the API call to complete and verify no default persona is shown + await adminPage.waitForResponse('api/v1/users/*'); + + await expect(adminPage.getByText('No default persona')).toBeVisible(); + }); + }); +}); diff --git a/openmetadata-ui/src/main/resources/ui/playwright/utils/customizeLandingPage.ts b/openmetadata-ui/src/main/resources/ui/playwright/utils/customizeLandingPage.ts index 2a34ec9f705..7cdef7b784b 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/utils/customizeLandingPage.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/utils/customizeLandingPage.ts @@ -186,13 +186,13 @@ export const setUserDefaultPersona = async ( ) => { await visitOwnProfilePage(page); - await page.locator('[data-testid="edit-user-persona"]').nth(1).click(); + await page.locator('[data-testid="default-edit-user-persona"]').click(); await expect( - page.locator('[data-testid="persona-select-list"]') + page.locator('[data-testid="default-persona-select-list"]') ).toBeVisible(); - await page.locator('[data-testid="persona-select-list"]').click(); + await page.locator('[data-testid="default-persona-select-list"]').click(); await page.waitForLoadState('networkidle'); const setDefaultPersona = page.waitForResponse('/api/v1/users/*'); @@ -200,7 +200,9 @@ export const setUserDefaultPersona = async ( // Click on the persona option by text within the dropdown await page.locator(`[data-testid="${personaName}-option"]`).click(); - await page.locator('[data-testid="user-profile-persona-edit-save"]').click(); + await page + .locator('[data-testid="user-profile-default-persona-edit-save"]') + .click(); await setDefaultPersona; diff --git a/openmetadata-ui/src/main/resources/ui/playwright/utils/persona.ts b/openmetadata-ui/src/main/resources/ui/playwright/utils/persona.ts index 27a632d75d2..1cf093060be 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/utils/persona.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/utils/persona.ts @@ -66,8 +66,8 @@ export const checkPersonaInProfile = async ( if (expectedPersonaName) { // Expect persona to be visible with specific name - await expect(page.getByTestId('default-persona-text')).toBeVisible(); - await expect(page.getByTestId('default-persona-text')).toContainText( + await expect(page.getByTestId('default-persona-chip')).toBeVisible(); + await expect(page.getByTestId('default-persona-chip')).toContainText( expectedPersonaName ); } else { diff --git a/openmetadata-ui/src/main/resources/ui/src/components/MyData/Persona/PersonaSelectableList/PersonaSelectableList.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/MyData/Persona/PersonaSelectableList/PersonaSelectableList.component.tsx index fd974d0ead0..6bec4b037b2 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/MyData/Persona/PersonaSelectableList/PersonaSelectableList.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/MyData/Persona/PersonaSelectableList/PersonaSelectableList.component.tsx @@ -190,7 +190,9 @@ export const PersonaSelectableList = ({ className={classNames('profile-edit-popover', { 'single-select': isDefaultPersona, })} - data-testid="persona-select-list" + data-testid={`${ + isDefaultPersona ? 'default-' : '' + }persona-select-list`} defaultValue={selectedPersonas.map((persona) => persona.id)} dropdownStyle={{ maxHeight: '200px', @@ -229,7 +231,9 @@ export const PersonaSelectableList = ({