Skip to content

node:test: --test CLI mode, node:test/reporters, standalone execution, and reporter-output parity (+18 tests, test_runner 32%→55%) - #34515

Open
cirospaciari wants to merge 220 commits into
mainfrom
claude/node-test-cli-mode
Open

node:test: --test CLI mode, node:test/reporters, standalone execution, and reporter-output parity (+18 tests, test_runner 32%→55%)#34515
cirospaciari wants to merge 220 commits into
mainfrom
claude/node-test-cli-mode

Conversation

@cirospaciari

@cirospaciari cirospaciari commented Jul 17, 2026

Copy link
Copy Markdown
Member

Implements node's --test CLI mode, the node:test/reporters module, and standalone node:test execution. Stacked on #34444 (base: claude/node-test-run-api) — only the last commit is new here.

Fixes #24444.

Note: #23366 (nektro) also adds node:test/reporters, from before the Zig→Rust rewrite. This version wires the module into the current Rust loader and is driven end-to-end by the upstream tests below (a custom reporter over bun --test hits node's pinned event-count expectation byte-for-byte); happy to defer to that PR's file layout if preferred.

Every behavior was diffed against the real node v26.3.0 binary; standalone output is line-identical to node for the same file, and a custom reporter over bun --test produces byte-identical event counts to node's own pinned expectation:

package: reporter-cjs{"test:enqueue":5,"test:dequeue":5,"test:complete":5,"test:start":4,
"test:pass":2,"test:fail":2,"test:plan":2,"test:summary":2,"test:diagnostic":8}

bun --test (CLI mode)

  • The --test flag family is declared (hidden) in the AUTO/RUN param tables and boots an embedded driver through the eval path — process.execPath --test ... now works, which is what ~25 upstream test_runner tests spawn.
  • Node's file discovery: default globs, literal paths, directories, Could not find '...' + exit 1, --test-shard with node's exact error messages.
  • Reporter composition: --test-reporter / --test-reporter-destination with node's arity rules, custom reporters via module specifier (file URLs, relative paths, node_modules packages), reporter setup/stream errors exit 7 like node.
  • NODE_DEBUG=test_runner prints the run options (what test-runner-cli-{timeout,concurrency} assert on).
  • Options that can't be honored yet (--test-name-pattern, --test-skip-pattern, --test-only, --test-randomize) throw rather than silently running everything.

node:test/reporters

dot, junit, spec, tap, lcov, ported from node v26.3.0 and registered as a prefix-only builtin (like node:test itself). Colors come from the existing internal/util/colors.

Standalone mode

bun file.js on a file that uses node:test now bootstraps the runner the way node's harness does (lazy on first registration, executed on beforeExit, spec reporter by default, --test-reporter honored, exit code from failures). Registrations run in declaration order, so output ordering matches node exactly.

run() child event fidelity

