// @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 RequireSession from './RequireSession.jsx'; import {toast} from 'sonner'; import DeleteAccountCard from './DeleteAccountCard.jsx'; import {fetchRoutes, json} from '../test/fetchRoutes.js'; 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 deleteAnswers = (answer, routes = {}) => fetchRoutes({'DELETE /api/users/me': answer, ...routes}); const renderCard = () => render( }>}/> landing

}/> login

}/>
); 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'); deleteAnswers(json(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 () => { deleteAnswers(json(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 () => { deleteAnswers({...json(429, {}), headers: new Headers({'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 () => { deleteAnswers(json(401, {}), {'POST /api/auth/refresh': json(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 () => { deleteAnswers(() => { throw new TypeError('Failed to fetch'); }); renderCard(); openAndFill(EMAIL, 'secret'); fireEvent.click(confirmButton()); await waitFor(() => expect(toast.error).toHaveBeenCalledWith('INFO.ERROR.NETWORK_FAILURE')); }); });