close
Skip to content

[jest-circus] Add describe-level retries - #16322

Merged
SimenB merged 12 commits into
jestjs:mainfrom
soltonigiri:feat/suite-level-retry-10520
Aug 18, 2026
Merged

[jest-circus] Add describe-level retries#16322
SimenB merged 12 commits into
jestjs:mainfrom
soltonigiri:feat/suite-level-retry-10520

Conversation

@soltonigiri

@soltonigiri soltonigiri commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Summary

Closes #10520.

This adds an entireDescribe option to jest.retryTimes():

describe('workflow', () => {
  jest.retryTimes(3, {entireDescribe: true});
});

When enabled, a failure recorded on a test reruns the complete current describe lifecycle: its beforeAll hooks, child tests and nested describes, and afterAll hooks. This includes ordinary failures and unhandled rejections owned by a test, beforeAll, beforeEach, or afterEach. Calling it at the test-file top level applies it to the root describe block. Existing per-test retry behavior is unchanged when entireDescribe is omitted or false.

Describe-level retries take precedence over per-test retries in their subtree. Each describe attempt finishes completely, including its afterAll hooks, before another attempt begins; retryImmediately continues to apply only to ordinary per-test retries. A describe_retry circus event is dispatched after rollback and before the next attempt starts.

Nested describe retry settings compose from the innermost block outwards, with descendant retry budgets restarting for each ancestor attempt. A zero-retry describe setting still suppresses per-test retries in its subtree.

Ownerless rejections remain reported. Failures from afterAll and process-level errors observed during an attempt abort further attempts. Randomized test order stays fixed across attempts. Cleanup only removes errors added during the current attempt, clears test-owned snapshot changes for tests in the retried subtree, and restores expect state from the attempt start, so ancestor errors and snapshots outside an inner retried block are preserved.

Describe retry options must be configured synchronously during collection. Calling jest.retryTimes(..., {entireDescribe: true}) after test execution has started throws at the call site.

Test plan

  • corepack yarn build
  • corepack yarn jest packages/jest-circus/src/__tests__ packages/jest-circus/src/legacy-code-todo-rewrite/__tests__ --runInBand (19 suites, 125 tests, and 34 snapshots passed)
  • corepack yarn jest e2e/__tests__/detectOpenHandles.ts e2e/__tests__/failures.test.ts e2e/__tests__/promiseAsyncHandling.test.ts e2e/__tests__/testRetries.test.ts e2e/__tests__/toMatchSnapshotWithRetries.test.ts e2e/__tests__/toMatchInlineSnapshotWithRetries.test.ts --runInBand --color=false (6 suites, 83 tests, and 36 snapshots passed)
  • corepack yarn typecheck:tests
  • corepack yarn lint
  • corepack yarn check-changelog 16322

@netlify

netlify Bot commented Aug 11, 2026

Copy link
Copy Markdown

Deploy Preview for jestjs ready!

Name Link
🔨 Latest commit a7d5afc
🔍 Latest deploy log https://app.netlify.com/projects/jestjs/deploys/6a8413c0762ab50008c15555
😎 Deploy Preview https://deploy-preview-16322--jestjs.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@linux-foundation-easycla

linux-foundation-easycla Bot commented Aug 11, 2026

Copy link
Copy Markdown

CLA Signed
The committers listed above are authorized under a signed CLA.

@github-actions github-actions Bot added the require-changelog If a PR does requires a changelog entry label Aug 11, 2026
@soltonigiri
soltonigiri marked this pull request as ready for review August 11, 2026 05:43
@pkg-pr-new

pkg-pr-new Bot commented Aug 11, 2026

Copy link
Copy Markdown

Open in StackBlitz

babel-jest

npm i https://pkg.pr.new/babel-jest@16322

babel-plugin-jest-hoist

npm i https://pkg.pr.new/babel-plugin-jest-hoist@16322

babel-preset-jest

npm i https://pkg.pr.new/babel-preset-jest@16322

create-jest

npm i https://pkg.pr.new/create-jest@16322

@jest/diff-sequences

npm i https://pkg.pr.new/@jest/diff-sequences@16322

expect

npm i https://pkg.pr.new/expect@16322

@jest/expect-utils

npm i https://pkg.pr.new/@jest/expect-utils@16322

jest

npm i https://pkg.pr.new/jest@16322

jest-changed-files

npm i https://pkg.pr.new/jest-changed-files@16322

jest-circus

npm i https://pkg.pr.new/jest-circus@16322

jest-cli

npm i https://pkg.pr.new/jest-cli@16322

jest-config

npm i https://pkg.pr.new/jest-config@16322

@jest/console

npm i https://pkg.pr.new/@jest/console@16322

@jest/core

npm i https://pkg.pr.new/@jest/core@16322

@jest/create-cache-key-function

npm i https://pkg.pr.new/@jest/create-cache-key-function@16322

jest-diff

npm i https://pkg.pr.new/jest-diff@16322

jest-docblock

npm i https://pkg.pr.new/jest-docblock@16322

jest-each

npm i https://pkg.pr.new/jest-each@16322

@jest/environment

npm i https://pkg.pr.new/@jest/environment@16322

jest-environment-jsdom

npm i https://pkg.pr.new/jest-environment-jsdom@16322

@jest/environment-jsdom-abstract

npm i https://pkg.pr.new/@jest/environment-jsdom-abstract@16322

jest-environment-node

npm i https://pkg.pr.new/jest-environment-node@16322

@jest/expect

npm i https://pkg.pr.new/@jest/expect@16322

@jest/fake-timers

npm i https://pkg.pr.new/@jest/fake-timers@16322

@jest/get-type

npm i https://pkg.pr.new/@jest/get-type@16322

@jest/globals

npm i https://pkg.pr.new/@jest/globals@16322

jest-haste-map

npm i https://pkg.pr.new/jest-haste-map@16322

jest-jasmine2

npm i https://pkg.pr.new/jest-jasmine2@16322

jest-leak-detector

npm i https://pkg.pr.new/jest-leak-detector@16322

jest-matcher-utils

npm i https://pkg.pr.new/jest-matcher-utils@16322

jest-message-util

npm i https://pkg.pr.new/jest-message-util@16322

jest-mock

npm i https://pkg.pr.new/jest-mock@16322

@jest/pattern

npm i https://pkg.pr.new/@jest/pattern@16322

jest-phabricator

npm i https://pkg.pr.new/jest-phabricator@16322

jest-regex-util

npm i https://pkg.pr.new/jest-regex-util@16322

@jest/reporters

npm i https://pkg.pr.new/@jest/reporters@16322

jest-resolve

npm i https://pkg.pr.new/jest-resolve@16322

jest-resolve-dependencies

npm i https://pkg.pr.new/jest-resolve-dependencies@16322

jest-runner

npm i https://pkg.pr.new/jest-runner@16322

jest-runtime

npm i https://pkg.pr.new/jest-runtime@16322

@jest/schemas

npm i https://pkg.pr.new/@jest/schemas@16322

jest-snapshot

npm i https://pkg.pr.new/jest-snapshot@16322

@jest/snapshot-utils

npm i https://pkg.pr.new/@jest/snapshot-utils@16322

@jest/source-map

npm i https://pkg.pr.new/@jest/source-map@16322

@jest/test-result

npm i https://pkg.pr.new/@jest/test-result@16322

@jest/test-sequencer

npm i https://pkg.pr.new/@jest/test-sequencer@16322

@jest/transform

npm i https://pkg.pr.new/@jest/transform@16322

@jest/types

npm i https://pkg.pr.new/@jest/types@16322

jest-util

npm i https://pkg.pr.new/jest-util@16322

jest-validate

npm i https://pkg.pr.new/jest-validate@16322

jest-watcher

npm i https://pkg.pr.new/jest-watcher@16322

jest-worker

npm i https://pkg.pr.new/jest-worker@16322

pretty-format

npm i https://pkg.pr.new/pretty-format@16322

commit: 4f01ede

@SimenB SimenB left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

thanks, this generally looks really good! impressive test coverage 👍

as mentioned inline, there seems to be some bug fixes bundled into this change - would you mind extracting those to its own PR?

Comment thread packages/jest-circus/src/utils.ts Outdated
Comment thread packages/jest-circus/src/utils.ts Outdated
Comment thread packages/jest-circus/src/eventHandler.ts
Comment thread packages/jest-circus/src/legacy-code-todo-rewrite/jestAdapterInit.ts Outdated
Comment thread packages/jest-circus/src/types.ts Outdated
Comment thread packages/jest-expect/src/types.ts Outdated
Comment thread packages/jest-runtime/src/internals/JestGlobals.ts Outdated
Comment thread packages/jest-circus/src/unhandledRejectionHandler.ts Outdated
@soltonigiri

Copy link
Copy Markdown
Contributor Author

@SimenB All requested changes are addressed and the new head is green. Ready for re-review when you have a chance.

@SimenB

SimenB commented Aug 15, 2026

Copy link
Copy Markdown
Member

Thanks! Will take a look tomorrow or next week.

As mentioned, I'd love to see the bug fixes you made as its own PR as bug fixes are easier to review and land than features/new APIs 🙂

@soltonigiri

Copy link
Copy Markdown
Contributor Author

@SimenB

SimenB commented Aug 16, 2026

Copy link
Copy Markdown
Member

Awesome, thanks! All three landed now, which has created a bunch of conflicts here 😅


I've tried to sketch out a design with Claude for the snapshot clearing that works both with what landed in #16344 on its own, but also as a mechanism used by the describe retries on this PR. Here's the result:

What main gives you now

clear is clear(testIdentity). Every test has a stable identity — MatcherState.currentTestIdentity, set by circus from the async-local scope, the same object across one test's retries and distinct across tests even when full names collide. SnapshotState records what each test contributed since its current attempt began, and clear(identity) inverts exactly that: counts, key counters, inline snapshots, file snapshot data, unchecked keys, and the dirty flag. Other tests' work is untouched.

Two properties matter for this PR: the records are disjoint per test, and clear(identity) is order-independent — it does not require that nothing else happened since the attempt started.

What that means here

_inlineSnapshotsForTestRetry and commit() are redundant. They solve per-test retry rollback against a committed baseline; main solves the same problem per test. Dropping them resolves most of the State.ts conflict.

getRetryCheckpoint/restore can go too. A describe attempt's snapshot work is the union of the work of the tests inside it. Because the records are disjoint, rolling back the attempt decomposes into clear(identity) for each test in the subtree — no whole-state snapshot required. This PR already disables per-test retries inside a describe attempt, so nothing interleaves.

The missing piece is hook identity. beforeAll/afterAll run outside the async-local scope, so their snapshots carry no identity and nothing can undo them — and a describe retry re-runs beforeAll. Setting the scope around the *All hooks, with the describe block as the identity, closes that. Rollback then becomes clear() per test in the subtree plus clear() for the block, and needs no new SnapshotState API.

Why not keep both mechanisms

They do not compose, and the failure is silent. The checkpoint does not save _attemptRecordsByTest, so a restore() leaves records holding prior values captured before the restore. A later clear(identity) then undoes work the restore already undid: double-subtracted counts, resurrected file snapshot values.

Cost also differs by an order. The checkpoint copies _snapshotData, _uncheckedKeys, _counters and _inlineSnapshots wholesale on every attempt, including attempts that succeed and discard the copy. The records cost only what the attempt actually touched.

Knock-on

SnapshotStateWithRetry and the optional getRetryCheckpoint? go away with the checkpoint. The version-skew concern that motivated the optional method does not apply to clear: it exists in every published version, so against a mismatched jest-snapshot a clear(identity) call degrades to the old full wipe rather than throwing, with no optional-method probe needed.

@soltonigiri

Copy link
Copy Markdown
Contributor Author

Thanks for the detailed design — I updated the branch to follow it.

I removed _inlineSnapshotsForTestRetry, commit(), and the getRetryCheckpoint() / restore() path, and now use main’s identity-scoped SnapshotState.clear() implementation. beforeAll and afterAll run with their owning describe block as the snapshot identity, while tests and per-test hooks use the test entry.

A describe retry clears every test identity and every describe-block identity in that subtree. Nested describe blocks are included because their beforeAll and afterAll hooks are rerun by the outer describe retry. Ordinary test retries continue to clear only event.test. The resulting implementation does not add a new retry API to SnapshotState.

I also added focused identity-scoping coverage and an E2E case combining file and inline snapshots from beforeAll, tests, and afterAll with a later ordinary test retry.

All checks on the updated head have completed with no failures. Ready for another look when you have a chance.

@SimenB SimenB left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I like how this is turning out. I of course saw something that didn't smell right (which I lead you to add), but quite close!

Two things inline. The hook identity one reverses part of the earlier sketch, so sorry for the churn there.

It has a knock-on for the fixtures. entireDescribeBeforeAllSnapshot pins beforeAll snapshot behaviour, which is heading for a hard error rather than support. The coverage worth keeping is that a describe retry rolls back snapshots and gets the counts right, so move those assertions into the test body and drop the beforeAll ones — entireDescribeAncestorFailures already covers the hook side without snapshots.

One more while jest.retryTimes is open. The setter pushes to state.unhandledErrors when called after the run has started, so the mistake surfaces as a file-level failure with a stack pointing into Jest. Throwing from the setter instead propagates back out through jest.retryTimes to the line the user actually wrote.

Comment thread packages/jest-circus/src/run.ts
Comment thread packages/jest-circus/src/run.ts
@soltonigiri

Copy link
Copy Markdown
Contributor Author

Addressed all requested changes:

  • memoized getTestID with a WeakMap;
  • limited snapshot identity and retry cleanup to tests, with the snapshot assertions moved out of *All hooks;
  • made late entireDescribe retry configuration throw at the user's call site.

The Jest Circus unit suites and the focused retry/snapshot E2E suites pass, along with build, test typechecking, lint, and the changelog check.

An unhandled rejection owned by a test no longer vetoes a describe retry,
but an uncaught exception attributed to the same test still did, so a stray
timer that throws made the block unretryable while a floating promise did
not. Both are async errors pushed onto the owning test, and both are rolled
back by `clearTestErrors` with the rest of that test's errors, so neither
needs to stop the attempt.