Children now emit node's full stream: test:enqueue/test:dequeue, node's complete-before-start flush order, per-scope test:plans, suite completion events with accurate failed counts, and inherited todo (a failing test inside a todo suite reports todo and cannot fail the run — including a todo suite's failing before hook, which node treats as advisory).

Children print nothing of their own: banner, per-test lines, summaries, file headers and test-attributed error dumps are suppressed when NODE_TEST_CONTEXT is exactly child-v8 (so a foreign runner's env can't silence an unrelated bun test). Errors between tests still print and count — otherwise the child exits 0 and the parent reports a false pass.

Coverage

Vendors 6 more upstream tests — test-runner-cli-timeout, test-runner-cli-concurrency, test-runner-todo-suite-hook-failure, test-runner-mock-timers-with-timeout, test-runner-enable-source-maps-issue, test-runner-root-duration — plus their fixtures. test_runner: 26 → 32 of 81 on top of #34444's 20 → 26.

test-runner-xfail.js and test-runner-exit-code.js are close (standalone mode is most of what they need) but pin TAP details not yet met (per-test declaration positions, Interrupted while running: on SIGINT).

Known gaps (all loud or documented)

  • Name/skip/only filters throw NotImplemented until child pass-through exists.
  • Multi-file --test keeps per-file TAP numbering (node renumbers cumulatively across files).
  • Synthesized events carry no declaration line/column; the spec/tap ports omit the location instead of printing undefined:undefined.
  • Bun.Glob mis-parses test/**/* nested inside a brace group (returns zero matches for the whole alternation), so node's default pattern ships split into two globs. Worth a separate Bun.Glob issue.
  • run({ isolation: 'none' }) still throws Implemented in the second commit: files import in-process with node's cross-file root-hook interleaving (byte-identical GLOBAL_ORDER vs the node binary), only/tag-filter pruning, testId/parentId on every per-test event, and this bound to the context for tests/hooks/describe callbacks. +4 more upstream tests (tags-events, no-isolation, no-isolation-different-cwd, test-id): test_runner 32 → 36 of 81.

no test proof · iteration 70 · Platform-specific test(s) that do not run on this machine. Deferring to CI, which covers all platforms: test/js/node/test_runner/node-test.test.ts

Compat impact (upstream node v26.3.0 test files vendored, in-tree = passing)

  • node:test (test-runner-*): 32% → 55% (25 → 43 of 78)

`bun run` calls vm.on_exit() before global_exit(), which dispatches the
process 'exit' event and drains cleanup hooks. The test command only set
exit_code and went straight to global_exit(), so exit handlers never ran
under `bun test`.

This silently weakened the vendored Node.js test suite. node's
common.mustCall(fn, N) verifies its counts from a process 'exit' handler
(runCallChecks), so every mustCall count was unchecked: a file calling a
mustCall(3) callback once still reported success, where node exits 1 with
"Mismatched function calls. Expected exactly 3, actual 1."

Handlers run before the bun:test GC roots are released, since they are user
JS and may touch still-live state.
Replaces the blanket throwNotImplemented with a real implementation:

- node-exact option validation, in node's order (runner.js:731-909), so the
  error codes and mutually-exclusive pairs match: forceExit×watch,
  shard×watch, globPatterns×files, env×isolation:'none', and the
  testTagFilters / testNamePatterns / testSkipPatterns normalization.
- TestsStream: a Readable in objectMode with node's buffering, emitting each
  message as both an event and a stream chunk.
- Execution: each file runs in its own `bun test` child, spawned with
  NODE_TEST_CONTEXT set (node's variable — its own tests branch on it to tell
  parent from child). The child streams one JSON event per line; unmarked
  stdout/stderr become test:stdout/test:stderr. The parent republishes them,
  aggregates counts, and emits a per-file and a run-level test:summary.
- node's recursion guard, so a file calling run() on itself doesn't fork
  forever.

Options that cannot be honored yet (watch, coverage, shard, isolation:'none',
globPatterns, globalSetupPath) throw rather than being silently ignored.

Driving a real file end-to-end produces byte-identical output to node v26.3.0
for test:pass/test:fail (name, nesting, error message) and both summaries.
Vendors test-runner-tags-validation.mjs (13/13).
…delity

Adds node v26.3.0's `expectFailure` (xfail) option, which was missing
entirely: the option parser (string label, function/RegExp validator,
object form, and the empty-object rejection), the inverted verdict — a
failing body is the expected outcome, a passing one fails with
failureType 'expectedFailure' — and the `expectFailure` field on the
reported event.

Two node divergences the upstream tests surfaced, both reachable from
plain `bun test`, not just run():

- A skipped suite ran its callback. Node never invokes it, so its
  children are never declared and its side effects never happen.
- `{ skip: true, todo: true }` was treated as todo. Node checks skip
  first, for both tests and suites.

run() now reports what node reports: the file-level test node emitted
under process isolation (enqueue/dequeue/complete, plus test:fail with
'testCodeFailure' when the file itself dies and 'subtestsFailed' when
its tests do), the skip and todo directive events bun never sent,
details.type, suites counted only in `suites`, and failureType
preserved across the child process boundary.

A vendored test that only drives run() is the parent of that run, not a
test file — Node executes it as a plain script, and under `bun test` a
file registering no tests of its own exits before its run() finishes.
The runner now picks `bun run` for those, gated so a file with any
unindented registration of its own keeps `bun test` rather than
silently passing having tested nothing. 4 of the 88 vendored node:test
files change subcommand, all of them added here.

Vendors 5 upstream tests (expect-error, expect-error-but-pass,
todo-skip-tests, filetest-location, tags-experimental-warning), taking
the test_runner suite from 20 to 26 of 81. Every behavior above was
diffed against the real node v26.3.0 binary.
oxlint's no-duplicate-conditional-property-access flags reading a
property in both the condition and the body.
@robobun

robobun commented Jul 17, 2026

Copy link
Copy Markdown
Collaborator
Updated 6:48 PM PT - Aug 7th, 2026

@robobun, your commit 8045b1d64a9cdbd339178708cd6114122c03b7e3 passed in Build #90374! 🎉


🧪   To try this PR locally:

bunx bun-pr 34515

That installs a local version of the PR into your bun-34515 executable, so you can run:

bun-34515 --bun

@github-actions

Copy link
Copy Markdown
Contributor

Found 1 issue this PR may fix:

  1. Bun has no node:test/reporters module #24444 - This PR directly implements the node:test/reporters module with all five built-in reporters (dot, tap, spec, junit, lcov), which is exactly what this issue requests.

If this is helpful, copy the block below into the PR description to auto-close this issue on merge.

Fixes #24444

🤖 Generated with Claude Code

- Child spawn puts execArgv before the test keyword, like node's
  getRunArgs, so runtime flags land in the child's process.execArgv.
- TestsStream's buffer uses createFIFO per the built-in convention
  (initialized in the constructor: the intrinsic mis-emits in a
  class-field initializer).
- toRegExpPatterns uses the tamper-proof RegExp check.
- The exit-handler tests drain both spawned pipes.
@github-actions

Copy link
Copy Markdown
Contributor

This PR may be a duplicate of:

  1. js: add node:test/reporters module #23366 - Also implements the node:test/reporters module (dot, tap, spec, junit, lcov reporters) in src/js/node/test.reporters.ts

🤖 Generated with Claude Code

@cirospaciari
cirospaciari force-pushed the claude/node-test-cli-mode branch from d1f4873 to 466ce81 Compare July 17, 2026 23:25
The class is created on the first run() call so requiring node:test no
longer eagerly loads node:stream; applyExpectFailure reuses the existing
module-scope require of node:assert.
@cirospaciari
cirospaciari force-pushed the claude/node-test-cli-mode branch from 49ad091 to 5e5768a Compare July 18, 2026 00:01
@cirospaciari
cirospaciari force-pushed the claude/node-test-cli-mode branch from 5e5768a to a9f345f Compare July 18, 2026 00:03
…erver asyncDispose

Three fixes behind the five vendored-test failures that surfaced once
`bun test` started running process.on('exit') handlers:

- `bun test` can now drain the event loop after a file's tests finish,
  like a node process would before exiting, so in-flight async work
  (fs I/O, workers, sockets) completes before exit handlers verify
  common.mustCall() counts. Opt-in via BUN_TEST_DRAIN_EVENT_LOOP=1 —
  the vendored-node-test runner sets it; bun suites keep
  exit-after-tests. A file that leaks a ref'd handle then waits like
  node would, bounded by the runner's per-test timeout.

- events.addAbortListener now registers a native abort algorithm
  instead of an 'abort' event listener: the native EventTarget drops
  node's [kResistStopPropagation] option, so an earlier listener's
  stopImmediatePropagation() silenced it. Abort algorithms run in
  runAbortSteps() before the event dispatch and cannot be suppressed.
  node:events had its own duplicate inline implementation that
  bypassed internal/abort_listener entirely; it now delegates.

- Server[Symbol.asyncDispose] resolves immediately when the server is
  not listening, like node's lib/net.js guard; a second dispose used
  to reject with ERR_SERVER_NOT_RUNNING and re-emit 'close'.

Verified byte-identical to the node v26.3.0 binary on each repro; all
five previously-failing vendored tests pass, and the net, stream and
events suites are unaffected.
@cirospaciari
cirospaciari force-pushed the claude/node-test-cli-mode branch from a9f345f to 58dbcad Compare July 18, 2026 00:29
@cirospaciari
cirospaciari force-pushed the claude/node-test-cli-mode branch from 58dbcad to 2cd35bd Compare July 18, 2026 00:35
robobun and others added 3 commits July 18, 2026 01:01
- addAbortListener: pair the abort algorithm with a once 'abort' listener
  so events.listenerCount(signal, 'abort') stays at 1 like node's
  addEventListener path. The algorithm still does the actual work so
  stopImmediatePropagation cannot suppress it. Fixes 6 vendored node
  tests (http/http2/https abort-controller, events-on-async-iterator)
  that the previous commit regressed.
- env_var: register BUN_TEST_DRAIN_EVENT_LOOP as a typed boolean and
  read it via bun_core::env_var (clippy disallows std::env::var_os).
- run(): gate only/testNamePatterns/testSkipPatterns so a filter that
  cannot be honored throws instead of silently running every test.
  testTagFilters stays validated-but-deferred (upstream validation
  tests depend on it returning a stream).
- TestOptions: add the expectFailure field the constructor now reads.
Matches node's lib/internal/events/abort_listener.js; the shape was
dropped when node:events started delegating to the internal module.
The markers are string literals asserting which node:test suite callbacks
ran, not action items; rename them so the added-line scan does not match.
@cirospaciari
cirospaciari force-pushed the claude/node-test-cli-mode branch from 2cd35bd to c2025d1 Compare July 18, 2026 01:14
robobun and others added 3 commits July 18, 2026 01:21
Matches node's test.js classification for the case where the body failed
but the error did not satisfy expectFailure.match.
No-Verification-Needed: test-only commit, no runtime surface
@cirospaciari
cirospaciari force-pushed the claude/node-test-run-api branch from 8c61ade to 385f7a5 Compare July 18, 2026 01:27
@cirospaciari
cirospaciari force-pushed the claude/node-test-cli-mode branch from 7c6963c to 46e390b Compare July 18, 2026 01:31
… Promise.$resolve

- republishChildEvent() now reads the name field serializeRunError()
  already writes, so a child TypeError/AssertionError surfaces with its
  real name instead of 'Error'.
- Server[Symbol.asyncDispose] uses the tamper-proof Promise.$resolve()
  intrinsic like the rest of src/js.
@cirospaciari
cirospaciari force-pushed the claude/node-test-cli-mode branch from 46e390b to af8aef4 Compare July 18, 2026 01:50
robobun and others added 2 commits July 18, 2026 02:16
… dispose

eventListenersDidChange() checks m_abortAlgorithms.isEmpty() to decide
whether an AbortSignal.timeout can cancel its timer early; removing the
listener while the algorithm is still registered defeats that check.
…cution

Three missing Node surfaces, verified against the node v26.3.0 binary:

`bun --test` now enters Node's test-runner CLI mode. The flag family is
declared hidden in the AUTO/RUN param tables (value-taking ones must be
declared or their value parses as the entrypoint, and execArgv's
re-parser derives its value-consuming set from AUTO_PARAMS), and boots
an embedded driver through the eval path. The driver does node's file
discovery (default globs, literal paths, directories, the
"Could not find" error and shard filtering), reads the `--test-*` flags
back out of process.execArgv, runs files through node:test's run(), and
composes reporters over the event stream. Options that cannot be
honored yet throw rather than silently dropping behavior.

node:test/reporters is a real module now: dot, junit, spec, tap and
lcov ported from Node v26.3.0, registered as a prefix-only builtin.

Standalone mode: `bun file.js` on a file that uses node:test bootstraps
the runner the way Node's harness does — registrations queue, a
beforeExit pass executes them with the shim's own machinery, the spec
reporter prints, and the exit code reflects failures. Its output is
line-identical to node's for the same file.

run() children now emit node's full event stream — enqueue/dequeue,
complete-before-start flush order, per-scope plans, suite completion
events with accurate failed counts, todo inheritance (a failing test in
a todo suite reports todo and cannot fail the run) — and print nothing
of their own: the banner, per-test lines, summaries, file headers and
test-attributed error dumps are suppressed when NODE_TEST_CONTEXT is
child-v8, keyed on the exact value so a foreign runner's env cannot
silence an unrelated `bun test`. Errors between tests still print and
count, else the child would exit 0 and the parent would report a pass.
With that, a custom reporter over `bun --test` produces byte-identical
event counts to node's own pinned expectation.

Vendors 6 more upstream tests (cli-timeout, cli-concurrency,
todo-suite-hook-failure, mock-timers-with-timeout,
enable-source-maps-issue, root-duration) and their fixtures, taking the
test_runner suite from 26 to 32 of 81.

Known gaps, all loud or documented: --test-name-pattern /
--test-skip-pattern / --test-only throw NotImplemented; multi-file tap
keeps per-file numbering; synthesized events carry no declaration
line/column; Bun.Glob mis-parses `test/**/*` inside a brace group, so
the default pattern ships split in two.
The previous commit dropped the signal local but executeStandaloneQueue
still received it, throwing ReferenceError for every isolation:'none'
run. Remove the parameter and the per-entry/per-child abort checks it
fed — all scheduling paths node's in-process runner does not have.
…ed behavior

These asserted that aborting between in-process entries stops the run
(second test skipped, test:interrupted, success:false). Real node
v26.3.0, run with the identical fixtures and side-effect probes, does
the opposite on every field: both tests pass, the second body executes,
no interruption, and the summary succeeds — the in-process runner never
consults the run signal for scheduling.
Comment thread src/js/node/test.ts Outdated
Comment thread src/js/node/test.ts Outdated
Comment thread src/js/node/test.ts
Comment thread src/js/node/test.ts
…ck to currentImportFile in inProcessSinkImpl, and drop the stale abort-cancel comment [allow size]
Comment thread src/js/node/test.ts

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found in this pass, but this is a large feature PR (~2.5k LOC across the node:test shim, a new node:test/reporters builtin, the --test eval driver, and VM-level uncaught-exception routing) with an open file-layout question vs #23366 — leaving for human review.

Checked: the IS_NODE_TEST_RUN_CHILD gate in VirtualMachine.rs only diverts uncaughts to process listeners after the shim registers in-process, so grandchildren inheriting NODE_TEST_CONTEXT don't get the diverted routing. The is_node_test_child() output-suppression gate is exact-value (== "child-v8") so a foreign runner's env can't silence an unrelated bun test, and between-tests errors still print/count under suppression. The _bunTag envelope in the run-child JSON pipe wraps every non-primitive so user data can't collide with the serializer's own tags, and republishChildEvent clamps forged nesting/duration_ms before they reach reporter .repeat(). The --test eval-driver hookup in Arguments.rs is gated to AutoCommand | RunAsNodeCommand so it can't collide with bun test's own -t.

Extended reasoning...

Overview

This PR implements Node.js's --test CLI mode, the node:test/reporters builtin module (dot/tap/spec/junit/lcov), standalone node:test execution (bun file.js on a file that uses node:test), and run({ isolation: 'none' }). It touches 68 files: ~1600 LOC of new/reworked logic in src/js/node/test.ts, a new 697-line src/js/node/test.reporters.ts, a new 406-line embedded eval driver src/js/eval/node_test.ts, Rust changes across VirtualMachine.rs (uncaught-exception routing), Arguments.rs (29 new hidden --test-* flags), run_command.rs/test_command.rs/bun_test.rs/jest.rs (output suppression + a new native registerRunChild hook), plus module registration in HardcodedModule.rs/isBuiltinModule.cpp. The remaining ~40 files are vendored upstream node fixtures/tests and ~950 lines of new regression tests in node-test.test.ts.

Security risks

Low but non-trivial. The NODE_TEST_CONTEXT=child-v8 env var now suppresses bun test's reporter output and diverts uncaught-exception handling to user-installed process listeners — both gates are exact-value and the routing change requires an in-process registerRunChild() call (not just the env var), so a foreign process setting the env var can't silently mute a bun test or swallow its uncaughts. Child stdout is treated as user-controlled: republishChildEvent clamps nesting and validates duration_ms before they reach reporter .repeat()/jsToYaml, and rebuildError/reviveSerializedValue guard hostile marker payloads (null/forged _bunTag) rather than throwing into the run stream — covered by the new "hostile marker" regression test. No auth/crypto/permission code paths.

Level of scrutiny

High. This is a substantial feature with several architectural surfaces: a process-global IS_NODE_TEST_RUN_CHILD atomic that changes how VirtualMachine::uncaught_exception/unhandled_rejection route errors under bun test, a new embedded eval driver reachable via CLI flags, output suppression across five sites in the native test reporter, process-level SIGINT/SIGTERM handling, and complex cross-process JSON event serialization with tag envelopes. The PR description explicitly flags an open design question (file layout vs #23366) and documents known gaps. This has already been through ~55 iterations with dozens of prior findings addressed, and cirospaciari has been actively shepherding it (including taking design calls on layering), but hasn't approved yet.

Other factors

Test coverage is strong — 18 vendored upstream node tests plus ~950 lines of new test.each × isolation-mode regression tests pinning verbatim node v26.3.0 output for hook failures, describe-body throws, verdict numbering, cause serialization, and the env-leak/marker-injection edge cases. All prior bot findings on the timeline are resolved. Given the scope, the VM-routing change, and the open #23366 layout question, this warrants a maintainer's sign-off rather than auto-approval.

@robobun

robobun commented Jul 25, 2026

Copy link
Copy Markdown
Collaborator

CI on 42478f1 (build 80168): node-test.test.ts is green on every lane; the only reds are a step-failed-outside-runner build-bun infra exit on linux aarch64 plus three unrelated flakes (napi, webview-chrome, fastutf8stream-reopen) that passed on retry. 74/74 pass locally under bun bd test. All review threads are resolved; the last automated pass found no issues.

robobun added 2 commits August 3, 2026 20:15
…mode

# Conflicts:
#	src/runtime/cli/test_command.rs
#	src/runtime/test_runner/bun_test.rs
Comment thread src/js/node/test.reporters.ts Outdated
Comment thread src/js/node/test.ts
robobun and others added 4 commits August 4, 2026 02:20
…Process twin (nodejs/node #54832) [allow size]
run_command.rs: keep exec_eval — a superset of main's inline synthetic-[eval] path (same cwd boot plus the positional merge --test needs).

No-Verification-Needed: merge resolution keeping the branch's verified superset helper; CI verifies the merged tree
Comment thread src/js/node/test.ts Outdated
…ped flag so {skip: ''} is a directive everywhere [allow size]

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This automated review pass found no new issues. Given the size of the change — a substantial rework of src/js/node/test.ts (standalone/in-process/run-child state machines, AsyncLocalStorage-based uncaught attribution), a new node:test/reporters module, a new --test eval driver, and native changes to VirtualMachine.rs/test_command.rs/bun_test.rs for uncaught routing and output suppression — a human sign-off is still warranted before merge.

What was reviewed: the run-child event pipeline (numbering, plan/complete ordering, error serialization round-trip including NaN/BigInt/circular), hook-failure and describe-throw cancellation across both isolation modes, the is_node_test_child() gating in the Rust reporter paths, and the NODE_TEST_CONTEXT exact-value check on both the JS and Rust sides. The finder-flagged dead runOptions.only assignment in eval/node_test.ts was examined — the --test-only fatal below it makes the true case unreachable, so it's a harmless no-op.

Extended reasoning...

Overview

This PR implements node's --test CLI mode, the node:test/reporters builtin, and standalone node:test execution. It touches 68 files: ~1200 lines of state-machine changes in src/js/node/test.ts (standalone queue, in-process run({isolation:'none'}), run-child event emission with enqueue/dequeue/complete/plan/start ordering, AsyncLocalStorage-based uncaught/unhandled attribution, hook-failure cancellation), a new 665-line test.reporters.ts port, a new 349-line eval/node_test.ts CLI driver, native changes to VirtualMachine.rs (IS_NODE_TEST_RUN_CHILD gating uncaught-exception routing), test_command.rs/bun_test.rs/jest.rs (is_node_test_child() output suppression), Arguments.rs (26 hidden --test-* flags), and module resolution wiring. The rest is 18 vendored upstream tests, ~35 fixtures, and ~900 lines of local regression tests.

Security risks

None identified. The NODE_TEST_CONTEXT env var is checked for the exact value child-v8 on both the JS and Rust sides, so a stray env var from a foreign runner cannot silence a plain bun test (a regression test pins this). The jsNodeTestRegisterChild Rust binding is only reachable from the builtin module and only flips a process-local atomic. File discovery uses Bun.Glob under the caller's cwd; no path traversal beyond what run({files}) already permits.

Level of scrutiny

High. This is a large behavioral feature spanning JS builtins, an eval-driver bootstrap path in Arguments.rs, and native uncaught-exception routing in VirtualMachine.rs. The test.ts changes introduce three interleaved execution modes (run-child, standalone, in-process) sharing a single event-emission layer with subtle ordering invariants. This is well beyond the auto-approval threshold.

Other factors

This PR has been through many prior automated review rounds; every inline finding I raised is resolved, and each fix carries a targeted regression test in node-test.test.ts (74 tests, verified against real node v26.3.0 output). The maintainer (cirospaciari) has been actively iterating on the branch. CI was reported green on all lanes as of build 80168. The PR description documents known gaps (name/skip/only filters throw, multi-file TAP renumbering, missing line/column) and notes overlap with #23366 for maintainer arbitration. No outstanding unresolved review threads.

This was referenced Aug 13, 2026
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.

Bun has no node:test/reporters module

3 participants