mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-06-21 02:23:12 +08:00
feat: my settings page tests (#5499)
* feat: my settings page tests * chore: improve mysettings test names * chore: remove commented code and console.log * chore: add missing parentheses
This commit is contained in:
parent
6781c29082
commit
fe398bcc49
@ -90,18 +90,23 @@ function PasswordContainer(): JSX.Element {
|
|||||||
return (
|
return (
|
||||||
<Card>
|
<Card>
|
||||||
<Space direction="vertical" size="small">
|
<Space direction="vertical" size="small">
|
||||||
<Typography.Title level={4} style={{ marginTop: 0 }}>
|
<Typography.Title
|
||||||
|
level={4}
|
||||||
|
style={{ marginTop: 0 }}
|
||||||
|
data-testid="change-password-header"
|
||||||
|
>
|
||||||
{t('change_password', {
|
{t('change_password', {
|
||||||
ns: 'settings',
|
ns: 'settings',
|
||||||
})}
|
})}
|
||||||
</Typography.Title>
|
</Typography.Title>
|
||||||
<Space direction="vertical">
|
<Space direction="vertical">
|
||||||
<Typography>
|
<Typography data-testid="current-password-label">
|
||||||
{t('current_password', {
|
{t('current_password', {
|
||||||
ns: 'settings',
|
ns: 'settings',
|
||||||
})}
|
})}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Password
|
<Password
|
||||||
|
data-testid="current-password-textbox"
|
||||||
disabled={isLoading}
|
disabled={isLoading}
|
||||||
placeholder={defaultPlaceHolder}
|
placeholder={defaultPlaceHolder}
|
||||||
onChange={(event): void => {
|
onChange={(event): void => {
|
||||||
@ -111,12 +116,13 @@ function PasswordContainer(): JSX.Element {
|
|||||||
/>
|
/>
|
||||||
</Space>
|
</Space>
|
||||||
<Space direction="vertical">
|
<Space direction="vertical">
|
||||||
<Typography>
|
<Typography data-testid="new-password-label">
|
||||||
{t('new_password', {
|
{t('new_password', {
|
||||||
ns: 'settings',
|
ns: 'settings',
|
||||||
})}
|
})}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Password
|
<Password
|
||||||
|
data-testid="new-password-textbox"
|
||||||
disabled={isLoading}
|
disabled={isLoading}
|
||||||
placeholder={defaultPlaceHolder}
|
placeholder={defaultPlaceHolder}
|
||||||
onChange={(event): void => {
|
onChange={(event): void => {
|
||||||
@ -129,6 +135,7 @@ function PasswordContainer(): JSX.Element {
|
|||||||
<Space>
|
<Space>
|
||||||
{isPasswordPolicyError && (
|
{isPasswordPolicyError && (
|
||||||
<Typography.Paragraph
|
<Typography.Paragraph
|
||||||
|
data-testid="validation-message"
|
||||||
style={{
|
style={{
|
||||||
color: '#D89614',
|
color: '#D89614',
|
||||||
marginTop: '0.50rem',
|
marginTop: '0.50rem',
|
||||||
@ -143,8 +150,13 @@ function PasswordContainer(): JSX.Element {
|
|||||||
loading={isLoading}
|
loading={isLoading}
|
||||||
onClick={onChangePasswordClickHandler}
|
onClick={onChangePasswordClickHandler}
|
||||||
type="primary"
|
type="primary"
|
||||||
|
data-testid="update-password-button"
|
||||||
>
|
>
|
||||||
<Save size={12} style={{ marginRight: '8px' }} />{' '}
|
<Save
|
||||||
|
size={12}
|
||||||
|
style={{ marginRight: '8px' }}
|
||||||
|
data-testid="update-password-icon"
|
||||||
|
/>{' '}
|
||||||
{t('change_password', {
|
{t('change_password', {
|
||||||
ns: 'settings',
|
ns: 'settings',
|
||||||
})}
|
})}
|
||||||
|
@ -86,8 +86,11 @@ function UserInfo(): JSX.Element {
|
|||||||
|
|
||||||
<Flex gap={16}>
|
<Flex gap={16}>
|
||||||
<Space>
|
<Space>
|
||||||
<Typography className="userInfo-label">Name</Typography>
|
<Typography className="userInfo-label" data-testid="name-label">
|
||||||
|
Name
|
||||||
|
</Typography>
|
||||||
<NameInput
|
<NameInput
|
||||||
|
data-testid="name-textbox"
|
||||||
placeholder="Your Name"
|
placeholder="Your Name"
|
||||||
onChange={(event): void => {
|
onChange={(event): void => {
|
||||||
setChangedName(event.target.value);
|
setChangedName(event.target.value);
|
||||||
@ -102,6 +105,7 @@ function UserInfo(): JSX.Element {
|
|||||||
loading={loading}
|
loading={loading}
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
onClick={onClickUpdateHandler}
|
onClick={onClickUpdateHandler}
|
||||||
|
data-testid="update-name-button"
|
||||||
type="primary"
|
type="primary"
|
||||||
>
|
>
|
||||||
<PencilIcon size={12} /> Update
|
<PencilIcon size={12} /> Update
|
||||||
@ -109,13 +113,29 @@ function UserInfo(): JSX.Element {
|
|||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
<Space>
|
<Space>
|
||||||
<Typography className="userInfo-label"> Email </Typography>
|
<Typography className="userInfo-label" data-testid="email-label">
|
||||||
<Input className="userInfo-value" value={user.email} disabled />
|
{' '}
|
||||||
|
Email{' '}
|
||||||
|
</Typography>
|
||||||
|
<Input
|
||||||
|
className="userInfo-value"
|
||||||
|
data-testid="email-textbox"
|
||||||
|
value={user.email}
|
||||||
|
disabled
|
||||||
|
/>
|
||||||
</Space>
|
</Space>
|
||||||
|
|
||||||
<Space>
|
<Space>
|
||||||
<Typography className="userInfo-label"> Role </Typography>
|
<Typography className="userInfo-label" data-testid="role-label">
|
||||||
<Input className="userInfo-value" value={role || ''} disabled />
|
{' '}
|
||||||
|
Role{' '}
|
||||||
|
</Typography>
|
||||||
|
<Input
|
||||||
|
className="userInfo-value"
|
||||||
|
value={role || ''}
|
||||||
|
disabled
|
||||||
|
data-testid="role-textbox"
|
||||||
|
/>
|
||||||
</Space>
|
</Space>
|
||||||
</Space>
|
</Space>
|
||||||
</Card>
|
</Card>
|
||||||
|
219
frontend/src/container/MySettings/__tests__/MySettings.test.tsx
Normal file
219
frontend/src/container/MySettings/__tests__/MySettings.test.tsx
Normal file
@ -0,0 +1,219 @@
|
|||||||
|
import MySettingsContainer from 'container/MySettings';
|
||||||
|
import { act, fireEvent, render, screen, waitFor } from 'tests/test-utils';
|
||||||
|
|
||||||
|
const toggleThemeFunction = jest.fn();
|
||||||
|
|
||||||
|
jest.mock('hooks/useDarkMode', () => ({
|
||||||
|
__esModule: true,
|
||||||
|
useIsDarkMode: jest.fn(() => ({
|
||||||
|
toggleTheme: toggleThemeFunction,
|
||||||
|
})),
|
||||||
|
default: jest.fn(() => ({
|
||||||
|
toggleTheme: toggleThemeFunction,
|
||||||
|
})),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const errorNotification = jest.fn();
|
||||||
|
const successNotification = jest.fn();
|
||||||
|
jest.mock('hooks/useNotifications', () => ({
|
||||||
|
__esModule: true,
|
||||||
|
useNotifications: jest.fn(() => ({
|
||||||
|
notifications: {
|
||||||
|
error: errorNotification,
|
||||||
|
success: successNotification,
|
||||||
|
},
|
||||||
|
})),
|
||||||
|
}));
|
||||||
|
|
||||||
|
enum ThemeOptions {
|
||||||
|
Dark = 'Dark',
|
||||||
|
Light = 'Light',
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('MySettings Flows', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
|
||||||
|
render(<MySettingsContainer />);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Dark/Light Theme Switch', () => {
|
||||||
|
it('Should display Dark and Light theme buttons properly', async () => {
|
||||||
|
expect(screen.getByText('Dark')).toBeInTheDocument();
|
||||||
|
|
||||||
|
const darkThemeIcon = screen.getByTestId('dark-theme-icon');
|
||||||
|
expect(darkThemeIcon).toBeInTheDocument();
|
||||||
|
expect(darkThemeIcon.tagName).toBe('svg');
|
||||||
|
|
||||||
|
expect(screen.getByText('Light')).toBeInTheDocument();
|
||||||
|
const lightThemeIcon = screen.getByTestId('light-theme-icon');
|
||||||
|
expect(lightThemeIcon).toBeInTheDocument();
|
||||||
|
expect(lightThemeIcon.tagName).toBe('svg');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should activate Dark and Light buttons on click', async () => {
|
||||||
|
const initialSelectedOption = screen.getByRole('radio', {
|
||||||
|
name: ThemeOptions.Dark,
|
||||||
|
});
|
||||||
|
expect(initialSelectedOption).toBeChecked();
|
||||||
|
|
||||||
|
const newThemeOption = screen.getByRole('radio', {
|
||||||
|
name: ThemeOptions.Light,
|
||||||
|
});
|
||||||
|
fireEvent.click(newThemeOption);
|
||||||
|
|
||||||
|
expect(newThemeOption).toBeChecked();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should switch the them on clicking Light theme', async () => {
|
||||||
|
const lightThemeOption = screen.getByRole('radio', {
|
||||||
|
name: /light/i,
|
||||||
|
});
|
||||||
|
fireEvent.click(lightThemeOption);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(toggleThemeFunction).toBeCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('User Details Form', () => {
|
||||||
|
it('Should properly display the User Details Form', () => {
|
||||||
|
const userDetailsHeader = screen.getByRole('heading', {
|
||||||
|
name: /user details/i,
|
||||||
|
});
|
||||||
|
const nameLabel = screen.getByTestId('name-label');
|
||||||
|
const nameTextbox = screen.getByTestId('name-textbox');
|
||||||
|
const updateNameButton = screen.getByTestId('update-name-button');
|
||||||
|
const emailLabel = screen.getByTestId('email-label');
|
||||||
|
const emailTextbox = screen.getByTestId('email-textbox');
|
||||||
|
const roleLabel = screen.getByTestId('role-label');
|
||||||
|
const roleTextbox = screen.getByTestId('role-textbox');
|
||||||
|
|
||||||
|
expect(userDetailsHeader).toBeInTheDocument();
|
||||||
|
expect(nameLabel).toBeInTheDocument();
|
||||||
|
expect(nameTextbox).toBeInTheDocument();
|
||||||
|
expect(updateNameButton).toBeInTheDocument();
|
||||||
|
expect(emailLabel).toBeInTheDocument();
|
||||||
|
expect(emailTextbox).toBeInTheDocument();
|
||||||
|
expect(roleLabel).toBeInTheDocument();
|
||||||
|
expect(roleTextbox).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should update the name on clicking Update button', async () => {
|
||||||
|
const nameTextbox = screen.getByTestId('name-textbox');
|
||||||
|
const updateNameButton = screen.getByTestId('update-name-button');
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
fireEvent.change(nameTextbox, { target: { value: 'New Name' } });
|
||||||
|
});
|
||||||
|
|
||||||
|
fireEvent.click(updateNameButton);
|
||||||
|
|
||||||
|
await waitFor(() =>
|
||||||
|
expect(successNotification).toHaveBeenCalledWith({
|
||||||
|
message: 'success',
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Reset password', () => {
|
||||||
|
let currentPasswordTextbox: Node | Window;
|
||||||
|
let newPasswordTextbox: Node | Window;
|
||||||
|
let submitButtonElement: HTMLElement;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
currentPasswordTextbox = screen.getByTestId('current-password-textbox');
|
||||||
|
newPasswordTextbox = screen.getByTestId('new-password-textbox');
|
||||||
|
submitButtonElement = screen.getByTestId('update-password-button');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should properly display the Password Reset Form', () => {
|
||||||
|
const passwordResetHeader = screen.getByTestId('change-password-header');
|
||||||
|
expect(passwordResetHeader).toBeInTheDocument();
|
||||||
|
|
||||||
|
const currentPasswordLabel = screen.getByTestId('current-password-label');
|
||||||
|
expect(currentPasswordLabel).toBeInTheDocument();
|
||||||
|
|
||||||
|
expect(currentPasswordTextbox).toBeInTheDocument();
|
||||||
|
|
||||||
|
const newPasswordLabel = screen.getByTestId('new-password-label');
|
||||||
|
expect(newPasswordLabel).toBeInTheDocument();
|
||||||
|
|
||||||
|
expect(newPasswordTextbox).toBeInTheDocument();
|
||||||
|
expect(submitButtonElement).toBeInTheDocument();
|
||||||
|
|
||||||
|
const savePasswordIcon = screen.getByTestId('update-password-icon');
|
||||||
|
expect(savePasswordIcon).toBeInTheDocument();
|
||||||
|
expect(savePasswordIcon.tagName).toBe('svg');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should display validation error if password is less than 8 characters', async () => {
|
||||||
|
const currentPasswordTextbox = screen.getByTestId(
|
||||||
|
'current-password-textbox',
|
||||||
|
);
|
||||||
|
act(() => {
|
||||||
|
fireEvent.change(currentPasswordTextbox, { target: { value: '123' } });
|
||||||
|
});
|
||||||
|
const validationMessage = await screen.findByTestId('validation-message');
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(validationMessage).toHaveTextContent(
|
||||||
|
'Password must a have minimum of 8 characters',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("Should display 'inavlid credentials' error if different current and new passwords are provided", async () => {
|
||||||
|
act(() => {
|
||||||
|
fireEvent.change(currentPasswordTextbox, {
|
||||||
|
target: { value: '123456879' },
|
||||||
|
});
|
||||||
|
|
||||||
|
fireEvent.change(newPasswordTextbox, { target: { value: '123456789' } });
|
||||||
|
});
|
||||||
|
|
||||||
|
fireEvent.click(submitButtonElement);
|
||||||
|
|
||||||
|
await waitFor(() => expect(errorNotification).toHaveBeenCalled());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should check if the "Change Password" button is disabled in case current / new password is less than 8 characters', () => {
|
||||||
|
act(() => {
|
||||||
|
fireEvent.change(currentPasswordTextbox, {
|
||||||
|
target: { value: '123' },
|
||||||
|
});
|
||||||
|
fireEvent.change(newPasswordTextbox, { target: { value: '123' } });
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(submitButtonElement).toBeDisabled();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("Should check if 'Change Password' button is enabled when password is at least 8 characters ", async () => {
|
||||||
|
expect(submitButtonElement).toBeDisabled();
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
fireEvent.change(currentPasswordTextbox, {
|
||||||
|
target: { value: '123456789' },
|
||||||
|
});
|
||||||
|
fireEvent.change(newPasswordTextbox, { target: { value: '1234567890' } });
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(submitButtonElement).toBeEnabled();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("Should check if 'Change Password' button is disabled when current and new passwords are the same ", async () => {
|
||||||
|
expect(submitButtonElement).toBeDisabled();
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
fireEvent.change(currentPasswordTextbox, {
|
||||||
|
target: { value: '123456789' },
|
||||||
|
});
|
||||||
|
fireEvent.change(newPasswordTextbox, { target: { value: '123456789' } });
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(submitButtonElement).toBeDisabled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -17,7 +17,7 @@ function MySettings(): JSX.Element {
|
|||||||
{
|
{
|
||||||
label: (
|
label: (
|
||||||
<div className="theme-option">
|
<div className="theme-option">
|
||||||
<Moon size={12} /> Dark{' '}
|
<Moon data-testid="dark-theme-icon" size={12} /> Dark{' '}
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
value: 'dark',
|
value: 'dark',
|
||||||
@ -25,7 +25,7 @@ function MySettings(): JSX.Element {
|
|||||||
{
|
{
|
||||||
label: (
|
label: (
|
||||||
<div className="theme-option">
|
<div className="theme-option">
|
||||||
<Sun size={12} /> Light{' '}
|
<Sun size={12} data-testid="light-theme-icon" /> Light{' '}
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
value: 'light',
|
value: 'light',
|
||||||
@ -63,6 +63,7 @@ function MySettings(): JSX.Element {
|
|||||||
value={theme}
|
value={theme}
|
||||||
optionType="button"
|
optionType="button"
|
||||||
buttonStyle="solid"
|
buttonStyle="solid"
|
||||||
|
data-testid="theme-selector"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -74,7 +75,12 @@ function MySettings(): JSX.Element {
|
|||||||
<Password />
|
<Password />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Button className="flexBtn" onClick={(): void => Logout()} type="primary">
|
<Button
|
||||||
|
className="flexBtn"
|
||||||
|
onClick={(): void => Logout()}
|
||||||
|
type="primary"
|
||||||
|
data-testid="logout-button"
|
||||||
|
>
|
||||||
<LogOut size={12} /> Logout
|
<LogOut size={12} /> Logout
|
||||||
</Button>
|
</Button>
|
||||||
</Space>
|
</Space>
|
||||||
|
@ -155,6 +155,24 @@ export const handlers = [
|
|||||||
rest.post('http://localhost/api/v1/invite', (_, res, ctx) =>
|
rest.post('http://localhost/api/v1/invite', (_, res, ctx) =>
|
||||||
res(ctx.status(200), ctx.json(inviteUser)),
|
res(ctx.status(200), ctx.json(inviteUser)),
|
||||||
),
|
),
|
||||||
|
rest.put('http://localhost/api/v1/user/:id', (_, res, ctx) =>
|
||||||
|
res(
|
||||||
|
ctx.status(200),
|
||||||
|
ctx.json({
|
||||||
|
data: 'user updated successfully',
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
rest.post('http://localhost/api/v1/changePassword', (_, res, ctx) =>
|
||||||
|
res(
|
||||||
|
ctx.status(403),
|
||||||
|
ctx.json({
|
||||||
|
status: 'error',
|
||||||
|
errorType: 'forbidden',
|
||||||
|
error: 'invalid credentials',
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
rest.get(
|
rest.get(
|
||||||
'http://localhost/api/v3/autocomplete/aggregate_attributes',
|
'http://localhost/api/v3/autocomplete/aggregate_attributes',
|
||||||
|
Loading…
x
Reference in New Issue
Block a user