Public Access
feat(settings): let users delete their account
This commit is contained in:
1 parent
cfceca5e5e
commit
46587dfb5c
8 files changed
+374
-2
No files matched your search
@@ -0,0 +1,110 @@
|
||||
// @vitest-environment jsdom
|
||||
import {afterEach, beforeEach, describe, expect, it, vi} from 'vitest';
|
||||
import {cleanup, fireEvent, render, screen, waitFor} from '@testing-library/react';
|
||||
import {MemoryRouter, Route, Routes} from 'react-router-dom';
|
||||
import {toast} from 'sonner';
|
||||
import DeleteAccountCard from './DeleteAccountCard.jsx';
|
||||
|
||||
vi.mock('react-i18next', () => import('../test/i18n-mock.js'));
|
||||
vi.mock('sonner', () => ({toast: {success: vi.fn(), error: vi.fn()}}));
|
||||
|
||||
const EMAIL = 'player@example.com';
|
||||
|
||||
const answer = (status, headers = {}) => ({ok: status < 300, status, headers: new Headers(headers), json: async () => ({})});
|
||||
|
||||
const renderCard = () => render(
|
||||
<MemoryRouter initialEntries={['/settings']}>
|
||||
<Routes>
|
||||
<Route path="/settings" element={<DeleteAccountCard email={EMAIL}/>}/>
|
||||
<Route path="/" element={<p>landing</p>}/>
|
||||
<Route path="/login" element={<p>login</p>}/>
|
||||
</Routes>
|
||||
</MemoryRouter>);
|
||||
|
||||
const openAndFill = (email, password) => {
|
||||
fireEvent.click(screen.getByRole('button', {name: 'SETTINGS.DELETE_ACCOUNT.BUTTON'}));
|
||||
fireEvent.change(screen.getByLabelText('SETTINGS.DELETE_ACCOUNT.TYPE_EMAIL'), {target: {value: email}});
|
||||
fireEvent.change(screen.getByLabelText('SETTINGS.DELETE_ACCOUNT.PASSWORD'), {target: {value: password}});
|
||||
};
|
||||
|
||||
const confirmButton = () => screen.getByRole('button', {name: 'SETTINGS.DELETE_ACCOUNT.CONFIRM'});
|
||||
|
||||
describe('DeleteAccountCard', () => {
|
||||
beforeEach(() => {
|
||||
localStorage.setItem('tbp_session', '1');
|
||||
globalThis.fetch = vi.fn(async () => answer(204));
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
cleanup();
|
||||
localStorage.clear();
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it('asks for the own email and a password before it deletes', () => {
|
||||
renderCard();
|
||||
|
||||
openAndFill('someone@else.com', 'secret');
|
||||
expect(confirmButton().disabled).toBe(true);
|
||||
|
||||
fireEvent.change(screen.getByLabelText('SETTINGS.DELETE_ACCOUNT.TYPE_EMAIL'), {target: {value: ' Player@Example.com '}});
|
||||
expect(confirmButton().disabled).toBe(false);
|
||||
});
|
||||
|
||||
it('sends the password, ends the session and goes to the landing page', async () => {
|
||||
renderCard();
|
||||
openAndFill(EMAIL, 'secret');
|
||||
|
||||
fireEvent.click(confirmButton());
|
||||
|
||||
expect(await screen.findByText('landing')).toBeTruthy();
|
||||
const [url, init] = fetch.mock.calls[0];
|
||||
expect(url).toMatch(/\/api\/users\/me$/);
|
||||
expect(init.method).toBe('DELETE');
|
||||
expect(JSON.parse(init.body)).toEqual({password: 'secret'});
|
||||
expect(localStorage.getItem('tbp_session')).toBeNull();
|
||||
expect(toast.success).toHaveBeenCalledWith('SETTINGS.DELETE_ACCOUNT.DONE');
|
||||
});
|
||||
|
||||
it('keeps the dialog open on a wrong password', async () => {
|
||||
fetch.mockResolvedValue(answer(403));
|
||||
renderCard();
|
||||
openAndFill(EMAIL, 'wrong');
|
||||
|
||||
fireEvent.click(confirmButton());
|
||||
|
||||
await waitFor(() => expect(toast.error).toHaveBeenCalledWith('SETTINGS.DELETE_ACCOUNT.WRONG_PASSWORD'));
|
||||
expect(confirmButton()).toBeTruthy();
|
||||
expect(localStorage.getItem('tbp_session')).toBe('1');
|
||||
});
|
||||
|
||||
it('shows the lock message after too many wrong passwords', async () => {
|
||||
fetch.mockResolvedValue(answer(429, {'Retry-After': '900'}));
|
||||
renderCard();
|
||||
openAndFill(EMAIL, 'wrong');
|
||||
|
||||
fireEvent.click(confirmButton());
|
||||
|
||||
await waitFor(() => expect(toast.error).toHaveBeenCalledWith('SETTINGS.DELETE_ACCOUNT.TOO_MANY'));
|
||||
});
|
||||
|
||||
it('opens the login page when the session is gone', async () => {
|
||||
fetch.mockResolvedValue(answer(401));
|
||||
renderCard();
|
||||
openAndFill(EMAIL, 'secret');
|
||||
|
||||
fireEvent.click(confirmButton());
|
||||
|
||||
expect(await screen.findByText('login')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('shows the network error when Auth does not answer', async () => {
|
||||
fetch.mockRejectedValue(new TypeError('Failed to fetch'));
|
||||
renderCard();
|
||||
openAndFill(EMAIL, 'secret');
|
||||
|
||||
fireEvent.click(confirmButton());
|
||||
|
||||
await waitFor(() => expect(toast.error).toHaveBeenCalledWith('INFO.ERROR.NETWORK_FAILURE'));
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user