fix(map): select capture tests with a precise grep, not a loose substring - #340
Merged
Merged
Conversation
…ring
`styleproof-map` selected the capture with `--grep 'styleproof capture'`.
Playwright compiles a plain `--grep` string as `new RegExp(pattern, 'gi')` —
case-INSENSITIVE — and matches it against the whole grep title path: file path,
every enclosing describe, the test title, and tags. So the selector matched any
consumer test whose title merely MENTIONED StyleProof capture in prose.
That is not hypothetical. A consumer spec titled
"ci runners: the StyleProof capture fixture actually contains the lane panel"
was selected as a capture test. `styleproof-ci --spec-ref` overlays the head
harness onto the BASE checkout, so it then ran against the base application,
asserted head-only UI, failed, and took the whole base capture down with it:
base capture failed (exit 1) — continuing with a bare baseline
230 new surface(s) with no baseline
The head capture passed, the run looked green, and the gate certified nothing.
The selector is now the regex literal `/(?:^|\s)styleproof capture(?:\s|$)/`.
The `/.../` form makes Playwright honour it as a regex with no flags, so
matching is case-SENSITIVE; the boundaries stop it matching a longer word. It
still selects both capture blocks and the `styleproof browser build` test nested
inside them, since a describe title is part of every descendant's grep title.
It also now lives in `src/runner.ts` beside the `test.describe` titles it must
agree with, and `bin/styleproof-map.mjs` imports it. The selector and the titles
were two independent string literals with nothing holding them together; a test
pins that the CLI no longer carries its own copy.
Not exported from `src/index.ts` — this is an internal contract between the
runner and the map CLI, so the public API is unchanged.
Considered and rejected:
- Restricting selection to `--spec`'s file. A consumer may legitimately split
captures across files, and dropping those would silently reduce the captured
surface set — the one failure this tool exists to prevent.
- Playwright tags (`{ tag: '@styleproof' }`). They need >= 1.42 and the declared
peer floor is >= 1.40.
Residual, stated plainly: a consumer title containing the exactly-lowercase
phrase "styleproof capture" would still be selected. Case-sensitivity removes
the realistic collision — prose naturally capitalises the product name — without
a peer bump or a silent coverage change.
Verified: `npm test` 635 passed; typecheck, lint and format:check clean. The new
suite asserts against Playwright's own `forceRegExp` semantics, reproduced from
`playwright/lib/util.js`, rather than a hand-written regex — including a case
proving the OLD bare-string selector did match the consumer title.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EFwLkpuAEXYKNNDNQqy4uc
🗺️ StyleProof report📊 View the side-by-side visual report →Coverage, determinism, or report/diff consistency evidence is incomplete — repair the capture or reflow source; visual approval cannot clear this failure. |
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 does this feature exist?
styleproof-mapselected the capture with--grep 'styleproof capture'.Playwright compiles a plain
--grepstring asnew RegExp(pattern, 'gi')— case-insensitive — and matches it against the whole grep title path: file path, every enclosing describe, the test title, and tags. Fromplaywright/lib/util.js:So the selector matched any consumer test whose title merely mentioned StyleProof capture in prose.
Found in a consumer whose behaviour spec was titled:
That spec asserts head-only UI.
styleproof-ci --spec-refoverlays the head harness onto the base checkout, so once selected as a capture test it ran against the base application, failed, and took the entire base capture down with it:The head capture passed and the run reported success. A gate that certifies nothing while looking green is the worst failure this tool can have, and the consumer needed a full investigation to find it — the log names a spec, not a selector.
What changed?
/(?:^|\s)styleproof capture(?:\s|$)/. The/.../form makes Playwright honour it as a regex with no flags, so matching is case-sensitive, and the boundaries stop it matching a longer word. It still selects both capture blocks and thestyleproof browser buildtest nested inside them, because a describe title is part of every descendant's grep title.src/runner.tsbeside thetest.describetitles it has to agree with, exported asCAPTURE_TEST_GREP, andbin/styleproof-map.mjsimports it. Those were two independent string literals with nothing holding them together; either could drift silently.src/index.ts— an internal contract between the runner and the map CLI, so the public API, the README tables and the Action inputs are all unchanged.Considered and rejected:
--spec's file. A consumer may legitimately split captures across files, and silently dropping those would reduce the captured surface set — the one failure this tool exists to prevent. Worse than the bug.test.describe('…', { tag: '@styleproof' })). Collision-proof, but tags need Playwright >= 1.42 and the declared peer floor is>= 1.40.Residual, stated plainly: a consumer title containing the exactly-lowercase phrase
styleproof capturewould still be selected. Case-sensitivity removes the realistic collision — prose naturally capitalises the product name, which is exactly how the consumer above hit it — without a peer bump or a silent change to what gets captured.Behavioural Proof (with video and screenshots)
Not applicable— this changes which Playwright tests a CLI selects. There is no rendered surface, no captured map and no report output to screenshot; the observable behaviour is the argument passed toplaywright testand the set of tests it matches. The deterministic evidence is the unit suite below, which asserts against Playwright's own matching semantics.test:e2enot run: this touches neithersrc/capture.tsnor the browser-evaluated functions (capturePage,snapSubtree,pathsForSelector).Verification Summary
npm test(builds, thennode --testovertest/*.test.mjs)npm run typechecknpm run lintnpm run format:checknpm test— 635 passed, 0 failedtest/capture-grep.test.mjsreproduces Playwright'sforceRegExpfromplaywright/lib/util.jsrather than asserting against a hand-written regex, so it tracks what Playwright actually does with the string we hand it. It covers:restyleproof capturely) is not matched;bin/styleproof-map.mjsselects with the shared constant and carries no literal of its own.test/cli.test.mjsthat pinned the old invocation string were updated to the new one.build (node 22)is red on this branch and onmain: theDependency auditstep fails on afast-uriadvisory (GHSA-7p8r-x3mc-p8w7) in a transitive dependency. Reproduced on a cleanmaincheckout withnpm audit. Unrelated to this change, which adds no dependencies. Note the audit step gates theUnit testsstep after it, so node 22 reports no test result on any branch until that advisory is resolved.Generated by Claude Code