test: migrate from jest to node:test runner#1321
Conversation
Replace jest with the built-in Node.js test runner and rewrite tests as native ECMAScript modules. Uses experimental snapshot and module mock APIs that ship with Node 22. Changes: - Remove jest devDependency - Add `node --test` based scripts (uses --experimental-test-snapshots and --experimental-test-module-mocks) - Convert src/ to native ESM (add `.js` extensions on relative imports, load options.json via createRequire, switch the remaining CJS `require()` calls in utils.js to a top-level createRequire) - Add src/package.json and test/package.json with `"type": "module"` so the source and tests are loaded as ESM while keeping the published dist output as CommonJS - Rename src/cjs.js -> src/cjs.cjs (entry stays CJS for webpack loader-runner) and update `main` accordingly. The post-build step removes the copied package.json from dist/ so the published CommonJS output is unaffected - Rename test helpers / webpack configs that must stay CJS to .cjs (testLoader, watch/, manual/webpack.config) - Convert all .test.js files to .test.mjs, replacing expect(...).toX(...) with node:assert/strict equivalents, jest.spyOn / jest.fn with mock.method / mock.fn, and .toMatchSnapshot() with t.assert.snapshot() - Drop the old Jest __snapshots__/ folder; node:test writes per-file *.test.mjs.snapshot files instead - Two mock.module tests in implementation-option are skipped because the experimental ESM module mocker collides with the existing test/node_modules/sass fixture directory - Bump engines / babel target to Node 22 (required for the new test runner features) and add a test-files override in eslint.config.mjs for the rules that no longer apply (devDependencies, src imports, experimental node:test usage, ...) - Update cspell/prettier ignore lists for the new snapshot file format
Per follow-up review feedback: ship the source tree as CommonJS and
remove the babel toolchain. A later PR will move the source to ECMAScript
modules and re-introduce babel to generate a CommonJS fallback.
Changes:
- Rewrite src/index.js, src/utils.js, and src/cjs.js to CommonJS
(`require` + `module.exports`); remove the src/package.json shim
that previously marked src as ESM
- Remove @babel/cli, @babel/core, @babel/preset-env from devDependencies
and delete babel.config.js
- Replace the babel build step with a plain `fs.cpSync('src', 'dist')`
copy so the published `dist/` layout is unchanged
- Restore `dist/cjs.js` (was renamed to `dist/cjs.cjs` while src was
briefly ESM) and update the `main` field
- Update test/helpers/getCompiler.js, test/sourceMap-options.test.mjs,
test/watch/webpack.config.cjs, and test/manual/webpack.config.cjs
to reference src/cjs.js / dist/cjs.js again
- Switch eslint to `recommended-commonjs` and add a sourceType override
for the `test/**/*.js` helpers (which stay ESM under test/package.json)
- Regenerate the snapshot files that captured the old `src/index.js`
loader path
|
|
Add a node --import setup hook (`test/setup-snapshots.mjs`) that calls `snapshot.setResolveSnapshotPath` so node:test writes / reads each suite's snapshots from `test/__snapshots__/<file>.test.mjs.snap` instead of the default sibling `*.snapshot` location. Matches the previous (jest) layout. - Add `test/setup-snapshots.mjs` and load it via `--import` in the `test:only` and `test:coverage` scripts. - Move the existing seven snapshot files into `test/__snapshots__/` and rename them with the `.snap` suffix. - Drop the now-unneeded `**/*.snapshot` entries from `.cspell.json` and `.prettierignore` (the existing `**/__snapshots__/**` rule covers them again).
Replace jest with the built-in Node.js test runner. Tests are written
as native ECMAScript modules (test/package.json sets "type": "module")
and use the experimental snapshot and module mock APIs that ship with
Node 22.
Key changes:
- Remove jest devDependency and add `node --test` based scripts (with
--experimental-test-snapshots, --experimental-test-module-mocks and
--test-force-exit to release sass-embedded subprocesses)
- Rewrite src/ to CommonJS (no `import` syntax in src) and drop the
babel toolchain. The build is now a plain `fs.cpSync('src','dist')`;
a future PR can re-introduce babel to generate a CJS fallback from
an ESM source tree.
- Convert each .test.js to .test.mjs (ESM tests), replacing
expect(...).toX(...) with node:assert (assert.strictEqual /
notStrictEqual / deepStrictEqual / match / throws / rejects),
jest.spyOn / jest.fn with mock.method / mock.fn, and
.toMatchSnapshot() with t.assert.snapshot()
- Add test/setup-snapshots.mjs (loaded via `node --import`) that
configures `snapshot.setResolveSnapshotPath()` so snapshots stay in
test/__snapshots__/<file>.test.mjs.snap, matching the previous jest
layout
- Rename helpers/webpack configs that must remain CJS to .cjs
(testLoader, watch/, manual/webpack.config); update getCompiler.js
to use `fileURLToPath(import.meta.url)` for __dirname
- Two `mock.module` tests in implementation-option are skipped because
the experimental ESM module mocker collides with the existing
test/node_modules/sass fixture directory (same package name, no
package.json/entry point)
- Update eslint flat-config (`recommended-commonjs` baseline with a
test/**/*.js sourceType:"module" override) and remove
`**/*.snapshot` from the cspell/prettier ignore lists now that
snapshots live under __snapshots__/ again
- Drop the old `__snapshots__/*.test.js.snap` jest files; they're
regenerated as `*.test.mjs.snap` via `node --test --test-update-snapshots`
f8616d1 to
f730939
Compare
…r-A4n6K' into claude/migrate-nodejs-test-runner-A4n6K # Conflicts: # commitlint.config.js # package-lock.json # package.json # test/__snapshots__/additionalData-option.test.mjs.snap # test/__snapshots__/implementation-option.test.mjs.snap # test/__snapshots__/sassOptions-option.test.mjs.snap # test/__snapshots__/sourceMap-options.test.mjs.snap # test/__snapshots__/validate-options.test.mjs.snap # test/__snapshots__/warnRuleAsWarning.test.mjs.snap # test/__snapshots__/webpackImporter-options.test.mjs.snap # test/additionalData-option.test.mjs # test/cjs.test.mjs # test/helpers/customImporter.js # test/helpers/index.js # test/implementation-option.test.mjs # test/loader.test.mjs # test/resolver.test.mjs # test/sassOptions-option.test.mjs # test/sourceMap-options.test.mjs # test/validate-options.test.mjs # test/warnRuleAsWarning.test.mjs # test/webpackImporter-options.test.mjs
`test:coverage` configured a `lcov` reporter for `node --test` but never turned coverage collection on, so the produced `coverage.lcov` was a stub `TN:` line and Codecov rejected all uploads as "incorrect data format". Add `--experimental-test-coverage` to the script (kept along with the existing `--test-coverage-include="src/**/*.js"`) so the reporter has real V8 coverage data to serialize.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #1321 +/- ##
==========================================
+ Coverage 92.35% 96.64% +4.28%
==========================================
Files 3 3
Lines 301 834 +533
Branches 107 0 -107
==========================================
+ Hits 278 806 +528
- Misses 20 28 +8
+ Partials 3 0 -3 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
Replace jest with the built-in Node.js test runner and rewrite tests as
native ECMAScript modules. Uses experimental snapshot and module mock
APIs that ship with Node 22.
Changes:
node --testbased scripts (uses --experimental-test-snapshotsand --experimental-test-module-mocks)
.jsextensions on relative imports,load options.json via createRequire, switch the remaining CJS
require()calls in utils.js to a top-level createRequire)"type": "module"so the source and tests are loaded as ESM while keeping the published
dist output as CommonJS
loader-runner) and update
mainaccordingly. The post-build stepremoves the copied package.json from dist/ so the published
CommonJS output is unaffected
(testLoader, watch/, manual/webpack.config)
expect(...).toX(...) with node:assert/strict equivalents,
jest.spyOn / jest.fn with mock.method / mock.fn, and
.toMatchSnapshot() with t.assert.snapshot()
per-file *.test.mjs.snapshot files instead
the experimental ESM module mocker collides with the existing
test/node_modules/sass fixture directory
runner features) and add a test-files override in eslint.config.mjs
for the rules that no longer apply (devDependencies, src imports,
experimental node:test usage, ...)