import { expect, test } from '../helpers/serve-dist'; test.beforeEach(async ({ page }) => { await page.addInitScript(() => { const OriginalAudioContext = window.AudioContext; (window as unknown as { __audioConstructed: number }).__audioConstructed = 0; (window as unknown as { __audioContexts: AudioContext[] }).__audioContexts = []; if (!OriginalAudioContext) return; class TrackingAudioContext extends OriginalAudioContext { constructor(options?: AudioContextOptions) { super(options); const state = window as unknown as { __audioConstructed: number; __audioContexts: AudioContext[] }; state.__audioConstructed += 1; state.__audioContexts.push(this); } } Object.defineProperty(window, 'AudioContext', { configurable: true, value: TrackingAudioContext }); }); }); test('starts Web Audio synchronously from the visitor gesture and exposes accessible controls', async ({ page, distUrl }) => { await page.goto(distUrl, { waitUntil: 'domcontentloaded' }); await page.evaluate(() => { (window as unknown as { __constructedDuringGesture?: number }).__constructedDuringGesture = -1; window.addEventListener('click', (event) => { if ((event.target as HTMLElement | null)?.textContent?.includes('START THE DRIVE')) { const state = window as unknown as { __audioConstructed: number; __constructedDuringGesture: number }; state.__constructedDuringGesture = state.__audioConstructed; } }); }); await expect(page.getByRole('button', { name: 'START THE DRIVE' })).toBeVisible(); await expect(page.getByText('Enables the interactive soundtrack.')).toBeVisible(); await expect(page.getByLabel('Sound volume')).toHaveCount(0); await expect.poll(() => page.evaluate(() => (window as unknown as { __audioConstructed: number }).__audioConstructed)).toBe(0); await page.getByRole('button', { name: 'START THE DRIVE' }).click(); await expect(page.getByRole('button', { name: 'Mute sound' })).toBeVisible(); await expect(page.getByLabel('Sound volume')).toBeVisible(); await expect.poll(() => page.evaluate(() => (window as unknown as { __constructedDuringGesture: number }).__constructedDuringGesture)).toBe(1); await expect.poll(() => page.evaluate(() => (window as unknown as { __audioConstructed: number }).__audioConstructed)).toBe(1); await page.getByRole('button', { name: 'Mute sound' }).click(); await expect(page.getByRole('button', { name: 'Unmute sound' })).toBeVisible(); }); test('persists native volume and keeps sound independent from Static motion', async ({ page, distUrl }) => { await page.goto(distUrl, { waitUntil: 'domcontentloaded' }); await page.getByRole('button', { name: 'START THE DRIVE' }).click(); const volume = page.getByLabel('Sound volume'); await volume.fill('0.65'); await expect(volume).toHaveValue('0.65'); await expect.poll(() => page.evaluate(() => localStorage.getItem('portfolio.audio.volume'))).toBe('0.65'); await page.getByLabel('Quality control').selectOption('static'); await expect(page.getByRole('button', { name: 'Unmute sound' })).toHaveCount(0); await expect(page.getByRole('button', { name: 'Mute sound' })).toBeVisible(); await expect(volume).toBeVisible(); }); test('requires a direct visitor action after browser interruption without creating another graph', async ({ page, distUrl }) => { await page.goto(distUrl, { waitUntil: 'domcontentloaded' }); await page.getByRole('button', { name: 'START THE DRIVE' }).click(); await page.evaluate(() => { const context = (window as unknown as { __audioContexts: AudioContext[] }).__audioContexts[0]; Object.defineProperty(context, 'state', { configurable: true, value: 'interrupted' }); context?.dispatchEvent(new Event('statechange')); }); await expect(page.getByRole('button', { name: 'Resume sound' })).toBeVisible(); await page.getByRole('button', { name: 'Resume sound' }).click(); await expect(page.getByRole('button', { name: 'Mute sound' })).toBeVisible(); await expect.poll(() => page.evaluate(() => (window as unknown as { __audioConstructed: number }).__audioConstructed)).toBe(1); }); test('shows a concise non-blocking message when initial audio start is rejected', async ({ page, distUrl }) => { await page.addInitScript(() => { const OriginalAudioContext = window.AudioContext; if (!OriginalAudioContext) return; class RejectingAudioContext extends OriginalAudioContext { override resume(): Promise { return Promise.reject(new DOMException('blocked', 'NotAllowedError')); } } Object.defineProperty(window, 'AudioContext', { configurable: true, value: RejectingAudioContext }); }); await page.goto(distUrl, { waitUntil: 'domcontentloaded' }); await page.getByRole('button', { name: 'START THE DRIVE' }).click(); await expect(page.getByText('Audio unavailable. Continue without sound.')).toBeVisible(); await expect(page.locator('main#portfolio-content')).toBeVisible(); });