Ctrl K
ComunidadGuías…

e2e/zzzz-technical-design.spec.ts

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
import { openProject, navigateWorkspace } from "./workspace-controls";
import { expect, test, type Page } from "@playwright/test";

async function signIn(page: Page) {
  await page.goto("/");
  await page.getByLabel("Workspace password").fill("demo-password-please-change");
  await page.getByRole("button", { name: "Sign in", exact: true }).click();
  await expect(page.getByRole("heading", { name: "Projects", exact: true })).toBeVisible();
}
const navigate = navigateWorkspace;
async function theme(page: Page, name: string) {
  await navigate(page, "Settings");
  await page.getByRole("radio", { name, exact: true }).check();
}
for (const width of [1536, 390, 320]) for (const mode of ["Light", "Dark"]) test(`technical workspace stays readable in ${mode} at ${width}px`, async ({ page }) => {
  await page.setViewportSize({ width, height: 900 }); await signIn(page); await theme(page, mode);
  for (const section of ["Projects", "Inventory", "Project details", "Settings"]) {
    await navigate(page, section === "Project details" ? "Projects" : section);
    if (section === "Project details") {
      await openProject(page, "Synthetic H2D desk lamp");
      await expect(page.getByRole("heading", { name: "Synthetic H2D desk lamp", exact: true })).toBeVisible();
    }
    await expect(page.locator("html")).toHaveAttribute("data-theme", mode.toLowerCase());
    expect(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth)).toBe(true);
    expect(await page.locator("main").evaluate((element) => { const rect = element.getBoundingClientRect(); return rect.left >= 0 && rect.right <= innerWidth; })).toBe(true);
  }
  await page.getByLabel("Open workspace commands").click();
  await expect(page.getByRole("dialog", { name: "Workspace commands", exact: true })).toBeVisible();
  expect(await page.getByRole("dialog").evaluate((element) => { const rect = element.getBoundingClientRect(); return rect.left >= 0 && rect.right <= innerWidth && rect.bottom <= innerHeight; })).toBe(true);
  await page.keyboard.press("Escape"); await expect(page.getByRole("dialog")).toHaveCount(0);
});
test("appearance and compact rows persist without business-data writes", async ({ page }) => {
  await page.setViewportSize({ width: 1440, height: 900 }); await signIn(page);
  const writes: string[] = []; page.on("request", request => { if (request.url().includes("/api/") && !["GET", "HEAD", "OPTIONS"].includes(request.method())) writes.push(request.url()); });
  await navigate(page, "Inventory"); await expect(page.locator(".inventory-table tbody tr").first()).toBeVisible();
  const before = await page.locator(".inventory-table tbody tr").first().evaluate(row => row.getBoundingClientRect().height);
  await navigate(page, "Settings");
  await page.getByRole("radio", { name: "Compact", exact: true }).check();
  await page.getByRole("radio", { name: "Dark", exact: true }).check();
  await navigate(page, "Inventory");
  const after = await page.locator(".inventory-table tbody tr").first().evaluate(row => row.getBoundingClientRect().height);
  expect(after).toBeLessThan(before);
  await page.reload();
  await expect(page.locator("html")).toHaveAttribute("data-theme", "dark");
  await expect(page.locator("html")).toHaveAttribute("data-density", "compact");
  await navigate(page, "Projects");
  await expect(page.getByRole("heading", { name: "Projects", exact: true })).toBeVisible();
  expect(writes).toEqual([]);
});
test("commands open pages and forms by keyboard while retaining focus boundaries", async ({ page }) => {
  await signIn(page); await page.keyboard.press("Control+Shift+K");
  const input = page.getByRole("combobox", { name: "Find a page, project or action", exact: true }); await expect(input).toBeFocused();
  await input.fill("settings"); await input.press("Enter"); await expect(page.getByRole("heading", { name: "Settings", exact: true })).toBeVisible(); await expect(page.getByRole("dialog")).toHaveCount(0);
  await page.getByLabel("Open workspace commands").click(); await input.fill("new project"); await input.press("Enter");
  await expect(page.getByRole("dialog", { name: "Create project", exact: true })).toBeVisible(); await expect(page.locator(".app-background")).toHaveAttribute("aria-hidden", "true");
  await page.keyboard.press("Escape"); await expect(page.getByRole("dialog")).toHaveCount(0);
  await page.keyboard.press("Control+k"); await expect(page.getByRole("textbox", { name: "Search inventory", exact: true })).toBeFocused();
});
test("system colour changes and reduced motion work without reload", async ({ page }) => {
  await page.emulateMedia({ colorScheme: "light", reducedMotion: "reduce" }); await signIn(page);
  await expect(page.locator("html")).toHaveAttribute("data-theme", "light");
  await page.emulateMedia({ colorScheme: "dark", reducedMotion: "reduce" }); await expect(page.locator("html")).toHaveAttribute("data-theme", "dark");
  await page.getByLabel("Open workspace commands").click(); expect(await page.getByRole("dialog").evaluate((element) => getComputedStyle(element).animationName)).toBe("none");
  await page.keyboard.press("Escape"); await expect(page.getByLabel("Open workspace commands")).toBeFocused();
});
test("the skip link moves focus without changing the selected page", async ({ page }) => {
  await signIn(page); await navigate(page, "Inventory"); const url = page.url();
  await page.getByRole("link", { name: "Skip to workspace", exact: true }).focus();
  await page.keyboard.press("Enter"); await expect(page.locator("#main-content")).toBeFocused(); expect(page.url()).toBe(url);
  await expect(page.getByRole("heading", { name: "Inventory", exact: true })).toBeVisible();
});
test("both themes retain text contrast and serve fonts from the application", async ({ page }) => {
  const fontOrigins: string[] = []; page.on("request", (request) => { if (/\.woff2?(?:\?|$)/u.test(request.url())) fontOrigins.push(new URL(request.url()).origin); });
  await signIn(page); await page.evaluate(() => document.fonts.ready);
  expect(fontOrigins.length).toBeGreaterThan(0); expect(new Set(fontOrigins)).toEqual(new Set([new URL(page.url()).origin]));
  for (const name of ["Light", "Dark"]) {
    await theme(page, name);
    const contrast = await page.evaluate(() => {
      const style = getComputedStyle(document.documentElement);
      // CSS minifiers may shorten hex values or emit named/rgb colours.
      // Measure the browser's rendered sRGB value instead of assuming six hex digits.
      const canvas = document.createElement("canvas"); canvas.width = canvas.height = 1;
      const context = canvas.getContext("2d")!;
      const luminance = (token: string) => {
        context.fillStyle = style.getPropertyValue(token).trim(); context.fillRect(0, 0, 1, 1);
        const rgb = Array.from(context.getImageData(0, 0, 1, 1).data).slice(0, 3).map((channel) => channel / 255).map((channel) => channel <= 0.04045 ? channel / 12.92 : ((channel + 0.055) / 1.055) ** 2.4);
        return rgb[0]! * 0.2126 + rgb[1]! * 0.7152 + rgb[2]! * 0.0722;
      };
      return [["--ink", "--surface"], ["--ink-soft", "--surface-strong"], ["--ink-muted", "--surface-soft"], ["--on-accent", "--accent"], ["--moss-dark", "--moss-soft"], ["--amber-ink", "--amber-soft"], ["--red", "--red-soft"], ["--slate", "--slate-soft"]].map(([a, b]) => { const first = luminance(a!), second = luminance(b!); return { token: a, ratio: (Math.max(first, second) + 0.05) / (Math.min(first, second) + 0.05) }; });
    });
    for (const pair of contrast) expect(pair.ratio, `${name} ${pair.token}`).toBeGreaterThanOrEqual(4.5);
  }
});