// @vitest-environment jsdom import {afterEach, beforeEach, describe, expect, it, vi} from 'vitest'; import {act, cleanup, fireEvent, screen, waitFor} from '@testing-library/react'; import {useEffect} from 'react'; import {toast} from 'sonner'; import WelcomePage from './WelcomePage.jsx'; import WelcomeGate from '../components/WelcomeGate.jsx'; import {useCurrentUserContext} from '../context/CurrentUserContext.jsx'; import {fetchRoutes, json} from '../test/fetchRoutes.js'; import {renderWithProviders} from '../test/renderWithProviders.jsx'; vi.mock('react-i18next', () => import('../test/i18n-mock.js')); vi.mock('sonner', () => ({toast: {error: vi.fn()}})); const me = (overrides = {}) => ({id: 1, email: 'a@b.de', role: 'USER', steamId: null, settings: {welcomeSeenAt: null}, ...overrides}); const putCalls = () => fetch.mock.calls.filter(([, init]) => init?.method === 'PUT'); const renderPage = async ({user = me(), summary = json(200, {total: 0}), seen = json(204)} = {}) => { fetchRoutes({ 'GET /api/users/me': json(200, user), 'GET /api/library/summary': summary, 'PUT /api/users/settings/welcome-seen': seen, }); const result = renderWithProviders(, { path: '/welcome', entries: ['/welcome'], routes: {'/dashboard':

dashboard

}, }); await act(async () => undefined); return result; }; describe('WelcomePage', () => { beforeEach(() => localStorage.setItem('tbp_session', '1')); afterEach(() => { cleanup(); localStorage.clear(); vi.clearAllMocks(); }); it('shows six feature cards, five of them with a link to their page', async () => { await renderPage(); expect(screen.getAllByRole('heading', {level: 2})).toHaveLength(6); expect(screen.getAllByRole('link', {name: 'WELCOME.OPEN'}).map((link) => link.getAttribute('href'))) .toEqual(['/library', '/library', '/stats', '/settings', '/import']); }); it('checks the steps that are done', async () => { await renderPage({user: me({steamId: '7656'}), summary: json(200, {total: 1})}); expect(await screen.findAllByText('WELCOME.DONE')).toHaveLength(2); }); it('checks nothing for a new user without games', async () => { await renderPage(); expect(screen.queryByText('WELCOME.DONE')).toBeNull(); }); it('renders without checks when the summary fails', async () => { await renderPage({summary: json(500, {})}); expect(screen.getByText('WELCOME.TITLE')).toBeTruthy(); expect(screen.queryByText('WELCOME.DONE')).toBeNull(); }); it.each(['WELCOME.GO', 'WELCOME.SKIP'])('"%s" stamps once and opens the dashboard', async (label) => { const {router} = await renderPage(); fireEvent.click(screen.getByText(label)); await waitFor(() => expect(router.state.location.pathname).toBe('/dashboard')); expect(putCalls().map(([url]) => url)).toEqual([expect.stringContaining('/api/users/settings/welcome-seen')]); }); it('still opens the dashboard, silently, when the stamp fails', async () => { const {router} = await renderPage({seen: json(500, {})}); fireEvent.click(screen.getByText('WELCOME.GO')); await waitFor(() => expect(router.state.location.pathname).toBe('/dashboard')); expect(toast.error).not.toHaveBeenCalled(); }); it('staysOnTheDashboardWhenItReloadsTheUserBeforeTheStampLands', async () => { let stamped = false; function DashboardStub() { const {reload} = useCurrentUserContext(); useEffect(() => { reload(); }, [reload]); return

dashboard

; } fetchRoutes({ 'GET /api/users/me': () => json(200, me({settings: {welcomeSeenAt: stamped ? '2026-10-01T10:00:00Z' : null}})), 'GET /api/library/summary': json(200, {total: 0}), 'PUT /api/users/settings/welcome-seen': () => new Promise((resolve) => setTimeout(() => { stamped = true; resolve(json(204)); }, 20)), }); const {router} = renderWithProviders(, { path: '/welcome', entries: ['/welcome'], routes: {'/dashboard': }, }); await screen.findByText('WELCOME.TITLE'); fireEvent.click(screen.getByText('WELCOME.GO')); await waitFor(() => expect(stamped).toBe(true)); await act(async () => new Promise((resolve) => setTimeout(resolve, 30))); expect(router.state.location.pathname).toBe('/dashboard'); }); it('sendsTheStampWithATimeout', async () => { await renderPage({seen: () => new Promise(() => undefined)}); fireEvent.click(screen.getByText('WELCOME.GO')); await waitFor(() => expect(putCalls()).toHaveLength(1)); expect(putCalls()[0][1].signal).toBeInstanceOf(AbortSignal); }); });