// @vitest-environment jsdom import {afterEach, beforeEach, describe, expect, it, vi} from 'vitest'; import {cleanup, fireEvent, render, screen, waitFor} from '@testing-library/react'; import {MemoryRouter} from 'react-router-dom'; import GameCatalogPage from './GameCatalogPage.jsx'; import {PreferencesProvider} from '../context/PreferencesContext.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 hit = {appid: 10, name: 'Hades', imageUrl: null, platforms: ['PS5', 'Steam']}; const detail = {appid: 10, name: 'Hades', imageUrl: null, speedrunUrl: null, moods: [], endless: null, genres: [], timeToBeat: null, corrections: []}; const catalog = (content, totalPages = 1) => json(200, {content, page: 0, size: 20, totalElements: content.length, totalPages}); const renderPage = (routes, url = '/audit/games') => { fetchRoutes(routes); render(); }; const urls = () => fetch.mock.calls.map(([url]) => url); beforeEach(() => { localStorage.setItem('tbp_session', '1'); }); afterEach(() => { cleanup(); localStorage.clear(); vi.clearAllMocks(); }); describe('GameCatalogPage', () => { it('listsHitsWithPlatformsAndKey', async () => { renderPage({'GET /api/library/catalog': catalog([hit])}); expect(await screen.findByText('Hades')).toBeTruthy(); expect(screen.getByText(/PS5, Steam/)).toBeTruthy(); expect(screen.getByText(/CATALOG.KEY/)).toBeTruthy(); }); it('searchesWithTheTypedName', async () => { renderPage({'GET /api/library/catalog': catalog([hit])}); await screen.findByText('Hades'); fireEvent.change(screen.getByLabelText('CATALOG.SEARCH'), {target: {value: ' had '}}); await waitFor(() => expect(urls().some((u) => u.includes('q=had'))).toBe(true)); }); it('opensTheCorrectionDialogForTheChosenGame', async () => { renderPage({'GET /api/library/catalog': catalog([hit]), 'GET /api/library/catalog/10': json(200, detail)}); fireEvent.click(await screen.findByRole('button', {name: 'CATALOG.CORRECT'})); expect(await screen.findByText('CORRECTION.TITLE')).toBeTruthy(); expect(urls().some((u) => u.endsWith('/api/library/catalog/10'))).toBe(true); }); it('opensTheGameOfTheAppidInTheUrl', async () => { renderPage({'GET /api/library/catalog': catalog([]), 'GET /api/library/catalog/10': json(200, detail)}, '/audit/games?appid=10'); expect(await screen.findByText('CORRECTION.TITLE')).toBeTruthy(); }); it('saysSoWhenTheGameIsGone', async () => { const {toast} = await import('sonner'); renderPage({'GET /api/library/catalog': catalog([]), 'GET /api/library/catalog/10': json(404, {})}, '/audit/games?appid=10'); await waitFor(() => expect(toast.error).toHaveBeenCalledWith('CATALOG.GONE')); expect(screen.queryByText('CORRECTION.TITLE')).toBeNull(); }); it('showsNoAccessOn403', async () => { renderPage({'GET /api/library/catalog': json(403, {})}); expect(await screen.findByText('CATALOG.NO_ACCESS')).toBeTruthy(); }); it('pagesThroughTheCatalog', async () => { renderPage({'GET /api/library/catalog': () => catalog([hit], 2)}); await screen.findByText('Hades'); fireEvent.click(screen.getByRole('button', {name: /ADMIN.NEXT/})); await waitFor(() => expect(urls().at(-1)).toContain('page=1')); }); });