Public Access
feat(password): add reset and change password pages
/reset-password page for the email request and the new password, ChangePasswordCard in settings, password reset button on the admin users page, 'Forgot password?' link on the login page, de/en texts.
This commit is contained in:
1 parent
f71e8f7aff
commit
6627a8db66
19 files changed
+565
-25
No files matched your search
@@ -0,0 +1,92 @@
|
||||
// @vitest-environment jsdom
|
||||
import {afterEach, describe, expect, it, vi} from 'vitest';
|
||||
import {cleanup, fireEvent, render, screen} from '@testing-library/react';
|
||||
import {MemoryRouter} from 'react-router-dom';
|
||||
import ResetPasswordPage from './ResetPasswordPage.jsx';
|
||||
import {fetchRoutes, json} from '../test/fetchRoutes.js';
|
||||
|
||||
vi.mock('react-i18next', () => import('../test/i18n-mock.js'));
|
||||
|
||||
const renderPage = (hash = '') => render(
|
||||
<MemoryRouter initialEntries={[`/reset-password${hash}`]}><ResetPasswordPage/></MemoryRouter>,
|
||||
);
|
||||
|
||||
function submitPasswords(password, repeat = password) {
|
||||
fireEvent.change(screen.getByLabelText('REGISTRATION.PASSWORD'), {target: {value: password}});
|
||||
fireEvent.change(screen.getByLabelText('REGISTRATION.PASSWORD_CONFIRMATION'), {target: {value: repeat}});
|
||||
fireEvent.click(screen.getByRole('button', {name: 'RESET_PASSWORD.SUBMIT'}));
|
||||
}
|
||||
|
||||
function submitEmail(email = 'a@b.de') {
|
||||
fireEvent.change(screen.getByLabelText('RESET_PASSWORD.EMAIL'), {target: {value: email}});
|
||||
fireEvent.click(screen.getByRole('button', {name: 'RESET_PASSWORD.SEND'}));
|
||||
}
|
||||
|
||||
describe('ResetPasswordPage', () => {
|
||||
afterEach(cleanup);
|
||||
|
||||
it('without a fragment asks for the email and shows the same text for any address', async () => {
|
||||
const mock = fetchRoutes({'POST /api/auth/password-reset': json(204, null)});
|
||||
renderPage();
|
||||
|
||||
submitEmail();
|
||||
|
||||
expect(await screen.findByText('RESET_PASSWORD.SENT')).toBeTruthy();
|
||||
expect(JSON.parse(mock.mock.calls[0][1].body)).toEqual({email: 'a@b.de'});
|
||||
});
|
||||
|
||||
it('shows the lock text with the minutes on a 429', async () => {
|
||||
fetchRoutes({'POST /api/auth/password-reset': {...json(429, {}), headers: new Headers({'Retry-After': '900'})}});
|
||||
renderPage();
|
||||
|
||||
submitEmail();
|
||||
|
||||
expect(await screen.findByText('RESET_PASSWORD.ERROR_TOO_MANY')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('with a fragment sends the token and the new password only on the click and then offers the login', async () => {
|
||||
const mock = fetchRoutes({'POST /api/auth/password-reset/confirm': json(204, null)});
|
||||
renderPage('#tok-123');
|
||||
|
||||
expect(mock).not.toHaveBeenCalled();
|
||||
submitPasswords('secret123');
|
||||
|
||||
expect(await screen.findByText('RESET_PASSWORD.DONE')).toBeTruthy();
|
||||
expect(JSON.parse(mock.mock.calls[0][1].body)).toEqual({token: 'tok-123', password: 'secret123'});
|
||||
expect(screen.getByRole('link', {name: 'RESET_PASSWORD.TO_LOGIN'}).getAttribute('href')).toBe('/login');
|
||||
});
|
||||
|
||||
it.each([
|
||||
['a short password', 'short', 'short', 'VALIDATION.PASSWORD_TOO_SHORT'],
|
||||
['a different repeat', 'secret123', 'secret124', 'VALIDATION.PASSWORD_MISMATCH'],
|
||||
])('sends nothing for %s', async (_name, password, repeat, message) => {
|
||||
const mock = fetchRoutes({});
|
||||
renderPage('#tok-123');
|
||||
|
||||
submitPasswords(password, repeat);
|
||||
|
||||
expect(await screen.findByText(message)).toBeTruthy();
|
||||
expect(mock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('shows the invalid text and the email form again for a used or expired link', async () => {
|
||||
fetchRoutes({'POST /api/auth/password-reset/confirm': json(410, {code: 'RESET_INVALID'})});
|
||||
renderPage('#old');
|
||||
|
||||
submitPasswords('secret123');
|
||||
|
||||
expect(await screen.findByText('RESET_PASSWORD.INVALID')).toBeTruthy();
|
||||
expect(screen.getByRole('button', {name: 'RESET_PASSWORD.SEND'})).toBeTruthy();
|
||||
});
|
||||
|
||||
it('shows the lock text with the minutes when the confirm is limited', async () => {
|
||||
fetchRoutes({
|
||||
'POST /api/auth/password-reset/confirm': {...json(429, {}), headers: new Headers({'Retry-After': '900'})},
|
||||
});
|
||||
renderPage('#tok');
|
||||
|
||||
submitPasswords('secret123');
|
||||
|
||||
expect(await screen.findByText('RESET_PASSWORD.ERROR_TOO_MANY')).toBeTruthy();
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user