commit 9278a39fab8b8cd830652a9d9ed506a707b9ba3d
parent e4d508d23248cf9f548325a47407393338be6723
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date: Thu, 24 Sep 2026 18:58:02 -0400
channels: rack audit
A screenshot spec, not assertions: 1440x900 and 390x844, grouped top,
grouped and flat scrolled bottom-right, the deck open (scrolled to the
bottom and parked over the column header), the Advanced popover open —
twelve shots into editor/test-results/rack-shots/. Skipped unless
RACK_SHOTS is set, so the ordinary suite never runs it. Each finding
becomes its own fix commit; the table is in the slice record.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Diffstat:
1 file changed, 134 insertions(+), 0 deletions(-)
diff --git a/editor/e2e/channels-rack-audit.spec.ts b/editor/e2e/channels-rack-audit.spec.ts
@@ -0,0 +1,134 @@
+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 RACK_SHOTS is set, so the ordinary suite
+// never spends time here:
+//
+// 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.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();
+}
+
+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 just above the bottom edge, under the deck, to see
+ // which paints on top.
+ test(`${vp.name} deck open over the column header`, async ({ page }) => {
+ await open(page);
+ await page.getByLabel("select all channels").check();
+ await expect(page.getByLabel("channel priority bulk")).toBeVisible();
+ await page.evaluate(() => {
+ const thead = document.querySelector("thead")!;
+ const top = thead.getBoundingClientRect().top + window.scrollY;
+ window.scrollTo(0, Math.max(0, top - window.innerHeight + 60));
+ });
+ 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`);
+ });
+ });
+}