feat(ext/node): support t.assert.snapshot in node:test#35476
Conversation
`node:test`'s test context exposed `t.assert` but not the snapshot assertions, so `t.assert.snapshot(value)` (and `t.assert.fileSnapshot`) threw "is not a function" (denoland#35402). This implements Node's snapshot testing in the `node:test` polyfill: - `t.assert.snapshot(value, options?)` and `t.assert.fileSnapshot(value, path, options?)`. - The `snapshot` module export with `setResolveSnapshotPath()` and `setDefaultSnapshotSerializers()`. Snapshots are stored next to the test file as `<testfile>.snapshot` in the same `exports[`name N`] = `...`;` format Node uses, so snapshot files are interchangeable between the two runtimes. `deno test --update-snapshots` takes the place of Node's `--test-update-snapshots`. The default snapshot location reuses the test runner's snapshot ops (via a new `nodeLayout` option) so it is resolved from the trusted test origin and stays exempt from permission checks, matching Node's permission-free behavior. `fileSnapshot` writes a user-supplied path and is subject to normal read/write permissions. Closes denoland#35402
|
@divybot merge main |
# Conflicts: # ext/node/polyfills/testing.ts
|
Merged |
# Conflicts: # ext/node/polyfills/testing.ts # ext/node/polyfills/testing_esm.ts
|
Nice work — this layers cleanly on top of #35139 (reuses the same A few things I'd like to discuss before merging: File format divergence — do we want two formats? This writes Node's I'm not sure we want to ship two on-disk formats. The Node format is desirable here for interchange with Node, but I'd like us to consciously decide whether we (a) accept the split as intentional (node:test interop wins), (b) converge both APIs on the Node format, or (c) converge on the Deno format and give up Node file interchange. Can we discuss before this lands? At minimum this should be called out explicitly in the PR description so it's a deliberate choice rather than an accident of two independent PRs. Correctness notes:
Test gaps (non-blocking): no coverage for |
|
Nice work — the escaping roundtrip and the permission story both check out (explicit 1.
enters the update branch, sets 2. TAP-mode wiring is effectively dead
Minor
Coverage gaps worth adding: |
Summary
Fixes #35402.
node:test's test context exposedt.assertbut none of the snapshot assertions, so the documentedt.assert.snapshot(value)API threwTypeError: t.assert.snapshot is not a function.This implements Node's snapshot testing in the
node:testpolyfill:t.assert.snapshot(value, options?)t.assert.fileSnapshot(value, path, options?)snapshotmodule export:snapshot.setResolveSnapshotPath(fn)andsnapshot.setDefaultSnapshotSerializers(serializers)The reproduction from the issue now works:
Details
<testfile>.snapshotin the sameexports[`name N`] = `...`;format Node uses, so snapshot files are interchangeable between the two runtimes. The default serializer matches Node (JSON.stringify(value, null, 2)).deno test --update-snapshotsplays the role of Node's--test-update-snapshots. In update mode the file is rewritten from the snapshots asserted in the run (stale entries dropped); otherwise the serialized value is compared withassert.strictEqual, producing a diff on mismatch.op_test_snapshot_read/write/in_update_mode) via a newnodeLayoutoption, so it is resolved from the trusted test origin and stays exempt from permission checks — matching Node's permission-free behavior.fileSnapshottargets a user-supplied path and is subject to normal--allow-read/--allow-write.@types/node); this change provides the runtime implementation behind them.Tests
tests/specs/test/node_snapshot/: create→verify roundtrip (incl. asserting the exact on-disk file format), missing-snapshot error, and mismatch diff.node::node_test_*(24) and Deno-nativetest::snapshot(8) specs still pass (thenodeLayoutop change is backward compatible).