// @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 LinkQueuePage from './LinkQueuePage.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 pair = {a: {appid: 10, name: 'Dark Souls', imageUrl: null}, b: {appid: -7, name: 'Dark Souls Remastered', imageUrl: null}, igdb: true, requests: 2, createdAt: '2026-10-01T10:00:00Z'}; const queue = (content, totalPages = 1) => json(200, {content, page: 0, size: 20, totalElements: content.length, totalPages}); const renderPage = (routes) => { fetchRoutes(routes); render(); }; beforeEach(() => { localStorage.setItem('tbp_session', '1'); }); afterEach(() => { cleanup(); localStorage.clear(); vi.clearAllMocks(); }); describe('LinkQueuePage', () => { it('listsThePairWithItsBadges', async () => { renderPage({'GET /api/library/link-queue': queue([pair])}); expect(await screen.findByText('Dark Souls')).toBeTruthy(); expect(screen.getByText('Dark Souls Remastered')).toBeTruthy(); expect(screen.getByText('LINKS.FROM_IGDB')).toBeTruthy(); expect(screen.getByText('LINKS.REQUESTED_BY:2')).toBeTruthy(); }); it('linksThePairAndLoadsTheQueueAgain', async () => { let pending = [pair]; renderPage({ 'GET /api/library/link-queue': () => queue(pending), 'POST /api/library/games/10/link': () => { pending = []; return json(204); }, }); fireEvent.click(await screen.findByRole('button', {name: 'LINKS.LINK'})); expect(await screen.findByText('LINKS.QUEUE_EMPTY')).toBeTruthy(); const post = fetch.mock.calls.find(([, init]) => init?.method === 'POST'); expect(JSON.parse(post[1].body)).toEqual({otherAppid: -7}); }); it('dismissesThePair', async () => { renderPage({ 'GET /api/library/link-queue': queue([pair]), 'DELETE /api/library/link-queue/10/-7': json(204), }); fireEvent.click(await screen.findByRole('button', {name: 'LINKS.DISMISS'})); await waitFor(() => expect(fetch.mock.calls.some(([url, init]) => init?.method === 'DELETE' && url.endsWith('/link-queue/10/-7'))).toBe(true)); }); it('pagesThroughTheQueue', async () => { renderPage({'GET /api/library/link-queue': () => queue([pair], 2)}); await screen.findByText('Dark Souls'); fireEvent.click(screen.getByRole('button', {name: /ADMIN.NEXT/})); await waitFor(() => expect(fetch.mock.calls.at(-1)[0]).toContain('page=1')); expect(screen.getByText('LINKS.PAGE_OF')).toBeTruthy(); }); it('goesBackAPageWhenThePageIsEmpty', async () => { renderPage({'GET /api/library/link-queue': (url) => (url.includes('page=1') ? queue([], 2) : queue([pair], 2))}); await screen.findByText('Dark Souls'); fireEvent.click(screen.getByRole('button', {name: /ADMIN.NEXT/})); await waitFor(() => expect(fetch.mock.calls.at(-1)[0]).toContain('page=0')); expect(fetch.mock.calls.some(([url]) => url.includes('page=1'))).toBe(true); expect(await screen.findByText('Dark Souls')).toBeTruthy(); }); it('showsNoAccessOn403', async () => { renderPage({'GET /api/library/link-queue': json(403, {})}); expect(await screen.findByText('LINKS.NO_ACCESS')).toBeTruthy(); }); });