import { describe, it, expect, vi, beforeEach } from 'vitest'; import { render, screen, fireEvent, cleanup } from '@testing-library/react'; import { BeginScreen } from './BeginScreen'; import { appStore } from '../../store'; import { __resetAudioBootstrapForTest } from './use-audio-bootstrap'; // Mock the audio bootstrap module so happy-dom (which lacks AudioContext) // doesn't blow up; also gives us a spy to assert the click invokes it. vi.mock('./use-audio-bootstrap', async () => { const actual = await vi.importActual('./use-audio-bootstrap'); return { ...actual, bootstrapAudioContext: vi.fn().mockResolvedValue(null), }; }); import { bootstrapAudioContext } from './use-audio-bootstrap'; describe('BeginScreen (AEST-07, D-21, D-22)', () => { beforeEach(() => { cleanup(); appStore.setState({ beginGateDismissed: false }); __resetAudioBootstrapForTest(); vi.mocked(bootstrapAudioContext).mockClear(); }); it('renders the title and Begin CTA when not dismissed', () => { render(); expect(screen.getByText('The Last Garden')).toBeTruthy(); expect(screen.getByRole('button', { name: 'Begin' })).toBeTruthy(); }); it('renders nothing when beginGateDismissed=true (D-22 returning-player skip)', () => { appStore.setState({ beginGateDismissed: true }); const { container } = render(); expect(container.firstChild).toBeNull(); }); it('dismisses the gate and triggers audio bootstrap on click', () => { render(); fireEvent.click(screen.getByRole('button', { name: 'Begin' })); expect(bootstrapAudioContext).toHaveBeenCalledTimes(1); expect(appStore.getState().beginGateDismissed).toBe(true); }); it('subtitle is the externalized "tend" string (CLAUDE.md tone)', () => { render(); expect(screen.getByText('tend')).toBeTruthy(); }); });