test: migrate from jest to node:test runner#265
Merged
Conversation
Swap the test harness from Jest to Node's built-in test runner. Tests are kept close to verbatim by using the standalone `expect` package for assertions; coverage moves to `c8` to preserve the existing 100% gate and lcov output for the Codecov upload. - Imports updated to node:test + expect across both test files - describe.each blocks rewritten as for...of loops - jest.fn() / .toHaveBeenCalled() converted to mock.fn() / mock.callCount() - 4 toMatchSnapshot calls inlined as literal assertions; .snap deleted - Net -178 / +6 packages in node_modules - engines, support matrix, and runtime API unchanged Refs #260 Co-authored-by: Cursor <cursoragent@cursor.com>
Node 22.x changed how the test runner CLI handles directory args: passing `test/` is interpreted as a single module path instead of recursing, breaking with `Cannot find module '.../test'`. Switch to `test/*.test.js` (shell-expanded) which works consistently across Node 20.x through 25.x. Co-authored-by: Cursor <cursoragent@cursor.com>
|
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #265 +/- ##
==========================================
Coverage 100.00% 100.00%
==========================================
Files 4 5 +1
Lines 100 263 +163
Branches 14 0 -14
==========================================
+ Hits 100 263 +163 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
This was referenced May 1, 2026
Contributor
|
@arb is my hero. By the way, node:test does support snapshots and code coverage (it's listed as experimental, but that is mostly just in name at this point). |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Tracks #260. First step in evaluating a move off Jest, prompted by Jest's slowing release cadence. This PR keeps the framework decision narrow: swap the harness with the smallest possible test-body churn, document the tradeoffs, and let the rest of #260 weigh whether to stick with
node:testor revisit Vitest as a follow-up.What changed
node --test(Node's built-in test runner; no--experimental-vm-modulesflag needed anymore).expectpackage — the same code Jest ships internally — so the ~240 existingexpect(...)call sites stay verbatim.jest.fn()→mock.fn()fromnode:test. The 10.toHaveBeenCalled()sites became.mock.callCount()checks because the standaloneexpectmatchers expect Jest-mock shape thatnode:testmocks don't provide.describe.eachtemplate-literal tables rewritten as plainfor...ofloops.toMatchSnapshot()sites inlined as literalexpect(...).toEqual(...)assertions. TheMapsnapshot was rewritten as targeted.messageassertions instead of a structural equality check, becausejoi.ValidationErrorinstances carry internal state that makes deep equality fragile.is-ci-cliis preserved — the local-watch / CI-one-shot UX is unchanged.Coverage tooling (key #260 data point)
node --testdoesn't enforce coverage thresholds and the existingcoveragejob in .github/workflows/ci.yml consumes./coverage/lcov.infofor Codecov. To preserve both today's 100% threshold gate and the lcov upload, this PR addsc8as a devDep and runsc8 --reporter=lcov --reporter=text --100 node --test test/undertest:ci.This is the single biggest gap between
node:testand Jest for this repo: Jest had threshold enforcement built in;node:testrequires a separate tool (c8) or a custom parser to get the same gate. Worth weighing in #260 against alternatives like Vitest, which bundles coverage + thresholds natively.Net dep change
jest(and its tree)expect@30.x.x,c8@10.x.xnode_modulesnet change: -178 packages, +6 packagesSupport matrix
Unchanged.
engines: ">=20"stays, .github/workflows/ci.yml matrix (Node 20.x/22.x/24.x/25.x × Express 4/5) stays. Verified locally on Node 20.19 (matrix floor): 62 tests pass, 100% coverage,coverage/lcov.infoproduced,eslintclean.Made with Cursor