Skip to content

TypeScript test selection: excluded and JavaScript tests, bare test.ts, Proxy traps, multi-graph --json - #1874

Merged
swapnilpaliwal-sd merged 5 commits into
apps/integration-0.1.9from
fix/ts-test-impact
Oct 9, 2026
Merged

swapnilpaliwal-sd merged 5 commits into
apps/integration-0.1.9from
fix/ts-test-impact

Conversation

@swapnilpaliwal-sd

Copy link
Copy Markdown
Contributor

Five fixes to TypeScript test selection, impact and path, found by a mutation oracle over 14 open-source TypeScript repositories: break one function, record which test files newly fail, compare each verb's selection with that truth.

before after
impact recall (macro / micro) 0.61 / 0.69 0.81 / 0.74
impact precision 0.22 0.23
mutants with an empty selection 54 17
path: failing test file to the broken function, chains found 507 / 674 561 / 674

Each fix carries a check that is red without it and a control that must not move.

  1. A test the build tsconfig excludes joins the program it imports. A build config that leaves its tests to the runner (exclude: ["test/**"]) made every test file NO_PROGRAM_CLAIMS_FILE; on one repository every verb answered nothing. An unclaimed file that imports the program now joins it; one that imports nothing is still reported. Parser check in discovery-tests.ts.
  2. A file named test.ts that registers a test is a test. Tests written beside their module (src/x/test.ts) were plain functions, so the test universe was empty. The bare name must also register a test (describe / it / test / suite), so a library's testing/test.ts and a vendored spec.ts stay code. Case a-file-named-test-is-a-test.
  3. --json is one document when no language's graph answers. In a repository with several graphs, a path with no chain printed the text answer's per-graph headers under --json: 279 of 1244 answers could not be parsed. Check in tests/multi_language.py.
  4. A JavaScript test of TypeScript source joins the TypeScript program. .js tests importing .ts source lived in the JavaScript graph with no edge into the TypeScript one. Parser check in discovery-tests.ts.
  5. The site that creates a Proxy hands over its handler's traps. new Proxy(t, h) / Proxy.revocable(t, h): the traps the runtime calls were reached by nothing. Only the ProxyHandler trap names, and only on an object that flows into the handler argument; every edge this adds was audited on six graphs, none on an unrelated get / set. Case proxy-handler-traps-are-handed-over.

Tests: TypeScript cases 269/269, JavaScript 307/307, Python 306/306; parser typescript-tests 49/49 and discovery checks pass. On this base, 6 checks of tests/multi_language.py and 1 of tests/surfaces.py fail with and without these commits.

Fixes 1 and 4 change the vendored parser, so they also need to go upstream to the parser repository.

swapnilpaliwal-sd and others added 5 commits October 8, 2026 23:50
…mports

A build config routinely excludes its tests (`exclude: ["test/**", "**/*.spec.ts"]`)
and leaves them to the test runner. No config claimed those files and nothing claimed
imports them, so they were skipped as NO_PROGRAM_CLAIMS_FILE and every test the change
reaches was invisible to test selection, impact and path.

An unclaimed file that imports a file of the program now joins it, with the unclaimed
files it pulls in, to a fixpoint. A file owned by another config is untouched, and an
unclaimed file that imports nothing of the program is still reported as before.

Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com>
A JavaScript or TypeScript test written beside the module it tests is often named only
for what it is — `src/isToday/test.ts` — or is a package's root `test.js`. Neither the name
nor a directory said test, so every test of such a tree was a plain function: the test
universe was empty and test selection named none of the tests a change breaks.

A file named test, tests, spec or specs is now a test file when it also registers a test
(describe / it / test / suite at the start of a line). The name alone is not enough: a
library ships `testing/test.ts` with its test utilities, and a vendored `spec.ts` holds a
specification's types; both stay code.

Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com>
In a repository indexed in several languages the dispatcher asks every graph. When
none answered — path finding no chain (a document with its leads, status 1), or the
same refusal from every graph — --json printed the text answer's per-graph headers
(`══ typescript graph ══`) and each graph's output after them. A machine reader got
nothing it could parse, for an answer that was correct.

--json now prints one document in those cases: the main graph's document with the
other graphs' under other_languages, or one {refusal, language, languages} for a
refusal every graph gave. The exit status is unchanged, and the front door prints a
refusal document as its text.

Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com>
…it imports

A repository written in TypeScript often keeps some of its tests in JavaScript
(`__tests__/base.js` importing `../src/immer`). Those files went to the JavaScript graph,
which has no edge into the TypeScript one, so a change to the source selected none of the
tests that break with it.

A .js / .jsx / .mjs / .cjs file that imports a file of the TypeScript program now joins it,
parsed as JavaScript, the same way an unclaimed TypeScript file does. `require("x")` counts
as an import for that. A JavaScript file that imports nothing of the program is left to the
JavaScript analyzer, and a TypeScript file importing JavaScript still does not pull it in.

Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com>
`new Proxy(target, handler)` and `Proxy.revocable(target, handler)` give the runtime an
object whose get / set / has … it calls on every touch of the proxy. No call site names a
trap, so what the traps call was reached by nothing: in a Proxy-based library the draft
logic was unreachable from every test that creates a draft.

The creating site now hands over each trap the handler defines, the way `xs.map(cb)`
hands over `cb` (callback_registered). The handler is followed through the holders it is
read from — a const, a reassigned let, a parameter it was passed as — to an object
literal's methods, or to the methods of the class a `new C()` builds and its bases. Only
the ProxyHandler trap names count, and only on an object that reaches a Proxy: an object
with a get method handed to any other call is untouched.

Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com>
@swapnilpaliwal-sd
swapnilpaliwal-sd merged commit a811ccf into apps/integration-0.1.9 Oct 9, 2026
12 checks passed
@swapnilpaliwal-sd
swapnilpaliwal-sd deleted the fix/ts-test-impact branch October 9, 2026 14:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant