Files
Portfolio/tests/browser/navigation.spec.ts

110 lines
4.0 KiB
TypeScript

import { expect, test } from "../helpers/serve-dist";
test("keeps canonical hrefs while creating exactly one or zero explicit history entries", async ({
page,
distUrl,
}) => {
await page.goto(distUrl, { waitUntil: "domcontentloaded" });
const initialLength = await page.evaluate(() => history.length);
const start = page.locator('nav a[href="#start"]');
const profile = page.locator('nav a[href="#profile"]');
await start.click();
await expect(page).toHaveURL(`${distUrl}/`);
expect(await page.evaluate(() => history.length)).toBe(initialLength);
await expect(page.locator("#start-heading")).toBeFocused();
await profile.click();
await expect(page).toHaveURL(`${distUrl}/#profile`);
expect(await page.evaluate(() => history.length)).toBe(initialLength + 1);
await expect(page.locator("#profile-heading")).toBeFocused();
await profile.click();
expect(await page.evaluate(() => history.length)).toBe(initialLength + 1);
await expect(page.locator("#profile-heading")).toBeFocused();
});
test("cancels pending explicit focus when wheel intent intervenes", async ({
page,
distUrl,
}) => {
await page.goto(distUrl, { waitUntil: "domcontentloaded" });
const link = page.locator('nav a[href="#infrastructure"]');
await link.focus();
await page.evaluate(() => {
const anchor = document.querySelector<HTMLAnchorElement>(
'nav a[href="#infrastructure"]',
);
anchor?.addEventListener(
"click",
() =>
queueMicrotask(() =>
window.dispatchEvent(new WheelEvent("wheel", { deltaY: 40 })),
),
{ once: true },
);
});
await link.click();
await page.waitForTimeout(100);
await expect(link).toBeFocused();
await expect(page.locator("#infrastructure-heading")).not.toBeFocused();
});
test("passive scrolling replaces a stable fragment without stealing focus", async ({
page,
distUrl,
}) => {
await page.goto(distUrl, { waitUntil: "domcontentloaded" });
const link = page.locator('nav a[href="#start"]');
await link.focus();
await page.evaluate(() => {
window.dispatchEvent(new WheelEvent("wheel", { deltaY: 20 }));
const heading = document.querySelector<HTMLElement>("#experience-heading");
if (heading === null) throw new Error("Missing experience heading");
const headingDocumentTop =
heading.getBoundingClientRect().top + window.scrollY;
// Place the heading just beyond the 35% activation line. Scroll offsets
// are integer-rounded in Chromium, so exact boundary equality is brittle.
window.scrollTo(0, headingDocumentTop - window.innerHeight * 0.34);
});
await expect(page).toHaveURL(`${distUrl}/#experience`, { timeout: 2000 });
await expect(link).toBeFocused();
await expect(page.locator("#experience-heading")).not.toBeFocused();
});
test("back and forward restore sections without another history mutation or focus move", async ({
page,
distUrl,
}) => {
await page.goto(distUrl, { waitUntil: "domcontentloaded" });
await page.locator('nav a[href="#profile"]').click();
await page.locator('nav a[href="#experience"]').click();
const historyLength = await page.evaluate(() => history.length);
await expect(page.locator("#experience-heading")).toBeFocused();
await page.goBack();
await expect(page).toHaveURL(`${distUrl}/#profile`);
await expect(page.locator("#profile")).toBeInViewport();
expect(await page.evaluate(() => history.length)).toBe(historyLength);
await expect(page.locator("#profile-heading")).not.toBeFocused();
await page.goForward();
await expect(page).toHaveURL(`${distUrl}/#experience`);
await expect(page.locator("#experience")).toBeInViewport();
expect(await page.evaluate(() => history.length)).toBe(historyLength);
await expect(page.locator("#experience-heading")).not.toBeFocused();
});
test("direct initial fragments align without moving focus", async ({
page,
distUrl,
}) => {
await page.goto(`${distUrl}/#toolkit`, { waitUntil: "domcontentloaded" });
await expect(page.locator("#toolkit")).toBeInViewport();
await expect(page.locator("#toolkit-heading")).not.toBeFocused();
});