import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; import { act, fireEvent, render, screen } from '@testing-library/react'; import { REACTION_SUCCESS_THRESHOLD_MS, ReactionGame } from '../../src/components/minigames/ReactionGame'; describe('ReactionGame', () => { beforeEach(() => { vi.useFakeTimers(); vi.setSystemTime(new Date('2026-03-18T12:00:00Z')); vi.spyOn(Math, 'random').mockReturnValue(0); }); afterEach(() => { vi.useRealTimers(); vi.restoreAllMocks(); }); it('fails on an early click before GO appears', () => { const onFail = vi.fn(); render(); fireEvent.click(screen.getByRole('button', { name: /Wait for GO!/i })); act(() => { vi.advanceTimersByTime(1000); }); expect(onFail).toHaveBeenCalledTimes(1); }); it('shows the cue after the randomized delay', () => { render(); act(() => { vi.advanceTimersByTime(1000); }); expect(screen.getByText('GO!')).toBeTruthy(); }); it('succeeds when reaction time is 500ms or faster', () => { const onSuccess = vi.fn(); render(); act(() => { vi.advanceTimersByTime(1000); }); vi.setSystemTime(new Date('2026-03-18T12:00:01.350Z')); fireEvent.click(screen.getByRole('button', { name: /CLICK!/i })); act(() => { vi.advanceTimersByTime(1000); }); expect(screen.getByText(/350/)).toBeTruthy(); expect(onSuccess).toHaveBeenCalledTimes(1); }); it('fails when reaction time is slower than the threshold', () => { const onFail = vi.fn(); render(); act(() => { vi.advanceTimersByTime(1000); }); vi.setSystemTime(new Date(`2026-03-18T12:00:01.${REACTION_SUCCESS_THRESHOLD_MS + 150}Z`)); fireEvent.click(screen.getByRole('button', { name: /CLICK!/i })); act(() => { vi.advanceTimersByTime(1000); }); expect(onFail).toHaveBeenCalledTimes(1); }); });