GitHub - TwistTheoryGames/testpick: Run only the tests your diff can actually break — coverage-based test selection for Jest & Vitest that catches what static import graphs miss. · GitHub
/" data-turbo-transient="true" />
Skip to content
Search or jump to...
Search code, repositories, users, issues, pull requests...
-->
Search
Clear
Search syntax tips
Provide feedback
--><br>We read every piece of feedback, and take your input very seriously.
Include my email address so I can be contacted
Cancel
Submit feedback
Saved searches
Use saved searches to filter your results more quickly
-->
Name
Query
To see all available qualifiers, see our documentation.
Cancel
Create saved search
Sign in
/;ref_cta:Sign up;ref_loc:header logged out"}"<br>Sign up
Appearance settings
Resetting focus
You signed in with another tab or window. Reload to refresh your session.<br>You signed out in another tab or window. Reload to refresh your session.<br>You switched accounts on another tab or window. Reload to refresh your session.
Dismiss alert
{{ message }}
TwistTheoryGames
testpick
Public
Notifications<br>You must be signed in to change notification settings
Fork
Star
main
BranchesTags
Go to file
CodeOpen more actions menu
Folders and files<br>NameNameLast commit message<br>Last commit date<br>Latest commit
History<br>7 Commits<br>7 Commits
.github/workflows
.github/workflows
bin
bin
src
src
test
test
.gitignore
.gitignore
CONTRIBUTING.md
CONTRIBUTING.md
LICENSE
LICENSE
README.md
README.md
demo.tape
demo.tape
package.json
package.json
View all files
Repository files navigation
testpick
Run only the tests your diff can actually break.
testpick is a test-selection CLI for JavaScript/TypeScript. It looks at what you<br>changed (git diff) and runs just the tests affected by those changes — turning<br>multi-minute CI runs into seconds.
npx testpick map # one-time: learn which tests touch which code<br>npx testpick run # from now on: run only what your changes affect
Demo
$ testpick map<br>Mapping 23 test file(s) with vitest in 8 single-pass shard(s).<br>✔ Map saved to .testpick/map.json — 24 source files tracked.
$ vim src/util.ts # change one file...
$ testpick run<br>testpick: 1/23 test file(s) affected by 1 change(s).
✓ src/greet.test.ts (1 test) 4ms
$ testpick explain # ...and see *why*<br>Changed files (1):<br>• src/util.ts<br>Decisions:<br>✓ src/util.ts [coverage map → 1 test(s)]<br>→ src/greet.test.ts<br>Result: run 1 of 23 test file(s).
Want a GIF for your README/socials? A ready-to-run vhs<br>script lives at demo.tape: brew install vhs && vhs demo.tape.
Why not just vitest --changed / jest --onlyChanged?
Those are great — until your code has couplings their static import graph can't<br>see :
a module loaded via a runtime-computed path — a plugin registry, DI<br>container, or import(/* @vite-ignore */ pathFromConfig). Vite can glob a<br>literal import(\./${name}.js`)`, but it cannot analyze a path that's data.
code reached only at runtime that the static graph over- or under-counts
testpick builds its map from runtime coverage — what each test actually<br>executed — so it captures those edges. Verified example (see testpick-real<br>fixture):
import(/* @vite-ignore */ REGISTRY[n]); // Vite can't see this">const REGISTRY = { feat: "../features/feat.ts" };<br>export const load = (n) => import(/* @vite-ignore */ REGISTRY[n]); // Vite can't see this
Change features/feat.ts<br>result
vitest related features/feat.ts<br>No test files found ❌
testpick run<br>runs loader.test.ts ✅
Note: testpick and Vite's module graph are complementary, not strictly better.<br>A coverage map is more precise for runtime-computed couplings and for trimming<br>imported-but-unexecuted modules; the static graph can flag a yet-to-run branch a<br>coverage map hasn't seen. That's why testpick always errs toward running more<br>(see below) — and why it works the same for Jest, where there's no Vite graph.
Safety first
A test selector is only useful if you can trust it not to skip something important.<br>testpick's rule: when in doubt, run more — never less.
Changed a file the map doesn't know about (new file, config)? → it runs all<br>tests by default.
Pass --ai and it asks an LLM to narrow those unmapped changes to likely tests —<br>but if the model is unsure, it still falls back to running everything. The AI<br>can never cause a skip.
testpick explain shows exactly why each test was selected or skipped.
Commands
] # build/refresh the coverage map<br>testpick run [--base ] # run only affected tests<br>testpick explain [--base ] # dry-run: print the selection + reasoning">testpick map [--base ref>] # build/refresh the coverage map<br>testpick run [--base ref>] # run only affected tests<br>testpick explain [--base ref>] # dry-run: print the selection + reasoning
Option<br>Meaning
--base<br>Diff against a ref (CI: --base origin/main). Default: working tree vs HEAD.
--ai<br>Use an LLM (needs ANTHROPIC_API_KEY) to resolve unmapped changes.
--all<br>Escape hatch: run the whole suite.
--full<br>map only: rebuild from...