mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Chore: Add tests for ChangePasswordPage and SendResetMailPage (#36313)
* added tests for changePassword and forgotPassword component * added tests for ChangePassword screen in user profile section * addressed review changes
This commit is contained in:
parent
3cce67c044
commit
36592e0927
@ -23,14 +23,16 @@ export const ChangePassword: FC<Props> = ({ onSubmit, onSkip }) => {
|
|||||||
<Field label="New password" invalid={!!errors.newPassword} error={errors?.newPassword?.message}>
|
<Field label="New password" invalid={!!errors.newPassword} error={errors?.newPassword?.message}>
|
||||||
<Input
|
<Input
|
||||||
autoFocus
|
autoFocus
|
||||||
|
id="new-password"
|
||||||
type="password"
|
type="password"
|
||||||
{...register('newPassword', {
|
{...register('newPassword', {
|
||||||
required: 'New password required',
|
required: 'New password is required',
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
</Field>
|
</Field>
|
||||||
<Field label="Confirm new password" invalid={!!errors.confirmNew} error={errors?.confirmNew?.message}>
|
<Field label="Confirm new password" invalid={!!errors.confirmNew} error={errors?.confirmNew?.message}>
|
||||||
<Input
|
<Input
|
||||||
|
id="confirm-new-password"
|
||||||
type="password"
|
type="password"
|
||||||
{...register('confirmNew', {
|
{...register('confirmNew', {
|
||||||
required: 'Confirmed password is required',
|
required: 'Confirmed password is required',
|
||||||
|
@ -0,0 +1,84 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { act, fireEvent, render, screen, waitFor } from '@testing-library/react';
|
||||||
|
import userEvent from '@testing-library/user-event';
|
||||||
|
import { getRouteComponentProps } from 'app/core/navigation/__mocks__/routeProps';
|
||||||
|
|
||||||
|
import { ChangePasswordPage, Props } from './ChangePasswordPage';
|
||||||
|
|
||||||
|
const postMock = jest.fn();
|
||||||
|
jest.mock('@grafana/runtime', () => ({
|
||||||
|
getBackendSrv: () => ({
|
||||||
|
post: postMock,
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.mock('app/core/config', () => {
|
||||||
|
return {
|
||||||
|
loginError: false,
|
||||||
|
buildInfo: {
|
||||||
|
version: 'v1.0',
|
||||||
|
commit: '1',
|
||||||
|
env: 'production',
|
||||||
|
edition: 'Open Source',
|
||||||
|
isEnterprise: false,
|
||||||
|
},
|
||||||
|
licenseInfo: {
|
||||||
|
stateInfo: '',
|
||||||
|
licenseUrl: '',
|
||||||
|
},
|
||||||
|
appSubUrl: '',
|
||||||
|
};
|
||||||
|
});
|
||||||
|
const props: Props = {
|
||||||
|
...getRouteComponentProps({
|
||||||
|
queryParams: { code: 'some code' },
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('ChangePassword Page', () => {
|
||||||
|
it('renders correctly', () => {
|
||||||
|
render(<ChangePasswordPage {...props} />);
|
||||||
|
|
||||||
|
expect(screen.getByLabelText('New password')).toBeInTheDocument();
|
||||||
|
expect(screen.getByLabelText('Confirm new password')).toBeInTheDocument();
|
||||||
|
|
||||||
|
expect(screen.getByRole('button', { name: 'Submit' })).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
it('should pass validation checks for password and confirm password field', async () => {
|
||||||
|
render(<ChangePasswordPage {...props} />);
|
||||||
|
|
||||||
|
fireEvent.click(screen.getByRole('button', { name: 'Submit' }));
|
||||||
|
expect(await screen.findByText('New password is required')).toBeInTheDocument();
|
||||||
|
expect(screen.getByText('Confirmed password is required')).toBeInTheDocument();
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
await userEvent.type(screen.getByLabelText('New password'), 'admin');
|
||||||
|
await userEvent.type(screen.getByLabelText('Confirm new password'), 'a');
|
||||||
|
expect(screen.getByText('Passwords must match!')).toBeInTheDocument();
|
||||||
|
|
||||||
|
await userEvent.type(screen.getByLabelText('Confirm new password'), 'dmin');
|
||||||
|
expect(screen.queryByText('Passwords must match!')).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('should navigate to default url if change password is successful', async () => {
|
||||||
|
Object.defineProperty(window, 'location', {
|
||||||
|
value: {
|
||||||
|
assign: jest.fn(),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
postMock.mockResolvedValueOnce({ message: 'Logged in' });
|
||||||
|
render(<ChangePasswordPage {...props} />);
|
||||||
|
|
||||||
|
await userEvent.type(screen.getByLabelText('New password'), 'test');
|
||||||
|
await userEvent.type(screen.getByLabelText('Confirm new password'), 'test');
|
||||||
|
fireEvent.click(screen.getByRole('button', { name: 'Submit' }));
|
||||||
|
await waitFor(() =>
|
||||||
|
expect(postMock).toHaveBeenCalledWith('/api/user/password/reset', {
|
||||||
|
code: 'some code',
|
||||||
|
confirmPassword: 'test',
|
||||||
|
newPassword: 'test',
|
||||||
|
})
|
||||||
|
);
|
||||||
|
expect(window.location.assign).toHaveBeenCalledWith('/');
|
||||||
|
});
|
||||||
|
});
|
@ -4,7 +4,7 @@ import { ChangePassword } from './ChangePassword';
|
|||||||
import LoginCtrl from '../Login/LoginCtrl';
|
import LoginCtrl from '../Login/LoginCtrl';
|
||||||
import { GrafanaRouteComponentProps } from 'app/core/navigation/types';
|
import { GrafanaRouteComponentProps } from 'app/core/navigation/types';
|
||||||
|
|
||||||
interface Props extends GrafanaRouteComponentProps<{}, { code: string }> {}
|
export interface Props extends GrafanaRouteComponentProps<{}, { code: string }> {}
|
||||||
|
|
||||||
export const ChangePasswordPage: FC<Props> = (props) => {
|
export const ChangePasswordPage: FC<Props> = (props) => {
|
||||||
return (
|
return (
|
||||||
|
@ -51,7 +51,11 @@ export const ForgottenPassword: FC = () => {
|
|||||||
invalid={!!errors.userOrEmail}
|
invalid={!!errors.userOrEmail}
|
||||||
error={errors?.userOrEmail?.message}
|
error={errors?.userOrEmail?.message}
|
||||||
>
|
>
|
||||||
<Input placeholder="Email or username" {...register('userOrEmail', { required: true })} />
|
<Input
|
||||||
|
id="user-input"
|
||||||
|
placeholder="Email or username"
|
||||||
|
{...register('userOrEmail', { required: 'Email or username is required' })}
|
||||||
|
/>
|
||||||
</Field>
|
</Field>
|
||||||
<HorizontalGroup>
|
<HorizontalGroup>
|
||||||
<Button>Send reset email</Button>
|
<Button>Send reset email</Button>
|
||||||
|
@ -0,0 +1,68 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { act, fireEvent, render, screen, waitFor } from '@testing-library/react';
|
||||||
|
import userEvent from '@testing-library/user-event';
|
||||||
|
|
||||||
|
import { SendResetMailPage } from './SendResetMailPage';
|
||||||
|
|
||||||
|
const postMock = jest.fn();
|
||||||
|
jest.mock('@grafana/runtime', () => ({
|
||||||
|
getBackendSrv: () => ({
|
||||||
|
post: postMock,
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.mock('app/core/config', () => {
|
||||||
|
return {
|
||||||
|
buildInfo: {
|
||||||
|
version: 'v1.0',
|
||||||
|
commit: '1',
|
||||||
|
env: 'production',
|
||||||
|
edition: 'Open Source',
|
||||||
|
isEnterprise: false,
|
||||||
|
},
|
||||||
|
licenseInfo: {
|
||||||
|
stateInfo: '',
|
||||||
|
licenseUrl: '',
|
||||||
|
},
|
||||||
|
appSubUrl: '',
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('VerifyEmail Page', () => {
|
||||||
|
it('renders correctly', () => {
|
||||||
|
render(<SendResetMailPage />);
|
||||||
|
expect(screen.getByText('Reset password')).toBeInTheDocument();
|
||||||
|
expect(screen.getByRole('textbox', { name: /User Enter your information/i })).toBeInTheDocument();
|
||||||
|
|
||||||
|
expect(screen.getByRole('button', { name: 'Send reset email' })).toBeInTheDocument();
|
||||||
|
|
||||||
|
expect(screen.getByRole('link', { name: 'Back to login' })).toBeInTheDocument();
|
||||||
|
expect(screen.getByRole('link', { name: 'Back to login' })).toHaveAttribute('href', '/login');
|
||||||
|
});
|
||||||
|
it('should pass validation checks for email field', async () => {
|
||||||
|
render(<SendResetMailPage />);
|
||||||
|
|
||||||
|
fireEvent.click(screen.getByRole('button', { name: 'Send reset email' }));
|
||||||
|
expect(await screen.findByText('Email or username is required')).toBeInTheDocument();
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
await userEvent.type(screen.getByRole('textbox', { name: /User Enter your information/i }), 'test@gmail.com');
|
||||||
|
expect(screen.queryByText('Email is invalid')).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('should show success meessage if reset-password is successful', async () => {
|
||||||
|
postMock.mockResolvedValueOnce({ message: 'Email sent' });
|
||||||
|
render(<SendResetMailPage />);
|
||||||
|
|
||||||
|
await userEvent.type(screen.getByRole('textbox', { name: /User Enter your information/i }), 'test@gmail.com');
|
||||||
|
fireEvent.click(screen.getByRole('button', { name: 'Send reset email' }));
|
||||||
|
await waitFor(() =>
|
||||||
|
expect(postMock).toHaveBeenCalledWith('/api/user/password/send-reset-email', {
|
||||||
|
userOrEmail: 'test@gmail.com',
|
||||||
|
})
|
||||||
|
);
|
||||||
|
expect(screen.getByText(/An email with a reset link/i)).toBeInTheDocument();
|
||||||
|
expect(screen.getByRole('link', { name: 'Back to login' })).toBeInTheDocument();
|
||||||
|
expect(screen.getByRole('link', { name: 'Back to login' })).toHaveAttribute('href', '/login');
|
||||||
|
});
|
||||||
|
});
|
@ -34,11 +34,16 @@ export const ChangePasswordForm: FC<Props> = ({ user, onChangePassword, isSaving
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Field label="Old password" invalid={!!errors.oldPassword} error={errors?.oldPassword?.message}>
|
<Field label="Old password" invalid={!!errors.oldPassword} error={errors?.oldPassword?.message}>
|
||||||
<Input type="password" {...register('oldPassword', { required: 'Old password is required' })} />
|
<Input
|
||||||
|
id="old-password"
|
||||||
|
type="password"
|
||||||
|
{...register('oldPassword', { required: 'Old password is required' })}
|
||||||
|
/>
|
||||||
</Field>
|
</Field>
|
||||||
|
|
||||||
<Field label="New password" invalid={!!errors.newPassword} error={errors?.newPassword?.message}>
|
<Field label="New password" invalid={!!errors.newPassword} error={errors?.newPassword?.message}>
|
||||||
<Input
|
<Input
|
||||||
|
id="new-password"
|
||||||
type="password"
|
type="password"
|
||||||
{...register('newPassword', {
|
{...register('newPassword', {
|
||||||
required: 'New password is required',
|
required: 'New password is required',
|
||||||
@ -52,6 +57,7 @@ export const ChangePasswordForm: FC<Props> = ({ user, onChangePassword, isSaving
|
|||||||
|
|
||||||
<Field label="Confirm password" invalid={!!errors.confirmNew} error={errors?.confirmNew?.message}>
|
<Field label="Confirm password" invalid={!!errors.confirmNew} error={errors?.confirmNew?.message}>
|
||||||
<Input
|
<Input
|
||||||
|
id="confirm-new-password"
|
||||||
type="password"
|
type="password"
|
||||||
{...register('confirmNew', {
|
{...register('confirmNew', {
|
||||||
required: 'New password confirmation is required',
|
required: 'New password confirmation is required',
|
||||||
|
122
public/app/features/profile/ChangePasswordPage.test.tsx
Normal file
122
public/app/features/profile/ChangePasswordPage.test.tsx
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
|
||||||
|
import userEvent from '@testing-library/user-event';
|
||||||
|
import config from 'app/core/config';
|
||||||
|
import { Props, ChangePasswordPage } from './ChangePasswordPage';
|
||||||
|
import { initialUserState } from './state/reducers';
|
||||||
|
import { getNavModel } from '../../core/selectors/navModel';
|
||||||
|
import { backendSrv } from '../../core/services/backend_srv';
|
||||||
|
|
||||||
|
const defaultProps: Props = {
|
||||||
|
...initialUserState,
|
||||||
|
user: {
|
||||||
|
id: 1,
|
||||||
|
name: 'Test User',
|
||||||
|
email: 'test@test.com',
|
||||||
|
login: 'test',
|
||||||
|
isDisabled: false,
|
||||||
|
isGrafanaAdmin: false,
|
||||||
|
orgId: 0,
|
||||||
|
authLabels: ['github'],
|
||||||
|
},
|
||||||
|
navModel: getNavModel(
|
||||||
|
{
|
||||||
|
'profile-settings': {
|
||||||
|
icon: 'sliders-v-alt',
|
||||||
|
id: 'profile-settings',
|
||||||
|
parentItem: {
|
||||||
|
id: 'profile',
|
||||||
|
text: 'Test User',
|
||||||
|
img: '/avatar/46d229b033af06a191ff2267bca9ae56',
|
||||||
|
url: '/profile',
|
||||||
|
},
|
||||||
|
text: 'Preferences',
|
||||||
|
url: '/profile',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'profile-settings'
|
||||||
|
),
|
||||||
|
loadUser: jest.fn(),
|
||||||
|
changePassword: jest.fn(),
|
||||||
|
};
|
||||||
|
|
||||||
|
async function getTestContext(overrides: Partial<Props> = {}) {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
jest.spyOn(backendSrv, 'get').mockResolvedValue({
|
||||||
|
id: 1,
|
||||||
|
name: 'Test User',
|
||||||
|
email: 'test@test.com',
|
||||||
|
login: 'test',
|
||||||
|
isDisabled: false,
|
||||||
|
isGrafanaAdmin: false,
|
||||||
|
orgId: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
const props = { ...defaultProps, ...overrides };
|
||||||
|
const { rerender } = render(<ChangePasswordPage {...props} />);
|
||||||
|
|
||||||
|
await waitFor(() => expect(props.loadUser).toHaveBeenCalledTimes(1));
|
||||||
|
|
||||||
|
return { rerender, props };
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('ChangePasswordPage', () => {
|
||||||
|
it('should show loading placeholder', async () => {
|
||||||
|
await getTestContext({ user: null });
|
||||||
|
|
||||||
|
expect(screen.getByText(/loading \.\.\./i)).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should show change password form when user has loaded', async () => {
|
||||||
|
await getTestContext();
|
||||||
|
expect(screen.getByText('Change Your Password')).toBeInTheDocument();
|
||||||
|
|
||||||
|
expect(screen.getByLabelText('Old password')).toBeInTheDocument();
|
||||||
|
expect(screen.getByLabelText('New password')).toBeInTheDocument();
|
||||||
|
expect(screen.getByLabelText('Confirm password')).toBeInTheDocument();
|
||||||
|
|
||||||
|
expect(screen.getByRole('button', { name: 'Change Password' })).toBeInTheDocument();
|
||||||
|
|
||||||
|
expect(screen.getByRole('link', { name: 'Cancel' })).toBeInTheDocument();
|
||||||
|
expect(screen.getByRole('link', { name: 'Cancel' })).toHaveAttribute('href', '/profile');
|
||||||
|
});
|
||||||
|
it('should call changePassword if change password is valid', async () => {
|
||||||
|
const { props } = await getTestContext();
|
||||||
|
|
||||||
|
await userEvent.type(screen.getByLabelText('Old password'), 'test');
|
||||||
|
await userEvent.type(screen.getByLabelText('New password'), 'admin');
|
||||||
|
await userEvent.type(screen.getByLabelText('Confirm password'), 'admin');
|
||||||
|
fireEvent.click(screen.getByRole('button', { name: 'Change Password' }));
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(props.changePassword).toHaveBeenCalledTimes(1);
|
||||||
|
expect(props.changePassword).toHaveBeenCalledWith(
|
||||||
|
{
|
||||||
|
confirmNew: 'admin',
|
||||||
|
newPassword: 'admin',
|
||||||
|
oldPassword: 'test',
|
||||||
|
},
|
||||||
|
expect.anything()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('should cannot change password form if ldap or authProxy enabled', async () => {
|
||||||
|
config.ldapEnabled = true;
|
||||||
|
const { rerender } = await getTestContext();
|
||||||
|
expect(
|
||||||
|
screen.getByText('You cannot change password when LDAP or auth proxy authentication is enabled.')
|
||||||
|
).toBeInTheDocument();
|
||||||
|
config.ldapEnabled = false;
|
||||||
|
config.authProxyEnabled = true;
|
||||||
|
rerender(<ChangePasswordPage {...defaultProps} />);
|
||||||
|
expect(
|
||||||
|
screen.getByText('You cannot change password when LDAP or auth proxy authentication is enabled.')
|
||||||
|
).toBeInTheDocument();
|
||||||
|
config.authProxyEnabled = false;
|
||||||
|
});
|
||||||
|
it('should show cannot change password if disableLoginForm is true and auth', async () => {
|
||||||
|
config.disableLoginForm = true;
|
||||||
|
await getTestContext();
|
||||||
|
expect(screen.getByText('Password cannot be changed here.')).toBeInTheDocument();
|
||||||
|
config.disableLoginForm = false;
|
||||||
|
});
|
||||||
|
});
|
@ -31,7 +31,7 @@ const mapDispatchToProps = {
|
|||||||
|
|
||||||
const connector = connect(mapStateToProps, mapDispatchToProps);
|
const connector = connect(mapStateToProps, mapDispatchToProps);
|
||||||
|
|
||||||
type Props = OwnProps & ConnectedProps<typeof connector>;
|
export type Props = OwnProps & ConnectedProps<typeof connector>;
|
||||||
|
|
||||||
export function ChangePasswordPage({ navModel, loadUser, isUpdating, user, changePassword }: Props) {
|
export function ChangePasswordPage({ navModel, loadUser, isUpdating, user, changePassword }: Props) {
|
||||||
useMount(() => loadUser());
|
useMount(() => loadUser());
|
||||||
|
Loading…
Reference in New Issue
Block a user