import { expect, expectInsetFocusIndicator, 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("makes the required soundtrack gesture explicit while the visual drive is already running", async ({ page, distUrl, }) => { await page.goto(distUrl, { waitUntil: "domcontentloaded" }); await expect(page.locator(".journey-canvas")).toBeVisible(); await expect( page.getByRole("button", { name: "Play soundtrack" }), ).toBeVisible(); await expect( page.getByRole("button", { name: "Start the drive" }), ).toHaveCount(0); }); 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( "PLAY SOUNDTRACK", ) ) { const state = window as unknown as { __audioConstructed: number; __constructedDuringGesture: number; }; state.__constructedDuringGesture = state.__audioConstructed; } }); }); await expect( page.getByRole("button", { name: "Play soundtrack" }), ).toBeVisible(); await expect(page.getByRole("button", { name: "Mute sound" })).toHaveCount(0); await expect(page.getByRole("button", { name: "Sound volume" })).toHaveCount( 0, ); await expect .poll(() => page.evaluate( () => (window as unknown as { __audioConstructed: number }) .__audioConstructed, ), ) .toBe(0); await page.getByRole("button", { name: "Play soundtrack" }).click(); await expect(page.getByRole("button", { name: "Mute sound" })).toBeVisible(); await expect( page.getByRole("button", { name: "Sound volume" }), ).toBeVisible(); await expect( page.getByRole("button", { name: "Pause motion" }), ).toBeVisible(); await expect( page.getByRole("button", { name: "Drive settings" }), ).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("keeps the compact controls usable and dismisses the upward panels", async ({ page, distUrl, }) => { await page.goto(distUrl, { waitUntil: "domcontentloaded" }); await page.getByRole("button", { name: "Play soundtrack" }).click(); const controls = page .getByRole("group", { name: "Drive controls" }) .getByRole("button"); const targets = await controls.evaluateAll((buttons) => buttons.map((button) => { const rect = button.getBoundingClientRect(); return { width: rect.width, height: rect.height }; }), ); expect( targets.every((target) => target.width >= 44 && target.height >= 44), ).toBe(true); const settings = page.getByRole("button", { name: "Drive settings" }); await settings.focus(); await expectInsetFocusIndicator(page, 'button[aria-label="Drive settings"]'); await settings.click(); await expect( page.getByRole("dialog", { name: "Drive settings" }), ).toBeVisible(); const surfaces = await page .locator(".journey-hud, .drive-controls__dock, .drive-popover") .evaluateAll((elements) => elements.map((element) => { const style = getComputedStyle(element); const alpha = Number.parseFloat( style.backgroundColor.match(/[\d.]+(?=\))/u)?.[0] ?? "1", ); return { alpha, backdropFilter: style.backdropFilter }; }), ); expect( surfaces.every( (surface) => surface.alpha >= 0.95 && surface.backdropFilter === "none", ), ).toBe(true); await page.keyboard.press("Escape"); await expect( page.getByRole("dialog", { name: "Drive settings" }), ).toHaveCount(0); await expect(settings).toBeFocused(); await settings.click(); await page .locator(".hero-journey__content") .click({ position: { x: 8, y: 8 } }); await expect( page.getByRole("dialog", { name: "Drive settings" }), ).toHaveCount(0); }); 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: "Play soundtrack" }).click(); await page.getByRole("button", { name: "Sound volume" }).click(); const volume = page.getByLabel("Sound volume level"); 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.getByRole("button", { name: "Drive settings" }).click(); 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).toHaveCount(0); await page.getByRole("button", { name: "Sound volume" }).click(); 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: "Play soundtrack" }).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: "Play soundtrack" }).click(); await expect( page.getByText("Audio unavailable. Continue without sound."), ).toBeVisible(); await expect(page.locator("main#portfolio-content")).toBeVisible(); });