feat(jest-snapshot): support ESM snapshotResolver and snapshotSerializers - #16402
feat(jest-snapshot): support ESM snapshotResolver and snapshotSerializers#16402soltonigiri wants to merge 8 commits into
Conversation
✅ Deploy Preview for jestjs ready!Built without sensitive environment variables
To edit notification comments on pull requests, go to your Netlify project configuration. |
babel-jest
babel-plugin-jest-hoist
babel-preset-jest
create-jest
@jest/diff-sequences
expect
@jest/expect-utils
jest
jest-changed-files
jest-circus
jest-cli
jest-config
@jest/console
@jest/core
@jest/create-cache-key-function
jest-diff
jest-docblock
jest-each
@jest/environment
jest-environment-jsdom
@jest/environment-jsdom-abstract
jest-environment-node
@jest/expect
@jest/fake-timers
@jest/get-type
@jest/globals
jest-haste-map
jest-jasmine2
jest-leak-detector
jest-matcher-utils
jest-message-util
jest-mock
@jest/pattern
jest-phabricator
jest-regex-util
@jest/reporters
jest-resolve
jest-resolve-dependencies
jest-runner
jest-runtime
@jest/schemas
jest-snapshot
@jest/snapshot-utils
@jest/source-map
@jest/test-result
@jest/test-sequencer
@jest/transform
@jest/types
jest-util
jest-validate
jest-watcher
jest-worker
pretty-format
commit: |
|
Thanks for picking this up! Docs tabs and extra tests are great 👍 A few things I'd like carried over / cleaned up before merge:
|
|
Thanks! I've addressed all three points 👍 I've kept the transformed TypeScript coverage in I also removed the redundant |
SimenB
left a comment
There was a problem hiding this comment.
Thanks, all three points from last round look good.
The transform fixture is the one real problem - see the comment on it (and apologies for sending you on a wrong path). loadSnapshotSetup I'd also like moved before merge. Everything else is small.
| @@ -0,0 +1,6 @@ | |||
| { | |||
There was a problem hiding this comment.
.mjs can't be transformed at all - requireOrImportModule picks the extension branch and hands off to native import() before the hook exists. There's also no transform key, no babel config and no deps here, and setting "transform": {} doesn't change the result.
My bad for essentially asking you to copy the test in #12014 verbatim without reading it closer.
Give it an extension the pipeline can claim. .ts, or .js with ESM syntax and no type: module, both go through require() and hit babel.
Note that the current tests doesn't need the yarn install thing. that might change dependening on how you do the transform ofc 🙂
| } | ||
|
|
||
| /** Loads snapshot config modules outside the test sandbox. */ | ||
| async loadSnapshotSetup(): Promise<SnapshotSetup> { |
There was a problem hiding this comment.
Only reads this._config, and skips the sandbox by design. Runtime is the documented subclass seam and everything on it loads through the sandbox - this looks like an override point and isn't.
Should be a free function in jest-snapshot instead. loadSnapshotSetup(config)?.
| localRequire?: Promise<LocalRequire> | LocalRequire, | ||
| ): Promise<SnapshotResolver> => { | ||
| const key = config.rootDir; | ||
| const key = `${config.rootDir}\0${config.snapshotResolver ?? ''}`; |
There was a problem hiding this comment.
Use config.id instead. It's sha1(rootDir + configPath + projectIndex) from normalize.ts:365, and it's already what jest-runner keys its resolvers on. What you have here still collides for two projects that share a rootDir and a resolver but differ in transform, and switching lets the comment above disappear as well.
| localRequire: Promise<LocalRequire> | LocalRequire = createTranspilingRequire( | ||
| config, | ||
| ), | ||
| localRequire?: Promise<LocalRequire> | LocalRequire, |
There was a problem hiding this comment.
Seems 100% unused now (outside of tests). add a TODO comment to mark it for removal in Jest 31?
| }; | ||
| ``` | ||
|
|
||
| Jest loads the module outside the test sandbox, so it does not use `jest.mock()` or `moduleNameMapper`. Your `transform` still applies. Jest loads `.mjs` and `.mts` files as native ESM, so they must be valid JavaScript as written. |
There was a problem hiding this comment.
"Your transform still applies" contradicts the sentence that follows it, and the second one is the accurate one: .mjs and .mts skip the transform entirely. Worth stating that outright rather than leaving a reader to reconcile the two.
This paragraph is also missing the default-export requirement that the snapshotSerializers section spells out, even though both paths go through applyInteropRequireDefault: true.
|
|
||
| `printer` is a function that serializes a value using existing plugins. | ||
|
|
||
| Jest loads serializers outside the test sandbox, so they do not use `jest.mock()` or `moduleNameMapper`. Your `transform` still applies. Jest loads `.mjs` and `.mts` files as native ESM, so they must be valid JavaScript as written. An ESM serializer must use a `default` export. |
There was a problem hiding this comment.
The transform sentence has the same problem here.
|
|
||
| ### Features | ||
|
|
||
| - `[jest-circus, jest-jasmine2, jest-runtime, jest-snapshot]` Support ESM `snapshotResolver` and `snapshotSerializers` configuration modules ([#16402](https://github.com/jestjs/jest/pull/16402)) |
There was a problem hiding this comment.
Please call out the behaviour change in this entry. Loading these modules outside the sandbox means their transitive imports no longer go through moduleNameMapper or jest.mock, and as written it reads like pure feature work. Anyone whose serializer pulls in a mapped module will find out the hard way.
Also, while it hits circus and jasmine, I don't think it's relevant to people reading the changelog?
|
|
||
| it('should transform the snapshotResolver', () => { | ||
| const result = runJest(dir, ['-w=1', '--no-cache', '--ci=false'], { | ||
| nodeOptions: '--experimental-vm-modules --no-warnings', |
There was a problem hiding this comment.
I don't see why --experimental-vm-modules would be needed to run the tests?
|
Thanks for the review, and no worries! I've addressed all eight comments 👍 The transform fixture now uses a The resolver cache now keys on The focused snapshot tests and transform E2Es are green with both Circus and Jasmine2. |
Summary
Jest loads both snapshot configuration options inside the test sandbox, where native ECMAScript module configuration files need the VM modules flag.
This change loads both options outside the sandbox with Jest's existing transform-aware loader. CommonJS and transformed TypeScript modules still work. The configured module order stays unchanged, and the project configuration ID scopes the snapshot resolver cache.
The tests cover the default and legacy test runners, including a
.jsresolver with ESM syntax that runs through Babel without a fixture install or the VM modules flag. The configuration docs include ECMAScript module examples and explain the transform and default-export behavior.Related to #11167.
Closes #12014.
Test plan
yarn buildyarn test-types: 24 files, 83 tests, and 1,585 assertions passed.