Public Access
fix: abort old loads, stale sync state, warn before unsaved edits are lost
This commit is contained in:
1 parent
cbafa3d540
commit
9ddc50f819
28 files changed
+753
-121
No files matched your search
@@ -0,0 +1,93 @@
|
||||
// @vitest-environment jsdom
|
||||
import {afterEach, beforeEach, describe, expect, it, vi} from 'vitest';
|
||||
import {cleanup, fireEvent, screen, waitFor} from '@testing-library/react';
|
||||
import {toast} from 'sonner';
|
||||
import ManualGameCard from './ManualGameCard.jsx';
|
||||
import {fetchRoutes, json} from '../test/fetchRoutes.js';
|
||||
import {renderWithProviders} from '../test/renderWithProviders.jsx';
|
||||
|
||||
vi.mock('react-i18next', () => import('../test/i18n-mock.js'));
|
||||
vi.mock('sonner', () => ({toast: {success: vi.fn(), error: vi.fn()}}));
|
||||
|
||||
const game = {appid: -1025, platform: 'PS5', playtimeMinutes: 90};
|
||||
|
||||
const answer = (routes) => fetchRoutes({'GET /api/users/me': json(200, {settings: {}}), ...routes});
|
||||
|
||||
const renderCard = (onSaved = vi.fn()) => renderWithProviders(<ManualGameCard game={game} onSaved={onSaved}/>,
|
||||
{path: '/library/:appid', entries: ['/library/-1025'], routes: {'/library': <p>library page</p>}});
|
||||
|
||||
const saveButton = () => screen.getByRole('button', {name: 'BACKLOG.SAVE'});
|
||||
|
||||
beforeEach(() => {
|
||||
localStorage.setItem('tbp_session', '1');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
cleanup();
|
||||
localStorage.clear();
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('ManualGameCard', () => {
|
||||
it('saves the trimmed platform and the minutes, then reports them', async () => {
|
||||
answer({'PUT /api/library/games/-1025/manual': json(200, {})});
|
||||
const onSaved = vi.fn();
|
||||
renderCard(onSaved);
|
||||
|
||||
fireEvent.change(screen.getByLabelText('MANUAL_GAME.PLATFORM'), {target: {value: ' Switch '}});
|
||||
fireEvent.change(screen.getByLabelText('MANUAL_GAME.HOURS'), {target: {value: '2'}});
|
||||
fireEvent.click(saveButton());
|
||||
|
||||
await waitFor(() => expect(onSaved).toHaveBeenCalledWith({platform: 'Switch', playtimeMinutes: 120}));
|
||||
const [url, init] = fetch.mock.calls.find(([, call]) => call?.method === 'PUT');
|
||||
expect(url).toContain('/api/library/games/-1025/manual');
|
||||
expect(JSON.parse(init.body)).toEqual({platform: 'Switch', playtimeMinutes: 120});
|
||||
});
|
||||
|
||||
it('keeps the save button disabled while nothing changed', () => {
|
||||
answer({});
|
||||
renderCard();
|
||||
|
||||
expect(saveButton().disabled).toBe(true);
|
||||
fireEvent.change(screen.getByLabelText('MANUAL_GAME.PLATFORM'), {target: {value: 'PS5 '}});
|
||||
expect(saveButton().disabled).toBe(true);
|
||||
});
|
||||
|
||||
it('removes the game only after a confirm and opens the library', async () => {
|
||||
answer({'DELETE /api/library/games/-1025': json(204, null)});
|
||||
const {router} = renderCard();
|
||||
|
||||
fireEvent.click(screen.getByRole('button', {name: 'MANUAL_GAME.REMOVE'}));
|
||||
expect(fetch.mock.calls.some(([, init]) => init?.method === 'DELETE')).toBe(false);
|
||||
fireEvent.click(screen.getByRole('button', {name: 'MANUAL_GAME.REMOVE_YES'}));
|
||||
|
||||
await waitFor(() => expect(router.state.location.pathname).toBe('/library'));
|
||||
});
|
||||
|
||||
it('shows an error when the save fails', async () => {
|
||||
answer({'PUT /api/library/games/-1025/manual': json(500, {})});
|
||||
const onSaved = vi.fn();
|
||||
renderCard(onSaved);
|
||||
|
||||
fireEvent.change(screen.getByLabelText('MANUAL_GAME.PLATFORM'), {target: {value: 'Switch'}});
|
||||
fireEvent.click(saveButton());
|
||||
|
||||
await waitFor(() => expect(toast.error).toHaveBeenCalledWith('BACKLOG.SAVE_FAILED'));
|
||||
expect(onSaved).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('shows no toast on a 401', async () => {
|
||||
answer({'PUT /api/library/games/-1025/manual': json(401, {}), 'POST /api/auth/refresh': json(401, {})});
|
||||
const onSaved = vi.fn();
|
||||
renderCard(onSaved);
|
||||
|
||||
fireEvent.change(screen.getByLabelText('MANUAL_GAME.PLATFORM'), {target: {value: 'Switch'}});
|
||||
fireEvent.click(saveButton());
|
||||
|
||||
await waitFor(() => expect(fetch.mock.calls.some(([url]) => url.endsWith('/api/auth/refresh'))).toBe(true));
|
||||
await waitFor(() => expect(saveButton().disabled).toBe(false));
|
||||
expect(toast.error).not.toHaveBeenCalled();
|
||||
expect(toast.success).not.toHaveBeenCalled();
|
||||
expect(onSaved).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user