test(e2e): guard sidebar brand vs macOS titlebar cluster (#366) (#374)

#366 (brand text overlapped by the fixed macOS TitlebarNav cluster on
board routes) is already fixed on main: #263 made the shell render the
topbar on every route, so the sidebar always hangs below the 56px
titlebar band and the brand never lands in the cluster's lane.

Add an e2e regression guard that locks the invariant in for the routes
the issue named — the brand must not overlap the fixed cluster and the
"Agent Orchestrator" wordmark must stay readable (not truncated) — on the
home board route and the project board route, plus across a board→session
transition (the persistent brand must not jump). Drives the live renderer
under a forced macOS UA. If a topbar-less route is ever reintroduced,
these fail.

Verified: passes against current main with no source changes;
typecheck clean.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Pritom Mazumdar 2026-06-22 13:14:01 +05:30 committed by GitHub
parent c6d9692d37
commit 348fd414d1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 82 additions and 0 deletions

View File

@ -0,0 +1,82 @@
import { expect, test, type Locator, type Page } from "@playwright/test";
// Regression guard for #366 (macOS): the sidebar's "Agent Orchestrator" brand
// must never sit under the fixed TitlebarNav cluster, and the wordmark must stay
// readable. The original bug was board routes (`/` and `/projects/:id`) having no
// topbar, so the sidebar stayed at top-0 and the brand landed in the cluster's
// 56px lane. It is now fixed structurally — the shell renders the topbar on every
// route, so the sidebar always hangs below the titlebar band — and these tests
// lock that invariant in: if a topbar-less route is ever reintroduced, they fail.
//
// macOS-only: TitlebarNav (and the bug) gate on navigator.userAgent looking like
// a Mac, read once at module load. Force a Mac UA so this is deterministic
// regardless of the host/CI OS.
test.use({
userAgent:
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
});
const brand = (page: Page) => page.getByText("Agent Orchestrator", { exact: true });
// Two boxes overlap iff they intersect on both axes.
function overlaps(a: { x: number; y: number; width: number; height: number }, b: typeof a) {
return a.x < b.x + b.width && a.x + a.width > b.x && a.y < b.y + b.height && a.y + a.height > b.y;
}
// The brand <span> has `truncate` (overflow:hidden), so it stays "visible" even
// when clipped to nothing. Compare scroll vs client width to prove the wordmark
// is actually fully rendered, not just present-but-clipped.
async function isTruncated(span: Locator) {
return span.evaluate((el) => el.scrollWidth > el.clientWidth + 1);
}
async function expectBrandClearsCluster(page: Page) {
const cluster = page.locator(".titlebar-nav");
await expect(cluster).toBeVisible();
const span = brand(page);
await expect(span).toBeVisible();
const clusterBox = await cluster.boundingBox();
const brandBox = await span.boundingBox();
expect(clusterBox).not.toBeNull();
expect(brandBox).not.toBeNull();
expect(overlaps(brandBox!, clusterBox!)).toBe(false);
expect(await isTruncated(span)).toBe(false);
}
test("home board route: brand clears the macOS titlebar cluster and stays readable", async ({ page }) => {
await page.goto("/");
await expect(page.getByText("Projects")).toBeVisible();
await expectBrandClearsCluster(page);
});
test("project board route: brand clears the macOS titlebar cluster and stays readable", async ({ page }) => {
await page.goto("/");
await expect(page.getByText("Projects")).toBeVisible();
// In-app nav to /projects/:id (a hard load boots the router at the board).
await page.getByRole("button", { name: "Open api-gateway dashboard" }).click();
// The active project row marks itself aria-current=page once navigation lands.
await expect(page.locator('[aria-current="page"]')).toBeVisible();
await expectBrandClearsCluster(page);
});
test("brand stays put and readable when navigating board → session", async ({ page }) => {
await page.goto("/");
await expect(page.getByText("Projects")).toBeVisible();
const boardBrandBox = await brand(page).boundingBox();
expect(boardBrandBox).not.toBeNull();
await page.getByRole("button", { name: "Open Split terminal mux responsibilities" }).click();
await expect(page.locator(".dashboard-app-header")).toBeVisible();
const sessionBrandBox = await brand(page).boundingBox();
expect(sessionBrandBox).not.toBeNull();
// Persistent shell element: no vertical/horizontal jump across the transition.
expect(Math.abs(sessionBrandBox!.x - boardBrandBox!.x)).toBeLessThanOrEqual(1);
expect(Math.abs(sessionBrandBox!.y - boardBrandBox!.y)).toBeLessThanOrEqual(1);
await expectBrandClearsCluster(page);
});