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 { RevealScreen } from '../../components/RevealScreen'; vi.mock('framer-motion', () => ({ motion: { div: ({ children, ...props }: React.PropsWithChildren>) =>
{children}
, button: ({ children, ...props }: React.PropsWithChildren>) => , p: ({ children, ...props }: React.PropsWithChildren>) =>

{children}

, }, AnimatePresence: ({ children }: React.PropsWithChildren) => <>{children}, useSpring: () => ({ set: vi.fn(), on: () => vi.fn() }), useTransform: () => ({ on: () => vi.fn() }), })); vi.mock('canvas-confetti', () => ({ default: vi.fn(), })); vi.mock('lucide-react', async () => { const React = await import('react'); const createMockIcon = (name: string) => { return function MockIcon(props: { size?: number; fill?: string; className?: string; strokeWidth?: number }) { return React.createElement('svg', { 'data-testid': `icon-${name}`, width: props.size || 24, height: props.size || 24, className: props.className }); }; }; return { Check: createMockIcon('check'), X: createMockIcon('x'), Flame: createMockIcon('flame'), ChevronRight: createMockIcon('chevron-right'), Triangle: createMockIcon('triangle'), Diamond: createMockIcon('diamond'), Circle: createMockIcon('circle'), Square: createMockIcon('square'), }; }); describe('RevealScreen', () => { const correctOption = { text: 'Correct Answer', isCorrect: true, shape: 'circle' as const, color: 'green' as const, reason: 'This is why it is correct', }; const defaultProps = { isCorrect: true, pointsEarned: 100, newScore: 500, streak: 3, correctOption, selectedOption: correctOption, role: 'CLIENT' as const, onNext: vi.fn(), }; beforeEach(() => { vi.clearAllMocks(); }); describe('host view', () => { it('shows Continue to Scoreboard button for host', () => { render(); expect(screen.getByRole('button', { name: /Continue to Scoreboard/i })).toBeInTheDocument(); }); it('calls onNext when host clicks Continue', async () => { const user = userEvent.setup(); const onNext = vi.fn(); render(); await user.click(screen.getByRole('button', { name: /Continue to Scoreboard/i })); expect(onNext).toHaveBeenCalled(); }); it('does not show button when onNext is undefined for host', () => { render(); expect(screen.queryByRole('button', { name: /Continue to Scoreboard/i })).not.toBeInTheDocument(); }); }); describe('client view - non-presenter', () => { it('does not show advance button for regular client', () => { render(); expect(screen.queryByRole('button', { name: /Continue to Scoreboard/i })).not.toBeInTheDocument(); }); it('shows correct/incorrect feedback', () => { render(); expect(screen.getByText('Correct!')).toBeInTheDocument(); }); it('shows incorrect feedback when wrong', () => { const wrongOption = { ...correctOption, isCorrect: false }; render( ); expect(screen.getByText('Incorrect')).toBeInTheDocument(); }); }); describe('presenter controls', () => { it('shows Continue button for presenter client', () => { render( ); expect(screen.getByRole('button', { name: /Continue to Scoreboard/i })).toBeInTheDocument(); }); it('calls onPresenterAdvance when presenter clicks Continue', async () => { const user = userEvent.setup(); const onPresenterAdvance = vi.fn(); render( ); await user.click(screen.getByRole('button', { name: /Continue to Scoreboard/i })); expect(onPresenterAdvance).toHaveBeenCalled(); }); it('does not show button when isPresenter is true but onPresenterAdvance is undefined', () => { render( ); expect(screen.queryByRole('button', { name: /Continue to Scoreboard/i })).not.toBeInTheDocument(); }); it('presenter button is fixed at bottom of screen', () => { render( ); const button = screen.getByRole('button', { name: /Continue to Scoreboard/i }); expect(button).toHaveClass('fixed'); }); }); describe('presenter edge cases', () => { it('handles isPresenter false gracefully', () => { render( ); expect(screen.queryByRole('button', { name: /Continue to Scoreboard/i })).not.toBeInTheDocument(); }); it('handles both isPresenter and onPresenterAdvance being undefined', () => { render( ); expect(screen.queryByRole('button', { name: /Continue to Scoreboard/i })).not.toBeInTheDocument(); }); it('presenter sees button on incorrect answer screen too', () => { render( ); expect(screen.getByRole('button', { name: /Continue to Scoreboard/i })).toBeInTheDocument(); }); it('adds padding to incorrect screen when presenter', () => { render( ); const incorrectContainer = document.querySelector('.absolute.bottom-0'); expect(incorrectContainer).toHaveClass('pb-20'); }); it('does not add padding when not presenter on incorrect screen', () => { render( ); const incorrectContainer = document.querySelector('.absolute.bottom-0'); expect(incorrectContainer).not.toHaveClass('pb-20'); }); }); describe('display elements', () => { it('shows points earned on correct answer', () => { render(); expect(screen.getByText('+150')).toBeInTheDocument(); }); it('shows streak indicator when streak > 1', () => { render(); expect(screen.getByText(/Answer Streak: 5/)).toBeInTheDocument(); }); it('does not show streak indicator when streak is 1', () => { render(); expect(screen.queryByText(/Answer Streak/)).not.toBeInTheDocument(); }); it('shows correct answer on host view', () => { render(); expect(screen.getByText('The correct answer is')).toBeInTheDocument(); expect(screen.getByText(correctOption.text)).toBeInTheDocument(); }); it('shows reason on host view when available', () => { render(); expect(screen.getByText(correctOption.reason!)).toBeInTheDocument(); }); }); });