import { test, expect, type Page } from "@playwright/test"; import { resetData, resolvePath, writeChannelConfig, writeSite, } from "./helpers"; // THE /channels RACK AUDIT — screenshots, not assertions. A person (and the // reviewer) LOOKS at every shot for layering, overlap, clipping and // misalignment; channels-rack-layers.spec.ts is where a finding becomes a // hit-test that fails. Skipped unless E2E_RACK_SHOTS is set, so the ordinary suite // never spends time here: // // E2E_RACK_SHOTS=1 pnpm e2e channels-rack-audit.spec.ts (from the repo root) // // Shots land in editor/test-results/rack-shots/ (gitignored; Playwright clears // test-results/ at the start of every run, so copy a set out before the next). test.skip(!process.env.E2E_RACK_SHOTS, "audit only"); const EXTRA = Array.from( { length: 16 }, (_, i) => `rack-${String(i + 1).padStart(2, "0")}`, ); // Two groups so a section boundary is on screen, and enough rows to scroll on // both axes. async function seed() { await resetData("two-slow-channels"); for (const slug of EXTRA) await writeChannelConfig(slug); await writeSite("alpha", { siteTitle: "Alpha", groups: [ { id: "default", name: "All channels", selectedByDefault: true, order: 2 }, { id: "news", name: "News", description: "Shows where the host is the guest", selectedByDefault: false, order: 1, }, ], channels: [ { slug: "slow-b", groupId: "news" }, { slug: "rack-01", groupId: "news" }, { slug: "slow-a" }, ...EXTRA.slice(1).map((slug) => ({ slug })), ], }); } const VIEWPORTS = [ { name: "desktop", width: 1440, height: 900 }, { name: "mobile", width: 390, height: 844 }, ] as const; async function shot(page: Page, name: string) { await page.screenshot({ path: resolvePath(`test-results/rack-shots/${name}.png`) }); } // Bottom-right: the region's own scroll (both axes on md+, the horizontal one // below md) and the document's (below md the document is what scrolls down). async function scrollBottomRight(page: Page) { await page.getByTestId("channels-rack").evaluate((el) => { el.scrollTop = el.scrollHeight; el.scrollLeft = el.scrollWidth; }); await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight)); // Let sticky / ResizeObserver settle before the shutter. await page.waitForTimeout(250); } async function open(page: Page) { await seed(); await page.goto("/channels?site=alpha"); await expect(page.getByRole("link", { name: "rack-16" })).toBeVisible(); // HYDRATED, not just painted: the stations are disabled until mount, and the // rack's measured custom properties (--thead-h, --rack-w) and every // onToggle exist only from then on. A click before it lands on server HTML. await expect(page.getByLabel("sync group News")).toBeEnabled(); } for (const vp of VIEWPORTS) { test.describe(`rack audit @ ${vp.name}`, () => { test.use({ viewport: { width: vp.width, height: vp.height } }); test(`${vp.name} grouped, top`, async ({ page }) => { await open(page); await shot(page, `${vp.name}-grouped-top`); }); test(`${vp.name} grouped, scrolled bottom-right`, async ({ page }) => { await open(page); await scrollBottomRight(page); await shot(page, `${vp.name}-grouped-scrolled`); }); test(`${vp.name} flat, scrolled bottom-right`, async ({ page }) => { await open(page); await page.getByLabel("Group by section").uncheck(); await expect(page.getByRole("rowheader")).toHaveCount(0); await scrollBottomRight(page); await shot(page, `${vp.name}-flat-scrolled`); }); test(`${vp.name} deck open, scrolled bottom`, async ({ page }) => { await open(page); await page.getByLabel("select all channels").check(); await expect(page.getByLabel("channel priority bulk")).toBeVisible(); await scrollBottomRight(page); await shot(page, `${vp.name}-deck-open`); }); // Below md the deck pins to the SCREEN while the document scrolls: park // the column header under the deck's top edge to see which paints on top. // A shorter window, because at 844 px the header sits above where the // deck starts before the page has scrolled at all. test(`${vp.name} deck open over the column header`, async ({ page }) => { await page.setViewportSize({ width: vp.width, height: 640 }); await open(page); await page.getByLabel("select all channels").check(); const deck = page.getByLabel("channel priority bulk"); await expect(deck).toBeVisible(); await page.evaluate(() => { const deckTop = document .querySelector('[aria-label="channel priority bulk"]')! .getBoundingClientRect().top; const theadTop = document.querySelector("thead")!.getBoundingClientRect().top; window.scrollBy(0, theadTop - (deckTop + 12)); }); await page.waitForTimeout(250); await shot(page, `${vp.name}-deck-over-thead`); }); test(`${vp.name} Advanced popover open`, async ({ page }) => { await open(page); await page.getByLabel("advanced priority for slow-a").click(); await expect(page.getByLabel("download override for slow-a")).toBeVisible(); await shot(page, `${vp.name}-popover-open`); }); }); }