Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#16521: fix issue in userProfilePage for roles. teams and displayName #16527

Merged
merged 8 commits into from
Jun 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -245,9 +245,9 @@ export const editTeams = (teamName: string) => {
cy.get('[data-testid="inline-save-btn"]').click({ timeout: 10000 });
verifyResponseStatusCode('@updateTeams', 200);

cy.get('.ant-collapse-expand-icon > .anticon > svg').click();
cy.get('.page-layout-v1-vertical-scroll').scrollTo(0, 0);
cy.get(`[data-testid="${teamName}"]`).should('be.visible');
cy.get(`[data-testid="${teamName}-link"]`)
.scrollIntoView()
.should('be.visible');
};

export const handleUserUpdateDetails = (
Expand Down Expand Up @@ -284,8 +284,7 @@ export const handleAdminUpdateDetails = (
editDisplayName(editedUserName);

// edit teams
cy.get('.ant-collapse-expand-icon > .anticon > svg').scrollIntoView();
cy.get('.ant-collapse-expand-icon > .anticon > svg').click();
cy.get('.ant-collapse-expand-icon > .anticon > svg').scrollIntoView().click();
editTeams(teamName);

// edit description
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -349,7 +349,8 @@ const Users = ({ userData, queryFilters, updateUserDetails }: Props) => {
<Chip
showNoDataPlaceholder
data={userData.personas ?? []}
icon={<PersonaIcon height={14} />}
entityType={EntityType.PERSONA}
icon={<PersonaIcon height={20} />}
noDataPlaceholder={t('message.no-persona-assigned')}
/>
</Card>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,12 +115,17 @@ const UserProfileDetails = ({
setIsDisplayNameEdit(false);
}, [userData.displayName, displayName, updateUserDetails]);

const handleCloseEditDisplayName = useCallback(() => {
setDisplayName(userData.displayName);
setIsDisplayNameEdit(false);
}, [userData.displayName]);

const displayNameRenderComponent = useMemo(
() =>
isDisplayNameEdit && hasEditPermission ? (
isDisplayNameEdit ? (
<InlineEdit
isLoading={isLoading}
onCancel={() => setIsDisplayNameEdit(false)}
onCancel={handleCloseEditDisplayName}
onSave={handleDisplayNameSave}>
<Input
className="w-full"
Expand Down Expand Up @@ -173,6 +178,7 @@ const UserProfileDetails = ({
getEntityName,
onDisplayNameChange,
handleDisplayNameSave,
handleCloseEditDisplayName,
]
);

Expand Down Expand Up @@ -286,6 +292,7 @@ const UserProfileDetails = ({
<Chip
showNoDataPlaceholder
data={defaultPersona ? [defaultPersona] : []}
entityType={EntityType.PERSONA}
noDataPlaceholder={NO_DATA_PLACEHOLDER}
/>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,13 +63,16 @@ jest.mock('../UserProfileImage/UserProfileImage.component', () => {
});

jest.mock('../../../../common/InlineEdit/InlineEdit.component', () => {
return jest.fn().mockImplementation(({ onSave, children }) => (
return jest.fn().mockImplementation(({ onSave, onCancel, children }) => (
<div data-testid="inline-edit">
<span>InlineEdit</span>
{children}
<button data-testid="display-name-save-button" onClick={onSave}>
DisplayNameButton
</button>
<button data-testid="display-name-cancel-button" onClick={onCancel}>
DisplayNameCancelButton
</button>
</div>
));
});
Expand Down Expand Up @@ -224,6 +227,28 @@ describe('Test User Profile Details Component', () => {
expect(screen.getByText('InlineEdit')).toBeInTheDocument();
});

it('should not render changed displayName in input if not saved', async () => {
render(<UserProfileDetails {...mockPropsData} />, {
wrapper: MemoryRouter,
});

fireEvent.click(screen.getByTestId('edit-displayName'));

act(() => {
fireEvent.change(screen.getByTestId('displayName'), {
target: { value: 'data-test' },
});
});

act(() => {
fireEvent.click(screen.getByTestId('display-name-cancel-button'));
});

fireEvent.click(screen.getByTestId('edit-displayName'));

expect(screen.getByTestId('displayName')).toHaveValue('');
});

it('should call updateUserDetails on click of DisplayNameButton', async () => {
render(<UserProfileDetails {...mockPropsData} />, {
wrapper: MemoryRouter,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

import { Image } from 'antd';
import React, { useEffect, useMemo, useState } from 'react';
import { getEntityName } from '../../../../../utils/EntityUtils';
import {
getImageWithResolutionAndFallback,
ImageQuality,
Expand Down Expand Up @@ -50,7 +51,7 @@ const UserProfileImage = ({ userData }: UserProfileImageProps) => {
/>
) : (
<ProfilePicture
displayName={userData?.displayName ?? userData.name}
displayName={getEntityName(userData)}
height="54"
name={userData?.name ?? ''}
width="54"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ jest.mock('../../../../common/ProfilePicture/ProfilePicture', () => {
return jest.fn().mockReturnValue(<p>ProfilePicture</p>);
});

jest.mock('../../../../../utils/EntityUtils', () => ({
getEntityName: jest.fn().mockReturnValue('getEntityName'),
}));

describe('Test User User Profile Image Component', () => {
it('should render user profile image component', async () => {
render(<UserProfileImage {...mockPropsData} />);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { Card, Typography } from 'antd';
import React from 'react';
import { useTranslation } from 'react-i18next';
import { ReactComponent as UserIcons } from '../../../../../assets/svg/user.svg';
import { EntityType } from '../../../../../enums/entity.enum';
import Chip from '../../../../common/Chip/Chip.component';
import { UserProfileInheritedRolesProps } from './UserProfileInheritedRoles.interface';

Expand All @@ -37,6 +38,7 @@ const UserProfileInheritedRoles = ({
}>
<Chip
data={inheritedRoles ?? []}
entityType={EntityType.ROLE}
icon={<UserIcons height={20} />}
noDataPlaceholder={t('message.no-inherited-roles-found')}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
import { Card, Select, Space, Tooltip, Typography } from 'antd';
import { AxiosError } from 'axios';
import { isEmpty, toLower } from 'lodash';
import React, { useEffect, useMemo, useState } from 'react';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { ReactComponent as EditIcon } from '../../../../../assets/svg/edit-new.svg';
import { ReactComponent as UserIcons } from '../../../../../assets/svg/user.svg';
Expand All @@ -24,6 +24,7 @@ import {
PAGE_SIZE_LARGE,
TERM_ADMIN,
} from '../../../../../constants/constants';
import { EntityType } from '../../../../../enums/entity.enum';
import { Role } from '../../../../../generated/entity/teams/role';
import { useAuth } from '../../../../../hooks/authHooks';
import { getRoles } from '../../../../../rest/rolesAPIV1';
Expand Down Expand Up @@ -87,6 +88,15 @@ const UserProfileRoles = ({
}
};

const setUserRoles = useCallback(() => {
const defaultUserRoles = [
...(userRoles?.map((role) => role.id) ?? []),
...(isUserAdmin ? [toLower(TERM_ADMIN)] : []),
];

setSelectedRoles(defaultUserRoles);
}, [userRoles, isUserAdmin]);

const handleRolesSave = async () => {
setIsLoading(true);
// filter out the roles , and exclude the admin one
Expand Down Expand Up @@ -122,6 +132,7 @@ const UserProfileRoles = ({
: []),
...(userRoles ?? []),
]}
entityType={EntityType.ROLE}
icon={<UserIcons height={20} />}
noDataPlaceholder={t('message.no-roles-assigned')}
showNoDataPlaceholder={!isUserAdmin}
Expand All @@ -130,14 +141,14 @@ const UserProfileRoles = ({
[userRoles, isUserAdmin]
);

useEffect(() => {
const defaultUserRoles = [
...(userRoles?.map((role) => role.id) ?? []),
...(isUserAdmin ? [toLower(TERM_ADMIN)] : []),
];
const handleCloseEditRole = useCallback(() => {
setIsRolesEdit(false);
setUserRoles();
}, [setUserRoles]);

setSelectedRoles(defaultUserRoles);
}, [isUserAdmin, userRoles]);
useEffect(() => {
setUserRoles();
}, [setUserRoles]);

useEffect(() => {
if (isRolesEdit && isEmpty(roles)) {
Expand Down Expand Up @@ -176,7 +187,7 @@ const UserProfileRoles = ({
<InlineEdit
direction="vertical"
isLoading={isLoading}
onCancel={() => setIsRolesEdit(false)}
onCancel={handleCloseEditRole}
onSave={handleRolesSave}>
<Select
allowClear
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,18 @@
* limitations under the License.
*/

import { act, fireEvent, render, screen } from '@testing-library/react';
import {
act,
findByRole,
fireEvent,
render,
screen,
waitForElement,
} from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React from 'react';
import { useAuth } from '../../../../../hooks/authHooks';
import { MOCK_USER_ROLE } from '../../../../../mocks/User.mock';
import { getRoles } from '../../../../../rest/rolesAPIV1';
import { mockUserRole } from '../../mocks/User.mocks';
import UserProfileRoles from './UserProfileRoles.component';
Expand All @@ -29,12 +38,16 @@ jest.mock('../../../../../hooks/authHooks', () => ({
}));

jest.mock('../../../../common/InlineEdit/InlineEdit.component', () => {
return jest.fn().mockImplementation(({ onSave }) => (
return jest.fn().mockImplementation(({ children, onCancel, onSave }) => (
<div data-testid="inline-edit">
<span>InlineEdit</span>
{children}
<button data-testid="save" onClick={onSave}>
save
</button>
<button data-testid="cancel" onClick={onCancel}>
cancel
</button>
</div>
));
});
Expand Down Expand Up @@ -147,4 +160,52 @@ describe('Test User Profile Roles Component', () => {

expect(screen.getByText('InlineEdit')).toBeInTheDocument();
});

it('should maintain initial state if edit is close without save', async () => {
(useAuth as jest.Mock).mockImplementation(() => ({
isAdminUser: true,
}));

render(
<UserProfileRoles
{...mockPropsData}
userRoles={MOCK_USER_ROLE.slice(0, 2)}
/>
);

fireEvent.click(screen.getByTestId('edit-roles-button'));

const selectInput = await findByRole(
screen.getByTestId('select-user-roles'),
'combobox'
);

await act(async () => {
userEvent.click(selectInput);
});

// wait for list to render, checked with item having in the list
await waitForElement(() => screen.findByText('admin'));

await act(async () => {
userEvent.click(screen.getByText('admin'));
});

fireEvent.click(screen.getByTestId('cancel'));

fireEvent.click(screen.getByTestId('edit-roles-button'));

await act(async () => {
userEvent.click(selectInput);
});

expect(
screen.getByText('37a00e0b-383c-4451-b63f-0bad4c745abc')
).toBeInTheDocument();
expect(
screen.getByText('afc5583c-e268-4f6c-a638-a876d04ebaa1')
).toBeInTheDocument();

expect(screen.queryByText('admin')).not.toBeInTheDocument();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,15 @@
*/

import { Card, Space, Tooltip, Typography } from 'antd';
import React, { useEffect, useMemo, useState } from 'react';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { ReactComponent as EditIcon } from '../../../../../assets/svg/edit-new.svg';
import { ReactComponent as IconTeamsGrey } from '../../../../../assets/svg/teams-grey.svg';
import {
DE_ACTIVE_COLOR,
ICON_DIMENSION,
} from '../../../../../constants/constants';
import { EntityType } from '../../../../../enums/entity.enum';
import { EntityReference } from '../../../../../generated/entity/type';
import { useAuth } from '../../../../../hooks/authHooks';
import { getNonDeletedTeams } from '../../../../../utils/CommonUtils';
Expand Down Expand Up @@ -55,17 +56,27 @@ const UserProfileTeams = ({
() => (
<Chip
data={getNonDeletedTeams(teams ?? [])}
entityType={EntityType.TEAM}
icon={<IconTeamsGrey height={20} />}
noDataPlaceholder={t('message.no-team-found')}
/>
),
[teams, getNonDeletedTeams]
);

useEffect(() => {
const setUserTeams = useCallback(() => {
setSelectedTeams(getNonDeletedTeams(teams ?? []));
}, [teams]);

const handleCloseEditTeam = useCallback(() => {
setIsTeamsEdit(false);
setUserTeams();
}, [setUserTeams]);

useEffect(() => {
setUserTeams();
}, [setUserTeams]);

return (
<Card
className="relative card-body-border-none card-padding-y-0"
Expand Down Expand Up @@ -97,7 +108,7 @@ const UserProfileTeams = ({
<InlineEdit
direction="vertical"
isLoading={isLoading}
onCancel={() => setIsTeamsEdit(false)}
onCancel={handleCloseEditTeam}
onSave={handleTeamsSave}>
<TeamsSelectable
filterJoinable
Expand Down
Loading
Loading