feat(visual-review): Playwright before/after capture across viewports - #758
feat(visual-review): Playwright before/after capture across viewports#758Helios531 wants to merge 8 commits into
Conversation
|
Note Gittensory Gate skippedPR closed before full evaluation. No late first comment was created.
💰 Earn for open-source contributions like this. Gittensor lets GitHub contributors earn for the work they already do — register to start earning →. Checked by Gittensory, a quiet PR intelligence layer for OSS maintainers. |
|
gittensory · advisory review Reviewed 5 changed file(s) — two independent AI reviewers. Changed files: Suggested action: 🛠️ Request changes. (reviewers split: request changes / merge) Address the suggestions below before merging. Reviewer A · Suggestions
Worth double-checking
Reviewer B · Suggestions
Worth double-checking
|
|
Superagent didn't find any vulnerabilities or security issues in this PR. |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #758 +/- ##
==========================================
+ Coverage 96.58% 96.59% +0.01%
==========================================
Files 98 99 +1
Lines 14196 14240 +44
Branches 5172 5183 +11
==========================================
+ Hits 13711 13755 +44
Misses 105 105
Partials 380 380 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Signed-off-by: Alexander Laurent <57456290+Helios531@users.noreply.github.com>
|
@JSONbored Could you plz review my PR? Thank you for your attention! |
JSONbored
left a comment
There was a problem hiding this comment.
PR #758 — feat(visual-review): Playwright before/after capture across viewports
Verdict: MERGE
Resolves issue #581: FULLY
CI: All required checks green (validate, test 1/2, workers, mcp, ui, lint, security, codecov patch+project).
Highlights:
- Pure browser-agnostic core (src/visual/capture.ts) with a thin Playwright/Chromium runner injected via a narrow CaptureBrowser interface — keeps testable logic out of the Worker bundle.
- Determinism (the headline AC) handled well: init script freezes Date + seeds Math.random before app code; injected CSS kills animations/transitions/caret/scrollbars; each shot waits network-idle + optional hydration selector + document.fonts.ready. Configurable desktop (1440x900) / mobile (390x844).
- Emits matched before/after PNG pairs with stable pair-aligned keys; 19 meaningful unit tests (pairing, nav order, settling, key stability, hydration on/off, error branches); core at 100% coverage; documented twice-run byte-identical capture.
Concerns / required changes:
- Lockfile/dep scope creep: package.json adds js-yaml/tar/ws overrides + bumps hono; package-lock.json churns ~4k lines — unrelated to a capture harness (ws/tar/js-yaml flagged by policy). Trim to what the feature needs.
- Routes/viewports hardcoded defaults; affected-route detection deferred to #579 (consistent with issue scope).
Completeness vs issue: Fully satisfies #581 — deterministic headless capture with hydration/network-idle waits, configurable viewports, stable matched before/after pairs, proven stable across runs. Only caveat is the unrelated dependency churn.
Signed-off-by: Alexander Laurent <57456290+Helios531@users.noreply.github.com>

Summary
Adds the Playwright before/after capture step for the owner-led visual-review pipeline (#581, epic #577, roadmap #525). Given a base origin and a head origin, it captures deterministic full-page screenshots of defined routes across desktop + mobile viewports and emits matching before/after PNG pairs per route + viewport.
Because Cloudflare Workers cannot run a browser, the capture lives in Node tooling and reuses the Playwright launch pattern from
scripts/smoke-ui-browser.mjs, as the issue requests. The testable logic is split out as a pure, browser-agnostic core so it is unit-tested and never bundled into the Worker:src/visual/capture.ts(pure core):DEFAULT_VIEWPORTS(desktop 1440×900, mobile 390×844),DEFAULT_ROUTES, deterministic settling sources (DETERMINISTIC_STYLEdisables animation/transition/caret/scrollbar;DETERMINISTIC_INIT_SCRIPTfreezesDate+ seedsMath.random;FONTS_READY_EXPRESSION), stable key naming(
visual-review/<viewport>/<route>.{before,after}.png),planCaptures, and therunVisualCaptureorchestrator (network-idle + optional hydration-selector + fonts-ready waits; one page per viewport×side; browser injected via a narrowCaptureBrowserinterface).scripts/visual-capture.ts(tsx runner): thin Chromium adapter mapping a PlaywrightPageto the core and writing the PNG pairs to disk.test/unit/visual-capture.test.ts: 19 unit tests; the core is at 100% coverage.package.json: adds"visual:capture": "tsx scripts/visual-capture.ts".Determinism is the headline acceptance requirement — the same route on the same side must render byte-stably across runs. This is achieved by freezing the clock + RNG before app code runs, stripping animations/transitions via injected CSS, and waiting for network idle, optional hydration, and web fonts before each shot.
Related Issue
Closes: #581
Change Type
Real behavior proof
The capture harness was run end-to-end with real Chromium (not just unit tests):
chromium_headless_shell-1228; the stale1194was already present, nothing already installed was reinstalled.Date.now()/new Date().toISOString(), andMath.random()), across both default viewports, base==head.visual-review/<viewport>/<route>.{before,after}.png.diff -rq run1 run2→IDENTICAL (byte-for-byte), proving the settling neutralizes the animation/clock/RNG.PNG 1440×900 8-bit RGB; desktop vs mobile hashes differ (viewport sizing applied), within-viewport hashes match (base==head fixture).Run it yourself:
BASE_URL=<base> HEAD_URL=<head> npm run visual:capture(prereqs:
npm install, thennpm run test:smoke:browser:install).Validation
git diff --checknpm run actionlint(not run — no workflow files changed)npm run typechecknpm run test:coverage— 1868 passed, 1 skipped; global gate met (statements 98.65%,branches 97.04%, functions 97.05%, lines 99.27% — all ≥ 97%); new core file at 100%
npm run test:workers