fix: synchronize responsive navigation and soundtrack cue
This commit is contained in:
@@ -56,7 +56,7 @@ async function enterState(
|
||||
await page.goto(distUrl, { waitUntil: "domcontentloaded" });
|
||||
|
||||
if (state === "audio-running") {
|
||||
await page.getByRole("button", { name: "START THE DRIVE" }).click();
|
||||
await page.getByRole("button", { name: "PLAY SOUNDTRACK" }).click();
|
||||
await expect(
|
||||
page.getByRole("button", { name: "Mute sound" }),
|
||||
).toBeVisible();
|
||||
|
||||
@@ -225,6 +225,7 @@ test("responsive navigation exposes the exact visible label as each link's acces
|
||||
).toHaveCount(0);
|
||||
|
||||
await page.setViewportSize({ width: 320, height: 800 });
|
||||
await revealNavigationLinks(page);
|
||||
const career = page.getByRole("link", { name: "Career", exact: true });
|
||||
await expect(career).toHaveText("Career");
|
||||
await expect(
|
||||
|
||||
@@ -26,6 +26,21 @@ test.beforeEach(async ({ page }) => {
|
||||
});
|
||||
});
|
||||
|
||||
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,
|
||||
@@ -38,7 +53,7 @@ test("starts Web Audio synchronously from the visitor gesture and exposes access
|
||||
window.addEventListener("click", (event) => {
|
||||
if (
|
||||
(event.target as HTMLElement | null)?.textContent?.includes(
|
||||
"START THE DRIVE",
|
||||
"PLAY SOUNDTRACK",
|
||||
)
|
||||
) {
|
||||
const state = window as unknown as {
|
||||
@@ -51,7 +66,7 @@ test("starts Web Audio synchronously from the visitor gesture and exposes access
|
||||
});
|
||||
|
||||
await expect(
|
||||
page.getByRole("button", { name: "Start the drive" }),
|
||||
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(
|
||||
@@ -67,7 +82,7 @@ test("starts Web Audio synchronously from the visitor gesture and exposes access
|
||||
)
|
||||
.toBe(0);
|
||||
|
||||
await page.getByRole("button", { name: "Start the drive" }).click();
|
||||
await page.getByRole("button", { name: "Play soundtrack" }).click();
|
||||
|
||||
await expect(page.getByRole("button", { name: "Mute sound" })).toBeVisible();
|
||||
await expect(
|
||||
@@ -109,7 +124,7 @@ test("keeps the compact controls usable and dismisses the upward panels", async
|
||||
distUrl,
|
||||
}) => {
|
||||
await page.goto(distUrl, { waitUntil: "domcontentloaded" });
|
||||
await page.getByRole("button", { name: "Start the drive" }).click();
|
||||
await page.getByRole("button", { name: "Play soundtrack" }).click();
|
||||
|
||||
const controls = page
|
||||
.getByRole("group", { name: "Drive controls" })
|
||||
@@ -167,7 +182,7 @@ test("persists native volume and keeps sound independent from Static motion", as
|
||||
distUrl,
|
||||
}) => {
|
||||
await page.goto(distUrl, { waitUntil: "domcontentloaded" });
|
||||
await page.getByRole("button", { name: "Start the drive" }).click();
|
||||
await page.getByRole("button", { name: "Play soundtrack" }).click();
|
||||
|
||||
await page.getByRole("button", { name: "Sound volume" }).click();
|
||||
const volume = page.getByLabel("Sound volume level");
|
||||
@@ -195,7 +210,7 @@ test("requires a direct visitor action after browser interruption without creati
|
||||
distUrl,
|
||||
}) => {
|
||||
await page.goto(distUrl, { waitUntil: "domcontentloaded" });
|
||||
await page.getByRole("button", { name: "Start the drive" }).click();
|
||||
await page.getByRole("button", { name: "Play soundtrack" }).click();
|
||||
|
||||
await page.evaluate(() => {
|
||||
const context = (window as unknown as { __audioContexts: AudioContext[] })
|
||||
@@ -242,7 +257,7 @@ test("shows a concise non-blocking message when initial audio start is rejected"
|
||||
});
|
||||
await page.goto(distUrl, { waitUntil: "domcontentloaded" });
|
||||
|
||||
await page.getByRole("button", { name: "Start the drive" }).click();
|
||||
await page.getByRole("button", { name: "Play soundtrack" }).click();
|
||||
|
||||
await expect(
|
||||
page.getByText("Audio unavailable. Continue without sound."),
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
@@ -16,7 +16,7 @@ test("synthetic persisted freeze and restore keeps at most one canvas, RAF loop,
|
||||
"data-scene-state",
|
||||
"running",
|
||||
);
|
||||
await page.getByRole("button", { name: "START THE DRIVE" }).click();
|
||||
await page.getByRole("button", { name: "PLAY SOUNDTRACK" }).click();
|
||||
await expect(page.getByRole("button", { name: "Mute sound" })).toBeVisible();
|
||||
|
||||
for (let cycle = 0; cycle < 3; cycle += 1) {
|
||||
@@ -57,7 +57,7 @@ test("persisted lifecycle events preserve paused and Static states without dupli
|
||||
"running",
|
||||
);
|
||||
|
||||
await page.getByRole("button", { name: "Start the drive" }).click();
|
||||
await page.getByRole("button", { name: "Play soundtrack" }).click();
|
||||
await page.getByRole("button", { name: "Pause motion" }).click();
|
||||
await dispatchPersistedPageTransition(page, "pagehide");
|
||||
await dispatchPersistedPageTransition(page, "pageshow");
|
||||
|
||||
@@ -1,26 +1,41 @@
|
||||
import { expect, test } from '../helpers/serve-dist';
|
||||
import { expect, test } from "../helpers/serve-dist";
|
||||
|
||||
test('keeps the decorative world separate from the semantic reading path', async ({ page, distUrl }) => {
|
||||
await page.goto(distUrl, { waitUntil: 'domcontentloaded' });
|
||||
test("keeps the decorative world separate from the semantic reading path", async ({
|
||||
page,
|
||||
distUrl,
|
||||
}) => {
|
||||
await page.goto(distUrl, { waitUntil: "domcontentloaded" });
|
||||
|
||||
await expect(page.locator('#journey-scene-description')).toContainText('night');
|
||||
await expect(page.locator('.journey-world')).toHaveAttribute('aria-hidden', 'true');
|
||||
await expect(page.locator('.journey-poster')).toHaveAttribute('alt', '');
|
||||
await expect(page.locator('.journey-poster')).toHaveAttribute('width', '1600');
|
||||
await expect(page.locator('main#portfolio-content')).toBeVisible();
|
||||
await expect(page.locator("#journey-scene-description")).toContainText(
|
||||
"night",
|
||||
);
|
||||
await expect(page.locator(".journey-world")).toHaveAttribute(
|
||||
"aria-hidden",
|
||||
"true",
|
||||
);
|
||||
await expect(page.locator(".journey-poster")).toHaveAttribute("alt", "");
|
||||
await expect(page.locator(".journey-poster")).toHaveAttribute(
|
||||
"width",
|
||||
"1600",
|
||||
);
|
||||
await expect(page.locator("main#portfolio-content")).toBeVisible();
|
||||
});
|
||||
|
||||
test('keeps the hero and navigation in paint layers above the world', async ({ page, distUrl }) => {
|
||||
test("keeps the hero and navigation in paint layers above the world", async ({
|
||||
page,
|
||||
distUrl,
|
||||
}) => {
|
||||
await page.setViewportSize({ width: 1440, height: 900 });
|
||||
await page.goto(distUrl, { waitUntil: 'domcontentloaded' });
|
||||
await page.goto(distUrl, { waitUntil: "domcontentloaded" });
|
||||
|
||||
const layers = await page.evaluate(() => {
|
||||
const world = document.querySelector<HTMLElement>('.journey-world');
|
||||
const shell = document.querySelector<HTMLElement>('.portfolio-shell');
|
||||
const hero = document.querySelector<HTMLElement>('.hero-journey__content');
|
||||
const heading = document.querySelector<HTMLElement>('.hero-journey h1');
|
||||
const navigation = document.querySelector<HTMLElement>('.site-navigation');
|
||||
if (!world || !shell || !hero || !heading || !navigation) throw new Error('Missing top-screen structure');
|
||||
const world = document.querySelector<HTMLElement>(".journey-world");
|
||||
const shell = document.querySelector<HTMLElement>(".portfolio-shell");
|
||||
const hero = document.querySelector<HTMLElement>(".hero-journey__content");
|
||||
const heading = document.querySelector<HTMLElement>(".hero-journey h1");
|
||||
const navigation = document.querySelector<HTMLElement>(".site-navigation");
|
||||
if (!world || !shell || !hero || !heading || !navigation)
|
||||
throw new Error("Missing top-screen structure");
|
||||
const worldStyle = getComputedStyle(world);
|
||||
const heroStyle = getComputedStyle(hero);
|
||||
const navigationStyle = getComputedStyle(navigation);
|
||||
@@ -29,14 +44,20 @@ test('keeps the hero and navigation in paint layers above the world', async ({ p
|
||||
shellZ: Number(getComputedStyle(shell).zIndex),
|
||||
navigationZ: Number(navigationStyle.zIndex),
|
||||
navigationPosition: navigationStyle.position,
|
||||
headingVisible: heading.getBoundingClientRect().width > 0 && heading.getBoundingClientRect().height > 0,
|
||||
navigationVisible: navigation.getBoundingClientRect().width > 0 && navigation.getBoundingClientRect().height > 0,
|
||||
heroOpacity: Number.parseFloat(heroStyle.backgroundColor.match(/[\d.]+(?=\))/gu)?.at(-1) ?? '1'),
|
||||
headingVisible:
|
||||
heading.getBoundingClientRect().width > 0 &&
|
||||
heading.getBoundingClientRect().height > 0,
|
||||
navigationVisible:
|
||||
navigation.getBoundingClientRect().width > 0 &&
|
||||
navigation.getBoundingClientRect().height > 0,
|
||||
heroOpacity: Number.parseFloat(
|
||||
heroStyle.backgroundColor.match(/[\d.]+(?=\))/gu)?.at(-1) ?? "1",
|
||||
),
|
||||
};
|
||||
});
|
||||
|
||||
expect(layers).toMatchObject({
|
||||
navigationPosition: 'fixed',
|
||||
navigationPosition: "fixed",
|
||||
headingVisible: true,
|
||||
navigationVisible: true,
|
||||
});
|
||||
@@ -45,21 +66,52 @@ test('keeps the hero and navigation in paint layers above the world', async ({ p
|
||||
expect(layers.heroOpacity).toBeGreaterThanOrEqual(0.94);
|
||||
});
|
||||
|
||||
test('keeps a scrolled content panel in a geometry-safe paint layer above the world', async ({ page, distUrl }) => {
|
||||
test("resets the navigation disclosure when crossing the compact breakpoint", async ({
|
||||
page,
|
||||
distUrl,
|
||||
}) => {
|
||||
await page.setViewportSize({ width: 1440, height: 900 });
|
||||
await page.goto(distUrl, { waitUntil: 'domcontentloaded' });
|
||||
await page.goto(distUrl, { waitUntil: "domcontentloaded" });
|
||||
|
||||
const disclosure = page.locator(".site-navigation details");
|
||||
const summary = page.locator(".site-navigation summary");
|
||||
const startLink = page.locator('.site-navigation a[href="#start"]');
|
||||
await expect(disclosure).toHaveAttribute("open", "");
|
||||
await expect(startLink).toBeVisible();
|
||||
|
||||
await page.setViewportSize({ width: 630, height: 900 });
|
||||
await expect(summary).toBeVisible();
|
||||
await expect(disclosure).not.toHaveAttribute("open", "");
|
||||
await expect(startLink).toBeHidden();
|
||||
|
||||
await summary.click();
|
||||
await expect(disclosure).toHaveAttribute("open", "");
|
||||
await page.setViewportSize({ width: 1440, height: 900 });
|
||||
await expect(summary).toBeHidden();
|
||||
await expect(disclosure).toHaveAttribute("open", "");
|
||||
await expect(startLink).toBeVisible();
|
||||
});
|
||||
|
||||
test("keeps a scrolled content panel in a geometry-safe paint layer above the world", async ({
|
||||
page,
|
||||
distUrl,
|
||||
}) => {
|
||||
await page.setViewportSize({ width: 1440, height: 900 });
|
||||
await page.goto(distUrl, { waitUntil: "domcontentloaded" });
|
||||
const profileLink = page.locator('nav a[href="#profile"]');
|
||||
if (!(await profileLink.isVisible())) await page.locator('.site-navigation summary').click();
|
||||
if (!(await profileLink.isVisible()))
|
||||
await page.locator(".site-navigation summary").click();
|
||||
await profileLink.click();
|
||||
await expect(page).toHaveURL(`${distUrl}/#profile`);
|
||||
await expect(page.locator('#profile-heading')).toBeInViewport();
|
||||
await expect(page.locator("#profile-heading")).toBeInViewport();
|
||||
|
||||
const layers = await page.evaluate(() => {
|
||||
const world = document.querySelector<HTMLElement>('.journey-world');
|
||||
const main = document.querySelector<HTMLElement>('.portfolio-main');
|
||||
const panel = document.querySelector<HTMLElement>('#profile');
|
||||
const heading = document.querySelector<HTMLElement>('#profile-heading');
|
||||
if (!world || !main || !panel || !heading) throw new Error('Missing scrolled panel structure');
|
||||
const world = document.querySelector<HTMLElement>(".journey-world");
|
||||
const main = document.querySelector<HTMLElement>(".portfolio-main");
|
||||
const panel = document.querySelector<HTMLElement>("#profile");
|
||||
const heading = document.querySelector<HTMLElement>("#profile-heading");
|
||||
if (!world || !main || !panel || !heading)
|
||||
throw new Error("Missing scrolled panel structure");
|
||||
const mainStyle = getComputedStyle(main);
|
||||
const panelStyle = getComputedStyle(panel);
|
||||
const headingRect = heading.getBoundingClientRect();
|
||||
@@ -68,73 +120,107 @@ test('keeps a scrolled content panel in a geometry-safe paint layer above the wo
|
||||
mainIsolation: mainStyle.isolation,
|
||||
mainPosition: mainStyle.position,
|
||||
headingVisible:
|
||||
headingRect.width > 0
|
||||
&& headingRect.height > 0
|
||||
&& headingRect.bottom > 0
|
||||
&& headingRect.top < window.innerHeight,
|
||||
panelOpacity: Number.parseFloat(panelStyle.backgroundColor.match(/[\d.]+(?=\))/gu)?.at(-1) ?? '1'),
|
||||
headingRect.width > 0 &&
|
||||
headingRect.height > 0 &&
|
||||
headingRect.bottom > 0 &&
|
||||
headingRect.top < window.innerHeight,
|
||||
panelOpacity: Number.parseFloat(
|
||||
panelStyle.backgroundColor.match(/[\d.]+(?=\))/gu)?.at(-1) ?? "1",
|
||||
),
|
||||
};
|
||||
});
|
||||
|
||||
expect(layers).toMatchObject({
|
||||
worldZ: 0,
|
||||
mainIsolation: 'isolate',
|
||||
mainPosition: 'static',
|
||||
mainIsolation: "isolate",
|
||||
mainPosition: "static",
|
||||
headingVisible: true,
|
||||
});
|
||||
expect(layers.panelOpacity).toBeGreaterThanOrEqual(0.94);
|
||||
});
|
||||
|
||||
test('provides native motion and quality controls with visible state', async ({ page, distUrl }) => {
|
||||
await page.goto(distUrl, { waitUntil: 'domcontentloaded' });
|
||||
test("provides native motion and quality controls with visible state", async ({
|
||||
page,
|
||||
distUrl,
|
||||
}) => {
|
||||
await page.goto(distUrl, { waitUntil: "domcontentloaded" });
|
||||
|
||||
await page.getByRole('button', { name: 'Drive settings' }).click();
|
||||
const quality = page.getByLabel('Quality control');
|
||||
await page.getByRole("button", { name: "Drive settings" }).click();
|
||||
const quality = page.getByLabel("Quality control");
|
||||
await expect(quality).toBeVisible();
|
||||
await expect(page.getByRole('status')).toHaveCount(0);
|
||||
await page.keyboard.press('Escape');
|
||||
await page.getByRole('button', { name: 'Start the drive' }).click();
|
||||
await expect(page.getByRole('button', { name: 'Pause motion' })).toBeVisible();
|
||||
const canvas = page.locator('.journey-canvas');
|
||||
await expect(page.getByRole("status")).toHaveCount(0);
|
||||
await page.keyboard.press("Escape");
|
||||
await page.getByRole("button", { name: "Play soundtrack" }).click();
|
||||
await expect(
|
||||
page.getByRole("button", { name: "Pause motion" }),
|
||||
).toBeVisible();
|
||||
const canvas = page.locator(".journey-canvas");
|
||||
const originalCanvas = await canvas.elementHandle();
|
||||
const originalGeneration = await page.locator('.journey-world').getAttribute('data-scene-generation');
|
||||
const originalGeneration = await page
|
||||
.locator(".journey-world")
|
||||
.getAttribute("data-scene-generation");
|
||||
|
||||
await page.getByRole('button', { name: 'Pause motion' }).click();
|
||||
await expect(page.getByRole('button', { name: 'Resume motion' })).toBeVisible();
|
||||
await page.getByRole("button", { name: "Pause motion" }).click();
|
||||
await expect(
|
||||
page.getByRole("button", { name: "Resume motion" }),
|
||||
).toBeVisible();
|
||||
await expect(canvas).toHaveCount(1);
|
||||
await expect(page.locator('.journey-world')).toHaveAttribute('data-scene-generation', originalGeneration ?? '');
|
||||
expect(await originalCanvas?.evaluate((element) => element.isConnected)).toBe(true);
|
||||
await expect(page.locator(".journey-world")).toHaveAttribute(
|
||||
"data-scene-generation",
|
||||
originalGeneration ?? "",
|
||||
);
|
||||
expect(await originalCanvas?.evaluate((element) => element.isConnected)).toBe(
|
||||
true,
|
||||
);
|
||||
|
||||
await page.getByRole('button', { name: 'Resume motion' }).click();
|
||||
await expect(page.getByRole('button', { name: 'Pause motion' })).toBeVisible();
|
||||
await page.getByRole("button", { name: "Resume motion" }).click();
|
||||
await expect(
|
||||
page.getByRole("button", { name: "Pause motion" }),
|
||||
).toBeVisible();
|
||||
await expect(canvas).toHaveCount(1);
|
||||
await expect(page.locator('.journey-world')).toHaveAttribute('data-scene-generation', originalGeneration ?? '');
|
||||
expect(await originalCanvas?.evaluate((element) => element.isConnected)).toBe(true);
|
||||
await expect(page.locator(".journey-world")).toHaveAttribute(
|
||||
"data-scene-generation",
|
||||
originalGeneration ?? "",
|
||||
);
|
||||
expect(await originalCanvas?.evaluate((element) => element.isConnected)).toBe(
|
||||
true,
|
||||
);
|
||||
|
||||
await page.getByRole('button', { name: 'Drive settings' }).click();
|
||||
await page.getByLabel('Quality control').selectOption('static');
|
||||
await expect(page.getByRole('button', { name: 'Enable motion' })).toBeVisible();
|
||||
await expect(page.locator('.journey-canvas')).toHaveCount(0);
|
||||
await expect(page.getByRole('status')).toContainText('Motion: static');
|
||||
await page.getByRole("button", { name: "Drive settings" }).click();
|
||||
await page.getByLabel("Quality control").selectOption("static");
|
||||
await expect(
|
||||
page.getByRole("button", { name: "Enable motion" }),
|
||||
).toBeVisible();
|
||||
await expect(page.locator(".journey-canvas")).toHaveCount(0);
|
||||
await expect(page.getByRole("status")).toContainText("Motion: static");
|
||||
});
|
||||
|
||||
test('never creates a canvas when the system requests reduced motion', async ({ page, distUrl }) => {
|
||||
await page.emulateMedia({ reducedMotion: 'reduce' });
|
||||
await page.goto(distUrl, { waitUntil: 'domcontentloaded' });
|
||||
test("never creates a canvas when the system requests reduced motion", async ({
|
||||
page,
|
||||
distUrl,
|
||||
}) => {
|
||||
await page.emulateMedia({ reducedMotion: "reduce" });
|
||||
await page.goto(distUrl, { waitUntil: "domcontentloaded" });
|
||||
|
||||
await expect(page.locator('.journey-canvas')).toHaveCount(0);
|
||||
await page.getByRole('button', { name: 'Drive settings' }).click();
|
||||
await expect(page.getByRole('status')).toContainText('reduced motion requested');
|
||||
await expect(page.getByLabel('Quality control')).toBeVisible();
|
||||
await expect(page.locator(".journey-canvas")).toHaveCount(0);
|
||||
await page.getByRole("button", { name: "Drive settings" }).click();
|
||||
await expect(page.getByRole("status")).toContainText(
|
||||
"reduced motion requested",
|
||||
);
|
||||
await expect(page.getByLabel("Quality control")).toBeVisible();
|
||||
});
|
||||
|
||||
test('shows ARRIVED from the authoritative section snapshot at Contact', async ({ page, distUrl }) => {
|
||||
await page.goto(distUrl, { waitUntil: 'domcontentloaded' });
|
||||
test("shows ARRIVED from the authoritative section snapshot at Contact", async ({
|
||||
page,
|
||||
distUrl,
|
||||
}) => {
|
||||
await page.goto(distUrl, { waitUntil: "domcontentloaded" });
|
||||
const contactLink = page.locator('nav a[href="#contact"]');
|
||||
if (!(await contactLink.isVisible())) await page.locator('.site-navigation summary').click();
|
||||
if (!(await contactLink.isVisible()))
|
||||
await page.locator(".site-navigation summary").click();
|
||||
await contactLink.click();
|
||||
|
||||
await expect(page.locator('.journey-hud')).toContainText('Contact');
|
||||
await expect(page.locator('.journey-hud')).toContainText('ARRIVED');
|
||||
await expect(page.locator('.journey-hud')).toContainText('100%');
|
||||
await expect(page.locator(".journey-hud")).toContainText("Contact");
|
||||
await expect(page.locator(".journey-hud")).toContainText("ARRIVED");
|
||||
await expect(page.locator(".journey-hud")).toContainText("100%");
|
||||
});
|
||||
|
||||
@@ -395,7 +395,7 @@ test.describe("production visual composition", () => {
|
||||
}, testInfo) => {
|
||||
await page.setViewportSize({ width: 1440, height: 900 });
|
||||
await waitForRenderedPortfolio(page, distUrl);
|
||||
await page.getByRole("button", { name: "START THE DRIVE" }).click();
|
||||
await page.getByRole("button", { name: "PLAY SOUNDTRACK" }).click();
|
||||
await expect(
|
||||
page.getByRole("button", { name: "Mute sound" }),
|
||||
).toBeVisible();
|
||||
|
||||
Reference in New Issue
Block a user