Public Access
feat(audit): game catalog search and time to beat correction
New page /audit/games: search every game and open the correction dialog without owning it. Time to beat as hours + minutes. Audit log game links open /audit/games?appid=. Menu entry next to the link queue.
This commit is contained in:
1 parent
6627a8db66
commit
0c300c455d
14 files changed
+439
-19
No files matched your search
@@ -0,0 +1,91 @@
|
||||
// @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(<MemoryRouter initialEntries={[url]}><PreferencesProvider><GameCatalogPage/></PreferencesProvider></MemoryRouter>);
|
||||
};
|
||||
|
||||
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'));
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user