Run the Playwright suite in CI - #223
Merged
Merged
Conversation
The 192 browser specs ran nowhere but a maintainer's laptop, and the gap was not theoretical. PR #222 arrived with every check green while dropping `position-relative-to-grid` from every absolutely-snapped blueprint - 325 of the corpus's 367 - and failing `blueprint-round-trip.spec.ts` on the one blueprint that carries a grid position. Neither `vp check` nor `vp test` can see that: the whole decode -> model -> serialize path needs `FD` loaded, which needs a browser and the sprite server. It needs no Factorio and no network beyond npm - the corpus is committed under test-blueprints/ and the sprite data under packages/exporter/data/output - so this stays as offline as the rest of CI. Three choices worth stating, since each has a wrong version that looks right: - It reuses `npm run localpreview` rather than carrying its own copy of how to start the two servers. A second copy is a bound written down twice, and the one that loses is the one nothing tests. - The browser cache is keyed on the *resolved* @playwright/test version read out of node_modules, not on the caret range in package.json. The resolved version is what decides the browser build, so keying on `^1.62.0` would serve yesterday's browser to today's Playwright - which fails as a missing chrome-headless-shell and reads as a suite-wide regression, not a cache miss. - The server step redirects stdout to a file instead of inheriting it. A backgrounded process holding the step's stdout pipe open can stop that step from ever completing. `deploy` stays gated on `checks` alone. Making it wait on this too is a real question and a different change from adding the job. Also corrects the six comments that asserted Playwright does not run in CI. The reasoning under each survives - a pure unit test still answers in seconds inside `checks` where this job takes five minutes and two dev servers - but the flat claim does not, and a stale claim here is the thing this repo treats as a bug. One loose end left deliberately: localpreview serves the sprite data with `npx --yes serve` and `serve` is declared nowhere, so it is the one unpinned fetch in CI. Declaring it costs ~990 lines of lockfile for a dev-only static file server, which is a dependency decision rather than a CI one. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017Dbza77QEZnkqt1mGssY71
Two loose ends the previous commit named and left, both now closed. `deploy` waits on `e2e` as well as `checks`. The class of bug the browser suite exists to catch is precisely the one that reaches production otherwise: PR #222 type-checks, lints and passes every unit test while dropping `position-relative-to-grid` from 325 of the corpus's 367 blueprints, and `checks` was green on it. Gating on `checks` alone leaves that free to deploy. The price is about five minutes on the deploy's critical path. `serve` becomes a declared devDependency instead of something localpreview fetches with `npx --yes`. Unpinned was tolerable while that command only ran on a laptop; as a CI dependency it was a package resolved at run time in a job whose previous step is `vp install --frozen-lockfile`, which is the one thing that pipeline is meant not to do. It costs ~990 lines of lockfile for a dev-only static file server and buys a reproducible sprite server plus Renovate tracking it. Verified: `npx --no-install serve --version` answers 14.2.6, so nothing is fetched, and localpreview still brings both ports up - data.json 200 off 8081. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017Dbza77QEZnkqt1mGssY71
This was referenced Aug 12, 2026
wormeyman
added a commit
that referenced
this pull request
Aug 17, 2026
A vitest text scan over tests/*.spec.ts for `Control+A` and its four siblings. On macOS `Control+A` is the emacs "beginning of line" binding, not select-all, so nothing is selected, the typed text lands beside the old text, and the assertion reads "New textOld text". That is #197, fixed in 402fbe3. It reads source rather than driving a browser because the bug **passes on Linux**, and every runner this project has is ubuntu-latest - `vp test` and all four `e2e` shards. #223 and #225 put Playwright in CI, which does not help here: a sharded browser suite gating every PR will go green on this forever. The rule is not "never write Control". A chord reaching the app through actions.ts stays Control, where ModifierKey is Control | Shift | Alt and no Meta binding exists; a chord reaching a focused DOM <input> wants ControlOrMeta. A regex cannot separate them, so it flags both and the ALLOWLIST carries the distinction in writing - one entry today. Mutation-checked against the live instance: dropping PR #222's blueprint-grid-position.spec.ts into tests/ fails the guard naming line 97, and removing it goes green again. The knowledge already existed in prose. After 402fbe3 there is a five-line comment at display-panel-editor.spec.ts:159 explaining the whole trap, one line above its own corrected call, and a contributor then wrote `Control+A` in a different file. A comment at the scene of the last occurrence does not reach the next one. Every figure in the new file's header is measured rather than recalled, which caught two: the display panel comment is five lines and not three, and four specs hold `keyboard.down('Control')` for the ctrl-drag gesture and not five. Both came from #208's own write-up. Part 1 of #208. Deliberately not `Closes`: that issue's part 2, the display panel editor's uncovered surface (the alt-mode checkbox, the icon picker and the connected branch), is untouched here and is real work. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Mg2oe7JgL3xEzSoN5emTsd
wormeyman
added a commit
that referenced
this pull request
Aug 17, 2026
A vitest text scan over tests/*.spec.ts for `Control+A` and its four siblings. On macOS `Control+A` is the emacs "beginning of line" binding, not select-all, so nothing is selected, the typed text lands beside the old text, and the assertion reads "New textOld text". That is #197, fixed in 402fbe3. It reads source rather than driving a browser because the bug **passes on Linux**, and every runner this project has is ubuntu-latest - `vp test` and all four `e2e` shards. #223 and #225 put Playwright in CI, which does not help here: a sharded browser suite gating every PR will go green on this forever. The rule is not "never write Control". A chord reaching the app through actions.ts stays Control, where ModifierKey is Control | Shift | Alt and no Meta binding exists; a chord reaching a focused DOM <input> wants ControlOrMeta. A regex cannot separate them, so it flags both and the ALLOWLIST carries the distinction in writing - one entry today. Mutation-checked against the live instance: dropping PR #222's blueprint-grid-position.spec.ts into tests/ fails the guard naming line 97, and removing it goes green again. The knowledge already existed in prose. After 402fbe3 there is a five-line comment at display-panel-editor.spec.ts:159 explaining the whole trap, one line above its own corrected call, and a contributor then wrote `Control+A` in a different file. A comment at the scene of the last occurrence does not reach the next one. Every figure in the new file's header is measured rather than recalled, which caught two: the display panel comment is five lines and not three, and four specs hold `keyboard.down('Control')` for the ctrl-drag gesture and not five. Both came from #208's own write-up. Part 1 of #208. Deliberately not `Closes`: that issue's part 2, the display panel editor's uncovered surface (the alt-mode checkbox, the icon picker and the connected branch), is untouched here and is real work. Claude-Session: https://claude.ai/code/session_01Mg2oe7JgL3xEzSoN5emTsd Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why now
The 192 browser specs ran nowhere but a maintainer's laptop. That stopped being theoretical this week: PR #222 arrived with every check green and a
blueprint-round-trip.spec.tsfailure in it.Measured on that branch — 191 passed, 1 failed:
Every count and both position checksums held, which is the signature of a field dropped from serialized JSON with no geometry change. The field is
position-relative-to-grid, and a decode of the corpus says who it affects: 325 of 367 blueprints carry snapping and all 325 areabsolute-snapping: true— exactly the branch the new serialize condition excludes.Neither
vp checknorvp testcan see any of it. The decode → model → serialize path needsFDloaded, which needs a browser and the sprite server.What it costs, and what it doesn't need
No Factorio and no unpinned network. The corpus is committed under
test-blueprints/and the sprite data underpackages/exporter/data/output, so this stays as offline as the rest of CI. A separate job, parallel tocheckslikerustis — ~5 minutes at the config'sworkers: 1.Three choices worth stating
Each has a wrong version that looks right:
npm run localpreviewrather than carrying its own copy of how to start the two servers. A second copy is a bound written down twice, and the one that loses is the one nothing tests.@playwright/testversion read out ofnode_modules, not the caret range inpackage.json. The resolved version decides the browser build, so keying on^1.62.0serves yesterday's browser to today's Playwright — which fails as a missingchrome-headless-shelland reads as a suite-wide regression rather than a cache miss.Deploy is gated on it, and the sprite server is pinned
Both were flagged as open in the first commit and are closed in the second.
deploynow needs[checks, e2e]. Gating onchecksalone leaves exactly the #222 failure class free to reach fbe.factorygamefan.com. Costs ~5 minutes on the deploy path.servebecomes a declared devDependency instead of annpx --yesfetch. Unpinned was fine whilelocalpreviewonly ran on a laptop; as a CI dependency it was a run-time-resolved package in a job whose previous step isvp install --frozen-lockfile. ~990 lockfile lines for a dev-only static file server, in exchange for reproducibility and Renovate tracking it.Stale claims corrected
Eight comments across six files asserted Playwright does not run in CI (
playwright.config.ts,vite.config.ts,zoomLevels.ts,zoomLevels.test.ts,zoom-ladder.spec.ts,blueprint-files.test.ts, plus two inCLAUDE.md). The reasoning under each survives — a pure unit test still answers in seconds insidecheckswhere this job takes five minutes and two dev servers — but the flat claim does not. The split is now about cost rather than coverage.Verification
vp check .clean — 230 files formatted, 0 warnings/lint/type errorsvp test— 180 passedPlaywrightjob passed on this PR's own first run, every step including server startup and all 192 specsnpx --no-install serve --version→ 14.2.6, so nothing is fetched;localpreviewstill brings both ports up withdata.json200 off 8081actions/upload-artifactSHA verified against the livev4tag🤖 Generated with Claude Code
https://claude.ai/code/session_017Dbza77QEZnkqt1mGssY71