fix: synchronize responsive navigation and soundtrack cue

This commit is contained in:
2026-09-02 18:33:52 +03:00
parent b4728ceef0
commit 2ea3cab142
12 changed files with 661 additions and 232 deletions

View File

@@ -1,24 +1,32 @@
import { expect, test } from '../helpers/serve-dist';
import { expect, test } from "../helpers/serve-dist";
import {
assertSemanticFaultOracle,
authorizedFaultUrl,
collectBrowserErrors,
readUnhandledRejections,
triggerFaultAction,
} from '../helpers/test-build-auth';
import { installSceneInstrumentation, readSceneInstrumentation } from '../helpers/scene-instrumentation';
} from "../helpers/test-build-auth";
import {
installSceneInstrumentation,
readSceneInstrumentation,
} from "../helpers/scene-instrumentation";
test.beforeEach(async ({ page }, testInfo) => {
test.skip(testInfo.project.name !== 'desktop-chromium', 'Audio fault matrix runs once on current desktop Chromium.');
test.skip(
testInfo.project.name !== "desktop-chromium",
"Audio fault matrix runs once on current desktop Chromium.",
);
await installSceneInstrumentation(page);
});
const startAudio = async (page: import('@playwright/test').Page): Promise<void> => {
await page.getByRole('button', { name: 'START THE DRIVE' }).click();
const startAudio = async (
page: import("@playwright/test").Page,
): Promise<void> => {
await page.getByRole("button", { name: "PLAY SOUNDTRACK" }).click();
};
const assertAudioContained = async (
page: import('@playwright/test').Page,
page: import("@playwright/test").Page,
pageErrors: readonly string[],
expectedLiveContexts: number,
): Promise<void> => {
@@ -31,59 +39,104 @@ const assertAudioContained = async (
expect(resources.liveAudioContexts).toBe(expectedLiveContexts);
};
test('F10 contains initial Web Audio start rejection and creates no reusable graph', async ({ page, distUrl }) => {
test("F10 contains initial Web Audio start rejection and creates no reusable graph", async ({
page,
distUrl,
}) => {
const errors = await collectBrowserErrors(page);
await page.goto(authorizedFaultUrl(distUrl, 'F10-audio-start-rejection'), { waitUntil: 'domcontentloaded' });
await page.goto(authorizedFaultUrl(distUrl, "F10-audio-start-rejection"), {
waitUntil: "domcontentloaded",
});
await startAudio(page);
await expect(page.getByText('Audio unavailable. Continue without sound.')).toBeVisible();
await expect(page.getByRole('button', { name: 'START THE DRIVE' })).toHaveCount(0);
await expect(
page.getByText("Audio unavailable. Continue without sound."),
).toBeVisible();
await expect(
page.getByRole("button", { name: "PLAY SOUNDTRACK" }),
).toHaveCount(0);
await assertSemanticFaultOracle(page);
expect(errors.pageErrors).toEqual([]);
expect(await readUnhandledRejections(page)).toEqual([]);
expect((await readSceneInstrumentation(page)).audioContextsConstructed).toBe(0);
expect((await readSceneInstrumentation(page)).audioContextsConstructed).toBe(
0,
);
});
test('F11 keeps an interrupted intact graph suspended after resume rejection', async ({ page, distUrl }) => {
test("F11 keeps an interrupted intact graph suspended after resume rejection", async ({
page,
distUrl,
}) => {
const errors = await collectBrowserErrors(page);
await page.goto(authorizedFaultUrl(distUrl, 'F11-audio-resume-rejection'), { waitUntil: 'domcontentloaded' });
await page.goto(authorizedFaultUrl(distUrl, "F11-audio-resume-rejection"), {
waitUntil: "domcontentloaded",
});
await startAudio(page);
await expect(page.getByRole('button', { name: 'Mute sound' })).toBeVisible();
await expect(page.getByRole("button", { name: "Mute sound" })).toBeVisible();
await triggerFaultAction(page, 'audio-interruption');
await expect(page.getByRole('button', { name: 'Resume sound' })).toBeVisible();
await page.getByRole('button', { name: 'Resume sound' }).click();
await triggerFaultAction(page, "audio-interruption");
await expect(
page.getByRole("button", { name: "Resume sound" }),
).toBeVisible();
await page.getByRole("button", { name: "Resume sound" }).click();
await expect(page.getByRole('button', { name: 'Resume sound' })).toBeVisible();
await expect(page.getByText('Audio unavailable. Continue without sound.')).toHaveCount(0);
await expect(
page.getByRole("button", { name: "Resume sound" }),
).toBeVisible();
await expect(
page.getByText("Audio unavailable. Continue without sound."),
).toHaveCount(0);
await assertAudioContained(page, errors.pageErrors, 1);
});
test('F12 tears down a graph that becomes invalid during resume', async ({ page, distUrl }) => {
test("F12 tears down a graph that becomes invalid during resume", async ({
page,
distUrl,
}) => {
const errors = await collectBrowserErrors(page);
await page.goto(authorizedFaultUrl(distUrl, 'F12-audio-graph-invalid'), { waitUntil: 'domcontentloaded' });
await page.goto(authorizedFaultUrl(distUrl, "F12-audio-graph-invalid"), {
waitUntil: "domcontentloaded",
});
await startAudio(page);
await triggerFaultAction(page, 'audio-interruption');
await expect(page.getByRole('button', { name: 'Resume sound' })).toBeVisible();
await page.getByRole('button', { name: 'Resume sound' }).click();
await triggerFaultAction(page, "audio-interruption");
await expect(
page.getByRole("button", { name: "Resume sound" }),
).toBeVisible();
await page.getByRole("button", { name: "Resume sound" }).click();
await expect(page.getByText('Audio unavailable. Continue without sound.')).toBeVisible();
await expect.poll(async () => (await readSceneInstrumentation(page)).liveAudioContexts).toBe(0);
await expect(
page.getByText("Audio unavailable. Continue without sound."),
).toBeVisible();
await expect
.poll(async () => (await readSceneInstrumentation(page)).liveAudioContexts)
.toBe(0);
await assertAudioContained(page, errors.pageErrors, 0);
});
test('F13 synchronously silences and closes the graph when hide-time suspend rejects', async ({ page, distUrl }) => {
test("F13 synchronously silences and closes the graph when hide-time suspend rejects", async ({
page,
distUrl,
}) => {
const errors = await collectBrowserErrors(page);
await page.goto(authorizedFaultUrl(distUrl, 'F13-audio-suspend-rejection'), { waitUntil: 'domcontentloaded' });
await page.goto(authorizedFaultUrl(distUrl, "F13-audio-suspend-rejection"), {
waitUntil: "domcontentloaded",
});
await startAudio(page);
await expect(page.getByRole('button', { name: 'Mute sound' })).toBeVisible();
await expect(page.getByRole("button", { name: "Mute sound" })).toBeVisible();
await page.evaluate(() => {
Object.defineProperty(document, 'visibilityState', { configurable: true, value: 'hidden' });
document.dispatchEvent(new Event('visibilitychange'));
Object.defineProperty(document, "visibilityState", {
configurable: true,
value: "hidden",
});
document.dispatchEvent(new Event("visibilitychange"));
});
await expect(page.getByText('Audio unavailable. Continue without sound.')).toBeVisible();
await expect.poll(async () => (await readSceneInstrumentation(page)).liveAudioContexts).toBe(0);
await expect(
page.getByText("Audio unavailable. Continue without sound."),
).toBeVisible();
await expect
.poll(async () => (await readSceneInstrumentation(page)).liveAudioContexts)
.toBe(0);
await assertAudioContained(page, errors.pageErrors, 0);
});