Files
Portfolio/tests/browser/scene-controls.spec.ts

227 lines
8.3 KiB
TypeScript

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" });
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,
}) => {
await page.setViewportSize({ width: 1440, height: 900 });
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 worldStyle = getComputedStyle(world);
const heroStyle = getComputedStyle(hero);
const navigationStyle = getComputedStyle(navigation);
return {
worldZ: Number(worldStyle.zIndex),
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",
),
};
});
expect(layers).toMatchObject({
navigationPosition: "fixed",
headingVisible: true,
navigationVisible: true,
});
expect(layers.shellZ).toBeGreaterThan(layers.worldZ);
expect(layers.navigationZ).toBeGreaterThan(layers.worldZ);
expect(layers.heroOpacity).toBeGreaterThanOrEqual(0.94);
});
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" });
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();
await profileLink.click();
await expect(page).toHaveURL(`${distUrl}/#profile`);
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 mainStyle = getComputedStyle(main);
const panelStyle = getComputedStyle(panel);
const headingRect = heading.getBoundingClientRect();
return {
worldZ: Number(getComputedStyle(world).zIndex),
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",
),
};
});
expect(layers).toMatchObject({
worldZ: 0,
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" });
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: "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");
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 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 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" });
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" });
const contactLink = page.locator('nav a[href="#contact"]');
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%");
});