// @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 {toast} from 'sonner'; import GameMergeDialog from './GameMergeDialog.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 hades = {appid: 1145360, name: 'Hades'}; const hit = (appid, name) => ({appid, igdbId: Math.abs(appid), name, year: 2020, imageUrl: null, platforms: ['Switch'], inLibrary: false, inCatalog: true}); function renderDialog(game, post, results) { fetchRoutes({ 'GET /api/library/game-search': json(200, {available: true, results}), [`POST /api/library/games/${game.appid}/merge`]: post, }); const onMerged = vi.fn(); render(); return onMerged; } const search = (value) => fireEvent.change(screen.getByRole('searchbox'), {target: {value}}); beforeEach(() => { localStorage.setItem('tbp_session', '1'); }); afterEach(() => { cleanup(); localStorage.clear(); vi.clearAllMocks(); }); describe('GameMergeDialog', () => { it('merges the picked game after the confirm step', async () => { const onMerged = renderDialog(hades, json(200, {appid: 1145360}), [hit(1145360, 'Hades'), hit(-113112, 'Hades (Switch)'), hit(1145350, 'Hades II'), {...hit(-4711, 'Hades Fan Remake'), inCatalog: false}]); search('hades'); const offered = await screen.findByRole('button', {name: /Hades \(Switch\)/}); // The page's own game, other Steam games and games not in ToBePlayed are not offered. expect(screen.queryByRole('button', {name: /^Hades \(2020\)/})).toBeNull(); expect(screen.queryByRole('button', {name: /Hades II/})).toBeNull(); expect(screen.queryByRole('button', {name: /Fan Remake/})).toBeNull(); fireEvent.click(offered); expect(fetch).toHaveBeenCalledTimes(1); fireEvent.click(screen.getByRole('button', {name: 'MERGE.CONFIRM_BUTTON'})); await waitFor(() => expect(onMerged).toHaveBeenCalledWith(1145360)); const [url, init] = fetch.mock.calls[1]; expect(url).toContain('/api/library/games/1145360/merge'); expect(JSON.parse(init.body)).toEqual({otherAppid: -113112}); expect(toast.success).toHaveBeenCalledWith('MERGE.DONE'); }); it('goes back from the confirm step without merging', async () => { renderDialog(hades, json(200, {}), [hit(-113112, 'Hades (Switch)')]); search('hades'); fireEvent.click(await screen.findByRole('button', {name: /Hades \(Switch\)/})); fireEvent.click(screen.getByRole('button', {name: 'MERGE.BACK'})); expect(await screen.findByRole('searchbox')).toBeTruthy(); expect(fetch).toHaveBeenCalledTimes(1); }); it('says why Core refused the merge', async () => { const onMerged = renderDialog({appid: -113112, name: 'Hades (Switch)'}, json(404, {code: 'GAME_NOT_FOUND'}), [hit(1145360, 'Hades')]); search('hades'); fireEvent.click(await screen.findByRole('button', {name: /Hades \(2020\)/})); fireEvent.click(screen.getByRole('button', {name: 'MERGE.CONFIRM_BUTTON'})); await waitFor(() => expect(toast.error).toHaveBeenCalledWith('MERGE.ERROR.GAME_NOT_FOUND')); expect(onMerged).not.toHaveBeenCalled(); }); });