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

@@ -51,7 +51,7 @@ Focused evidence is generated by:
npm run test:browser -- tests/browser/visual-regression.spec.ts npm run test:browser -- tests/browser/visual-regression.spec.ts
``` ```
The final 2026-09-02 complete browser run passed 165 checks with 43 deliberate project/capability skips and no failures. The 16 visual checks ran once in the available current Chromium project; the duplicate mobile-project visual cases were skipped intentionally. The final 2026-09-02 complete browser run passed 169 checks with 43 deliberate project/capability skips and no failures. The 16 visual checks ran once in the available current Chromium project; the duplicate mobile-project visual cases were skipped intentionally.
The compact 320×568 capture initially revealed that the fixed drive controls occupied 43.5% of the viewport and obscured the opening content. The mobile-only control grid reduced that surface to 24.0%; a regression guard now caps it at 34%. After the trace-driven fixed section navigation landed, the captures exposed the decorative HUD intersecting the navigation at phone widths and the controls at 768×1024. A shared exact navigation-height variable now keeps the fixed navigation and visible compact HUD edge-adjacent, reserves their space in the header, and leaves the controls independent. The final measured bounds were 0–93px navigation and 93–125px HUD at 320×568, and 0–45px navigation and 45–77px HUD at 768×1024. Fixed-navigation focus now uses an inward standards outline so the indicator remains visible inside the viewport. Passive-section coverage now positions the target safely beyond the actual 35% activation line rather than relying on a relative `offsetTop` or exact fractional equality. The compact 320×568 capture initially revealed that the fixed drive controls occupied 43.5% of the viewport and obscured the opening content. The mobile-only control grid reduced that surface to 24.0%; a regression guard now caps it at 34%. After the trace-driven fixed section navigation landed, the captures exposed the decorative HUD intersecting the navigation at phone widths and the controls at 768×1024. A shared exact navigation-height variable now keeps the fixed navigation and visible compact HUD edge-adjacent, reserves their space in the header, and leaves the controls independent. The final measured bounds were 0–93px navigation and 93–125px HUD at 320×568, and 0–45px navigation and 45–77px HUD at 768×1024. Fixed-navigation focus now uses an inward standards outline so the indicator remains visible inside the viewport. Passive-section coverage now positions the target safely beyond the actual 35% activation line rather than relying on a relative `offsetTop` or exact fractional equality.
@@ -59,6 +59,8 @@ A one-sampled-pixel accent floor was relaxed from 0.001 to 0.0005 after the capt
The final integrated review identified two high-confidence gaps. The GPU ledger now includes the composer and bloom render targets reported by the post-processing owner, and the infrastructure case study now includes a visible trust-boundary/request-flow key plus its adjacent text equivalent. The requested Contact wording was checked against the implementation plan and retained exactly because it already matches the approved current copy. The full runtime budget gate passed with no violations after recording the actual High-tier allocation, including 161,344,784 render-target bytes. The final integrated review identified two high-confidence gaps. The GPU ledger now includes the composer and bloom render targets reported by the post-processing owner, and the infrastructure case study now includes a visible trust-boundary/request-flow key plus its adjacent text equivalent. The requested Contact wording was checked against the implementation plan and retained exactly because it already matches the approved current copy. The full runtime budget gate passed with no violations after recording the actual High-tier allocation, including 161,344,784 render-target bytes.
The post-review resize pass now synchronizes the native navigation disclosure whenever the compact breakpoint changes: shrinking closes the overlay before it can cover the identity card, while widening restores the complete desktop navigator. The soundtrack's required user gesture is labeled `PLAY SOUNDTRACK` because the visual drive is already running and browser audio cannot begin before an explicit action.
## Direction review checklist ## Direction review checklist
- [x] Information uses near-black opaque panels rather than neon glass. - [x] Information uses near-black opaque panels rather than neon glass.

View File

