Archilyzer · Source

archilyzer

Archilyzer
git clone https://archilyzer.pages.dev/source/archilyzer.git
Log | Files | Refs | README | LICENSE

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:
Aeditor/e2e/channels-rack-audit.spec.ts | 134+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
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`); + }); + }); +}