// @vitest-environment jsdom import {afterEach, beforeEach, describe, expect, it, vi} from 'vitest'; import {act, cleanup, fireEvent, screen, waitFor} from '@testing-library/react'; import App from './App.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('./hooks/useLibrarySync.js', () => ({default: () => ({status: 'OK', lastSyncedAt: null, isRunning: false})})); vi.mock('./hooks/useForceSync.js', () => ({default: () => ({trigger: vi.fn(), busy: false})})); const DEBOUNCE_WAIT_MS = 700; const page = {content: [{appid: 220, name: 'Half-Life 2', playtimeMinutes: 0, status: 'BACKLOG'}], page: 0, size: 20, totalElements: 1, totalPages: 1}; const wait = (ms) => act(() => new Promise((resolve) => setTimeout(resolve, ms))); beforeEach(() => { localStorage.setItem('tbp_session', '1'); }); afterEach(() => { cleanup(); localStorage.clear(); }); describe('App', () => { it('ends the session on a 401 that the refresh cannot fix', async () => { let expired = false; const library = () => (expired ? json(401, {}) : json(200, page)); fetchRoutes({ 'GET /api/users/me': json(200, {settings: {}}), 'GET /api/rewards/wallet': json(200, {balance: 0, catalog: [], owned: [], equipped: {}, history: []}), 'GET /api/library/games': library, 'GET /api/library/games-count': json(200, {hasSteamId: true}), 'GET /api/library/steam-profile': json(200, null), 'POST /api/auth/refresh': json(401, {}), }); const {router} = renderWithProviders(, {entries: ['/library']}); await waitFor(() => expect(screen.getByText('Half-Life 2')).toBeTruthy()); expired = true; fireEvent.change(screen.getByRole('searchbox'), {target: {value: 'half'}}); await wait(DEBOUNCE_WAIT_MS); await waitFor(() => expect(router.state.location.pathname).toBe('/login')); expect(router.state.location.state).toEqual({sessionExpired: true, from: '/library?q=half&page=0'}); expect(localStorage.getItem('tbp_session')).toBeNull(); expect(fetch).toHaveBeenCalledWith(expect.stringContaining('/api/auth/refresh'), expect.objectContaining({method: 'POST'})); }); });