// @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 {fetchRoutes, json} from '../test/fetchRoutes.js'; import PlayingHint from './PlayingHint.jsx'; vi.mock('react-i18next', () => import('../test/i18n-mock.js')); vi.mock('sonner', () => ({toast: {success: vi.fn(), error: vi.fn()}})); const game = {appid: 220, status: 'BACKLOG', rating: 3, notes: 'keep', priority: 'HIGH', playtime2Weeks: 90}; const renderHint = (props) => render( {}} {...props}/>); describe('PlayingHint', () => { beforeEach(() => { localStorage.setItem('tbp_session', '1'); // Core changes only the status and answers the whole entry. fetchRoutes({'PUT /api/library/games/220/status': (url, init) => json(200, {...game, ...JSON.parse(init.body)})}); }); afterEach(() => { cleanup(); localStorage.clear(); }); it.each([ ['a game that is not in the backlog', {...game, status: 'PLAYING'}], ['a backlog game without recent playtime', {...game, playtime2Weeks: 0}], ])('is hidden for %s', (_, other) => { renderHint({game: other}); expect(screen.queryByRole('button')).toBeNull(); }); it('sends only the status, so Core keeps the other fields', async () => { const onChanged = vi.fn(); renderHint({onChanged}); fireEvent.click(screen.getByRole('button', {name: 'GAME_DETAIL.SET_PLAYING'})); await waitFor(() => expect(onChanged).toHaveBeenCalledWith(expect.objectContaining({status: 'PLAYING'}))); const [url, init] = fetch.mock.calls[0]; expect(url).toMatch(/\/api\/library\/games\/220\/status$/); expect(JSON.parse(init.body)).toEqual({status: 'PLAYING'}); }); });