Skip to content

test(sp3): visual-regression infrastructure (Playwright + SSIM) + golden refresh - #10

Merged
steeltroops-ai merged 1 commit into
mainfrom
tests/sp3-visual-regression
Apr 29, 2026
Merged

test(sp3): visual-regression infrastructure (Playwright + SSIM) + golden refresh#10
steeltroops-ai merged 1 commit into
mainfrom
tests/sp3-visual-regression

Conversation

@steeltroops-ai

Copy link
Copy Markdown
Owner

Closes the last two SP-3 groups in one PR: 3B wires the visual-regression
suite to a real Playwright + SSIM pipeline, 3D lands the maintenance
script that re-captures goldens behind a --confirm flag. The shader:check
alias used to match zero tests; it now invokes a real suite that the
user activates by running shader:update-goldens --confirm once.

Goldens themselves are NOT committed in this PR. The runner skip-warns
when a golden is missing so the suite stays green until they're captured;
that capture step needs the dev server up and a one-time visual review of
each PNG, which is the right shape for an operator workflow rather than
an autonomous PR step.

Group 3B - infrastructure:

  • tests/golden/manifest.json: 3 frame entries (Schwarzschild face-on,
    Kerr a=0.5 inclined, near-extremal a=0.99 edge-on) with per-frame
    ssim_threshold and max_pixel_delta. Threshold ladder (0.998 / 0.997 /
    0.996) reflects increasing visual variance with spin.
  • tests/visual-regression/capture.ts: Playwright headless drives the
    dev server, hashes the simulation config into the URL (mass, spin,
    zoom, diskTemp, etc., the same keys useUrlState reads), waits for
    canvas mount + a configurable number of requestAnimationFrame ticks
    for TAA to converge, screenshots at the manifest viewport.
  • tests/visual-regression/diff.ts: pixelmatch for the per-pixel count,
    ssim.js for the structural similarity score, manual sweep for the
    max-pixel-delta.
  • tests/visual-regression/runner.test.ts: vitest runner that boots the
    dev server, walks every manifest entry, and asserts SSIM + pixel
    delta against the committed PNG. Skip-warns if golden missing.
  • vitest.shader.config.ts: separate vitest config so shader:check stays
    out of the default suite (the boot path is expensive). The default
    vitest run now excludes tests/visual-regression entirely.

Group 3D - golden refresh:

  • scripts/update-goldens.ts: walks the manifest, captures each frame,
    stamps captured_at + captured_commit on each entry, refuses to run
    without --confirm. Reuses an already-running dev server if one is up
    at SHADER_CHECK_BASE_URL.
  • shader:update-goldens script wired in package.json.

Test plan

  • bun run type-check (clean)
  • SHADER_CHECK_SKIP_DEV=1 bunx vitest run --config vitest.shader.config.ts (4 tests pass; manifest loads + 3 frames skip-warn for missing goldens, as expected)
  • bun run test (default suite excludes tests/visual-regression cleanly)
  • bun run shader:update-goldens --confirm (operator workflow, runs once when satisfied with visual output)
  • commit tests/golden/*.png from the previous step in a follow-up PR

Follow-on PRs

The goldens themselves want their own PR so the visual review and the
infrastructure changes don't hide behind each other in the diff.

Wires shader:check to a real Playwright + SSIM suite and lands the
shader:update-goldens maintenance script. Goldens are not committed
in this PR; the suite skip-warns when one is missing so the default
test run stays green until they're captured. The user runs
shader:update-goldens --confirm once to populate tests/golden/, then
commits the PNGs in a follow-up.

The capture pipeline drives the dev server through Playwright headless,
hashes the simulation config into the URL (mass, spin, zoom, diskTemp,
etc., the same keys useUrlState already reads), waits for the canvas
to mount and complete enough requestAnimationFrame ticks for TAA to
converge, then screenshots at the manifest viewport. Deterministic
seeding is not yet wired; the stabilization-frames count is the lever
that controls run-to-run variance for now.

The diff stage uses pixelmatch for the per-pixel count plus ssim.js
for the structural similarity score and a manual max-pixel-delta
sweep. Per-frame thresholds live in tests/golden/manifest.json so
tightening or relaxing one frame doesn't ripple through the others;
the threshold ladder (0.998 / 0.997 / 0.996) reflects increasing
visual variance with spin.

The runner is its own vitest config (vitest.shader.config.ts) so the
expensive boot path stays opt-in; the default vitest run excludes
tests/visual-regression entirely. SHADER_CHECK_BASE_URL +
SHADER_CHECK_SKIP_DEV environment knobs let an already-running dev
server serve the suite without a second boot.

shader:update-goldens captures every frame in the manifest, stamps
captured_at + captured_commit on each entry, and refuses to run
without --confirm. The user diffs the PNG output before committing;
a wrong golden silently shifts the regression baseline.
@coderabbitai

coderabbitai Bot commented Apr 29, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@steeltroops-ai has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 32 minutes and 30 seconds before requesting another review.

To keep reviews running without waiting, you can enable usage-based add-on for your organization. This allows additional reviews beyond the hourly cap. Account admins can enable it under billing.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 205f8235-1e77-4351-8418-ccc465f72c23

📥 Commits

Reviewing files that changed from the base of the PR and between 29881eb and e727319.

⛔ Files ignored due to path filters (2)
  • bun.lock is excluded by !**/*.lock
  • physics-engine/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (11)
  • package.json
  • scripts/update-goldens.ts
  • tests/golden/.gitignore
  • tests/golden/manifest.json
  • tests/visual-regression/README.md
  • tests/visual-regression/capture.ts
  • tests/visual-regression/diff.ts
  • tests/visual-regression/manifest.ts
  • tests/visual-regression/runner.test.ts
  • vitest.config.ts
  • vitest.shader.config.ts
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch tests/sp3-visual-regression

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
Review rate limit: 0/1 reviews remaining, refill in 32 minutes and 30 seconds.

Comment @coderabbitai help to get the list of available commands and usage tips.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e72731990a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +41 to +45
devProc = spawn("bun", ["run", "dev"], {
stdio: "ignore",
shell: true,
detached: false,
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Refuse pre-existing dev server before running visual tests

When SHADER_CHECK_SKIP_DEV is not set, this always starts bun run dev and then only probes BASE_URL for readiness. If port 3000 is already occupied (for example, a stale dev server from another branch), the new process can fail to bind while waitForServerReady still succeeds against the old server, so the suite captures/diffs the wrong build and can report misleading pass/fail results.

Useful? React with 👍 / 👎.

@steeltroops-ai
steeltroops-ai merged commit 7021dd0 into main Apr 29, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant