refactor(frontend): lint, one session guard, one request helper, one user context

This commit is contained in:
lobo committed 2026-09-29 15:40:55 +02:00
1 parent 111ab1e200
commit d0ea9e900d
78 files changed
+2138 -624

No files matched your search

+32 -9
View File
@@ -5,11 +5,12 @@ import {MemoryRouter, Route, Routes} from 'react-router-dom';
import {toast} from 'sonner';
import ImportPage, {initialChoice, stepOf} from './ImportPage.jsx';
import {PreferencesProvider} from '../context/PreferencesContext.jsx';
import {CurrentUserProvider} from '../context/CurrentUserContext.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 json = (status, body) => ({ok: status < 400, status, json: async () => body});
const hades = {igdbId: 1, name: 'Hades', year: 2020, imageUrl: null, platforms: ['PC', 'Switch'], inLibrary: false};
const hades2 = {igdbId: 2, name: 'Hades II', year: 2025, imageUrl: null, platforms: ['PC'], inLibrary: false};
@@ -25,19 +26,20 @@ const ready = {
],
};
// routes: 'METHOD path after /imports' -> response or (init) => response; any other URL (Auth) answers the favorite
// routes: 'METHOD path after /imports' -> response or (init) => response; Auth answers the favorite
function renderPage(routes, entry = '/import') {
globalThis.fetch = vi.fn(async (url, init) => {
if (!url.includes('/api/library/imports')) return json(200, {settings: {favoritePlatform: 'Switch'}});
const path = new URL(url, 'http://x').pathname.replace(/.*\/api\/library\/imports/, '');
const answer = routes[`${init?.method ?? 'GET'} ${path}`];
if (!answer) throw new Error(`no route ${init?.method ?? 'GET'} ${path}`);
return typeof answer === 'function' ? answer(init) : answer;
fetchRoutes({
'GET /api/users/me': json(200, {settings: {favoritePlatform: 'Switch'}}),
...Object.fromEntries(Object.entries(routes).map(([key, answer]) => {
const [method, path] = key.split(' ');
return [`${method} /api/library/imports${path}`,
typeof answer === 'function' ? (url, init) => answer(init) : answer];
})),
});
render(
<MemoryRouter initialEntries={[entry]}>
<Routes>
<Route path="/import" element={<PreferencesProvider><ImportPage/></PreferencesProvider>}/>
<Route path="/import" element={<PreferencesProvider><CurrentUserProvider><ImportPage/></CurrentUserProvider></PreferencesProvider>}/>
<Route path="/login" element={<p>login</p>}/>
</Routes>
</MemoryRouter>,
@@ -250,3 +252,24 @@ describe('ImportPage', () => {
expect(await screen.findByText('IMPORT.MATCHING', {}, {timeout: 4000})).toBeTruthy();
}, 10000);
});
describe('review findings', () => {
it('shows the review when Auth cannot be reached for the favorite', async () => {
vi.spyOn(console, 'error').mockImplementation(() => undefined);
fetchRoutes({
'GET /api/users/me': () => Promise.reject(new TypeError('Failed to fetch')),
'GET /api/library/imports': json(200, {imports: []}),
'GET /api/library/imports/5': json(200, ready),
});
render(
<MemoryRouter initialEntries={['/import?id=5']}>
<Routes>
<Route path="/import" element={<PreferencesProvider><CurrentUserProvider><ImportPage/></CurrentUserProvider></PreferencesProvider>}/>
</Routes>
</MemoryRouter>,
);
expect(await screen.findByLabelText('IMPORT.LINE: Hades', {}, {timeout: 2000})).toBeTruthy();
});
});