import React from 'react'; import { render, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { describe, it, expect, vi, beforeEach } from 'vitest'; import { Scoreboard } from '../../components/Scoreboard'; import { Player } from '../../types'; vi.mock('framer-motion', () => ({ motion: { div: ({ children, ...props }: React.PropsWithChildren>) =>
{children}
, }, AnimatePresence: ({ children }: React.PropsWithChildren) => <>{children}, useSpring: () => ({ set: vi.fn(), on: () => vi.fn() }), useTransform: () => ({ on: () => vi.fn() }), LayoutGroup: ({ children }: React.PropsWithChildren) => <>{children}, })); const createPlayer = (overrides: Partial = {}): Player => ({ id: 'player-1', name: 'Test Player', score: 100, previousScore: 50, streak: 2, lastAnswerCorrect: true, selectedShape: 'circle', pointsBreakdown: { basePoints: 80, streakBonus: 10, comebackBonus: 0, firstCorrectBonus: 0, penalty: 0, total: 90, }, isBot: false, avatarSeed: 0.5, color: '#ff0000', ...overrides, }); describe('Scoreboard', () => { const defaultProps = { players: [ createPlayer({ id: 'player-1', name: 'Alice', score: 200 }), createPlayer({ id: 'player-2', name: 'Bob', score: 150 }), ], onNext: vi.fn(), isHost: true, currentPlayerId: 'player-1', }; beforeEach(() => { vi.clearAllMocks(); }); describe('host controls', () => { it('shows Next button for host', () => { render(); expect(screen.getByRole('button', { name: /Next/i })).toBeInTheDocument(); }); it('calls onNext when host clicks Next', async () => { const user = userEvent.setup(); render(); await user.click(screen.getByRole('button', { name: /Next/i })); expect(defaultProps.onNext).toHaveBeenCalled(); }); it('shows waiting message for non-host non-presenter', () => { render(); expect(screen.getByText('Waiting for host...')).toBeInTheDocument(); expect(screen.queryByRole('button', { name: /Next/i })).not.toBeInTheDocument(); }); }); describe('presenter controls', () => { it('shows Next button for presenter (non-host)', () => { render( ); expect(screen.getByRole('button', { name: /Next/i })).toBeInTheDocument(); }); it('calls onPresenterAdvance when presenter clicks Next', async () => { const user = userEvent.setup(); const onPresenterAdvance = vi.fn(); render( ); await user.click(screen.getByRole('button', { name: /Next/i })); expect(onPresenterAdvance).toHaveBeenCalled(); }); it('does not call onNext when presenter clicks (uses onPresenterAdvance)', async () => { const user = userEvent.setup(); const onNext = vi.fn(); const onPresenterAdvance = vi.fn(); render( ); await user.click(screen.getByRole('button', { name: /Next/i })); expect(onPresenterAdvance).toHaveBeenCalled(); expect(onNext).not.toHaveBeenCalled(); }); it('host uses onNext, not onPresenterAdvance', async () => { const user = userEvent.setup(); const onNext = vi.fn(); const onPresenterAdvance = vi.fn(); render( ); await user.click(screen.getByRole('button', { name: /Next/i })); expect(onNext).toHaveBeenCalled(); expect(onPresenterAdvance).not.toHaveBeenCalled(); }); }); describe('presenter edge cases', () => { it('shows waiting message when isPresenter is false', () => { render( ); expect(screen.getByText('Waiting for host...')).toBeInTheDocument(); }); it('shows waiting message when isPresenter is undefined', () => { render( ); expect(screen.getByText('Waiting for host...')).toBeInTheDocument(); }); it('handles missing onPresenterAdvance gracefully', async () => { const user = userEvent.setup(); render( ); const button = screen.getByRole('button', { name: /Next/i }); await user.click(button); }); it('both host and presenter see button, but only host uses onNext', async () => { const user = userEvent.setup(); const onNext = vi.fn(); const onPresenterAdvance = vi.fn(); render( ); await user.click(screen.getByRole('button', { name: /Next/i })); expect(onNext).toHaveBeenCalled(); expect(onPresenterAdvance).not.toHaveBeenCalled(); }); }); describe('display', () => { it('shows Scoreboard title', () => { render(); expect(screen.getByText('Scoreboard')).toBeInTheDocument(); }); it('shows player names', () => { render(); expect(screen.getByText('Alice (You)')).toBeInTheDocument(); expect(screen.getByText('Bob')).toBeInTheDocument(); }); it('marks current player with (You) suffix', () => { render(); expect(screen.getByText('Bob (You)')).toBeInTheDocument(); expect(screen.getByText('Alice')).toBeInTheDocument(); }); }); });