import { expect, test, type Page } from "@playwright/test"; import { branchedHistory, linearHistory } from "../fixtures/rows"; import { commitRows, mockTauri, openRepository } from "./tauri"; test.describe("structure", () => { test("puts every region behind a landmark", async ({ page }) => { await mockTauri(page, { rows: linearHistory(20) }); await page.goto("/"); await expect(page.getByRole("banner")).toBeVisible(); await expect(page.getByRole("navigation", { name: "Repository" })).toBeVisible(); await expect(page.getByRole("main")).toBeVisible(); await expect(page.getByRole("contentinfo")).toBeVisible(); }); test("keeps the history a list in every engine", async ({ page }) => { // WebKit drops list semantics when a list is styled with `list-style: none`, // which Tailwind's preflight applies — and WebKit is what the app actually // runs in on macOS. This is the test that would have caught it. await mockTauri(page, { rows: linearHistory(20) }); await page.goto("/"); await openRepository(page); await expect(page.getByRole("main").getByRole("list")).toHaveCount(1); await expect(commitRows(page)).toHaveCount(20); }); test("hides the graph drawing from assistive technology", async ({ page }) => { await mockTauri(page, { rows: branchedHistory() }); await page.goto("/"); await openRepository(page); // The row already reads correctly without the lanes; announcing the SVG // would only add noise. const svgs = page.locator("ol svg"); const count = await svgs.count(); expect(count).toBeGreaterThan(0); for (let index = 0; index < count; index++) { await expect(svgs.nth(index)).toHaveAttribute("aria-hidden", "true"); } }); test("names each ref badge, since its colour says nothing out loud", async ({ page }) => { await mockTauri(page, { rows: branchedHistory() }); await page.goto("/"); await openRepository(page); const first = commitRows(page).first(); await expect(first).toContainText("current branch"); await expect(first).toContainText("main"); }); }); test.describe("keyboard", () => { test.beforeEach(async ({ page }) => { await mockTauri(page, { rows: linearHistory(60) }); await page.goto("/"); await openRepository(page); await expect(commitRows(page).first()).toBeVisible(); }); test("reaches the history in a few tab stops, not one per commit", async ({ page }) => { const stops = await page.locator("ol button[tabindex='0']").count(); expect(stops).toBe(1); }); test("walks the history with the arrow keys", async ({ page }) => { await page.locator("[data-row='0']").focus(); await page.keyboard.press("ArrowDown"); await page.keyboard.press("ArrowDown"); await page.keyboard.press("ArrowDown"); await expect(page.locator(":focus")).toHaveAttribute("data-row", "3"); }); test("jumps to the ends", async ({ page }) => { await page.locator("[data-row='0']").focus(); await page.keyboard.press("End"); await expect(page.locator(":focus")).not.toHaveAttribute("data-row", "0"); await page.keyboard.press("Home"); await expect(page.locator(":focus")).toHaveAttribute("data-row", "0"); }); }); test.describe("skip link", () => { test("is the first thing a keyboard reaches, and moves focus to the history", async ({ page, }) => { // Deliberately no prior interaction: the point of a skip link is that it is // the very first tab stop on a fresh page. await mockTauri(page, { rows: linearHistory(20) }); await page.goto("/"); await page.keyboard.press("Tab"); // A button, not a link: WebKit keeps links out of the tab order unless the // system's keyboard navigation setting is on, which it is not by default — // and WebKit is what the app runs in on macOS. This assertion is the one // that caught it. await expect(page.getByRole("button", { name: "Skip to history" })).toBeFocused(); await page.keyboard.press("Enter"); // main carries tabindex="-1" so focus really lands there rather than the // page merely scrolling. await expect(page.getByRole("main")).toBeFocused(); }); }); /** The colours the theme actually resolved to, under one scheme. */ async function palette(page: Page, scheme: "light" | "dark") { await page.emulateMedia({ colorScheme: scheme }); await mockTauri(page, { rows: branchedHistory() }); await page.goto("/"); await openRepository(page); await expect(commitRows(page).first()).toBeVisible(); return { header: await page .getByRole("banner") .evaluate((element) => getComputedStyle(element).backgroundColor), lane: await page .locator("path.graph-line") .first() .evaluate((element) => getComputedStyle(element).stroke), }; } test.describe("appearance", () => { // Deliberately not screenshots: baselines differ by operating system and // font rendering, so they fail for reasons that have nothing to do with the // change under test. Computed colours assert the actual claim — that // light-dark() resolves differently per scheme with no JavaScript involved. test("resolves a different palette per colour scheme", async ({ page }) => { const light = await palette(page, "light"); const dark = await palette(page, "dark"); expect(light.header).not.toBe(dark.header); expect(light.lane).not.toBe(dark.lane); }); test("gives lanes a real colour rather than falling back to a default", async ({ page }) => { const { lane } = await palette(page, "light"); expect(lane).not.toBe("none"); expect(lane).toMatch(/^(rgb|oklch|color)/u); }); });