Show HN: Testpick – run only the tests your Git diff can break

Kazutaka_S1 pts0 comments

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...

testpick test tests vite coverage file

Related Articles