feat(storybook): add visual regression testing for stories#3392
feat(storybook): add visual regression testing for stories#3392pauldambra wants to merge 18 commits into
Conversation
Port posthog/posthog's Storybook test-runner approach: every story is snapshot-tested in Chromium in dark and light themes via @storybook/test-runner + jest-image-snapshot (ssim, 1% threshold). Baselines are Linux-rendered, live in apps/code/.storybook/__snapshots__, and are auto-committed to the PR branch by CI through the GitHub API so they satisfy the signed-commit ruleset and show as reviewable PNG diffs. The preview freezes time (mockdate) and seeds Math.random per story render under the test runner so elapsed timers and randomized status verbs render identically on every pass. Also fixes 8 story suites that could not load in Storybook at all: the @posthog/agent dist bundles are Node-targeted (createRequire banner, fs/path imports), so the browser build now shims node:module and node:fs and resolves node:path to pathe. Generated-By: PostHog Code Task-Id: c8fa7392-2afe-4919-b8be-e6e3b56e1419
|
Merging to
After your PR is submitted to the merge queue, this comment will be automatically updated with its status. If the PR fails, failure details will also be posted here |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3cf7b509cf
ℹ️ 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".
pauldambra
left a comment
There was a problem hiding this comment.
Note
🤖 Automated comment by QA Swarm — not written by a human
QA Swarm review complete. See inline comments.
|
Note 🤖 Automated comment by QA Swarm — not written by a human Multi-perspective review: qa-team (specialists + generalists), paul-reviewer, xp-reviewer, security-audit Verdict:
|
| Reviewer | Assessment |
|---|---|
| 🔍 qa-team | Careful port, but a verified showstopper in change detection plus three mediums (wait-swallow, loop guard, no pruning). |
| 👤 paul | Really nice work with why-comments throughout; wants the filter question nailed down before merge — a check that goes green by skipping is the worst kind. |
| 📐 xp | Clean infra, comments earn their place; the every filter makes the guard protect nothing while showing green (Rule 1: does it actually work?). |
| 🛡 security-audit | Fundamentals sound (fork gating, no injection, pathspec-constrained writes, SHA pins); fail-open filter is the main defect, plus token least-privilege hardening. |
Automated by QA Swarm — not a human review
Fixes the paths-filter predicate-quantifier bug that made the workflow never trigger, makes background build failures fail loudly instead of being swallowed by a bare `wait`, adds a real commit-loop circuit breaker, swaps to actions/create-github-app-token with narrowed permissions, prunes obsolete snapshot baselines via jest-image-snapshot's outdated-snapshot reporter, hardens the snapshot-commit script's git status parsing and stale-branch handling, and tightens several small issues in the test runner and preview decorator. Generated-By: PostHog Code Task-Id: c8fa7392-2afe-4919-b8be-e6e3b56e1419
Extract wait_all helper in the workflow build step, compute the auto-commit/fork conditions once, and tie the post-theme-flip wait to an actual paint (plus a short settle) instead of a flat sleep alone. Generated-By: PostHog Code Task-Id: c8fa7392-2afe-4919-b8be-e6e3b56e1419
…k-visual-regression # Conflicts: # pnpm-lock.yaml
|
React Doctor found no issues in the changed files. 🎉 Reviewed by React Doctor for commit |
…ping Skipping the retriggered run left the PR head with a skipped check that never proved the committed baselines pass. Verify mode still breaks the auto-commit loop (it never commits) but actually executes the suite. Generated-By: PostHog Code Task-Id: c8fa7392-2afe-4919-b8be-e6e3b56e1419
The story container handed out a fresh Proxy on every unbound service resolution, so hooks using the service as an effect dependency (useFeatureFlag) re-ran every render, and isEnabled returning a truthy Proxy meant setState never settled - an infinite update loop that crashed every ConversationView story with a user message into the error boundary (their committed baselines were error screens). Cache stubs per service id and bind a real FeatureFlags fake that returns booleans. Generated-By: PostHog Code Task-Id: c8fa7392-2afe-4919-b8be-e6e3b56e1419
…mitting PNGs Match posthog/posthog's actual model: PNGs are gitignored and never committed; CI captures every story and submits the images to the PostHog Visual Review product via the vr CLI (built from posthog/posthog master). VR diffs against the signed manifest at apps/code/snapshots.yml, gates the PR, and commits the approved manifest back to the branch. Removes the auto-commit script, the verify-mode circuit breaker (no commit loop exists anymore), and the 358 committed baseline PNGs. The vr step no-ops until one-time setup lands: VR repo registration for PostHog/code, a VR_API_TOKEN secret, and the seeded snapshots.yml (see docs/testing.md). Generated-By: PostHog Code Task-Id: c8fa7392-2afe-4919-b8be-e6e3b56e1419
Repo registered in Visual Review (project 2); this is the empty signed manifest the vr CLI reads its config from. VR populates it via approval commits once VR_API_TOKEN is set. Generated-By: PostHog Code Task-Id: c8fa7392-2afe-4919-b8be-e6e3b56e1419
With the baseline now committed, an empty token would fail the submit with an auth error instead of degrading gracefully. Generated-By: PostHog Code Task-Id: c8fa7392-2afe-4919-b8be-e6e3b56e1419
358 updated Run: bb85fa31-86d5-4e80-b96e-aaa8f237c1a5 Co-authored-by: pauldambra <984817+pauldambra@users.noreply.github.com>
The VR repo registration had no baseline_file_paths configured, so the approval commit landed at the backend default (root .snapshots.yml) while CI reads apps/code/snapshots.yml. Set baseline_file_paths to storybook: apps/code/snapshots.yml in Visual Review settings so future approval commits land on the path CI uses. Generated-By: PostHog Code Task-Id: c8fa7392-2afe-4919-b8be-e6e3b56e1419
…shes MockDate.set was gated on inStorybookTestRunner() at preview module scope, but the test runner appends its UA marker via an injected script after the page loads, so the check read false and the clock was never frozen - rendered wall-clock times drifted between runs (caught by Visual Review on the autoresearch dashboard stories; the drift sat just under jest-image-snapshot's 1% threshold, which is why local verify passes missed it). Freeze the clock in the outermost decorator, where the marker is visible, and pin the autoresearch fixture epoch since module-scope fixtures evaluate before any decorator runs. Also skip the visual-regression job when a push is the VR bot committing an approved baseline: paths-filter diffs the whole PR, so the existing filter exclusion can never catch it; compare the push delta instead, as posthog/posthog does. Generated-By: PostHog Code Task-Id: c8fa7392-2afe-4919-b8be-e6e3b56e1419
|
✅ Visual changes approved by @pauldambra — baseline updated in 11 changed. Changed …and 3 more — view all in PostHog. |
11 updated Run: b446dbac-f55e-4036-9076-46c5a1535517 Co-authored-by: pauldambra <984817+pauldambra@users.noreply.github.com>
What
Ports posthog/posthog's Storybook visual regression approach: every story is screenshot in Chromium in both dark and light themes using
@storybook/test-runner, and CI submits the captures to the PostHog Visual Review product for diffing, PR gating, and review — exactly like posthog/posthog. No PNGs are committed to the repo.How it works
apps/code/.storybook/test-runner.ts): waits for fonts, quill spinners/skeletons, image loads, and DOM stability; disables animations; flips the theme global per snapshot so one render pass produces<story-id>--dark.pngand--light.png. Component snapshots clip to the union of#storybook-rootand any Radix-portaled dialogs/popovers. Per-story knobs viaparameters.testOptions(viewport, waitForSelector, themes, snapshotTargetSelector) andtags: ["test-skip"].mockdateand re-seedsMath.randombefore every story render (needed becauseGeneratingIndicatorrenders a random verb and a live timer). Two consecutive full verify passes ran clean locally (358/358), and a CI verify run passed against a CI capture..github/workflows/code-storybook.yml): builds workspace packages + static Storybook, captures all stories, thenvr submits the PNGs to Visual Review (CLI built from posthog/posthog master, same as posthog's own CI). VR diffs against the signed manifest atapps/code/snapshots.yml, posts the verdict on the PR, and commits the approved manifest back to the branch on approval. Main pushes submit with--purpose observe(tracking, non-gating). PNGs and diff/failure output are gitignored; failure screenshots upload as artifacts; junit goes to Trunk.One-time setup required before VR gates anything
The vr step silently no-ops until this is done (the capture still runs and catches broken stories):
PostHog/codein Visual Review settings (project 2) — mints the repo UUID.visual_reviewscope; add it as theVR_API_TOKENActions secret here.apps/code/snapshots.yml(template indocs/testing.md).Bugs fixed along the way (both pre-existing)
@posthog/agentdist bundles are Node-targeted (tsupcreateRequirebanner,fs/pathimports) and crashed at module load in the browser. The Storybook Vite config now shimsnode:module/node:fsand resolvesnode:pathtopathe(Storybook-only).useFeatureFlaginto an infinite setState loop (React fix: hide electron process from tray #185). Stubs are now cached per service id andFEATURE_FLAGSis bound to a fake returning real booleans.Local usage
Docs in
docs/testing.md.Note for history: earlier iterations of this branch auto-committed baseline PNGs; those commits remain in the branch history and will fold away if the PR is squash-merged.
Created with PostHog Code