Only ownerless errors keep vetoing, which the `unhandledErrors` check in the
retry loop already covers.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@SimenB

SimenB commented Aug 18, 2026

Copy link
Copy Markdown
Member

Noticed one more thing and just pushed a fix rather than have you iterate again.

A test-owned unhandled rejection no longer vetoes a describe retry, but an uncaught exception attributed to the same test still did — so a stray timer that throws made the block un-retryable while a floating promise did not. Both end up on the owning test and both are rolled back by clearTestErrors, so neither needs to stop the attempt. Ownerless errors keep vetoing, which the unhandledErrors check in the retry loop already covers.

One of the unit assertions was pinning the old behaviour (processErrorGeneration being 1 after a test-owned process error), so that moved to 0, and there is a fixture covering both the immediate and the late-arriving case.

@SimenB SimenB left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

thank you! quite a few iterations, but I think it landed in a good place 🙂 Thanks for the PR (and your patience)!

@SimenB
SimenB merged commit 051594d into jestjs:main Aug 18, 2026
102 checks passed
@soltonigiri

Copy link
Copy Markdown
Contributor Author

Thanks so much! I really appreciate all the detailed reviews and guidance through the iterations. I’m glad we got it into a good place!

renovate Bot added a commit to andrei-picus-tink/auto-renovate that referenced this pull request Aug 29, 2026
| datasource | package | from   | to     |
| ---------- | ------- | ------ | ------ |
| npm        | jest    | 30.4.2 | 30.5.0 |


## [v30.5.0](https://github.com/jestjs/jest/blob/HEAD/CHANGELOG.md#3050)

##### Features