@@ -178,11 +178,11 @@ onBeforeUnmount(() => {
v-if="audioState.kind === 'idle'" v-if="audioState.kind === 'idle'"
class="drive-start" class="drive-start"
type="button" type="button"
aria-label="Start the drive" aria-label="Play soundtrack"
@click="emit('audioStart')" @click="emit('audioStart')"
> >
<IconGlyph name="play" /> <IconGlyph name="play" />
<span>START THE DRIVE</span> <span>PLAY SOUNDTRACK</span>
</button> </button>
<button <button

View File

@@ -1,7 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { onMounted, ref } from 'vue'; import { onBeforeUnmount, onMounted, ref } from "vue";
import type { SectionId } from '@/content/types'; import type { SectionId } from "@/content/types";
import type { JourneySnapshot } from '@/journey/types'; import type { JourneySnapshot } from "@/journey/types";
const props = defineProps<{ const props = defineProps<{
items: ReadonlyArray<{ id: SectionId; label: string }>; items: ReadonlyArray<{ id: SectionId; label: string }>;
@@ -10,25 +10,41 @@ const props = defineProps<{
}>(); }>();
const navigationLabels: Readonly<Record<SectionId, string>> = { const navigationLabels: Readonly<Record<SectionId, string>> = {
start: 'Start', start: "Start",
profile: 'Profile', profile: "Profile",
experience: 'Career', experience: "Career",
infrastructure: 'Infra', infrastructure: "Infra",
'selected-builds': 'Builds', "selected-builds": "Builds",
'additional-work': 'More work', "additional-work": "More work",
toolkit: 'Toolkit', toolkit: "Toolkit",
contact: 'Contact', contact: "Contact",
}; };
const disclosure = ref<HTMLDetailsElement | null>(null); const disclosure = ref<HTMLDetailsElement | null>(null);
let compactQuery: MediaQueryList | null = null;
const syncDisclosureToViewport = ({
matches,
}: Pick<MediaQueryList, "matches">): void => {
if (matches) disclosure.value?.removeAttribute("open");
else disclosure.value?.setAttribute("open", "");
};
onMounted(() => { onMounted(() => {
if (window.matchMedia('(max-width: 767px)').matches) disclosure.value?.removeAttribute('open'); compactQuery = window.matchMedia("(max-width: 767px)");
syncDisclosureToViewport(compactQuery);
compactQuery.addEventListener("change", syncDisclosureToViewport);
});
onBeforeUnmount(() => {
compactQuery?.removeEventListener("change", syncDisclosureToViewport);
compactQuery = null;
}); });
const navigate = (event: MouseEvent): void => { const navigate = (event: MouseEvent): void => {
props.onSectionLinkClick(event); props.onSectionLinkClick(event);
if (window.matchMedia('(max-width: 767px)').matches) disclosure.value?.removeAttribute('open'); if (window.matchMedia("(max-width: 767px)").matches)
disclosure.value?.removeAttribute("open");
}; };
</script> </script>

View File

@@ -56,7 +56,7 @@ async function enterState(
await page.goto(distUrl, { waitUntil: "domcontentloaded" }); await page.goto(distUrl, { waitUntil: "domcontentloaded" });
if (state === "audio-running") { if (state === "audio-running") {
await page.getByRole("button", { name: "START THE DRIVE" }).click(); await page.getByRole("button", { name: "PLAY SOUNDTRACK" }).click();
await expect( await expect(
page.getByRole("button", { name: "Mute sound" }), page.getByRole("button", { name: "Mute sound" }),
).toBeVisible(); ).toBeVisible();

View File

@@ -225,6 +225,7 @@ test("responsive navigation exposes the exact visible label as each link's acces
).toHaveCount(0); ).toHaveCount(0);
await page.setViewportSize({ width: 320, height: 800 }); await page.setViewportSize({ width: 320, height: 800 });
await revealNavigationLinks(page);
const career = page.getByRole("link", { name: "Career", exact: true }); const career = page.getByRole("link", { name: "Career", exact: true });
await expect(career).toHaveText("Career"); await expect(career).toHaveText("Career");
await expect( await expect(

View File

@@ -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 ({ test("starts Web Audio synchronously from the visitor gesture and exposes accessible controls", async ({
page, page,
distUrl, distUrl,
@@ -38,7 +53,7 @@ test("starts Web Audio synchronously from the visitor gesture and exposes access
window.addEventListener("click", (event) => { window.addEventListener("click", (event) => {
if ( if (
(event.target as HTMLElement | null)?.textContent?.includes( (event.target as HTMLElement | null)?.textContent?.includes(
"START THE DRIVE", "PLAY SOUNDTRACK",
) )
) { ) {
const state = window as unknown as { const state = window as unknown as {
@@ -51,7 +66,7 @@ test("starts Web Audio synchronously from the visitor gesture and exposes access
}); });
await expect( await expect(
page.getByRole("button", { name: "Start the drive" }), page.getByRole("button", { name: "Play soundtrack" }),
).toBeVisible(); ).toBeVisible();
await expect(page.getByRole("button", { name: "Mute sound" })).toHaveCount(0); await expect(page.getByRole("button", { name: "Mute sound" })).toHaveCount(0);
await expect(page.getByRole("button", { name: "Sound volume" })).toHaveCount( 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); .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(page.getByRole("button", { name: "Mute sound" })).toBeVisible();
await expect( await expect(
@@ -109,7 +124,7 @@ test("keeps the compact controls usable and dismisses the upward panels", async
distUrl, distUrl,
}) => { }) => {
await page.goto(distUrl, { waitUntil: "domcontentloaded" }); 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 const controls = page
.getByRole("group", { name: "Drive controls" }) .getByRole("group", { name: "Drive controls" })
@@ -167,7 +182,7 @@ test("persists native volume and keeps sound independent from Static motion", as
distUrl, distUrl,
}) => { }) => {
await page.goto(distUrl, { waitUntil: "domcontentloaded" }); 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(); await page.getByRole("button", { name: "Sound volume" }).click();
const volume = page.getByLabel("Sound volume level"); const volume = page.getByLabel("Sound volume level");
@@ -195,7 +210,7 @@ test("requires a direct visitor action after browser interruption without creati
distUrl, distUrl,
}) => { }) => {
await page.goto(distUrl, { waitUntil: "domcontentloaded" }); 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(() => { await page.evaluate(() => {
const context = (window as unknown as { __audioContexts: AudioContext[] }) 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.goto(distUrl, { waitUntil: "domcontentloaded" });
await page.getByRole("button", { name: "Start the drive" }).click(); await page.getByRole("button", { name: "Play soundtrack" }).click();
await expect( await expect(
page.getByText("Audio unavailable. Continue without sound."), page.getByText("Audio unavailable. Continue without sound."),

View File

@@ -1,24 +1,32 @@
import { expect, test } from '../helpers/serve-dist'; import { expect, test } from "../helpers/serve-dist";
import { import {
assertSemanticFaultOracle, assertSemanticFaultOracle,
authorizedFaultUrl, authorizedFaultUrl,
collectBrowserErrors, collectBrowserErrors,
readUnhandledRejections, readUnhandledRejections,
triggerFaultAction, triggerFaultAction,
} from '../helpers/test-build-auth'; } from "../helpers/test-build-auth";
import { installSceneInstrumentation, readSceneInstrumentation } from '../helpers/scene-instrumentation'; import {
installSceneInstrumentation,
readSceneInstrumentation,
} from "../helpers/scene-instrumentation";
test.beforeEach(async ({ page }, testInfo) => { 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); await installSceneInstrumentation(page);
}); });
const startAudio = async (page: import('@playwright/test').Page): Promise<void> => { const startAudio = async (
await page.getByRole('button', { name: 'START THE DRIVE' }).click(); page: import("@playwright/test").Page,
): Promise<void> => {
await page.getByRole("button", { name: "PLAY SOUNDTRACK" }).click();
}; };
const assertAudioContained = async ( const assertAudioContained = async (
page: import('@playwright/test').Page, page: import("@playwright/test").Page,
pageErrors: readonly string[], pageErrors: readonly string[],
expectedLiveContexts: number, expectedLiveContexts: number,
): Promise<void> => { ): Promise<void> => {
@@ -31,59 +39,104 @@ const assertAudioContained = async (
expect(resources.liveAudioContexts).toBe(expectedLiveContexts); 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); 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 startAudio(page);
await expect(page.getByText('Audio unavailable. Continue without sound.')).toBeVisible(); await expect(
await expect(page.getByRole('button', { name: 'START THE DRIVE' })).toHaveCount(0); page.getByText("Audio unavailable. Continue without sound."),
).toBeVisible();
await expect(
page.getByRole("button", { name: "PLAY SOUNDTRACK" }),
).toHaveCount(0);
await assertSemanticFaultOracle(page); await assertSemanticFaultOracle(page);
expect(errors.pageErrors).toEqual([]); expect(errors.pageErrors).toEqual([]);
expect(await readUnhandledRejections(page)).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); 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 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 triggerFaultAction(page, "audio-interruption");
await expect(page.getByRole('button', { name: 'Resume sound' })).toBeVisible(); await expect(
await page.getByRole('button', { name: 'Resume sound' }).click(); 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(
await expect(page.getByText('Audio unavailable. Continue without sound.')).toHaveCount(0); page.getByRole("button", { name: "Resume sound" }),
).toBeVisible();
await expect(
page.getByText("Audio unavailable. Continue without sound."),
).toHaveCount(0);
await assertAudioContained(page, errors.pageErrors, 1); 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); 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 startAudio(page);
await triggerFaultAction(page, 'audio-interruption'); await triggerFaultAction(page, "audio-interruption");
await expect(page.getByRole('button', { name: 'Resume sound' })).toBeVisible(); await expect(
await page.getByRole('button', { name: 'Resume sound' }).click(); 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(
await expect.poll(async () => (await readSceneInstrumentation(page)).liveAudioContexts).toBe(0); page.getByText("Audio unavailable. Continue without sound."),
).toBeVisible();
await expect
.poll(async () => (await readSceneInstrumentation(page)).liveAudioContexts)
.toBe(0);
await assertAudioContained(page, errors.pageErrors, 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); 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 startAudio(page);
await expect(page.getByRole('button', { name: 'Mute sound' })).toBeVisible(); await expect(page.getByRole("button", { name: "Mute sound" })).toBeVisible();
await page.evaluate(() => { await page.evaluate(() => {
Object.defineProperty(document, 'visibilityState', { configurable: true, value: 'hidden' }); Object.defineProperty(document, "visibilityState", {
document.dispatchEvent(new Event('visibilitychange')); configurable: true,
value: "hidden",
});
document.dispatchEvent(new Event("visibilitychange"));
}); });
await expect(page.getByText('Audio unavailable. Continue without sound.')).toBeVisible(); await expect(
await expect.poll(async () => (await readSceneInstrumentation(page)).liveAudioContexts).toBe(0); page.getByText("Audio unavailable. Continue without sound."),
).toBeVisible();
await expect
.poll(async () => (await readSceneInstrumentation(page)).liveAudioContexts)
.toBe(0);
await assertAudioContained(page, errors.pageErrors, 0); await assertAudioContained(page, errors.pageErrors, 0);
}); });

View File

@@ -16,7 +16,7 @@ test("synthetic persisted freeze and restore keeps at most one canvas, RAF loop,
"data-scene-state", "data-scene-state",
"running", "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(); await expect(page.getByRole("button", { name: "Mute sound" })).toBeVisible();
for (let cycle = 0; cycle < 3; cycle += 1) { for (let cycle = 0; cycle < 3; cycle += 1) {
@@ -57,7 +57,7 @@ test("persisted lifecycle events preserve paused and Static states without dupli
"running", "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 page.getByRole("button", { name: "Pause motion" }).click();
await dispatchPersistedPageTransition(page, "pagehide"); await dispatchPersistedPageTransition(page, "pagehide");
await dispatchPersistedPageTransition(page, "pageshow"); await dispatchPersistedPageTransition(page, "pageshow");

View File

@@ -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 }) => { test("keeps the decorative world separate from the semantic reading path", async ({
await page.goto(distUrl, { waitUntil: 'domcontentloaded' }); page,
distUrl,
}) => {
await page.goto(distUrl, { waitUntil: "domcontentloaded" });
await expect(page.locator('#journey-scene-description')).toContainText('night'); await expect(page.locator("#journey-scene-description")).toContainText(
await expect(page.locator('.journey-world')).toHaveAttribute('aria-hidden', 'true'); "night",
await expect(page.locator('.journey-poster')).toHaveAttribute('alt', ''); );
await expect(page.locator('.journey-poster')).toHaveAttribute('width', '1600'); await expect(page.locator(".journey-world")).toHaveAttribute(
await expect(page.locator('main#portfolio-content')).toBeVisible(); "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.setViewportSize({ width: 1440, height: 900 });
await page.goto(distUrl, { waitUntil: 'domcontentloaded' }); await page.goto(distUrl, { waitUntil: "domcontentloaded" });
const layers = await page.evaluate(() => { const layers = await page.evaluate(() => {
const world = document.querySelector<HTMLElement>('.journey-world'); const world = document.querySelector<HTMLElement>(".journey-world");
const shell = document.querySelector<HTMLElement>('.portfolio-shell'); const shell = document.querySelector<HTMLElement>(".portfolio-shell");
const hero = document.querySelector<HTMLElement>('.hero-journey__content'); const hero = document.querySelector<HTMLElement>(".hero-journey__content");
const heading = document.querySelector<HTMLElement>('.hero-journey h1'); const heading = document.querySelector<HTMLElement>(".hero-journey h1");
const navigation = document.querySelector<HTMLElement>('.site-navigation'); const navigation = document.querySelector<HTMLElement>(".site-navigation");
if (!world || !shell || !hero || !heading || !navigation) throw new Error('Missing top-screen structure'); if (!world || !shell || !hero || !heading || !navigation)
throw new Error("Missing top-screen structure");
const worldStyle = getComputedStyle(world); const worldStyle = getComputedStyle(world);
const heroStyle = getComputedStyle(hero); const heroStyle = getComputedStyle(hero);
const navigationStyle = getComputedStyle(navigation); 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), shellZ: Number(getComputedStyle(shell).zIndex),
navigationZ: Number(navigationStyle.zIndex), navigationZ: Number(navigationStyle.zIndex),
navigationPosition: navigationStyle.position, navigationPosition: navigationStyle.position,
headingVisible: heading.getBoundingClientRect().width > 0 && heading.getBoundingClientRect().height > 0, headingVisible:
navigationVisible: navigation.getBoundingClientRect().width > 0 && navigation.getBoundingClientRect().height > 0, heading.getBoundingClientRect().width > 0 &&
heroOpacity: Number.parseFloat(heroStyle.backgroundColor.match(/[\d.]+(?=\))/gu)?.at(-1) ?? '1'), 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({ expect(layers).toMatchObject({
navigationPosition: 'fixed', navigationPosition: "fixed",
headingVisible: true, headingVisible: true,
navigationVisible: 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); 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.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"]'); 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 profileLink.click();
await expect(page).toHaveURL(`${distUrl}/#profile`); 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 layers = await page.evaluate(() => {
const world = document.querySelector<HTMLElement>('.journey-world'); const world = document.querySelector<HTMLElement>(".journey-world");
const main = document.querySelector<HTMLElement>('.portfolio-main'); const main = document.querySelector<HTMLElement>(".portfolio-main");
const panel = document.querySelector<HTMLElement>('#profile'); const panel = document.querySelector<HTMLElement>("#profile");
const heading = document.querySelector<HTMLElement>('#profile-heading'); const heading = document.querySelector<HTMLElement>("#profile-heading");
if (!world || !main || !panel || !heading) throw new Error('Missing scrolled panel structure'); if (!world || !main || !panel || !heading)
throw new Error("Missing scrolled panel structure");
const mainStyle = getComputedStyle(main); const mainStyle = getComputedStyle(main);
const panelStyle = getComputedStyle(panel); const panelStyle = getComputedStyle(panel);
const headingRect = heading.getBoundingClientRect(); 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, mainIsolation: mainStyle.isolation,
mainPosition: mainStyle.position, mainPosition: mainStyle.position,
headingVisible: headingVisible:
headingRect.width > 0 headingRect.width > 0 &&
&& headingRect.height > 0 headingRect.height > 0 &&
&& headingRect.bottom > 0 headingRect.bottom > 0 &&
&& headingRect.top < window.innerHeight, headingRect.top < window.innerHeight,
panelOpacity: Number.parseFloat(panelStyle.backgroundColor.match(/[\d.]+(?=\))/gu)?.at(-1) ?? '1'), panelOpacity: Number.parseFloat(
panelStyle.backgroundColor.match(/[\d.]+(?=\))/gu)?.at(-1) ?? "1",
),
}; };
}); });
expect(layers).toMatchObject({ expect(layers).toMatchObject({
worldZ: 0, worldZ: 0,
mainIsolation: 'isolate', mainIsolation: "isolate",
mainPosition: 'static', mainPosition: "static",
headingVisible: true, headingVisible: true,
}); });
expect(layers.panelOpacity).toBeGreaterThanOrEqual(0.94); expect(layers.panelOpacity).toBeGreaterThanOrEqual(0.94);
}); });
test('provides native motion and quality controls with visible state', async ({ page, distUrl }) => { test("provides native motion and quality controls with visible state", async ({
await page.goto(distUrl, { waitUntil: 'domcontentloaded' }); page,
distUrl,
}) => {
await page.goto(distUrl, { waitUntil: "domcontentloaded" });
await page.getByRole('button', { name: 'Drive settings' }).click(); await page.getByRole("button", { name: "Drive settings" }).click();
const quality = page.getByLabel('Quality control'); const quality = page.getByLabel("Quality control");
await expect(quality).toBeVisible(); await expect(quality).toBeVisible();
await expect(page.getByRole('status')).toHaveCount(0); await expect(page.getByRole("status")).toHaveCount(0);
await page.keyboard.press('Escape'); await page.keyboard.press("Escape");
await page.getByRole('button', { name: 'Start the drive' }).click(); await page.getByRole("button", { name: "Play soundtrack" }).click();
await expect(page.getByRole('button', { name: 'Pause motion' })).toBeVisible(); await expect(
const canvas = page.locator('.journey-canvas'); page.getByRole("button", { name: "Pause motion" }),
).toBeVisible();
const canvas = page.locator(".journey-canvas");
const originalCanvas = await canvas.elementHandle(); 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 page.getByRole("button", { name: "Pause motion" }).click();
await expect(page.getByRole('button', { name: 'Resume motion' })).toBeVisible(); await expect(
page.getByRole("button", { name: "Resume motion" }),
).toBeVisible();
await expect(canvas).toHaveCount(1); await expect(canvas).toHaveCount(1);
await expect(page.locator('.journey-world')).toHaveAttribute('data-scene-generation', originalGeneration ?? ''); await expect(page.locator(".journey-world")).toHaveAttribute(
expect(await originalCanvas?.evaluate((element) => element.isConnected)).toBe(true); "data-scene-generation",
originalGeneration ?? "",
);
expect(await originalCanvas?.evaluate((element) => element.isConnected)).toBe(
true,
);
await page.getByRole('button', { name: 'Resume motion' }).click(); await page.getByRole("button", { name: "Resume motion" }).click();
await expect(page.getByRole('button', { name: 'Pause motion' })).toBeVisible(); await expect(
page.getByRole("button", { name: "Pause motion" }),
).toBeVisible();
await expect(canvas).toHaveCount(1); await expect(canvas).toHaveCount(1);
await expect(page.locator('.journey-world')).toHaveAttribute('data-scene-generation', originalGeneration ?? ''); await expect(page.locator(".journey-world")).toHaveAttribute(
expect(await originalCanvas?.evaluate((element) => element.isConnected)).toBe(true); "data-scene-generation",
originalGeneration ?? "",
);
expect(await originalCanvas?.evaluate((element) => element.isConnected)).toBe(
true,
);
await page.getByRole('button', { name: 'Drive settings' }).click(); await page.getByRole("button", { name: "Drive settings" }).click();
await page.getByLabel('Quality control').selectOption('static'); await page.getByLabel("Quality control").selectOption("static");
await expect(page.getByRole('button', { name: 'Enable motion' })).toBeVisible(); await expect(
await expect(page.locator('.journey-canvas')).toHaveCount(0); page.getByRole("button", { name: "Enable motion" }),
await expect(page.getByRole('status')).toContainText('Motion: static'); ).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 }) => { test("never creates a canvas when the system requests reduced motion", async ({
await page.emulateMedia({ reducedMotion: 'reduce' }); page,
await page.goto(distUrl, { waitUntil: 'domcontentloaded' }); distUrl,
}) => {
await page.emulateMedia({ reducedMotion: "reduce" });
await page.goto(distUrl, { waitUntil: "domcontentloaded" });
await expect(page.locator('.journey-canvas')).toHaveCount(0); await expect(page.locator(".journey-canvas")).toHaveCount(0);
await page.getByRole('button', { name: 'Drive settings' }).click(); await page.getByRole("button", { name: "Drive settings" }).click();
await expect(page.getByRole('status')).toContainText('reduced motion requested'); await expect(page.getByRole("status")).toContainText(
await expect(page.getByLabel('Quality control')).toBeVisible(); "reduced motion requested",
);
await expect(page.getByLabel("Quality control")).toBeVisible();
}); });
test('shows ARRIVED from the authoritative section snapshot at Contact', async ({ page, distUrl }) => { test("shows ARRIVED from the authoritative section snapshot at Contact", async ({
await page.goto(distUrl, { waitUntil: 'domcontentloaded' }); page,
distUrl,
}) => {
await page.goto(distUrl, { waitUntil: "domcontentloaded" });
const contactLink = page.locator('nav a[href="#contact"]'); 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 contactLink.click();
await expect(page.locator('.journey-hud')).toContainText('Contact'); await expect(page.locator(".journey-hud")).toContainText("Contact");
await expect(page.locator('.journey-hud')).toContainText('ARRIVED'); await expect(page.locator(".journey-hud")).toContainText("ARRIVED");
await expect(page.locator('.journey-hud')).toContainText('100%'); await expect(page.locator(".journey-hud")).toContainText("100%");
}); });

View File

@@ -395,7 +395,7 @@ test.describe("production visual composition", () => {
}, testInfo) => { }, testInfo) => {
await page.setViewportSize({ width: 1440, height: 900 }); await page.setViewportSize({ width: 1440, height: 900 });
await waitForRenderedPortfolio(page, distUrl); await waitForRenderedPortfolio(page, distUrl);
await page.getByRole("button", { name: "START THE DRIVE" }).click(); await page.getByRole("button", { name: "PLAY SOUNDTRACK" }).click();
await expect( await expect(
page.getByRole("button", { name: "Mute sound" }), page.getByRole("button", { name: "Mute sound" }),
).toBeVisible(); ).toBeVisible();

View File

@@ -1,10 +1,16 @@
import { performance as nodePerformance } from 'node:perf_hooks'; import { performance as nodePerformance } from "node:perf_hooks";
import { writeFile } from 'node:fs/promises'; import { writeFile } from "node:fs/promises";
import type { CDPSession, Page } from '@playwright/test'; import type { CDPSession, Page } from "@playwright/test";
import { expect, test } from '../helpers/serve-dist'; import { expect, test } from "../helpers/serve-dist";
import { authorizedPerformanceUrl } from '../helpers/test-build-auth'; import { authorizedPerformanceUrl } from "../helpers/test-build-auth";
import { loadAuthenticatedReferenceRun, validateObservedReferenceEnvironment } from './reference-run'; import {
import { calculatePeakRequestConcurrency, evaluateRuntimeTraceBudgetEvidence } from '../../scripts/lib/bundle-budgets.mjs'; loadAuthenticatedReferenceRun,
validateObservedReferenceEnvironment,
} from "./reference-run";
import {
calculatePeakRequestConcurrency,
evaluateRuntimeTraceBudgetEvidence,
} from "../../scripts/lib/bundle-budgets.mjs";
import { import {
calculateCls, calculateCls,
calculateLabTbt, calculateLabTbt,
@@ -16,24 +22,46 @@ import {
postRunningLongTasks, postRunningLongTasks,
unexpectedLateShifts, unexpectedLateShifts,
type ScriptedActionSample, type ScriptedActionSample,
} from '../../src/testing/performance-instrumentation'; } from "../../src/testing/performance-instrumentation";
type NamedProfile = 'desktop-high' | 'mobile-low'; type NamedProfile = "desktop-high" | "mobile-low";
const profileForProject = (name: string): { const profileForProject = (
name: string,
): {
name: NamedProfile; name: NamedProfile;
tier: 'high' | 'low'; tier: "high" | "low";
viewport: { width: number; height: number }; viewport: { width: number; height: number };
latency: number; latency: number;
download: number; download: number;
upload: number; upload: number;
sceneCpuP95: number; sceneCpuP95: number;
lostShare: number; lostShare: number;
} => name === 'performance-mobile' } =>
? { name: 'mobile-low', tier: 'low', viewport: { width: 390, height: 844 }, latency: 150, download: 200_000, upload: 93_750, sceneCpuP95: 24, lostShare: 0.1 } name === "performance-mobile"
: { name: 'desktop-high', tier: 'high', viewport: { width: 1_440, height: 900 }, latency: 40, download: 1_250_000, upload: 1_250_000, sceneCpuP95: 12, lostShare: 0.05 }; ? {
name: "mobile-low",
tier: "low",
viewport: { width: 390, height: 844 },
latency: 150,
download: 200_000,
upload: 93_750,
sceneCpuP95: 24,
lostShare: 0.1,
}
: {
name: "desktop-high",
tier: "high",
viewport: { width: 1_440, height: 900 },
latency: 40,
download: 1_250_000,
upload: 1_250_000,
sceneCpuP95: 12,
lostShare: 0.05,
};
const sleep = (duration: number): Promise<void> => new Promise((resolve) => setTimeout(resolve, duration)); const sleep = (duration: number): Promise<void> =>
new Promise((resolve) => setTimeout(resolve, duration));
const stableTargetCenter = async (page: Page, selector: string) => { const stableTargetCenter = async (page: Page, selector: string) => {
const locator = page.locator(selector).filter({ visible: true }); const locator = page.locator(selector).filter({ visible: true });
@@ -55,19 +83,42 @@ const dispatchPointerAction = async (
await at(offset - 50); await at(offset - 50);
const center = await stableTargetCenter(page, selector); const center = await stableTargetCenter(page, selector);
const markerSelector = `[data-drive-target="${id}"]`; const markerSelector = `[data-drive-target="${id}"]`;
await page.locator(selector).filter({ visible: true }).evaluate((element, actionId) => { await page
element.setAttribute('data-drive-target', actionId); .locator(selector)
.filter({ visible: true })
.evaluate((element, actionId) => {
element.setAttribute("data-drive-target", actionId);
}, id); }, id);
await at(offset - 20); await at(offset - 20);
await cdp.send('Input.dispatchMouseEvent', { type: 'mouseMoved', ...center }); await cdp.send("Input.dispatchMouseEvent", { type: "mouseMoved", ...center });
await at(offset); await at(offset);
await page.evaluate(({ actionId, target }) => window.__PORTFOLIO_PERFORMANCE__?.beginAction(actionId, target), { actionId: id, target: markerSelector }); await page.evaluate(
await cdp.send('Input.dispatchMouseEvent', { type: 'mousePressed', button: 'left', clickCount: 1, ...center }); ({ actionId, target }) =>
window.__PORTFOLIO_PERFORMANCE__?.beginAction(actionId, target),
{ actionId: id, target: markerSelector },
);
await cdp.send("Input.dispatchMouseEvent", {
type: "mousePressed",
button: "left",
clickCount: 1,
...center,
});
await at(offset + 40); await at(offset + 40);
await cdp.send('Input.dispatchMouseEvent', { type: 'mouseReleased', button: 'left', clickCount: 1, ...center }); await cdp.send("Input.dispatchMouseEvent", {
type: "mouseReleased",
button: "left",
clickCount: 1,
...center,
});
}; };
const dispatchWheelBurst = async (page: Page, cdp: CDPSession, at: (offset: number) => Promise<void>, offset: number, deltaY: number) => { const dispatchWheelBurst = async (
page: Page,
cdp: CDPSession,
at: (offset: number) => Promise<void>,
offset: number,
deltaY: number,
) => {
const center = await page.evaluate(() => { const center = await page.evaluate(() => {
const viewport = window.visualViewport; const viewport = window.visualViewport;
return { return {
@@ -77,52 +128,85 @@ const dispatchWheelBurst = async (page: Page, cdp: CDPSession, at: (offset: numb
}); });
for (const deltaOffset of [0, 40, 80, 120, 160, 200]) { for (const deltaOffset of [0, 40, 80, 120, 160, 200]) {
await at(offset + deltaOffset); await at(offset + deltaOffset);
await cdp.send('Input.dispatchMouseEvent', { type: 'mouseWheel', deltaX: 0, deltaY, ...center }); await cdp.send("Input.dispatchMouseEvent", {
type: "mouseWheel",
deltaX: 0,
deltaY,
...center,
});
} }
}; };
test('drive-trace.v1 runs on current Chromium with fail-closed reference authentication', async ({ browser, page, distUrl }, testInfo) => { test("drive-trace.v1 runs on current Chromium with fail-closed reference authentication", async ({
browser,
page,
distUrl,
}, testInfo) => {
test.setTimeout(75_000); test.setTimeout(75_000);
const profile = profileForProject(testInfo.project.name); const profile = profileForProject(testInfo.project.name);
const referenceRun = await loadAuthenticatedReferenceRun(profile.name); const referenceRun = await loadAuthenticatedReferenceRun(profile.name);
await page.setViewportSize(profile.viewport); await page.setViewportSize(profile.viewport);
const cdp = await page.context().newCDPSession(page); const cdp = await page.context().newCDPSession(page);
await cdp.send('Network.enable'); await cdp.send("Network.enable");
await cdp.send('Network.clearBrowserCache'); await cdp.send("Network.clearBrowserCache");
await cdp.send('Network.emulateNetworkConditions', { await cdp.send("Network.emulateNetworkConditions", {
offline: false, offline: false,
latency: profile.latency, latency: profile.latency,
downloadThroughput: profile.download, downloadThroughput: profile.download,
uploadThroughput: profile.upload, uploadThroughput: profile.upload,
}); });
await page.emulateMedia({ reducedMotion: 'no-preference' }); await page.emulateMedia({ reducedMotion: "no-preference" });
await page.goto('about:blank'); await page.goto("about:blank");
const startedFromAboutBlank = page.url() === 'about:blank'; const startedFromAboutBlank = page.url() === "about:blank";
const loadStarted = nodePerformance.now(); const loadStarted = nodePerformance.now();
await page.goto(authorizedPerformanceUrl(distUrl, profile.tier), { waitUntil: 'load', timeout: 10_000 }); await page.goto(authorizedPerformanceUrl(distUrl, profile.tier), {
waitUntil: "load",
timeout: 10_000,
});
const loadDuration = nodePerformance.now() - loadStarted; const loadDuration = nodePerformance.now() - loadStarted;
expect(loadDuration).toBeLessThanOrEqual(10_000); expect(loadDuration).toBeLessThanOrEqual(10_000);
await expect.poll(() => page.evaluate(() => window.__PORTFOLIO_PERFORMANCE__?.snapshot().lcp.length ?? 0), { timeout: 10_000 }).toBeGreaterThan(0); await expect
await expect(page.locator('.journey-world')).toHaveAttribute('data-scene-state', 'running', { timeout: 5_000 }); .poll(
await expect(page.locator('.journey-hud__quality')).toHaveText(profile.tier === 'high' ? 'High' : 'Low'); () =>
page.evaluate(
() => window.__PORTFOLIO_PERFORMANCE__?.snapshot().lcp.length ?? 0,
),
{ timeout: 10_000 },
)
.toBeGreaterThan(0);
await expect(page.locator(".journey-world")).toHaveAttribute(
"data-scene-state",
"running",
{ timeout: 5_000 },
);
await expect(page.locator(".journey-hud__quality")).toHaveText(
profile.tier === "high" ? "High" : "Low",
);
if (referenceRun !== null) { if (referenceRun !== null) {
const observed = await page.evaluate(() => ({ const observed = await page.evaluate(() => ({
viewport: { width: innerWidth, height: innerHeight }, viewport: { width: innerWidth, height: innerHeight },
visible: document.visibilityState === 'visible', visible: document.visibilityState === "visible",
reducedMotion: matchMedia('(prefers-reduced-motion: reduce)').matches ? 'reduce' as const : 'no-preference' as const, reducedMotion: matchMedia("(prefers-reduced-motion: reduce)").matches
saveData: Boolean((navigator as Navigator & { connection?: { saveData?: boolean } }).connection?.saveData), ? ("reduce" as const)
: ("no-preference" as const),
saveData: Boolean(
(navigator as Navigator & { connection?: { saveData?: boolean } })
.connection?.saveData,
),
pageZoom: visualViewport?.scale ?? 1, pageZoom: visualViewport?.scale ?? 1,
devicePixelRatio, devicePixelRatio,
})); }));
validateObservedReferenceEnvironment(referenceRun, { validateObservedReferenceEnvironment(referenceRun, {
...observed, ...observed,
browserName: 'Chromium', browserName: "Chromium",
browserVersion: browser.version(), browserVersion: browser.version(),
startedFromAboutBlank, startedFromAboutBlank,
}); });
} }
const warmupRafs = await page.evaluate(() => new Promise<number[]>((resolve) => { const warmupRafs = await page.evaluate(
() =>
new Promise<number[]>((resolve) => {
const samples: number[] = []; const samples: number[] = [];
const frame = (now: number): void => { const frame = (now: number): void => {
samples.push(now); samples.push(now);
@@ -130,11 +214,17 @@ test('drive-trace.v1 runs on current Chromium with fail-closed reference authent
else requestAnimationFrame(frame); else requestAnimationFrame(frame);
}; };
requestAnimationFrame(frame); requestAnimationFrame(frame);
})); }),
const warmupIntervals = warmupRafs.slice(1).map((stamp, index) => stamp - warmupRafs[index]!); );
const warmupMedian = [...warmupIntervals].sort((a, b) => a - b)[Math.floor(warmupIntervals.length / 2)]!; const warmupIntervals = warmupRafs
.slice(1)
.map((stamp, index) => stamp - warmupRafs[index]!);
const warmupMedian = [...warmupIntervals].sort((a, b) => a - b)[
Math.floor(warmupIntervals.length / 2)
]!;
const validReferenceCadence = Math.abs(warmupMedian - 16.667) <= 1; const validReferenceCadence = Math.abs(warmupMedian - 16.667) <= 1;
if (process.env.PORTFOLIO_REFERENCE_LAB === '1') expect(validReferenceCadence).toBe(true); if (process.env.PORTFOLIO_REFERENCE_LAB === "1")
expect(validReferenceCadence).toBe(true);
await page.evaluate(() => window.__PORTFOLIO_PERFORMANCE__?.startTrace()); await page.evaluate(() => window.__PORTFOLIO_PERFORMANCE__?.startTrace());
const nodeTraceStart = nodePerformance.now(); const nodeTraceStart = nodePerformance.now();
@@ -143,68 +233,207 @@ test('drive-trace.v1 runs on current Chromium with fail-closed reference authent
if (remaining > 0) await sleep(remaining); if (remaining > 0) await sleep(remaining);
}; };
await dispatchPointerAction(page, cdp, at, 1_000, 'start', 'button:has-text("START THE DRIVE")'); await dispatchPointerAction(
page,
cdp,
at,
1_000,
"start",
'button:has-text("PLAY SOUNDTRACK")',
);
await dispatchWheelBurst(page, cdp, at, 1_500, 100); await dispatchWheelBurst(page, cdp, at, 1_500, 100);
await at(2_350); await at(2_350);
await cdp.send('Input.dispatchKeyEvent', { type: 'keyDown', key: 'Tab', code: 'Tab', windowsVirtualKeyCode: 9 }); await cdp.send("Input.dispatchKeyEvent", {
type: "keyDown",
key: "Tab",
code: "Tab",
windowsVirtualKeyCode: 9,
});
await at(2_370); await at(2_370);
await cdp.send('Input.dispatchKeyEvent', { type: 'keyUp', key: 'Tab', code: 'Tab', windowsVirtualKeyCode: 9 }); await cdp.send("Input.dispatchKeyEvent", {
await expect(page.getByRole('slider', { name: 'Sound volume' })).toBeFocused(); type: "keyUp",
key: "Tab",
code: "Tab",
windowsVirtualKeyCode: 9,
});
await expect(
page.getByRole("slider", { name: "Sound volume" }),
).toBeFocused();
await at(2_400); await at(2_400);
await page.evaluate(() => window.__PORTFOLIO_PERFORMANCE__?.beginAction('volume', 'input[aria-label="Sound volume"]')); await page.evaluate(() =>
await cdp.send('Input.dispatchKeyEvent', { type: 'keyDown', key: 'ArrowLeft', code: 'ArrowLeft', windowsVirtualKeyCode: 37 }); window.__PORTFOLIO_PERFORMANCE__?.beginAction(
"volume",
'input[aria-label="Sound volume"]',
),
);
await cdp.send("Input.dispatchKeyEvent", {
type: "keyDown",
key: "ArrowLeft",
code: "ArrowLeft",
windowsVirtualKeyCode: 37,
});
await at(2_440); await at(2_440);
await cdp.send('Input.dispatchKeyEvent', { type: 'keyUp', key: 'ArrowLeft', code: 'ArrowLeft', windowsVirtualKeyCode: 37 }); await cdp.send("Input.dispatchKeyEvent", {
await expect(page.getByRole('slider', { name: 'Sound volume' })).toHaveValue('0.35'); type: "keyUp",
key: "ArrowLeft",
code: "ArrowLeft",
windowsVirtualKeyCode: 37,
});
await expect(page.getByRole("slider", { name: "Sound volume" })).toHaveValue(
"0.35",
);
await dispatchWheelBurst(page, cdp, at, 2_800, 100); await dispatchWheelBurst(page, cdp, at, 2_800, 100);
await dispatchPointerAction(page, cdp, at, 3_700, 'mute', 'button:has-text("Mute sound")'); await dispatchPointerAction(
await expect(page.getByRole('button', { name: 'Unmute sound' })).toBeVisible(); page,
cdp,
at,
3_700,
"mute",
'button:has-text("Mute sound")',
);
await expect(
page.getByRole("button", { name: "Unmute sound" }),
).toBeVisible();
await dispatchWheelBurst(page, cdp, at, 4_100, 100); await dispatchWheelBurst(page, cdp, at, 4_100, 100);
await dispatchPointerAction(page, cdp, at, 5_000, 'unmute', 'button:has-text("Unmute sound")'); await dispatchPointerAction(
page,
cdp,
at,
5_000,
"unmute",
'button:has-text("Unmute sound")',
);
await dispatchWheelBurst(page, cdp, at, 5_400, 100); await dispatchWheelBurst(page, cdp, at, 5_400, 100);
await dispatchPointerAction(page, cdp, at, 6_300, 'pause', 'button:has-text("Pause motion")'); await dispatchPointerAction(
page,
cdp,
at,
6_300,
"pause",
'button:has-text("Pause motion")',
);
await dispatchWheelBurst(page, cdp, at, 6_700, 100); await dispatchWheelBurst(page, cdp, at, 6_700, 100);
await dispatchPointerAction(page, cdp, at, 7_600, 'resume', 'button:has-text("Resume motion")'); await dispatchPointerAction(
page,
cdp,
at,
7_600,
"resume",
'button:has-text("Resume motion")',
);
await dispatchWheelBurst(page, cdp, at, 8_000, 100); await dispatchWheelBurst(page, cdp, at, 8_000, 100);
await at(8_800); await at(8_800);
await page.evaluate(() => window.__PORTFOLIO_PERFORMANCE__?.armNavigationSettlementBarrier()); await page.evaluate(() =>
await dispatchPointerAction(page, cdp, at, 8_900, 'toolkit', 'nav a[href="#toolkit"]'); window.__PORTFOLIO_PERFORMANCE__?.armNavigationSettlementBarrier(),
);
await dispatchPointerAction(
page,
cdp,
at,
8_900,
"toolkit",
'nav a[href="#toolkit"]',
);
await dispatchWheelBurst(page, cdp, at, 9_300, -100); await dispatchWheelBurst(page, cdp, at, 9_300, -100);
await dispatchWheelBurst(page, cdp, at, 10_600, -100); await dispatchWheelBurst(page, cdp, at, 10_600, -100);
await dispatchWheelBurst(page, cdp, at, 11_900, 100); await dispatchWheelBurst(page, cdp, at, 11_900, 100);
await dispatchWheelBurst(page, cdp, at, 13_200, 100); await dispatchWheelBurst(page, cdp, at, 13_200, 100);
await dispatchPointerAction(page, cdp, at, 14_500, 'contact', 'nav a[href="#contact"]'); await dispatchPointerAction(
page,
cdp,
at,
14_500,
"contact",
'nav a[href="#contact"]',
);
await at(20_000); await at(20_000);
await page.evaluate(() => window.__PORTFOLIO_PERFORMANCE__?.endTrace()); await page.evaluate(() => window.__PORTFOLIO_PERFORMANCE__?.endTrace());
await expect(page).toHaveURL(/#contact$/u); await expect(page).toHaveURL(/#contact$/u);
await expect(page.locator('#contact > h2')).toBeFocused(); await expect(page.locator("#contact > h2")).toBeFocused();
await expect(page.locator('.journey-hud__quality')).toHaveText(profile.tier === 'high' ? 'High' : 'Low'); await expect(page.locator(".journey-hud__quality")).toHaveText(
const snapshot = await page.evaluate(() => window.__PORTFOLIO_PERFORMANCE__?.snapshot()); profile.tier === "high" ? "High" : "Low",
if (snapshot === undefined || snapshot.traceStart === null || snapshot.traceEnd === null || snapshot.firstSceneFrame === null) { );
throw new Error('The authenticated performance snapshot is incomplete.'); const snapshot = await page.evaluate(() =>
window.__PORTFOLIO_PERFORMANCE__?.snapshot(),
);
if (
snapshot === undefined ||
snapshot.traceStart === null ||
snapshot.traceEnd === null ||
snapshot.firstSceneFrame === null
) {
throw new Error("The authenticated performance snapshot is incomplete.");
} }
if (snapshot.runtimeAllocation === null) throw new Error('The production scene did not emit an actual allocation ledger.'); if (snapshot.runtimeAllocation === null)
throw new Error(
"The production scene did not emit an actual allocation ledger.",
);
const expectedPeriod = warmupMedian; const expectedPeriod = warmupMedian;
const fcp = await page.evaluate(() => performance.getEntriesByName('first-contentful-paint')[0]?.startTime ?? null); const fcp = await page.evaluate(
() =>
performance.getEntriesByName("first-contentful-paint")[0]?.startTime ??
null,
);
const lcp = latestLcpBeforeCutoff(snapshot.lcp, snapshot.cutoff); const lcp = latestLcpBeforeCutoff(snapshot.lcp, snapshot.cutoff);
const cls = calculateCls(snapshot.layoutShifts); const cls = calculateCls(snapshot.layoutShifts);
const tbt = calculateLabTbt(snapshot.longTasks, fcp, snapshot.traceStart); const tbt = calculateLabTbt(snapshot.longTasks, fcp, snapshot.traceStart);
const postRunning = postRunningLongTasks(snapshot.longTasks, snapshot.traceStart, snapshot.traceEnd); const postRunning = postRunningLongTasks(
const eventToPaint = evaluateEventToNextPaint(snapshot.actions as ScriptedActionSample[]); snapshot.longTasks,
const continuity = evaluateSceneContinuity(snapshot.sceneIntervals, snapshot.sceneFrames, expectedPeriod); snapshot.traceStart,
const lost = calculateLostPresentationShare(snapshot.pageRafs, expectedPeriod, Math.floor(20_000 / expectedPeriod) - 3); snapshot.traceEnd,
const lateShifts = unexpectedLateShifts(snapshot.layoutShifts, snapshot.firstSceneFrame, snapshot.traceEnd); );
const sceneCpuP95 = nearestRankP95(snapshot.sceneFrames.map((frame) => frame.end - frame.start)); const eventToPaint = evaluateEventToNextPaint(
const resourceTimings = await page.evaluate(() => performance.getEntriesByType('resource').map((entry) => { snapshot.actions as ScriptedActionSample[],
);
const continuity = evaluateSceneContinuity(
snapshot.sceneIntervals,
snapshot.sceneFrames,
expectedPeriod,
);
const lost = calculateLostPresentationShare(
snapshot.pageRafs,
expectedPeriod,
Math.floor(20_000 / expectedPeriod) - 3,
);
const lateShifts = unexpectedLateShifts(
snapshot.layoutShifts,
snapshot.firstSceneFrame,
snapshot.traceEnd,
);
const sceneCpuP95 = nearestRankP95(
snapshot.sceneFrames.map((frame) => frame.end - frame.start),
);
const resourceTimings = await page.evaluate(() =>
performance.getEntriesByType("resource").map((entry) => {
const resource = entry as PerformanceResourceTiming; const resource = entry as PerformanceResourceTiming;
return { name: resource.name, initiatorType: resource.initiatorType, startTime: resource.startTime, responseEnd: resource.responseEnd }; return {
})); name: resource.name,
initiatorType: resource.initiatorType,
startTime: resource.startTime,
responseEnd: resource.responseEnd,
};
}),
);
const peakRequests = calculatePeakRequestConcurrency(resourceTimings); const peakRequests = calculatePeakRequestConcurrency(resourceTimings);
const runtimeBudgetEvidence = { schema: 'portfolio-runtime-trace-evidence/v1', peakRequests, allocation: snapshot.runtimeAllocation }; const runtimeBudgetEvidence = {
const runtimeBudget = evaluateRuntimeTraceBudgetEvidence(runtimeBudgetEvidence); schema: "portfolio-runtime-trace-evidence/v1",
const runtimeResourcePass = !runtimeBudget.violations.some((item) => ['mobileTextureDimension', 'lowTierGpuEstimate', 'highTierGpuEstimate'].includes(item.budget)); peakRequests,
const requestConcurrencyPass = !runtimeBudget.violations.some((item) => ['imageSceneConcurrency', 'videoConcurrency'].includes(item.budget)); allocation: snapshot.runtimeAllocation,
};
const runtimeBudget = evaluateRuntimeTraceBudgetEvidence(
runtimeBudgetEvidence,
);
const runtimeResourcePass = !runtimeBudget.violations.some((item) =>
[
"mobileTextureDimension",
"lowTierGpuEstimate",
"highTierGpuEstimate",
].includes(item.budget),
);
const requestConcurrencyPass = !runtimeBudget.violations.some((item) =>
["imageSceneConcurrency", "videoConcurrency"].includes(item.budget),
);
const budgetResults = { const budgetResults = {
lcp: lcp <= 3_000, lcp: lcp <= 3_000,
@@ -214,32 +443,59 @@ test('drive-trace.v1 runs on current Chromium with fail-closed reference authent
eventToNextPaint: eventToPaint.p95 <= 200, eventToNextPaint: eventToPaint.p95 <= 200,
sceneCpu: sceneCpuP95 <= profile.sceneCpuP95, sceneCpu: sceneCpuP95 <= profile.sceneCpuP95,
continuity: continuity.passes, continuity: continuity.passes,
lostPresentation: lost.hasSufficientCoverage && lost.share <= profile.lostShare, lostPresentation:
lost.hasSufficientCoverage && lost.share <= profile.lostShare,
lateShifts: lateShifts.length === 0, lateShifts: lateShifts.length === 0,
runtimeResources: runtimeResourcePass, runtimeResources: runtimeResourcePass,
requestConcurrency: requestConcurrencyPass, requestConcurrency: requestConcurrencyPass,
}; };
if (process.env.PORTFOLIO_REFERENCE_LAB === '1') { if (process.env.PORTFOLIO_REFERENCE_LAB === "1") {
expect(Object.values(budgetResults).every(Boolean)).toBe(true); expect(Object.values(budgetResults).every(Boolean)).toBe(true);
} }
expect(snapshot.navigationBarrier.cancelledEpoch).not.toBeNull(); expect(snapshot.navigationBarrier.cancelledEpoch).not.toBeNull();
const report = { const report = {
schema: 'drive-trace.v1', schema: "drive-trace.v1",
evidenceKind: referenceRun === null ? 'current-chromium-developer-machine-non-reference' : 'validated-reference-candidate-unsigned', evidenceKind:
referenceRun === null
? "current-chromium-developer-machine-non-reference"
: "validated-reference-candidate-unsigned",
gatingReferenceResult: false, gatingReferenceResult: false,
referenceCandidateValidated: referenceRun !== null, referenceCandidateValidated: referenceRun !== null,
...(referenceRun === null ? {} : { referenceRun: { runId: referenceRun.runId, runIndex: referenceRun.runIndex, attestationDigest: referenceRun.attestationDigest } }), ...(referenceRun === null
? {}
: {
referenceRun: {
runId: referenceRun.runId,
runIndex: referenceRun.runIndex,
attestationDigest: referenceRun.attestationDigest,
},
}),
profile: profile.name, profile: profile.name,
tier: profile.tier, tier: profile.tier,
browser: testInfo.project.name, browser: testInfo.project.name,
metrics: { warmupMedian, lcp, cls, tbt, postRunningLongTaskCount: postRunning.length, eventToNextPaintP95: eventToPaint.p95, sceneCpuP95, continuity, lostPresentationShare: lost.share, lateShiftCount: lateShifts.length }, metrics: {
warmupMedian,
lcp,
cls,
tbt,
postRunningLongTaskCount: postRunning.length,
eventToNextPaintP95: eventToPaint.p95,
sceneCpuP95,
continuity,
lostPresentationShare: lost.share,
lateShiftCount: lateShifts.length,
},
runtimeBudgetEvidence, runtimeBudgetEvidence,
runtimeBudgetSummary: runtimeBudget.tier, runtimeBudgetSummary: runtimeBudget.tier,
budgetResults, budgetResults,
referenceBudgetsPass: validReferenceCadence && Object.values(budgetResults).every(Boolean), referenceBudgetsPass:
validReferenceCadence && Object.values(budgetResults).every(Boolean),
validReferenceCadence, validReferenceCadence,
pendingPhysicalReferenceRuns: referenceRun === null ? 5 : 0, pendingPhysicalReferenceRuns: referenceRun === null ? 5 : 0,
}; };
await writeFile(testInfo.outputPath('drive-trace.v1.json'), `${JSON.stringify(report, null, 2)}\n`); await writeFile(
testInfo.outputPath("drive-trace.v1.json"),
`${JSON.stringify(report, null, 2)}\n`,
);
}); });

View File

@@ -262,14 +262,14 @@ describe("DriveControls", () => {
}, },
}); });
it("presents one explicit drive action before audio activation", async () => { it("presents one explicit soundtrack action before audio activation", async () => {
const wrapper = mountControls(policy()); const wrapper = mountControls(policy());
expect(wrapper.get('[data-drive-state="idle"]').text()).toContain( expect(wrapper.get('[data-drive-state="idle"]').text()).toContain(
"START THE DRIVE", "PLAY SOUNDTRACK",
); );
expect(wrapper.find('[aria-label="Mute sound"]').exists()).toBe(false); expect(wrapper.find('[aria-label="Mute sound"]').exists()).toBe(false);
await wrapper.get('[aria-label="Start the drive"]').trigger("click"); await wrapper.get('[aria-label="Play soundtrack"]').trigger("click");
expect(wrapper.emitted("audioStart")).toHaveLength(1); expect(wrapper.emitted("audioStart")).toHaveLength(1);
wrapper.unmount(); wrapper.unmount();
}); });