118 lines
4.2 KiB
TypeScript
118 lines
4.2 KiB
TypeScript
import {
|
|
clickNavigationLink,
|
|
expect,
|
|
revealNavigationLinks,
|
|
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);
|
|
|
|
await clickNavigationLink(page, "#start");
|
|
await expect(page).toHaveURL(`${distUrl}/`);
|
|
expect(await page.evaluate(() => history.length)).toBe(initialLength);
|
|
await expect(page.locator("#start-heading")).toBeFocused();
|
|
|
|
await clickNavigationLink(page, "#profile");
|
|
await expect(page).toHaveURL(`${distUrl}/#profile`);
|
|
expect(await page.evaluate(() => history.length)).toBe(initialLength + 1);
|
|
await expect(page.locator("#profile-heading")).toBeFocused();
|
|
|
|
await clickNavigationLink(page, "#profile");
|
|
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,
|
|
}, testInfo) => {
|
|
test.skip(
|
|
testInfo.project.name === "mobile-chromium",
|
|
"Hidden-focus cancellation is covered once in desktop Chromium.",
|
|
);
|
|
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" });
|
|
await revealNavigationLinks(page);
|
|
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 clickNavigationLink(page, "#profile");
|
|
await clickNavigationLink(page, "#experience");
|
|
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();
|
|
});
|