- `[@jest/expect-utils, jest-mock]` Add `mockFn.whenCalledWith(...args)` for configuring return values per argument list, with first-class asymmetric-matcher support ([#16053](jestjs/jest#16053))
- `[@jest/expect-utils]` Export `AsymmetricMatcher` and `FunctionParameters` types (previously private to `expect`) ([#16053](jestjs/jest#16053))
- `[jest-circus, jest-core, jest-jasmine2, jest-test-result, jest-types]` `--collectTests` now expands `test.each`/`describe.each` cases and reports per-status counts (skipped/todo via the new `wouldRun` flag for selected tests) plus a summary line that match a real run, including under `--testNamePattern` and `.only`/`fdescribe` focus on both the circus and jasmine2 runners ([#16259](jestjs/jest#16259))
- `[jest-circus, jest-environment, jest-runtime, jest-types]` Add describe-level retries via `jest.retryTimes(..., {entireDescribe: true})` ([#16322](jestjs/jest#16322))
- `[jest-circus, jest-message-util, jest-reporters, jest-types]` Add `retryMessages` to `AssertionResult` and export `formatErrorStack`, so the retry log renders nested `cause` and `AggregateError` sections with code frames instead of serialized `[cause]:`/`[errors]:` markers ([#16316](jestjs/jest#16316))
- `[jest-circus, jest-types]` Add `unhandledErrorsDetailed` to `Circus.RunResult`, so an unhandled rejection reports its `cause` chain and `AggregateError` entries with code frames instead of a pre-serialized stack ([#16316](jestjs/jest#16316))
- `[jest-haste-map]` Replace `NodeWatcher` and `FSEventsWatcher` with `@parcel/watcher` for the non-watchman watch path ([#16188](jestjs/jest#16188))
- `[jest-resolve]` Bump `unrs-resolver` to 1.12.1, remove `jest-pnp-resolver` and unnecessary checks ([#15721](jestjs/jest#15721))
- `[jest-resolve]` Honor Node's `--preserve-symlinks` / `NODE_PRESERVE_SYMLINKS` in the default resolver by passing `symlinks: false` to `unrs-resolver` ([#16260](jestjs/jest#16260))
- `[jest-runtime]` Apply automocking and manual `__mocks__` files to synchronously evaluable ESM graphs on Node 24.9+ - static imports, dynamic `import()` and `require()` of an ESM file now generate an automock from the real module's namespace instead of failing with "Attempting to import a mock without a factory". Graphs that need async evaluation (top-level await) or an async-only resolver or transformer still throw ([#16391](jestjs/jest#16391))
- `[jest-runtime]` Route `process.getBuiltinModule` through the sandbox, so it returns the sandbox `process` and the hooked `node:module` instead of the host's ([#16391](jestjs/jest#16391))
- `[jest-runtime]` Throw an actionable error from `module.register()` and `module.registerHooks()` inside a test - the hooks attached to the loader running Jest itself, never saw the sandboxed requires they were meant for, and stayed registered for every later test file in the worker ([#16391](jestjs/jest#16391))
- `[jest-runtime]` Surface resolution and import-attribute errors in an ESM graph before executing any of its CJS dependencies on Node 24.9+, matching Node's run-nothing-on-a-broken-graph behavior; the legacy loader on older versions keeps its linking-time execution order ([#16391](jestjs/jest#16391))
- `[jest-runtime]` Throw `ERR_SOURCE_PHASE_NOT_DEFINED` with an actionable message for `import source` and `import.source()`, instead of failing at instantiation with V8's bare "Source phase import object is not defined" ([#16391](jestjs/jest#16391))
- `[jest-runtime]` Emit the JSON-without-import-attribute deprecation warning once per test file instead of once per worker, so it is no longer silently swallowed for every file after the first ([#16391](jestjs/jest#16391))
- `[jest-runtime]` Set `import.meta.main` to `true` in the test file and `false` in every module it loads, matching Node 24+ ([#16367](jestjs/jest#16367))
- `[jest-runtime]` Resolve the `module-sync` export condition, so a package that exposes its ESM entry point for `require()` loads the same file Node would ([#16336](jestjs/jest#16336))
- `[jest-snapshot]` Add external snapshot paths to custom reporter failure details ([#16374](jestjs/jest#16374))

##### Fixes

- `[jest-console, jest-reporters]` `CustomConsole` now buffers console output so `TestResult.console` is populated for reporters when `verbose` is enabled, while `GitHubActionsReporter` avoids replaying buffered output in verbose mode ([#16155](jestjs/jest#16155))
- `[expect, jest-message-util, jest-pattern, jest-regex-util, jest-util]` Revert `node:` protocol imports to restore webpack/browser-bundle compatibility ([#16167](jestjs/jest#16167))
- `[expect]` Widen `toMatchObject` and `objectContaining` parameter type from `Record<string, unknown>` to `object` so class instances are accepted ([#16196](jestjs/jest#16196))
- `[jest-circus]` Call a generator test body with the shared test context, so `this` matches what a regular test function receives ([#16347](jestjs/jest#16347))
- `[jest-circus]` Capture the error listeners of the parent process instead of the in-sandbox `process`, so listeners registered before the test file survive teardown and sandbox listeners no longer leak onto the parent ([#16347](jestjs/jest#16347))
- `[jest-circus]` Clear `currentlyRunningTest` after skipped and todo tests ([#16342](jestjs/jest#16342))
- `[jest-circus]` Prevent late `done()` callbacks from affecting later test or hook invocations ([#16343](jestjs/jest#16343))
- `[jest-circus, jest-jasmine2]` Honor `--expand` when formatting `node:assert` failures, instead of always collapsing the diff ([#16347](jestjs/jest#16347))
- `[jest-circus, jest-jasmine2, jest-message-util]` Serialize the inner errors of an `AggregateError` into `failureMessages`, `retryReasons` and `unhandledErrors`, so `--json` output and reporter annotations include them ([#16316](jestjs/jest#16316))
- `[jest-circus, jest-snapshot]` Keep snapshot state and counts correct when a test retries ([#16344](jestjs/jest#16344))
- `[@jest/create-cache-key-function]` Include the caller support flags in the generated key, so a transformer that emits ESM or CJS based on them no longer shares one cache entry between the two ([#16331](jestjs/jest#16331))
- `[@jest/create-cache-key-function]` Include the stringified project config in the generated key, so editing a transformer's own settings invalidates what it cached ([#16331](jestjs/jest#16331))
- `[@jest/transform]` Include the caller support flags in a transform's cache key, so a file transformed both as ESM and as CJS no longer serves one shape's output for the other ([#16331](jestjs/jest#16331))
- `[jest-config]` Add missing `findRelatedTests`, `outputFile`, and `replname` entries to `ValidConfig` so they no longer trigger spurious "Unknown option" warnings ([#16224](jestjs/jest#16224))
- `[jest-config]` Use `--config` for the global config when multiple `--projects` are specified ([#16273](jestjs/jest#16273))
- `[jest-core]` Serialize `bigint` values in `--json` and `--outputFile` output as their literal form (`4n`), instead of failing the run with `TypeError: Do not know how to serialize a BigInt` ([#16338](jestjs/jest#16338))
- `[jest-core]` Do not report a `CustomGC` async resource (used by N-API addons such as napi-rs for per-isolate GC bookkeeping) as an open handle, since it is `napi_unref`'d by the addon and can never keep the event loop alive ([#16379](jestjs/jest#16379))
- `[jest-each]` Keep a `$&`, `` $` ``, `$'` or `$$` inside a `%p` param value out of the replacement, so the title shows the value instead of the text around it ([#16338](jestjs/jest#16338))
- `[jest-each]` Interpolate a `bigint` into a `%j` title as its literal form (`"4n"`) at any depth, instead of throwing `TypeError: Do not know how to serialize a BigInt` while collecting the tests ([#16338](jestjs/jest#16338))
- `[jest-environment, jest-runtime]` Bind `sandboxInjectedGlobals` to the right values when `injectGlobals` is `false`, instead of shifting every one of them by a position ([#16377](jestjs/jest#16377))
- `[jest-environment-node, jest-util]` Only warn about a conflicting `globalsCleanup` mode when one was explicitly configured, and follow the mode that is actually in effect ([#16323](jestjs/jest#16323))
- `[jest-environment-node, jest-util]` Stop resolving lazy globals when setting up an environment, so Node 26's builtin module globals are no longer loaded (and no longer emit their deprecation warnings) for every test file ([#16324](jestjs/jest#16324))
- `[jest-haste-map]` Keep watch mode alive when an outside process briefly makes a file unreadable on Windows, instead of tearing the watcher down on `EPERM` ([#16295](jestjs/jest#16295))
- `[jest-haste-map]` Keep indexing when an outside process holds a file open on Windows, instead of failing the whole crawl on `EPERM` ([#16358](jestjs/jest#16358))
- `[jest-haste-map]` Keep a duplicated manual mock resolving when the file it pointed at is deleted in watch mode ([#16360](jestjs/jest#16360))
- `[jest-haste-map]` Shut the worker farm down when a duplicate manual mock aborts the build under `throwOnModuleCollision` ([#16354](jestjs/jest#16354))
- `[jest-haste-map]` Attach the watchman client's `error` listener before the first command, so a watchman failure falls back to the node crawler instead of crashing on an unhandled `error` event, and always end the client ([#16355](jestjs/jest#16355))
- `[jest-haste-map]` Stop delivering watch events after `WatchmanWatcher` is closed, and route its warnings through the configured console ([#16355](jestjs/jest#16355))
- `[jest-haste-map]` Restore the nested `duplicates` index correctly in `ModuleMap.fromJSON`, so a haste collision reported inside a test worker raises `DuplicateHasteCandidatesError` instead of a `TypeError` ([#16353](jestjs/jest#16353))
- `[jest-haste-map]` Match watched files on a full extension, so `moduleFileExtensions: ['js']` no longer accepts `foo.mjs` ([#16352](jestjs/jest#16352))
- `[jest-haste-map]` Delimit the fields that make up the haste map cache key, so two different option sets cannot hash to the same cache file ([#16352](jestjs/jest#16352))
- `[jest-message-util]` Print the inner errors of an `AggregateError` thrown inside a test ([#16316](jestjs/jest#16316))
- `[jest-message-util]` Indent nested `cause` and `AggregateError` sections of a test failure by one level per depth, so the nesting is legible instead of rendering flat ([#16316](jestjs/jest#16316))
- `[jest-message-util]` Color stack traces line by line so blank lines stay blank ([#16316](jestjs/jest#16316))
- `[jest-message-util]` Detect Jest's own frames without assuming the checkout directory's name, and cover `@jest/*` packages, so stack traces and code frames point at user code ([#16326](jestjs/jest#16326))
- `[jest-mock]` `mockResolvedValue` / `mockRejectedValue` now see all overload return types, so a Promise-returning overload survives even when a later overload returns a non-Promise (e.g. `pg.Client['end']`) ([#16237](jestjs/jest#16237))
- `[@jest-environment/jsdom-abstract]` Make `@types/jsdom` a peer dependency ([#16166](jestjs/jest#16166))
- `[jest-mock]` Remove the leftover own accessor descriptor when restoring a `spyOn` of an inherited getter or setter, so the instance keeps reflecting the prototype ([#16226](jestjs/jest#16226))
- `[jest-resolve]` Include `extensionsToTreatAsEsm` in the `shouldLoadAsEsm` cache key, so projects with different extension lists don't read each other's answers ([#16369](jestjs/jest#16369))
- `[jest-resolve]` Make `getModuleIDAsync` build and cache `data:` URI module IDs the same way as `getModuleID` ([#16370](jestjs/jest#16370))
- `[jest-resolve]` Keep the `node:` prefix when resolving a core module asynchronously, so a builtin that only exists prefixed (`node:sea`, `node:sqlite`, `node:test`, `node:test/reporters`) resolves instead of failing as a missing bare package ([#16388](jestjs/jest#16388))
- `[jest-resolve]` Look up manual mocks for `node:` protocol specifiers under the unprefixed name they are stored as ([#16388](jestjs/jest#16388))
- `[jest-resolve]` Apply `moduleNameMapper` consistently to both spellings of core module specifiers (`fs` vs `node:fs`) ([#16390](jestjs/jest#16390))
- `[jest-resolve]` Keep virtual and ordinary mock module IDs isolated across test files ([#16296](jestjs/jest#16296))
- `[jest-resolve]` Guard missing `require.resolve.paths` ([#16052](jestjs/jest#16052))
- `[jest-resolve, jest-config, jest-runner]` Support a user resolver written as an ES module ([#16332](jestjs/jest#16332))
- `[jest-resolve, jest-runtime]` Throw the CJS parse error for ESM syntax in a `"type": "commonjs"` package or a `.cjs` file instead of loading it as ESM, matching Node ([#16368](jestjs/jest#16368))
- `[@jest/source-map]` Keep source map sources that name a scheme, such as `webpack:///`, instead of resolving them into a path that does not exist ([#16327](jestjs/jest#16327))
- `[@jest/source-map]` Look up `--testLocationInResults` positions at the right column, and keep a mapping to the first column instead of discarding it ([#16327](jestjs/jest#16327))
- `[@jest/source-map]` Warn when a source map cannot be parsed, instead of silently leaving its frames untranslated ([#16327](jestjs/jest#16327))
- `[jest-runner, @jest/source-map]` Keep a source-mapped stack for an error thrown after the test environment was torn down ([#16327](jestjs/jest#16327))
- `[jest-runtime, @jest/source-map]` Keep source maps past teardown and past the next test file's `install`, so a stack from a file no earlier stack mentioned still points at the original source ([#16330](jestjs/jest#16330))
- `[jest-runtime]` Report that no coverage was collected when `getAllV8CoverageInfoCopy` is called after `teardown`, instead of returning an empty result ([#16385](jestjs/jest#16385))
- `[jest-runtime]` Cache a CJS module's parsed exports before walking its re-exports, so two modules that re-export each other no longer overflow the stack when imported from ESM ([#16363](jestjs/jest#16363))
- `[jest-runtime]` Keep a re-exported ES module's parse failure from marking the re-exporting CommonJS file as ESM, so `module.exports = require('./dep.mjs')` loads instead of failing with `module is not defined` ([#16363](jestjs/jest#16363))
- `[jest-runtime]` Scope module mocks instantiated inside `jest.isolateModules`/`isolateModulesAsync` to that block, so a mock first imported there no longer outlives it - matching how CommonJS mocks already behave ([#16365](jestjs/jest#16365))
- `[jest-runtime]` Suspend module isolation while generating an automock, so loading the real module to read its shape no longer populates the isolated registry ([#16365](jestjs/jest#16365))
- `[jest-runtime]` Check a cached ES module's status before `require()` returns it, so a module whose evaluation threw rethrows that error and one left linked by a failed sibling is evaluated instead of returning uninitialized bindings ([#16364](jestjs/jest#16364))
- `[jest-runtime]` Report the original `ERR_REQUIRE_ASYNC_MODULE` when a `require()` of a top-level-await graph is retried, instead of a spurious "concurrent `import()`" error ([#16364](jestjs/jest#16364))
- `[jest-runtime]` Throw the evaluation error when another caller's `import()` of the same module failed while we awaited it, instead of resolving with the errored module ([#16364](jestjs/jest#16364))
- `[jest-runtime]` Mark the result of `require()`ing an ES module that has a default export with `__esModule: true` through a live-binding facade, and serve the same object from `require.cache`, matching Node ([#16367](jestjs/jest#16367))
- `[jest-runtime]` Provide a CommonJS module's exports under the `'module.exports'` named export when imported from ESM, matching Node 23+ ([#16367](jestjs/jest#16367))
- `[jest-runtime]` Give the test file itself a non-null `require.main` ([#16367](jestjs/jest#16367))
- `[jest-runtime]` Populate `module.children` with the modules a file loads, matching Node ([#16368](jestjs/jest#16368))
- `[jest-runtime]` Provide `import.meta.resolve` and `import.meta.jest` in `data:` URI modules, accept any-case mediatype parameters, and use Node's error codes for invalid `data:` URIs ([#16368](jestjs/jest#16368))
- `[jest-runtime]` Key ES modules by full URL, so query and fragment suffixes create the same module instances as Node and show up in `import.meta.url` ([#16375](jestjs/jest#16375))
- `[jest-runtime]` Share modules between overlapping graphs when a CommonJS module `require()`s an ES module mid-load, instead of evaluating shared dependencies twice ([#16375](jestjs/jest#16375))
- `[jest-runtime]` Throw `ERR_REQUIRE_CYCLE_MODULE` like Node when a CommonJS module `require()`s an ES module that is still being loaded, instead of evaluating the module a second time ([#16366](jestjs/jest#16366))
- `[jest-runtime]` Key builtin modules in the ESM registry by one canonical specifier ([#16341](jestjs/jest#16341))
- `[jest-runtime]` `import.meta.resolve()` for a builtin uses its `node:` specifier ([#16341](jestjs/jest#16341))
- `[jest-runtime]` Fall back to native ESM when a `.js` file contains ESM syntax but has no `"type":"module"` marker ([#16152](jestjs/jest#16152))
- `[jest-runtime]` Allow `require()` of ESM-marked files on Node < 24.9 via transform fallback ([#16244](jestjs/jest#16244))
- `[jest-runtime, @jest/transform]` Surface actionable `ERR_REQUIRE_ESM` error for files with untransformed ESM syntax instead of the generic "unexpected token" message ([#16244](jestjs/jest#16244))
- `[jest-runtime]` Support older test environments whose `moduleMocker` does not implement `clearMocksOnScope` ([#16169](jestjs/jest#16169))
- `[jest-runtime]` Apply `jest.unstable_mockModule` when the mocked file itself is `require()`d, not only when it is imported as a dependency ([#16389](jestjs/jest#16389))
- `[jest-runtime]` Apply `jest.unstable_mockModule` to statically imported `data:` URIs on Node 24.9+, matching dynamic `import()` ([#16389](jestjs/jest#16389))
- `[jest-runtime]` Run an async `jest.unstable_mockModule` factory once per module instead of twice, and fail the import instead of crashing the worker when the factory rejects ([#16389](jestjs/jest#16389))
- `[jest-runtime]` Hide a `require(esm)` module that failed to evaluate from `require.cache`, as Node does, instead of exposing a namespace with uninitialized bindings ([#16389](jestjs/jest#16389))
- `[jest-runtime]` Strip the byte-order mark when importing a JSON module, matching `require()` and Node ([#16389](jestjs/jest#16389))
- `[jest-runtime]` Throw `ERR_REQUIRE_ASYNC_MODULE` when `require(esm)` runs under an async-only custom resolver, instead of silently resolving with the default resolver ([#16389](jestjs/jest#16389))
- `[jest-runtime]` Parse imported JSON modules with the test realm's `JSON`, so their objects pass `instanceof Object` inside the test like `require()`d JSON does ([#16389](jestjs/jest#16389))
- `[jest-runtime]` Accept every `file:` URL string in the sandboxed `module.createRequire`, including one with a `localhost` authority, as Node does ([#16389](jestjs/jest#16389))
- `[jest-runtime]` Point at `{virtual: true}` when `jest.mock` or `jest.unstable_mockModule` is given a module that cannot be resolved ([#16389](jestjs/jest#16389))
- `[jest-reporters]` Fix coverage report table formatting in CI/GitHub Actions environments where `process.stdout.columns` is undefined by falling back to the `COLUMNS` env var or `80` columns in CI, preserving existing behaviour in other non-TTY environments ([#16227](jestjs/jest#16227))
- `[jest-runtime]` Support CJS-in-ESM exports via `"module.exports"` named exports ([#16277](jestjs/jest#16277))
- `[jest-snapshot]` Keep a skipped or failed test's hinted snapshots, instead of reporting them obsolete ([#16348](jestjs/jest#16348))
- `[jest-util]` Stop `globsToMatcher` reusing a cached matcher compiled with different picomatch options, and keep its `dot: true` default when `dot` is passed as `undefined` ([#16381](jestjs/jest#16381))
- `[pretty-format]` Move the `react-is` aliases into the `@jest` scope, so they cannot be shadowed by unrelated packages published under the alias names ([#16333](jestjs/jest#16333))

##### Chore & Maintenance

- `[docs]` Document the intentional divergences from Node's module system in the ECMAScript Modules page ([#16368](jestjs/jest#16368))
- `[docs]` Note deprecation of `react-test-renderer` in React Native tutorial and `pretty-format` README ([#16294](jestjs/jest#16294))
- `[docs]` Use `@testing-library/react-native` in the React Native tutorial instead of the deprecated `react-test-renderer` ([#16318](jestjs/jest#16318))
- `[babel-jest, @jest/transform]` Update `babel-plugin-istanbul` to v8 ([#16049](jestjs/jest#16049))
- `[jest-config, @jest/reporters, jest-runtime]` Update `glob` to v13 ([#16397](jestjs/jest#16397))
- `[jest-haste-map]` Refactor massive class into multiple files ([#16180](jestjs/jest#16180))
- `[jest-haste-map]` Drop `walker` dependency; replace hand-rolled directory recursion in the JS crawler and watcher startup with `fdir` ([#16187](jestjs/jest#16187))
- `[jest-haste-map]` Reuse cached metadata for files whose haste name is a known duplicate, instead of re-reading and re-parsing them on every startup ([#16351](jestjs/jest#16351))
- `[jest-haste-map]` Cache the watchman socket path and replace the `watchman --version` probe with `get-sockname`, so warm runs spawn no watchman processes ([#16386](jestjs/jest#16386))
- `[jest-resolve]` Store the per-directory package-type lookup in the cache it reads, so it actually memoizes ([#16369](jestjs/jest#16369))
- `[jest-resolve, jest-runtime]` Cut repeated work on the resolution hot path: hoist the platform-extension list to construction, memoize `isCoreModule` and the options cache-key serialization, skip mapper preparation when no `moduleNameMapper` is configured, run each mapper regex once, and stop re-parsing `NODE_OPTIONS` on every default-resolver call ([#16371](jestjs/jest#16371))
- `[jest-resolve]` Cut warm resolution cost to about a third: reuse one `unrs-resolver` factory per options shape instead of cloning per resolution, compose the factory cache key from per-array cached strings instead of serializing options, and stop constructing an `Error` for misses that `findNodeModule` swallows; add a `__benchmarks__` suite for the default resolver ([#16373](jestjs/jest#16373))
- `[jest-runner, @jest/source-map]` Replace `source-map-support` with an implementation in `@jest/source-map` ([#16327](jestjs/jest#16327))
- `[jest-snapshot]` Load babel, semver and synckit lazily, so requiring the package (which every test process does through `@jest/expect`) no longer loads \~200 modules that only writing inline snapshots needs ([#16387](jestjs/jest#16387))
- `[jest-runtime]` Reduce per-require overhead: skip module ID resolution when no mock can apply, answer core modules before probing for a manual mock, share one `require.cache` proxy across modules, and cache empty files ([#16376](jestjs/jest#16376))
- `[@jest/source-map]` Deprecate `getCallsite` in favour of `SourceMapSupport#getCallsite` ([#16327](jestjs/jest#16327))
- `[jest-runtime]` Avoid magical `null` value in ESM loader ([#16160](jestjs/jest#16160))
renovate Bot added a commit to andrei-picus-tink/auto-renovate that referenced this pull request Sep 1, 2026
| datasource | package | from   | to     |
| ---------- | ------- | ------ | ------ |
| npm        | jest    | 30.4.2 | 30.5.0 |


## [v30.5.0](https://github.com/jestjs/jest/blob/HEAD/CHANGELOG.md#3050)

##### Features

- `[@jest/expect-utils, jest-mock]` Add `mockFn.whenCalledWith(...args)` for configuring return values per argument list, with first-class asymmetric-matcher support ([#16053](jestjs/jest#16053))
- `[@jest/expect-utils]` Export `AsymmetricMatcher` and `FunctionParameters` types (previously private to `expect`) ([#16053](jestjs/jest#16053))
- `[jest-circus, jest-core, jest-jasmine2, jest-test-result, jest-types]` `--collectTests` now expands `test.each`/`describe.each` cases and reports per-status counts (skipped/todo via the new `wouldRun` flag for selected tests) plus a summary line that match a real run, including under `--testNamePattern` and `.only`/`fdescribe` focus on both the circus and jasmine2 runners ([#16259](jestjs/jest#16259))
- `[jest-circus, jest-environment, jest-runtime, jest-types]` Add describe-level retries via `jest.retryTimes(..., {entireDescribe: true})` ([#16322](jestjs/jest#16322))
- `[jest-circus, jest-message-util, jest-reporters, jest-types]` Add `retryMessages` to `AssertionResult` and export `formatErrorStack`, so the retry log renders nested `cause` and `AggregateError` sections with code frames instead of serialized `[cause]:`/`[errors]:` markers ([#16316](jestjs/jest#16316))
- `[jest-circus, jest-types]` Add `unhandledErrorsDetailed` to `Circus.RunResult`, so an unhandled rejection reports its `cause` chain and `AggregateError` entries with code frames instead of a pre-serialized stack ([#16316](jestjs/jest#16316))
- `[jest-haste-map]` Replace `NodeWatcher` and `FSEventsWatcher` with `@parcel/watcher` for the non-watchman watch path ([#16188](jestjs/jest#16188))
- `[jest-resolve]` Bump `unrs-resolver` to 1.12.1, remove `jest-pnp-resolver` and unnecessary checks ([#15721](jestjs/jest#15721))
- `[jest-resolve]` Honor Node's `--preserve-symlinks` / `NODE_PRESERVE_SYMLINKS` in the default resolver by passing `symlinks: false` to `unrs-resolver` ([#16260](jestjs/jest#16260))
- `[jest-runtime]` Apply automocking and manual `__mocks__` files to synchronously evaluable ESM graphs on Node 24.9+ - static imports, dynamic `import()` and `require()` of an ESM file now generate an automock from the real module's namespace instead of failing with "Attempting to import a mock without a factory". Graphs that need async evaluation (top-level await) or an async-only resolver or transformer still throw ([#16391](jestjs/jest#16391))
- `[jest-runtime]` Route `process.getBuiltinModule` through the sandbox, so it returns the sandbox `process` and the hooked `node:module` instead of the host's ([#16391](jestjs/jest#16391))
- `[jest-runtime]` Throw an actionable error from `module.register()` and `module.registerHooks()` inside a test - the hooks attached to the loader running Jest itself, never saw the sandboxed requires they were meant for, and stayed registered for every later test file in the worker ([#16391](jestjs/jest#16391))
- `[jest-runtime]` Surface resolution and import-attribute errors in an ESM graph before executing any of its CJS dependencies on Node 24.9+, matching Node's run-nothing-on-a-broken-graph behavior; the legacy loader on older versions keeps its linking-time execution order ([#16391](jestjs/jest#16391))
- `[jest-runtime]` Throw `ERR_SOURCE_PHASE_NOT_DEFINED` with an actionable message for `import source` and `import.source()`, instead of failing at instantiation with V8's bare "Source phase import object is not defined" ([#16391](jestjs/jest#16391))
- `[jest-runtime]` Emit the JSON-without-import-attribute deprecation warning once per test file instead of once per worker, so it is no longer silently swallowed for every file after the first ([#16391](jestjs/jest#16391))
- `[jest-runtime]` Set `import.meta.main` to `true` in the test file and `false` in every module it loads, matching Node 24+ ([#16367](jestjs/jest#16367))
- `[jest-runtime]` Resolve the `module-sync` export condition, so a package that exposes its ESM entry point for `require()` loads the same file Node would ([#16336](jestjs/jest#16336))
- `[jest-snapshot]` Add external snapshot paths to custom reporter failure details ([#16374](jestjs/jest#16374))

##### Fixes

- `[jest-console, jest-reporters]` `CustomConsole` now buffers console output so `TestResult.console` is populated for reporters when `verbose` is enabled, while `GitHubActionsReporter` avoids replaying buffered output in verbose mode ([#16155](jestjs/jest#16155))
- `[expect, jest-message-util, jest-pattern, jest-regex-util, jest-util]` Revert `node:` protocol imports to restore webpack/browser-bundle compatibility ([#16167](jestjs/jest#16167))
- `[expect]` Widen `toMatchObject` and `objectContaining` parameter type from `Record<string, unknown>` to `object` so class instances are accepted ([#16196](jestjs/jest#16196))
- `[jest-circus]` Call a generator test body with the shared test context, so `this` matches what a regular test function receives ([#16347](jestjs/jest#16347))
- `[jest-circus]` Capture the error listeners of the parent process instead of the in-sandbox `process`, so listeners registered before the test file survive teardown and sandbox listeners no longer leak onto the parent ([#16347](jestjs/jest#16347))
- `[jest-circus]` Clear `currentlyRunningTest` after skipped and todo tests ([#16342](jestjs/jest#16342))
- `[jest-circus]` Prevent late `done()` callbacks from affecting later test or hook invocations ([#16343](jestjs/jest#16343))
- `[jest-circus, jest-jasmine2]` Honor `--expand` when formatting `node:assert` failures, instead of always collapsing the diff ([#16347](jestjs/jest#16347))
- `[jest-circus, jest-jasmine2, jest-message-util]` Serialize the inner errors of an `AggregateError` into `failureMessages`, `retryReasons` and `unhandledErrors`, so `--json` output and reporter annotations include them ([#16316](jestjs/jest#16316))
- `[jest-circus, jest-snapshot]` Keep snapshot state and counts correct when a test retries ([#16344](jestjs/jest#16344))
- `[@jest/create-cache-key-function]` Include the caller support flags in the generated key, so a transformer that emits ESM or CJS based on them no longer shares one cache entry between the two ([#16331](jestjs/jest#16331))
- `[@jest/create-cache-key-function]` Include the stringified project config in the generated key, so editing a transformer's own settings invalidates what it cached ([#16331](jestjs/jest#16331))
- `[@jest/transform]` Include the caller support flags in a transform's cache key, so a file transformed both as ESM and as CJS no longer serves one shape's output for the other ([#16331](jestjs/jest#16331))
- `[jest-config]` Add missing `findRelatedTests`, `outputFile`, and `replname` entries to `ValidConfig` so they no longer trigger spurious "Unknown option" warnings ([#16224](jestjs/jest#16224))
- `[jest-config]` Use `--config` for the global config when multiple `--projects` are specified ([#16273](jestjs/jest#16273))
- `[jest-core]` Serialize `bigint` values in `--json` and `--outputFile` output as their literal form (`4n`), instead of failing the run with `TypeError: Do not know how to serialize a BigInt` ([#16338](jestjs/jest#16338))
- `[jest-core]` Do not report a `CustomGC` async resource (used by N-API addons such as napi-rs for per-isolate GC bookkeeping) as an open handle, since it is `napi_unref`'d by the addon and can never keep the event loop alive ([#16379](jestjs/jest#16379))
- `[jest-each]` Keep a `$&`, `` $` ``, `$'` or `$$` inside a `%p` param value out of the replacement, so the title shows the value instead of the text around it ([#16338](jestjs/jest#16338))
- `[jest-each]` Interpolate a `bigint` into a `%j` title as its literal form (`"4n"`) at any depth, instead of throwing `TypeError: Do not know how to serialize a BigInt` while collecting the tests ([#16338](jestjs/jest#16338))
- `[jest-environment, jest-runtime]` Bind `sandboxInjectedGlobals` to the right values when `injectGlobals` is `false`, instead of shifting every one of them by a position ([#16377](jestjs/jest#16377))
- `[jest-environment-node, jest-util]` Only warn about a conflicting `globalsCleanup` mode when one was explicitly configured, and follow the mode that is actually in effect ([#16323](jestjs/jest#16323))
- `[jest-environment-node, jest-util]` Stop resolving lazy globals when setting up an environment, so Node 26's builtin module globals are no longer loaded (and no longer emit their deprecation warnings) for every test file ([#16324](jestjs/jest#16324))
- `[jest-haste-map]` Keep watch mode alive when an outside process briefly makes a file unreadable on Windows, instead of tearing the watcher down on `EPERM` ([#16295](jestjs/jest#16295))
- `[jest-haste-map]` Keep indexing when an outside process holds a file open on Windows, instead of failing the whole crawl on `EPERM` ([#16358](jestjs/jest#16358))
- `[jest-haste-map]` Keep a duplicated manual mock resolving when the file it pointed at is deleted in watch mode ([#16360](jestjs/jest#16360))
- `[jest-haste-map]` Shut the worker farm down when a duplicate manual mock aborts the build under `throwOnModuleCollision` ([#16354](jestjs/jest#16354))
- `[jest-haste-map]` Attach the watchman client's `error` listener before the first command, so a watchman failure falls back to the node crawler instead of crashing on an unhandled `error` event, and always end the client ([#16355](jestjs/jest#16355))
- `[jest-haste-map]` Stop delivering watch events after `WatchmanWatcher` is closed, and route its warnings through the configured console ([#16355](jestjs/jest#16355))
- `[jest-haste-map]` Restore the nested `duplicates` index correctly in `ModuleMap.fromJSON`, so a haste collision reported inside a test worker raises `DuplicateHasteCandidatesError` instead of a `TypeError` ([#16353](jestjs/jest#16353))
- `[jest-haste-map]` Match watched files on a full extension, so `moduleFileExtensions: ['js']` no longer accepts `foo.mjs` ([#16352](jestjs/jest#16352))
- `[jest-haste-map]` Delimit the fields that make up the haste map cache key, so two different option sets cannot hash to the same cache file ([#16352](jestjs/jest#16352))
- `[jest-message-util]` Print the inner errors of an `AggregateError` thrown inside a test ([#16316](jestjs/jest#16316))
- `[jest-message-util]` Indent nested `cause` and `AggregateError` sections of a test failure by one level per depth, so the nesting is legible instead of rendering flat ([#16316](jestjs/jest#16316))
- `[jest-message-util]` Color stack traces line by line so blank lines stay blank ([#16316](jestjs/jest#16316))
- `[jest-message-util]` Detect Jest's own frames without assuming the checkout directory's name, and cover `@jest/*` packages, so stack traces and code frames point at user code ([#16326](jestjs/jest#16326))
- `[jest-mock]` `mockResolvedValue` / `mockRejectedValue` now see all overload return types, so a Promise-returning overload survives even when a later overload returns a non-Promise (e.g. `pg.Client['end']`) ([#16237](jestjs/jest#16237))
- `[@jest-environment/jsdom-abstract]` Make `@types/jsdom` a peer dependency ([#16166](jestjs/jest#16166))
- `[jest-mock]` Remove the leftover own accessor descriptor when restoring a `spyOn` of an inherited getter or setter, so the instance keeps reflecting the prototype ([#16226](jestjs/jest#16226))
- `[jest-resolve]` Include `extensionsToTreatAsEsm` in the `shouldLoadAsEsm` cache key, so projects with different extension lists don't read each other's answers ([#16369](jestjs/jest#16369))
- `[jest-resolve]` Make `getModuleIDAsync` build and cache `data:` URI module IDs the same way as `getModuleID` ([#16370](jestjs/jest#16370))
- `[jest-resolve]` Keep the `node:` prefix when resolving a core module asynchronously, so a builtin that only exists prefixed (`node:sea`, `node:sqlite`, `node:test`, `node:test/reporters`) resolves instead of failing as a missing bare package ([#16388](jestjs/jest#16388))
- `[jest-resolve]` Look up manual mocks for `node:` protocol specifiers under the unprefixed name they are stored as ([#16388](jestjs/jest#16388))
- `[jest-resolve]` Apply `moduleNameMapper` consistently to both spellings of core module specifiers (`fs` vs `node:fs`) ([#16390](jestjs/jest#16390))
- `[jest-resolve]` Keep virtual and ordinary mock module IDs isolated across test files ([#16296](jestjs/jest#16296))
- `[jest-resolve]` Guard missing `require.resolve.paths` ([#16052](jestjs/jest#16052))
- `[jest-resolve, jest-config, jest-runner]` Support a user resolver written as an ES module ([#16332](jestjs/jest#16332))
- `[jest-resolve, jest-runtime]` Throw the CJS parse error for ESM syntax in a `"type": "commonjs"` package or a `.cjs` file instead of loading it as ESM, matching Node ([#16368](jestjs/jest#16368))
- `[@jest/source-map]` Keep source map sources that name a scheme, such as `webpack:///`, instead of resolving them into a path that does not exist ([#16327](jestjs/jest#16327))
- `[@jest/source-map]` Look up `--testLocationInResults` positions at the right column, and keep a mapping to the first column instead of discarding it ([#16327](jestjs/jest#16327))
- `[@jest/source-map]` Warn when a source map cannot be parsed, instead of silently leaving its frames untranslated ([#16327](jestjs/jest#16327))
- `[jest-runner, @jest/source-map]` Keep a source-mapped stack for an error thrown after the test environment was torn down ([#16327](jestjs/jest#16327))
- `[jest-runtime, @jest/source-map]` Keep source maps past teardown and past the next test file's `install`, so a stack from a file no earlier stack mentioned still points at the original source ([#16330](jestjs/jest#16330))
- `[jest-runtime]` Report that no coverage was collected when `getAllV8CoverageInfoCopy` is called after `teardown`, instead of returning an empty result ([#16385](jestjs/jest#16385))
- `[jest-runtime]` Cache a CJS module's parsed exports before walking its re-exports, so two modules that re-export each other no longer overflow the stack when imported from ESM ([#16363](jestjs/jest#16363))
- `[jest-runtime]` Keep a re-exported ES module's parse failure from marking the re-exporting CommonJS file as ESM, so `module.exports = require('./dep.mjs')` loads instead of failing with `module is not defined` ([#16363](jestjs/jest#16363))
- `[jest-runtime]` Scope module mocks instantiated inside `jest.isolateModules`/`isolateModulesAsync` to that block, so a mock first imported there no longer outlives it - matching how CommonJS mocks already behave ([#16365](jestjs/jest#16365))
- `[jest-runtime]` Suspend module isolation while generating an automock, so loading the real module to read its shape no longer populates the isolated registry ([#16365](jestjs/jest#16365))
- `[jest-runtime]` Check a cached ES module's status before `require()` returns it, so a module whose evaluation threw rethrows that error and one left linked by a failed sibling is evaluated instead of returning uninitialized bindings ([#16364](jestjs/jest#16364))
- `[jest-runtime]` Report the original `ERR_REQUIRE_ASYNC_MODULE` when a `require()` of a top-level-await graph is retried, instead of a spurious "concurrent `import()`" error ([#16364](jestjs/jest#16364))
- `[jest-runtime]` Throw the evaluation error when another caller's `import()` of the same module failed while we awaited it, instead of resolving with the errored module ([#16364](jestjs/jest#16364))
- `[jest-runtime]` Mark the result of `require()`ing an ES module that has a default export with `__esModule: true` through a live-binding facade, and serve the same object from `require.cache`, matching Node ([#16367](jestjs/jest#16367))
- `[jest-runtime]` Provide a CommonJS module's exports under the `'module.exports'` named export when imported from ESM, matching Node 23+ ([#16367](jestjs/jest#16367))
- `[jest-runtime]` Give the test file itself a non-null `require.main` ([#16367](jestjs/jest#16367))
- `[jest-runtime]` Populate `module.children` with the modules a file loads, matching Node ([#16368](jestjs/jest#16368))
- `[jest-runtime]` Provide `import.meta.resolve` and `import.meta.jest` in `data:` URI modules, accept any-case mediatype parameters, and use Node's error codes for invalid `data:` URIs ([#16368](jestjs/jest#16368))
- `[jest-runtime]` Key ES modules by full URL, so query and fragment suffixes create the same module instances as Node and show up in `import.meta.url` ([#16375](jestjs/jest#16375))
- `[jest-runtime]` Share modules between overlapping graphs when a CommonJS module `require()`s an ES module mid-load, instead of evaluating shared dependencies twice ([#16375](jestjs/jest#16375))
- `[jest-runtime]` Throw `ERR_REQUIRE_CYCLE_MODULE` like Node when a CommonJS module `require()`s an ES module that is still being loaded, instead of evaluating the module a second time ([#16366](jestjs/jest#16366))
- `[jest-runtime]` Key builtin modules in the ESM registry by one canonical specifier ([#16341](jestjs/jest#16341))
- `[jest-runtime]` `import.meta.resolve()` for a builtin uses its `node:` specifier ([#16341](jestjs/jest#16341))
- `[jest-runtime]` Fall back to native ESM when a `.js` file contains ESM syntax but has no `"type":"module"` marker ([#16152](jestjs/jest#16152))
- `[jest-runtime]` Allow `require()` of ESM-marked files on Node < 24.9 via transform fallback ([#16244](jestjs/jest#16244))
- `[jest-runtime, @jest/transform]` Surface actionable `ERR_REQUIRE_ESM` error for files with untransformed ESM syntax instead of the generic "unexpected token" message ([#16244](jestjs/jest#16244))
- `[jest-runtime]` Support older test environments whose `moduleMocker` does not implement `clearMocksOnScope` ([#16169](jestjs/jest#16169))
- `[jest-runtime]` Apply `jest.unstable_mockModule` when the mocked file itself is `require()`d, not only when it is imported as a dependency ([#16389](jestjs/jest#16389))
- `[jest-runtime]` Apply `jest.unstable_mockModule` to statically imported `data:` URIs on Node 24.9+, matching dynamic `import()` ([#16389](jestjs/jest#16389))
- `[jest-runtime]` Run an async `jest.unstable_mockModule` factory once per module instead of twice, and fail the import instead of crashing the worker when the factory rejects ([#16389](jestjs/jest#16389))
- `[jest-runtime]` Hide a `require(esm)` module that failed to evaluate from `require.cache`, as Node does, instead of exposing a namespace with uninitialized bindings ([#16389](jestjs/jest#16389))
- `[jest-runtime]` Strip the byte-order mark when importing a JSON module, matching `require()` and Node ([#16389](jestjs/jest#16389))
- `[jest-runtime]` Throw `ERR_REQUIRE_ASYNC_MODULE` when `require(esm)` runs under an async-only custom resolver, instead of silently resolving with the default resolver ([#16389](jestjs/jest#16389))
- `[jest-runtime]` Parse imported JSON modules with the test realm's `JSON`, so their objects pass `instanceof Object` inside the test like `require()`d JSON does ([#16389](jestjs/jest#16389))
- `[jest-runtime]` Accept every `file:` URL string in the sandboxed `module.createRequire`, including one with a `localhost` authority, as Node does ([#16389](jestjs/jest#16389))
- `[jest-runtime]` Point at `{virtual: true}` when `jest.mock` or `jest.unstable_mockModule` is given a module that cannot be resolved ([#16389](jestjs/jest#16389))
- `[jest-reporters]` Fix coverage report table formatting in CI/GitHub Actions environments where `process.stdout.columns` is undefined by falling back to the `COLUMNS` env var or `80` columns in CI, preserving existing behaviour in other non-TTY environments ([#16227](jestjs/jest#16227))
- `[jest-runtime]` Support CJS-in-ESM exports via `"module.exports"` named exports ([#16277](jestjs/jest#16277))
- `[jest-snapshot]` Keep a skipped or failed test's hinted snapshots, instead of reporting them obsolete ([#16348](jestjs/jest#16348))
- `[jest-util]` Stop `globsToMatcher` reusing a cached matcher compiled with different picomatch options, and keep its `dot: true` default when `dot` is passed as `undefined` ([#16381](jestjs/jest#16381))
- `[pretty-format]` Move the `react-is` aliases into the `@jest` scope, so they cannot be shadowed by unrelated packages published under the alias names ([#16333](jestjs/jest#16333))

##### Chore & Maintenance

- `[docs]` Document the intentional divergences from Node's module system in the ECMAScript Modules page ([#16368](jestjs/jest#16368))
- `[docs]` Note deprecation of `react-test-renderer` in React Native tutorial and `pretty-format` README ([#16294](jestjs/jest#16294))
- `[docs]` Use `@testing-library/react-native` in the React Native tutorial instead of the deprecated `react-test-renderer` ([#16318](jestjs/jest#16318))
- `[babel-jest, @jest/transform]` Update `babel-plugin-istanbul` to v8 ([#16049](jestjs/jest#16049))
- `[jest-config, @jest/reporters, jest-runtime]` Update `glob` to v13 ([#16397](jestjs/jest#16397))
- `[jest-haste-map]` Refactor massive class into multiple files ([#16180](jestjs/jest#16180))
- `[jest-haste-map]` Drop `walker` dependency; replace hand-rolled directory recursion in the JS crawler and watcher startup with `fdir` ([#16187](jestjs/jest#16187))
- `[jest-haste-map]` Reuse cached metadata for files whose haste name is a known duplicate, instead of re-reading and re-parsing them on every startup ([#16351](jestjs/jest#16351))
- `[jest-haste-map]` Cache the watchman socket path and replace the `watchman --version` probe with `get-sockname`, so warm runs spawn no watchman processes ([#16386](jestjs/jest#16386))
- `[jest-resolve]` Store the per-directory package-type lookup in the cache it reads, so it actually memoizes ([#16369](jestjs/jest#16369))
- `[jest-resolve, jest-runtime]` Cut repeated work on the resolution hot path: hoist the platform-extension list to construction, memoize `isCoreModule` and the options cache-key serialization, skip mapper preparation when no `moduleNameMapper` is configured, run each mapper regex once, and stop re-parsing `NODE_OPTIONS` on every default-resolver call ([#16371](jestjs/jest#16371))
- `[jest-resolve]` Cut warm resolution cost to about a third: reuse one `unrs-resolver` factory per options shape instead of cloning per resolution, compose the factory cache key from per-array cached strings instead of serializing options, and stop constructing an `Error` for misses that `findNodeModule` swallows; add a `__benchmarks__` suite for the default resolver ([#16373](jestjs/jest#16373))
- `[jest-runner, @jest/source-map]` Replace `source-map-support` with an implementation in `@jest/source-map` ([#16327](jestjs/jest#16327))
- `[jest-snapshot]` Load babel, semver and synckit lazily, so requiring the package (which every test process does through `@jest/expect`) no longer loads \~200 modules that only writing inline snapshots needs ([#16387](jestjs/jest#16387))
- `[jest-runtime]` Reduce per-require overhead: skip module ID resolution when no mock can apply, answer core modules before probing for a manual mock, share one `require.cache` proxy across modules, and cache empty files ([#16376](jestjs/jest#16376))
- `[@jest/source-map]` Deprecate `getCallsite` in favour of `SourceMapSupport#getCallsite` ([#16327](jestjs/jest#16327))
- `[jest-runtime]` Avoid magical `null` value in ESM loader ([#16160](jestjs/jest#16160))
renovate Bot added a commit to andrei-picus-tink/auto-renovate that referenced this pull request Sep 6, 2026
| datasource | package | from   | to     |
| ---------- | ------- | ------ | ------ |
| npm        | jest    | 30.4.2 | 30.5.1 |


## [v30.5.1](https://github.com/jestjs/jest/blob/HEAD/CHANGELOG.md#3051)

##### Fixes

- `[jest-config]` Don't warn about global-only options in the config that supplies the global config - the root config a project resolves to, or the first entry of `--projects` when no root config is passed ([#16411](jestjs/jest#16411))
- `[jest-config, jest-types]` Stop accepting `reporters`, `coverageReporters`, `workerIdleMemoryLimit`, `cwd` and `runnerOptions` in a project config - they were silently ignored, and now warn like the other global-only options ([#16411](jestjs/jest#16411))
- `[jest-config, jest-validate]` Warn about `maxWorkers` and `coverageThreshold` in a project config instead of dropping them without a word ([#16411](jestjs/jest#16411))
- `[jest-resolve]` Match `moduleNameMapper` patterns against the specifier as written again (reverting [#16390](jestjs/jest#16390)) ([#16417](jestjs/jest#16417))
- `[jest-runtime]` Resolve package `imports` specifiers like `#dep` under ESM again ([#16413](jestjs/jest#16413))

##### Chore & Maintenance

- `[jest-util]` Name the `testEnvironmentOptions.globalsCleanup` option and link the docs from the `JEST-01` deprecation warning, and document the option's modes ([#16404](jestjs/jest#16404))


## [v30.5.0](https://github.com/jestjs/jest/blob/HEAD/CHANGELOG.md#3050)

##### Features

- `[@jest/expect-utils, jest-mock]` Add `mockFn.whenCalledWith(...args)` for configuring return values per argument list, with first-class asymmetric-matcher support ([#16053](jestjs/jest#16053))
- `[@jest/expect-utils]` Export `AsymmetricMatcher` and `FunctionParameters` types (previously private to `expect`) ([#16053](jestjs/jest#16053))
- `[jest-circus, jest-core, jest-jasmine2, jest-test-result, jest-types]` `--collectTests` now expands `test.each`/`describe.each` cases and reports per-status counts (skipped/todo via the new `wouldRun` flag for selected tests) plus a summary line that match a real run, including under `--testNamePattern` and `.only`/`fdescribe` focus on both the circus and jasmine2 runners ([#16259](jestjs/jest#16259))
- `[jest-circus, jest-environment, jest-runtime, jest-types]` Add describe-level retries via `jest.retryTimes(..., {entireDescribe: true})` ([#16322](jestjs/jest#16322))
- `[jest-circus, jest-message-util, jest-reporters, jest-types]` Add `retryMessages` to `AssertionResult` and export `formatErrorStack`, so the retry log renders nested `cause` and `AggregateError` sections with code frames instead of serialized `[cause]:`/`[errors]:` markers ([#16316](jestjs/jest#16316))
- `[jest-circus, jest-types]` Add `unhandledErrorsDetailed` to `Circus.RunResult`, so an unhandled rejection reports its `cause` chain and `AggregateError` entries with code frames instead of a pre-serialized stack ([#16316](jestjs/jest#16316))
- `[jest-haste-map]` Replace `NodeWatcher` and `FSEventsWatcher` with `@parcel/watcher` for the non-watchman watch path ([#16188](jestjs/jest#16188))
- `[jest-resolve]` Bump `unrs-resolver` to 1.12.1, remove `jest-pnp-resolver` and unnecessary checks ([#15721](jestjs/jest#15721))
- `[jest-resolve]` Honor Node's `--preserve-symlinks` / `NODE_PRESERVE_SYMLINKS` in the default resolver by passing `symlinks: false` to `unrs-resolver` ([#16260](jestjs/jest#16260))
- `[jest-runtime]` Apply automocking and manual `__mocks__` files to synchronously evaluable ESM graphs on Node 24.9+ - static imports, dynamic `import()` and `require()` of an ESM file now generate an automock from the real module's namespace instead of failing with "Attempting to import a mock without a factory". Graphs that need async evaluation (top-level await) or an async-only resolver or transformer still throw ([#16391](jestjs/jest#16391))
- `[jest-runtime]` Route `process.getBuiltinModule` through the sandbox, so it returns the sandbox `process` and the hooked `node:module` instead of the host's ([#16391](jestjs/jest#16391))
- `[jest-runtime]` Throw an actionable error from `module.register()` and `module.registerHooks()` inside a test - the hooks attached to the loader running Jest itself, never saw the sandboxed requires they were meant for, and stayed registered for every later test file in the worker ([#16391](jestjs/jest#16391))
- `[jest-runtime]` Surface resolution and import-attribute errors in an ESM graph before executing any of its CJS dependencies on Node 24.9+, matching Node's run-nothing-on-a-broken-graph behavior; the legacy loader on older versions keeps its linking-time execution order ([#16391](jestjs/jest#16391))
- `[jest-runtime]` Throw `ERR_SOURCE_PHASE_NOT_DEFINED` with an actionable message for `import source` and `import.source()`, instead of failing at instantiation with V8's bare "Source phase import object is not defined" ([#16391](jestjs/jest#16391))
- `[jest-runtime]` Emit the JSON-without-import-attribute deprecation warning once per test file instead of once per worker, so it is no longer silently swallowed for every file after the first ([#16391](jestjs/jest#16391))
- `[jest-runtime]` Set `import.meta.main` to `true` in the test file and `false` in every module it loads, matching Node 24+ ([#16367](jestjs/jest#16367))
- `[jest-runtime]` Resolve the `module-sync` export condition, so a package that exposes its ESM entry point for `require()` loads the same file Node would ([#16336](jestjs/jest#16336))
- `[jest-snapshot]` Add external snapshot paths to custom reporter failure details ([#16374](jestjs/jest#16374))

##### Fixes

- `[jest-console, jest-reporters]` `CustomConsole` now buffers console output so `TestResult.console` is populated for reporters when `verbose` is enabled, while `GitHubActionsReporter` avoids replaying buffered output in verbose mode ([#16155](jestjs/jest#16155))
- `[expect, jest-message-util, jest-pattern, jest-regex-util, jest-util]` Revert `node:` protocol imports to restore webpack/browser-bundle compatibility ([#16167](jestjs/jest#16167))
- `[expect]` Widen `toMatchObject` and `objectContaining` parameter type from `Record<string, unknown>` to `object` so class instances are accepted ([#16196](jestjs/jest#16196))
- `[jest-circus]` Call a generator test body with the shared test context, so `this` matches what a regular test function receives ([#16347](jestjs/jest#16347))
- `[jest-circus]` Capture the error listeners of the parent process instead of the in-sandbox `process`, so listeners registered before the test file survive teardown and sandbox listeners no longer leak onto the parent ([#16347](jestjs/jest#16347))
- `[jest-circus]` Clear `currentlyRunningTest` after skipped and todo tests ([#16342](jestjs/jest#16342))
- `[jest-circus]` Prevent late `done()` callbacks from affecting later test or hook invocations ([#16343](jestjs/jest#16343))
- `[jest-circus, jest-jasmine2]` Honor `--expand` when formatting `node:assert` failures, instead of always collapsing the diff ([#16347](jestjs/jest#16347))
- `[jest-circus, jest-jasmine2, jest-message-util]` Serialize the inner errors of an `AggregateError` into `failureMessages`, `retryReasons` and `unhandledErrors`, so `--json` output and reporter annotations include them ([#16316](jestjs/jest#16316))
- `[jest-circus, jest-snapshot]` Keep snapshot state and counts correct when a test retries ([#16344](jestjs/jest#16344))
- `[@jest/create-cache-key-function]` Include the caller support flags in the generated key, so a transformer that emits ESM or CJS based on them no longer shares one cache entry between the two ([#16331](jestjs/jest#16331))
- `[@jest/create-cache-key-function]` Include the stringified project config in the generated key, so editing a transformer's own settings invalidates what it cached ([#16331](jestjs/jest#16331))
- `[@jest/transform]` Include the caller support flags in a transform's cache key, so a file transformed both as ESM and as CJS no longer serves one shape's output for the other ([#16331](jestjs/jest#16331))
- `[jest-config]` Add missing `findRelatedTests`, `outputFile`, and `replname` entries to `ValidConfig` so they no longer trigger spurious "Unknown option" warnings ([#16224](jestjs/jest#16224))
- `[jest-config]` Use `--config` for the global config when multiple `--projects` are specified ([#16273](jestjs/jest#16273))
- `[jest-core]` Serialize `bigint` values in `--json` and `--outputFile` output as their literal form (`4n`), instead of failing the run with `TypeError: Do not know how to serialize a BigInt` ([#16338](jestjs/jest#16338))
- `[jest-core]` Do not report a `CustomGC` async resource (used by N-API addons such as napi-rs for per-isolate GC bookkeeping) as an open handle, since it is `napi_unref`'d by the addon and can never keep the event loop alive ([#16379](jestjs/jest#16379))
- `[jest-each]` Keep a `$&`, `` $` ``, `$'` or `$$` inside a `%p` param value out of the replacement, so the title shows the value instead of the text around it ([#16338](jestjs/jest#16338))
- `[jest-each]` Interpolate a `bigint` into a `%j` title as its literal form (`"4n"`) at any depth, instead of throwing `TypeError: Do not know how to serialize a BigInt` while collecting the tests ([#16338](jestjs/jest#16338))
- `[jest-environment, jest-runtime]` Bind `sandboxInjectedGlobals` to the right values when `injectGlobals` is `false`, instead of shifting every one of them by a position ([#16377](jestjs/jest#16377))
- `[jest-environment-node, jest-util]` Only warn about a conflicting `globalsCleanup` mode when one was explicitly configured, and follow the mode that is actually in effect ([#16323](jestjs/jest#16323))
- `[jest-environment-node, jest-util]` Stop resolving lazy globals when setting up an environment, so Node 26's builtin module globals are no longer loaded (and no longer emit their deprecation warnings) for every test file ([#16324](jestjs/jest#16324))
- `[jest-haste-map]` Keep watch mode alive when an outside process briefly makes a file unreadable on Windows, instead of tearing the watcher down on `EPERM` ([#16295](jestjs/jest#16295))
- `[jest-haste-map]` Keep indexing when an outside process holds a file open on Windows, instead of failing the whole crawl on `EPERM` ([#16358](jestjs/jest#16358))
- `[jest-haste-map]` Keep a duplicated manual mock resolving when the file it pointed at is deleted in watch mode ([#16360](jestjs/jest#16360))
- `[jest-haste-map]` Shut the worker farm down when a duplicate manual mock aborts the build under `throwOnModuleCollision` ([#16354](jestjs/jest#16354))
- `[jest-haste-map]` Attach the watchman client's `error` listener before the first command, so a watchman failure falls back to the node crawler instead of crashing on an unhandled `error` event, and always end the client ([#16355](jestjs/jest#16355))
- `[jest-haste-map]` Stop delivering watch events after `WatchmanWatcher` is closed, and route its warnings through the configured console ([#16355](jestjs/jest#16355))
- `[jest-haste-map]` Restore the nested `duplicates` index correctly in `ModuleMap.fromJSON`, so a haste collision reported inside a test worker raises `DuplicateHasteCandidatesError` instead of a `TypeError` ([#16353](jestjs/jest#16353))
- `[jest-haste-map]` Match watched files on a full extension, so `moduleFileExtensions: ['js']` no longer accepts `foo.mjs` ([#16352](jestjs/jest#16352))
- `[jest-haste-map]` Delimit the fields that make up the haste map cache key, so two different option sets cannot hash to the same cache file ([#16352](jestjs/jest#16352))
- `[jest-message-util]` Print the inner errors of an `AggregateError` thrown inside a test ([#16316](jestjs/jest#16316))
- `[jest-message-util]` Indent nested `cause` and `AggregateError` sections of a test failure by one level per depth, so the nesting is legible instead of rendering flat ([#16316](jestjs/jest#16316))
- `[jest-message-util]` Color stack traces line by line so blank lines stay blank ([#16316](jestjs/jest#16316))
- `[jest-message-util]` Detect Jest's own frames without assuming the checkout directory's name, and cover `@jest/*` packages, so stack traces and code frames point at user code ([#16326](jestjs/jest#16326))
- `[jest-mock]` `mockResolvedValue` / `mockRejectedValue` now see all overload return types, so a Promise-returning overload survives even when a later overload returns a non-Promise (e.g. `pg.Client['end']`) ([#16237](jestjs/jest#16237))
- `[@jest-environment/jsdom-abstract]` Make `@types/jsdom` a peer dependency ([#16166](jestjs/jest#16166))
- `[jest-mock]` Remove the leftover own accessor descriptor when restoring a `spyOn` of an inherited getter or setter, so the instance keeps reflecting the prototype ([#16226](jestjs/jest#16226))
- `[jest-resolve]` Include `extensionsToTreatAsEsm` in the `shouldLoadAsEsm` cache key, so projects with different extension lists don't read each other's answers ([#16369](jestjs/jest#16369))
- `[jest-resolve]` Make `getModuleIDAsync` build and cache `data:` URI module IDs the same way as `getModuleID` ([#16370](jestjs/jest#16370))
- `[jest-resolve]` Keep the `node:` prefix when resolving a core module asynchronously, so a builtin that only exists prefixed (`node:sea`, `node:sqlite`, `node:test`, `node:test/reporters`) resolves instead of failing as a missing bare package ([#16388](jestjs/jest#16388))
- `[jest-resolve]` Look up manual mocks for `node:` protocol specifiers under the unprefixed name they are stored as ([#16388](jestjs/jest#16388))
- `[jest-resolve]` Apply `moduleNameMapper` consistently to both spellings of core module specifiers (`fs` vs `node:fs`) ([#16390](jestjs/jest#16390))
- `[jest-resolve]` Keep virtual and ordinary mock module IDs isolated across test files ([#16296](jestjs/jest#16296))
- `[jest-resolve]` Guard missing `require.resolve.paths` ([#16052](jestjs/jest#16052))
- `[jest-resolve, jest-config, jest-runner]` Support a user resolver written as an ES module ([#16332](jestjs/jest#16332))
- `[jest-resolve, jest-runtime]` Throw the CJS parse error for ESM syntax in a `"type": "commonjs"` package or a `.cjs` file instead of loading it as ESM, matching Node ([#16368](jestjs/jest#16368))
- `[@jest/source-map]` Keep source map sources that name a scheme, such as `webpack:///`, instead of resolving them into a path that does not exist ([#16327](jestjs/jest#16327))
- `[@jest/source-map]` Look up `--testLocationInResults` positions at the right column, and keep a mapping to the first column instead of discarding it ([#16327](jestjs/jest#16327))
- `[@jest/source-map]` Warn when a source map cannot be parsed, instead of silently leaving its frames untranslated ([#16327](jestjs/jest#16327))
- `[jest-runner, @jest/source-map]` Keep a source-mapped stack for an error thrown after the test environment was torn down ([#16327](jestjs/jest#16327))
- `[jest-runtime, @jest/source-map]` Keep source maps past teardown and past the next test file's `install`, so a stack from a file no earlier stack mentioned still points at the original source ([#16330](jestjs/jest#16330))
- `[jest-runtime]` Report that no coverage was collected when `getAllV8CoverageInfoCopy` is called after `teardown`, instead of returning an empty result ([#16385](jestjs/jest#16385))
- `[jest-runtime]` Cache a CJS module's parsed exports before walking its re-exports, so two modules that re-export each other no longer overflow the stack when imported from ESM ([#16363](jestjs/jest#16363))
- `[jest-runtime]` Keep a re-exported ES module's parse failure from marking the re-exporting CommonJS file as ESM, so `module.exports = require('./dep.mjs')` loads instead of failing with `module is not defined` ([#16363](jestjs/jest#16363))
- `[jest-runtime]` Scope module mocks instantiated inside `jest.isolateModules`/`isolateModulesAsync` to that block, so a mock first imported there no longer outlives it - matching how CommonJS mocks already behave ([#16365](jestjs/jest#16365))
- `[jest-runtime]` Suspend module isolation while generating an automock, so loading the real module to read its shape no longer populates the isolated registry ([#16365](jestjs/jest#16365))
- `[jest-runtime]` Check a cached ES module's status before `require()` returns it, so a module whose evaluation threw rethrows that error and one left linked by a failed sibling is evaluated instead of returning uninitialized bindings ([#16364](jestjs/jest#16364))
- `[jest-runtime]` Report the original `ERR_REQUIRE_ASYNC_MODULE` when a `require()` of a top-level-await graph is retried, instead of a spurious "concurrent `import()`" error ([#16364](jestjs/jest#16364))
- `[jest-runtime]` Throw the evaluation error when another caller's `import()` of the same module failed while we awaited it, instead of resolving with the errored module ([#16364](jestjs/jest#16364))
- `[jest-runtime]` Mark the result of `require()`ing an ES module that has a default export with `__esModule: true` through a live-binding facade, and serve the same object from `require.cache`, matching Node ([#16367](jestjs/jest#16367))
- `[jest-runtime]` Provide a CommonJS module's exports under the `'module.exports'` named export when imported from ESM, matching Node 23+ ([#16367](jestjs/jest#16367))
- `[jest-runtime]` Give the test file itself a non-null `require.main` ([#16367](jestjs/jest#16367))
- `[jest-runtime]` Populate `module.children` with the modules a file loads, matching Node ([#16368](jestjs/jest#16368))
- `[jest-runtime]` Provide `import.meta.resolve` and `import.meta.jest` in `data:` URI modules, accept any-case mediatype parameters, and use Node's error codes for invalid `data:` URIs ([#16368](jestjs/jest#16368))
- `[jest-runtime]` Key ES modules by full URL, so query and fragment suffixes create the same module instances as Node and show up in `import.meta.url` ([#16375](jestjs/jest#16375))
- `[jest-runtime]` Share modules between overlapping graphs when a CommonJS module `require()`s an ES module mid-load, instead of evaluating shared dependencies twice ([#16375](jestjs/jest#16375))
- `[jest-runtime]` Throw `ERR_REQUIRE_CYCLE_MODULE` like Node when a CommonJS module `require()`s an ES module that is still being loaded, instead of evaluating the module a second time ([#16366](jestjs/jest#16366))
- `[jest-runtime]` Key builtin modules in the ESM registry by one canonical specifier ([#16341](jestjs/jest#16341))
- `[jest-runtime]` `import.meta.resolve()` for a builtin uses its `node:` specifier ([#16341](jestjs/jest#16341))
- `[jest-runtime]` Fall back to native ESM when a `.js` file contains ESM syntax but has no `"type":"module"` marker ([#16152](jestjs/jest#16152))
- `[jest-runtime]` Allow `require()` of ESM-marked files on Node < 24.9 via transform fallback ([#16244](jestjs/jest#16244))
- `[jest-runtime, @jest/transform]` Surface actionable `ERR_REQUIRE_ESM` error for files with untransformed ESM syntax instead of the generic "unexpected token" message ([#16244](jestjs/jest#16244))
- `[jest-runtime]` Support older test environments whose `moduleMocker` does not implement `clearMocksOnScope` ([#16169](jestjs/jest#16169))
- `[jest-runtime]` Apply `jest.unstable_mockModule` when the mocked file itself is `require()`d, not only when it is imported as a dependency ([#16389](jestjs/jest#16389))
- `[jest-runtime]` Apply `jest.unstable_mockModule` to statically imported `data:` URIs on Node 24.9+, matching dynamic `import()` ([#16389](jestjs/jest#16389))
- `[jest-runtime]` Run an async `jest.unstable_mockModule` factory once per module instead of twice, and fail the import instead of crashing the worker when the factory rejects ([#16389](jestjs/jest#16389))
- `[jest-runtime]` Hide a `require(esm)` module that failed to evaluate from `require.cache`, as Node does, instead of exposing a namespace with uninitialized bindings ([#16389](jestjs/jest#16389))
- `[jest-runtime]` Strip the byte-order mark when importing a JSON module, matching `require()` and Node ([#16389](jestjs/jest#16389))
- `[jest-runtime]` Throw `ERR_REQUIRE_ASYNC_MODULE` when `require(esm)` runs under an async-only custom resolver, instead of silently resolving with the default resolver ([#16389](jestjs/jest#16389))
- `[jest-runtime]` Parse imported JSON modules with the test realm's `JSON`, so their objects pass `instanceof Object` inside the test like `require()`d JSON does ([#16389](jestjs/jest#16389))
- `[jest-runtime]` Accept every `file:` URL string in the sandboxed `module.createRequire`, including one with a `localhost` authority, as Node does ([#16389](jestjs/jest#16389))
- `[jest-runtime]` Point at `{virtual: true}` when `jest.mock` or `jest.unstable_mockModule` is given a module that cannot be resolved ([#16389](jestjs/jest#16389))
- `[jest-reporters]` Fix coverage report table formatting in CI/GitHub Actions environments where `process.stdout.columns` is undefined by falling back to the `COLUMNS` env var or `80` columns in CI, preserving existing behaviour in other non-TTY environments ([#16227](jestjs/jest#16227))
- `[jest-runtime]` Support CJS-in-ESM exports via `"module.exports"` named exports ([#16277](jestjs/jest#16277))
- `[jest-snapshot]` Keep a skipped or failed test's hinted snapshots, instead of reporting them obsolete ([#16348](jestjs/jest#16348))
- `[jest-util]` Stop `globsToMatcher` reusing a cached matcher compiled with different picomatch options, and keep its `dot: true` default when `dot` is passed as `undefined` ([#16381](jestjs/jest#16381))
- `[pretty-format]` Move the `react-is` aliases into the `@jest` scope, so they cannot be shadowed by unrelated packages published under the alias names ([#16333](jestjs/jest#16333))

##### Chore & Maintenance

- `[docs]` Document the intentional divergences from Node's module system in the ECMAScript Modules page ([#16368](jestjs/jest#16368))
- `[docs]` Note deprecation of `react-test-renderer` in React Native tutorial and `pretty-format` README ([#16294](jestjs/jest#16294))
- `[docs]` Use `@testing-library/react-native` in the React Native tutorial instead of the deprecated `react-test-renderer` ([#16318](jestjs/jest#16318))
- `[babel-jest, @jest/transform]` Update `babel-plugin-istanbul` to v8 ([#16049](jestjs/jest#16049))
- `[jest-config, @jest/reporters, jest-runtime]` Update `glob` to v13 ([#16397](jestjs/jest#16397))
- `[jest-haste-map]` Refactor massive class into multiple files ([#16180](jestjs/jest#16180))
- `[jest-haste-map]` Drop `walker` dependency; replace hand-rolled directory recursion in the JS crawler and watcher startup with `fdir` ([#16187](jestjs/jest#16187))
- `[jest-haste-map]` Reuse cached metadata for files whose haste name is a known duplicate, instead of re-reading and re-parsing them on every startup ([#16351](jestjs/jest#16351))
- `[jest-haste-map]` Cache the watchman socket path and replace the `watchman --version` probe with `get-sockname`, so warm runs spawn no watchman processes ([#16386](jestjs/jest#16386))
- `[jest-resolve]` Store the per-directory package-type lookup in the cache it reads, so it actually memoizes ([#16369](jestjs/jest#16369))
- `[jest-resolve, jest-runtime]` Cut repeated work on the resolution hot path: hoist the platform-extension list to construction, memoize `isCoreModule` and the options cache-key serialization, skip mapper preparation when no `moduleNameMapper` is configured, run each mapper regex once, and stop re-parsing `NODE_OPTIONS` on every default-resolver call ([#16371](jestjs/jest#16371))
- `[jest-resolve]` Cut warm resolution cost to about a third: reuse one `unrs-resolver` factory per options shape instead of cloning per resolution, compose the factory cache key from per-array cached strings instead of serializing options, and stop constructing an `Error` for misses that `findNodeModule` swallows; add a `__benchmarks__` suite for the default resolver ([#16373](jestjs/jest#16373))
- `[jest-runner, @jest/source-map]` Replace `source-map-support` with an implementation in `@jest/source-map` ([#16327](jestjs/jest#16327))
- `[jest-snapshot]` Load babel, semver and synckit lazily, so requiring the package (which every test process does through `@jest/expect`) no longer loads \~200 modules that only writing inline snapshots needs ([#16387](jestjs/jest#16387))
- `[jest-runtime]` Reduce per-require overhead: skip module ID resolution when no mock can apply, answer core modules before probing for a manual mock, share one `require.cache` proxy across modules, and cache empty files ([#16376](jestjs/jest#16376))
- `[@jest/source-map]` Deprecate `getCallsite` in favour of `SourceMapSupport#getCallsite` ([#16327](jestjs/jest#16327))
- `[jest-runtime]` Avoid magical `null` value in ESM loader ([#16160](jestjs/jest#16160))
renovate Bot added a commit to andrei-picus-tink/auto-renovate that referenced this pull request Sep 6, 2026
| datasource | package | from   | to     |
| ---------- | ------- | ------ | ------ |
| npm        | jest    | 30.4.2 | 30.5.1 |


## [v30.5.1](https://github.com/jestjs/jest/blob/HEAD/CHANGELOG.md#3051)

##### Fixes

- `[jest-config]` Don't warn about global-only options in the config that supplies the global config - the root config a project resolves to, or the first entry of `--projects` when no root config is passed ([#16411](jestjs/jest#16411))
- `[jest-config, jest-types]` Stop accepting `reporters`, `coverageReporters`, `workerIdleMemoryLimit`, `cwd` and `runnerOptions` in a project config - they were silently ignored, and now warn like the other global-only options ([#16411](jestjs/jest#16411))
- `[jest-config, jest-validate]` Warn about `maxWorkers` and `coverageThreshold` in a project config instead of dropping them without a word ([#16411](jestjs/jest#16411))
- `[jest-resolve]` Match `moduleNameMapper` patterns against the specifier as written again (reverting [#16390](jestjs/jest#16390)) ([#16417](jestjs/jest#16417))
- `[jest-runtime]` Resolve package `imports` specifiers like `#dep` under ESM again ([#16413](jestjs/jest#16413))

##### Chore & Maintenance

- `[jest-util]` Name the `testEnvironmentOptions.globalsCleanup` option and link the docs from the `JEST-01` deprecation warning, and document the option's modes ([#16404](jestjs/jest#16404))


## [v30.5.0](https://github.com/jestjs/jest/blob/HEAD/CHANGELOG.md#3050)

##### Features

- `[@jest/expect-utils, jest-mock]` Add `mockFn.whenCalledWith(...args)` for configuring return values per argument list, with first-class asymmetric-matcher support ([#16053](jestjs/jest#16053))
- `[@jest/expect-utils]` Export `AsymmetricMatcher` and `FunctionParameters` types (previously private to `expect`) ([#16053](jestjs/jest#16053))
- `[jest-circus, jest-core, jest-jasmine2, jest-test-result, jest-types]` `--collectTests` now expands `test.each`/`describe.each` cases and reports per-status counts (skipped/todo via the new `wouldRun` flag for selected tests) plus a summary line that match a real run, including under `--testNamePattern` and `.only`/`fdescribe` focus on both the circus and jasmine2 runners ([#16259](jestjs/jest#16259))
- `[jest-circus, jest-environment, jest-runtime, jest-types]` Add describe-level retries via `jest.retryTimes(..., {entireDescribe: true})` ([#16322](jestjs/jest#16322))
- `[jest-circus, jest-message-util, jest-reporters, jest-types]` Add `retryMessages` to `AssertionResult` and export `formatErrorStack`, so the retry log renders nested `cause` and `AggregateError` sections with code frames instead of serialized `[cause]:`/`[errors]:` markers ([#16316](jestjs/jest#16316))
- `[jest-circus, jest-types]` Add `unhandledErrorsDetailed` to `Circus.RunResult`, so an unhandled rejection reports its `cause` chain and `AggregateError` entries with code frames instead of a pre-serialized stack ([#16316](jestjs/jest#16316))
- `[jest-haste-map]` Replace `NodeWatcher` and `FSEventsWatcher` with `@parcel/watcher` for the non-watchman watch path ([#16188](jestjs/jest#16188))
- `[jest-resolve]` Bump `unrs-resolver` to 1.12.1, remove `jest-pnp-resolver` and unnecessary checks ([#15721](jestjs/jest#15721))
- `[jest-resolve]` Honor Node's `--preserve-symlinks` / `NODE_PRESERVE_SYMLINKS` in the default resolver by passing `symlinks: false` to `unrs-resolver` ([#16260](jestjs/jest#16260))
- `[jest-runtime]` Apply automocking and manual `__mocks__` files to synchronously evaluable ESM graphs on Node 24.9+ - static imports, dynamic `import()` and `require()` of an ESM file now generate an automock from the real module's namespace instead of failing with "Attempting to import a mock without a factory". Graphs that need async evaluation (top-level await) or an async-only resolver or transformer still throw ([#16391](jestjs/jest#16391))
- `[jest-runtime]` Route `process.getBuiltinModule` through the sandbox, so it returns the sandbox `process` and the hooked `node:module` instead of the host's ([#16391](jestjs/jest#16391))
- `[jest-runtime]` Throw an actionable error from `module.register()` and `module.registerHooks()` inside a test - the hooks attached to the loader running Jest itself, never saw the sandboxed requires they were meant for, and stayed registered for every later test file in the worker ([#16391](jestjs/jest#16391))
- `[jest-runtime]` Surface resolution and import-attribute errors in an ESM graph before executing any of its CJS dependencies on Node 24.9+, matching Node's run-nothing-on-a-broken-graph behavior; the legacy loader on older versions keeps its linking-time execution order ([#16391](jestjs/jest#16391))
- `[jest-runtime]` Throw `ERR_SOURCE_PHASE_NOT_DEFINED` with an actionable message for `import source` and `import.source()`, instead of failing at instantiation with V8's bare "Source phase import object is not defined" ([#16391](jestjs/jest#16391))
- `[jest-runtime]` Emit the JSON-without-import-attribute deprecation warning once per test file instead of once per worker, so it is no longer silently swallowed for every file after the first ([#16391](jestjs/jest#16391))
- `[jest-runtime]` Set `import.meta.main` to `true` in the test file and `false` in every module it loads, matching Node 24+ ([#16367](jestjs/jest#16367))
- `[jest-runtime]` Resolve the `module-sync` export condition, so a package that exposes its ESM entry point for `require()` loads the same file Node would ([#16336](jestjs/jest#16336))
- `[jest-snapshot]` Add external snapshot paths to custom reporter failure details ([#16374](jestjs/jest#16374))

##### Fixes

- `[jest-console, jest-reporters]` `CustomConsole` now buffers console output so `TestResult.console` is populated for reporters when `verbose` is enabled, while `GitHubActionsReporter` avoids replaying buffered output in verbose mode ([#16155](jestjs/jest#16155))
- `[expect, jest-message-util, jest-pattern, jest-regex-util, jest-util]` Revert `node:` protocol imports to restore webpack/browser-bundle compatibility ([#16167](jestjs/jest#16167))
- `[expect]` Widen `toMatchObject` and `objectContaining` parameter type from `Record<string, unknown>` to `object` so class instances are accepted ([#16196](jestjs/jest#16196))
- `[jest-circus]` Call a generator test body with the shared test context, so `this` matches what a regular test function receives ([#16347](jestjs/jest#16347))
- `[jest-circus]` Capture the error listeners of the parent process instead of the in-sandbox `process`, so listeners registered before the test file survive teardown and sandbox listeners no longer leak onto the parent ([#16347](jestjs/jest#16347))
- `[jest-circus]` Clear `currentlyRunningTest` after skipped and todo tests ([#16342](jestjs/jest#16342))
- `[jest-circus]` Prevent late `done()` callbacks from affecting later test or hook invocations ([#16343](jestjs/jest#16343))
- `[jest-circus, jest-jasmine2]` Honor `--expand` when formatting `node:assert` failures, instead of always collapsing the diff ([#16347](jestjs/jest#16347))
- `[jest-circus, jest-jasmine2, jest-message-util]` Serialize the inner errors of an `AggregateError` into `failureMessages`, `retryReasons` and `unhandledErrors`, so `--json` output and reporter annotations include them ([#16316](jestjs/jest#16316))
- `[jest-circus, jest-snapshot]` Keep snapshot state and counts correct when a test retries ([#16344](jestjs/jest#16344))
- `[@jest/create-cache-key-function]` Include the caller support flags in the generated key, so a transformer that emits ESM or CJS based on them no longer shares one cache entry between the two ([#16331](jestjs/jest#16331))
- `[@jest/create-cache-key-function]` Include the stringified project config in the generated key, so editing a transformer's own settings invalidates what it cached ([#16331](jestjs/jest#16331))
- `[@jest/transform]` Include the caller support flags in a transform's cache key, so a file transformed both as ESM and as CJS no longer serves one shape's output for the other ([#16331](jestjs/jest#16331))
- `[jest-config]` Add missing `findRelatedTests`, `outputFile`, and `replname` entries to `ValidConfig` so they no longer trigger spurious "Unknown option" warnings ([#16224](jestjs/jest#16224))
- `[jest-config]` Use `--config` for the global config when multiple `--projects` are specified ([#16273](jestjs/jest#16273))
- `[jest-core]` Serialize `bigint` values in `--json` and `--outputFile` output as their literal form (`4n`), instead of failing the run with `TypeError: Do not know how to serialize a BigInt` ([#16338](jestjs/jest#16338))
- `[jest-core]` Do not report a `CustomGC` async resource (used by N-API addons such as napi-rs for per-isolate GC bookkeeping) as an open handle, since it is `napi_unref`'d by the addon and can never keep the event loop alive ([#16379](jestjs/jest#16379))
- `[jest-each]` Keep a `$&`, `` $` ``, `$'` or `$$` inside a `%p` param value out of the replacement, so the title shows the value instead of the text around it ([#16338](jestjs/jest#16338))
- `[jest-each]` Interpolate a `bigint` into a `%j` title as its literal form (`"4n"`) at any depth, instead of throwing `TypeError: Do not know how to serialize a BigInt` while collecting the tests ([#16338](jestjs/jest#16338))
- `[jest-environment, jest-runtime]` Bind `sandboxInjectedGlobals` to the right values when `injectGlobals` is `false`, instead of shifting every one of them by a position ([#16377](jestjs/jest#16377))
- `[jest-environment-node, jest-util]` Only warn about a conflicting `globalsCleanup` mode when one was explicitly configured, and follow the mode that is actually in effect ([#16323](jestjs/jest#16323))
- `[jest-environment-node, jest-util]` Stop resolving lazy globals when setting up an environment, so Node 26's builtin module globals are no longer loaded (and no longer emit their deprecation warnings) for every test file ([#16324](jestjs/jest#16324))
- `[jest-haste-map]` Keep watch mode alive when an outside process briefly makes a file unreadable on Windows, instead of tearing the watcher down on `EPERM` ([#16295](jestjs/jest#16295))
- `[jest-haste-map]` Keep indexing when an outside process holds a file open on Windows, instead of failing the whole crawl on `EPERM` ([#16358](jestjs/jest#16358))
- `[jest-haste-map]` Keep a duplicated manual mock resolving when the file it pointed at is deleted in watch mode ([#16360](jestjs/jest#16360))
- `[jest-haste-map]` Shut the worker farm down when a duplicate manual mock aborts the build under `throwOnModuleCollision` ([#16354](jestjs/jest#16354))
- `[jest-haste-map]` Attach the watchman client's `error` listener before the first command, so a watchman failure falls back to the node crawler instead of crashing on an unhandled `error` event, and always end the client ([#16355](jestjs/jest#16355))
- `[jest-haste-map]` Stop delivering watch events after `WatchmanWatcher` is closed, and route its warnings through the configured console ([#16355](jestjs/jest#16355))
- `[jest-haste-map]` Restore the nested `duplicates` index correctly in `ModuleMap.fromJSON`, so a haste collision reported inside a test worker raises `DuplicateHasteCandidatesError` instead of a `TypeError` ([#16353](jestjs/jest#16353))
- `[jest-haste-map]` Match watched files on a full extension, so `moduleFileExtensions: ['js']` no longer accepts `foo.mjs` ([#16352](jestjs/jest#16352))
- `[jest-haste-map]` Delimit the fields that make up the haste map cache key, so two different option sets cannot hash to the same cache file ([#16352](jestjs/jest#16352))
- `[jest-message-util]` Print the inner errors of an `AggregateError` thrown inside a test ([#16316](jestjs/jest#16316))
- `[jest-message-util]` Indent nested `cause` and `AggregateError` sections of a test failure by one level per depth, so the nesting is legible instead of rendering flat ([#16316](jestjs/jest#16316))
- `[jest-message-util]` Color stack traces line by line so blank lines stay blank ([#16316](jestjs/jest#16316))
- `[jest-message-util]` Detect Jest's own frames without assuming the checkout directory's name, and cover `@jest/*` packages, so stack traces and code frames point at user code ([#16326](jestjs/jest#16326))
- `[jest-mock]` `mockResolvedValue` / `mockRejectedValue` now see all overload return types, so a Promise-returning overload survives even when a later overload returns a non-Promise (e.g. `pg.Client['end']`) ([#16237](jestjs/jest#16237))
- `[@jest-environment/jsdom-abstract]` Make `@types/jsdom` a peer dependency ([#16166](jestjs/jest#16166))
- `[jest-mock]` Remove the leftover own accessor descriptor when restoring a `spyOn` of an inherited getter or setter, so the instance keeps reflecting the prototype ([#16226](jestjs/jest#16226))
- `[jest-resolve]` Include `extensionsToTreatAsEsm` in the `shouldLoadAsEsm` cache key, so projects with different extension lists don't read each other's answers ([#16369](jestjs/jest#16369))
- `[jest-resolve]` Make `getModuleIDAsync` build and cache `data:` URI module IDs the same way as `getModuleID` ([#16370](jestjs/jest#16370))
- `[jest-resolve]` Keep the `node:` prefix when resolving a core module asynchronously, so a builtin that only exists prefixed (`node:sea`, `node:sqlite`, `node:test`, `node:test/reporters`) resolves instead of failing as a missing bare package ([#16388](jestjs/jest#16388))
- `[jest-resolve]` Look up manual mocks for `node:` protocol specifiers under the unprefixed name they are stored as ([#16388](jestjs/jest#16388))
- `[jest-resolve]` Apply `moduleNameMapper` consistently to both spellings of core module specifiers (`fs` vs `node:fs`) ([#16390](jestjs/jest#16390))
- `[jest-resolve]` Keep virtual and ordinary mock module IDs isolated across test files ([#16296](jestjs/jest#16296))
- `[jest-resolve]` Guard missing `require.resolve.paths` ([#16052](jestjs/jest#16052))
- `[jest-resolve, jest-config, jest-runner]` Support a user resolver written as an ES module ([#16332](jestjs/jest#16332))
- `[jest-resolve, jest-runtime]` Throw the CJS parse error for ESM syntax in a `"type": "commonjs"` package or a `.cjs` file instead of loading it as ESM, matching Node ([#16368](jestjs/jest#16368))
- `[@jest/source-map]` Keep source map sources that name a scheme, such as `webpack:///`, instead of resolving them into a path that does not exist ([#16327](jestjs/jest#16327))
- `[@jest/source-map]` Look up `--testLocationInResults` positions at the right column, and keep a mapping to the first column instead of discarding it ([#16327](jestjs/jest#16327))
- `[@jest/source-map]` Warn when a source map cannot be parsed, instead of silently leaving its frames untranslated ([#16327](jestjs/jest#16327))
- `[jest-runner, @jest/source-map]` Keep a source-mapped stack for an error thrown after the test environment was torn down ([#16327](jestjs/jest#16327))
- `[jest-runtime, @jest/source-map]` Keep source maps past teardown and past the next test file's `install`, so a stack from a file no earlier stack mentioned still points at the original source ([#16330](jestjs/jest#16330))
- `[jest-runtime]` Report that no coverage was collected when `getAllV8CoverageInfoCopy` is called after `teardown`, instead of returning an empty result ([#16385](jestjs/jest#16385))
- `[jest-runtime]` Cache a CJS module's parsed exports before walking its re-exports, so two modules that re-export each other no longer overflow the stack when imported from ESM ([#16363](jestjs/jest#16363))
- `[jest-runtime]` Keep a re-exported ES module's parse failure from marking the re-exporting CommonJS file as ESM, so `module.exports = require('./dep.mjs')` loads instead of failing with `module is not defined` ([#16363](jestjs/jest#16363))
- `[jest-runtime]` Scope module mocks instantiated inside `jest.isolateModules`/`isolateModulesAsync` to that block, so a mock first imported there no longer outlives it - matching how CommonJS mocks already behave ([#16365](jestjs/jest#16365))
- `[jest-runtime]` Suspend module isolation while generating an automock, so loading the real module to read its shape no longer populates the isolated registry ([#16365](jestjs/jest#16365))
- `[jest-runtime]` Check a cached ES module's status before `require()` returns it, so a module whose evaluation threw rethrows that error and one left linked by a failed sibling is evaluated instead of returning uninitialized bindings ([#16364](jestjs/jest#16364))
- `[jest-runtime]` Report the original `ERR_REQUIRE_ASYNC_MODULE` when a `require()` of a top-level-await graph is retried, instead of a spurious "concurrent `import()`" error ([#16364](jestjs/jest#16364))
- `[jest-runtime]` Throw the evaluation error when another caller's `import()` of the same module failed while we awaited it, instead of resolving with the errored module ([#16364](jestjs/jest#16364))
- `[jest-runtime]` Mark the result of `require()`ing an ES module that has a default export with `__esModule: true` through a live-binding facade, and serve the same object from `require.cache`, matching Node ([#16367](jestjs/jest#16367))
- `[jest-runtime]` Provide a CommonJS module's exports under the `'module.exports'` named export when imported from ESM, matching Node 23+ ([#16367](jestjs/jest#16367))
- `[jest-runtime]` Give the test file itself a non-null `require.main` ([#16367](jestjs/jest#16367))
- `[jest-runtime]` Populate `module.children` with the modules a file loads, matching Node ([#16368](jestjs/jest#16368))
- `[jest-runtime]` Provide `import.meta.resolve` and `import.meta.jest` in `data:` URI modules, accept any-case mediatype parameters, and use Node's error codes for invalid `data:` URIs ([#16368](jestjs/jest#16368))
- `[jest-runtime]` Key ES modules by full URL, so query and fragment suffixes create the same module instances as Node and show up in `import.meta.url` ([#16375](jestjs/jest#16375))
- `[jest-runtime]` Share modules between overlapping graphs when a CommonJS module `require()`s an ES module mid-load, instead of evaluating shared dependencies twice ([#16375](jestjs/jest#16375))
- `[jest-runtime]` Throw `ERR_REQUIRE_CYCLE_MODULE` like Node when a CommonJS module `require()`s an ES module that is still being loaded, instead of evaluating the module a second time ([#16366](jestjs/jest#16366))
- `[jest-runtime]` Key builtin modules in the ESM registry by one canonical specifier ([#16341](jestjs/jest#16341))
- `[jest-runtime]` `import.meta.resolve()` for a builtin uses its `node:` specifier ([#16341](jestjs/jest#16341))
- `[jest-runtime]` Fall back to native ESM when a `.js` file contains ESM syntax but has no `"type":"module"` marker ([#16152](jestjs/jest#16152))
- `[jest-runtime]` Allow `require()` of ESM-marked files on Node < 24.9 via transform fallback ([#16244](jestjs/jest#16244))
- `[jest-runtime, @jest/transform]` Surface actionable `ERR_REQUIRE_ESM` error for files with untransformed ESM syntax instead of the generic "unexpected token" message ([#16244](jestjs/jest#16244))
- `[jest-runtime]` Support older test environments whose `moduleMocker` does not implement `clearMocksOnScope` ([#16169](jestjs/jest#16169))
- `[jest-runtime]` Apply `jest.unstable_mockModule` when the mocked file itself is `require()`d, not only when it is imported as a dependency ([#16389](jestjs/jest#16389))
- `[jest-runtime]` Apply `jest.unstable_mockModule` to statically imported `data:` URIs on Node 24.9+, matching dynamic `import()` ([#16389](jestjs/jest#16389))
- `[jest-runtime]` Run an async `jest.unstable_mockModule` factory once per module instead of twice, and fail the import instead of crashing the worker when the factory rejects ([#16389](jestjs/jest#16389))
- `[jest-runtime]` Hide a `require(esm)` module that failed to evaluate from `require.cache`, as Node does, instead of exposing a namespace with uninitialized bindings ([#16389](jestjs/jest#16389))
- `[jest-runtime]` Strip the byte-order mark when importing a JSON module, matching `require()` and Node ([#16389](jestjs/jest#16389))
- `[jest-runtime]` Throw `ERR_REQUIRE_ASYNC_MODULE` when `require(esm)` runs under an async-only custom resolver, instead of silently resolving with the default resolver ([#16389](jestjs/jest#16389))
- `[jest-runtime]` Parse imported JSON modules with the test realm's `JSON`, so their objects pass `instanceof Object` inside the test like `require()`d JSON does ([#16389](jestjs/jest#16389))
- `[jest-runtime]` Accept every `file:` URL string in the sandboxed `module.createRequire`, including one with a `localhost` authority, as Node does ([#16389](jestjs/jest#16389))
- `[jest-runtime]` Point at `{virtual: true}` when `jest.mock` or `jest.unstable_mockModule` is given a module that cannot be resolved ([#16389](jestjs/jest#16389))
- `[jest-reporters]` Fix coverage report table formatting in CI/GitHub Actions environments where `process.stdout.columns` is undefined by falling back to the `COLUMNS` env var or `80` columns in CI, preserving existing behaviour in other non-TTY environments ([#16227](jestjs/jest#16227))
- `[jest-runtime]` Support CJS-in-ESM exports via `"module.exports"` named exports ([#16277](jestjs/jest#16277))
- `[jest-snapshot]` Keep a skipped or failed test's hinted snapshots, instead of reporting them obsolete ([#16348](jestjs/jest#16348))
- `[jest-util]` Stop `globsToMatcher` reusing a cached matcher compiled with different picomatch options, and keep its `dot: true` default when `dot` is passed as `undefined` ([#16381](jestjs/jest#16381))
- `[pretty-format]` Move the `react-is` aliases into the `@jest` scope, so they cannot be shadowed by unrelated packages published under the alias names ([#16333](jestjs/jest#16333))

##### Chore & Maintenance

- `[docs]` Document the intentional divergences from Node's module system in the ECMAScript Modules page ([#16368](jestjs/jest#16368))
- `[docs]` Note deprecation of `react-test-renderer` in React Native tutorial and `pretty-format` README ([#16294](jestjs/jest#16294))
- `[docs]` Use `@testing-library/react-native` in the React Native tutorial instead of the deprecated `react-test-renderer` ([#16318](jestjs/jest#16318))
- `[babel-jest, @jest/transform]` Update `babel-plugin-istanbul` to v8 ([#16049](jestjs/jest#16049))
- `[jest-config, @jest/reporters, jest-runtime]` Update `glob` to v13 ([#16397](jestjs/jest#16397))
- `[jest-haste-map]` Refactor massive class into multiple files ([#16180](jestjs/jest#16180))
- `[jest-haste-map]` Drop `walker` dependency; replace hand-rolled directory recursion in the JS crawler and watcher startup with `fdir` ([#16187](jestjs/jest#16187))
- `[jest-haste-map]` Reuse cached metadata for files whose haste name is a known duplicate, instead of re-reading and re-parsing them on every startup ([#16351](jestjs/jest#16351))
- `[jest-haste-map]` Cache the watchman socket path and replace the `watchman --version` probe with `get-sockname`, so warm runs spawn no watchman processes ([#16386](jestjs/jest#16386))
- `[jest-resolve]` Store the per-directory package-type lookup in the cache it reads, so it actually memoizes ([#16369](jestjs/jest#16369))
- `[jest-resolve, jest-runtime]` Cut repeated work on the resolution hot path: hoist the platform-extension list to construction, memoize `isCoreModule` and the options cache-key serialization, skip mapper preparation when no `moduleNameMapper` is configured, run each mapper regex once, and stop re-parsing `NODE_OPTIONS` on every default-resolver call ([#16371](jestjs/jest#16371))
- `[jest-resolve]` Cut warm resolution cost to about a third: reuse one `unrs-resolver` factory per options shape instead of cloning per resolution, compose the factory cache key from per-array cached strings instead of serializing options, and stop constructing an `Error` for misses that `findNodeModule` swallows; add a `__benchmarks__` suite for the default resolver ([#16373](jestjs/jest#16373))
- `[jest-runner, @jest/source-map]` Replace `source-map-support` with an implementation in `@jest/source-map` ([#16327](jestjs/jest#16327))
- `[jest-snapshot]` Load babel, semver and synckit lazily, so requiring the package (which every test process does through `@jest/expect`) no longer loads \~200 modules that only writing inline snapshots needs ([#16387](jestjs/jest#16387))
- `[jest-runtime]` Reduce per-require overhead: skip module ID resolution when no mock can apply, answer core modules before probing for a manual mock, share one `require.cache` proxy across modules, and cache empty files ([#16376](jestjs/jest#16376))
- `[@jest/source-map]` Deprecate `getCallsite` in favour of `SourceMapSupport#getCallsite` ([#16327](jestjs/jest#16327))
- `[jest-runtime]` Avoid magical `null` value in ESM loader ([#16160](jestjs/jest#16160))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

require-changelog If a PR does requires a changelog entry

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[jest-circus] suite level retry

2 participants