feat: add gear provenance models and browser fetcher
This commit is contained in:
@@ -11,3 +11,53 @@ for (const path of [
|
||||
expect(results.violations.filter(item => ["critical", "serious"].includes(item.impact ?? ""))).toEqual([]);
|
||||
});
|
||||
}
|
||||
|
||||
test("admin keyboard navigation keeps focus out of the document body", async ({ page }) => {
|
||||
await page.goto("/admin");
|
||||
await page.keyboard.press("Tab");
|
||||
await expect(page.locator(".skip-link")).toBeFocused();
|
||||
|
||||
const focusedLabels: string[] = [];
|
||||
for (let index = 0; index < 20; index += 1) {
|
||||
await page.keyboard.press("Tab");
|
||||
focusedLabels.push(await page.evaluate(() => {
|
||||
const active = document.activeElement;
|
||||
if (active?.tagName === "BODY") return "BODY";
|
||||
if (active?.matches(".admin-login input")) return "ADMIN_TOKEN_INPUT";
|
||||
if (active?.matches(".admin-login button")) return "ADMIN_PANEL_BUTTON";
|
||||
return (active?.getAttribute("aria-label") || active?.textContent || active?.tagName || "").trim().slice(0, 80);
|
||||
}));
|
||||
}
|
||||
|
||||
expect(focusedLabels).not.toContain("BODY");
|
||||
expect(focusedLabels).toContain("ADMIN_TOKEN_INPUT");
|
||||
expect(focusedLabels).toContain("ADMIN_PANEL_BUTTON");
|
||||
});
|
||||
|
||||
test("print media keeps a light color scheme and removes hero image filters", async ({ page }) => {
|
||||
await page.emulateMedia({ media: "print" });
|
||||
await page.goto("/");
|
||||
const printState = await page.evaluate(() => ({
|
||||
colorScheme: getComputedStyle(document.documentElement).colorScheme,
|
||||
heroFilter: document.querySelector(".lake-card img") ? getComputedStyle(document.querySelector(".lake-card img")!).filter : "none",
|
||||
}));
|
||||
expect(printState.colorScheme).toBe("light");
|
||||
expect(printState.heroFilter).toBe("none");
|
||||
});
|
||||
|
||||
test("forced colors keeps theme controls visibly bordered", async ({ page }) => {
|
||||
await page.emulateMedia({ forcedColors: "active" });
|
||||
await page.goto("/admin");
|
||||
const forcedColorsState = await page.evaluate(() => {
|
||||
const control = document.querySelector(".theme-switcher button");
|
||||
const pressed = document.querySelector(".theme-switcher button[aria-pressed='true']");
|
||||
return {
|
||||
borderStyle: control ? getComputedStyle(control).borderStyle : "",
|
||||
borderWidth: control ? getComputedStyle(control).borderWidth : "",
|
||||
activeBackground: pressed ? getComputedStyle(pressed).backgroundColor : "",
|
||||
};
|
||||
});
|
||||
expect(forcedColorsState.borderStyle).toBe("solid");
|
||||
expect(forcedColorsState.borderWidth).toBe("1px");
|
||||
expect(forcedColorsState.activeBackground).not.toBe("transparent");
|
||||
});
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
import { expect, test } from "@playwright/test";
|
||||
|
||||
const routes = [
|
||||
"/", "/waterbodies", "/records", "/report", "/status", "/media", "/rules",
|
||||
"/waterbodies/р-вьюнок", "/fish/pike", "/admin", "/admin/moderation",
|
||||
"/admin/external-sources", "/admin/media",
|
||||
];
|
||||
const viewports = [
|
||||
{ width: 320, height: 800 },
|
||||
{ width: 390, height: 844 },
|
||||
{ width: 768, height: 900 },
|
||||
{ width: 1280, height: 900 },
|
||||
];
|
||||
const themes = ["system", "light", "dark"] as const;
|
||||
|
||||
test("ROADMAP visual matrix keeps routes within the viewport", async ({ page, context }) => {
|
||||
for (const theme of themes) {
|
||||
await context.clearCookies();
|
||||
if (theme !== "system") {
|
||||
await context.addCookies([{ name: "rf4-theme", value: theme, url: "http://127.0.0.1:4321" }]);
|
||||
}
|
||||
await page.emulateMedia({ colorScheme: theme === "system" ? "light" : theme });
|
||||
for (const viewport of viewports) {
|
||||
await page.setViewportSize(viewport);
|
||||
for (const route of routes) {
|
||||
await page.goto(route);
|
||||
const state = await page.evaluate(() => ({
|
||||
clientWidth: document.documentElement.clientWidth,
|
||||
scrollWidth: document.documentElement.scrollWidth,
|
||||
hasMain: Boolean(document.querySelector("main")),
|
||||
hasHeading: Boolean(document.querySelector("h1")),
|
||||
theme: document.documentElement.dataset.theme || "system",
|
||||
}));
|
||||
expect(state.scrollWidth, `${theme} ${viewport.width}px ${route} overflows`).toBeLessThanOrEqual(state.clientWidth);
|
||||
expect(state.hasMain, `${route} has no main landmark`).toBe(true);
|
||||
expect(state.hasHeading, `${route} has no h1`).toBe(true);
|
||||
expect(state.theme, `${theme} SSR theme mismatch on ${route}`).toBe(theme);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user