@jsenv/snapshot
A powerful snapshot testing tool for JavaScript applications.
📸 Create readable, reliable test snapshots
🔄 Document code behavior with markdown
📊 Track changes in code output over time
🧩 Normalize fluctuating values for stable comparisons
Table of Contents
- @jsenv/snapshot
Introduction to Snapshot Testing
Snapshot testing is a technique that:
- Captures the output of code execution into files (snapshots)
- Validates future code changes by:
- Reading the existing snapshot
- Executing the code
- Generating a new snapshot
- Comparing the two snapshots and reporting differences
This approach ensures your code continues to behave as expected by verifying its outputs remain consistent over time.
Installation
npm install --save-dev @jsenv/snapshot
How @jsenv/snapshot
Works
When running tests:
- First run: If no snapshot exists, one will be generated without comparison
- Subsequent runs: Snapshots are compared with the following behavior:
- In CI environments (
process.env.CI
is set): An error is thrown if differences are detected - Locally: No error is thrown, allowing you to review changes with tools like git diff
- In CI environments (
Note: All functions accept a
throwWhenDiff
parameter to force errors even in local environments.
API Reference
snapshotTests(testFileUrl, fnRegisteringTests, options)
The most powerful feature of this library - creates readable markdown snapshots of test executions.
import { snapshotTests } from "@jsenv/snapshot";
const getCircleArea = (circleRadius) => {
if (isNaN(circleRadius)) {
throw new TypeError(
`circleRadius must be a number, received ${circleRadius}`,
);
}
return circleRadius * circleRadius * Math.PI;
};
await snapshotTests(import.meta.url, ({ test }) => {
test("when radius is 2", () => {
return getCircleArea(2);
});
test("when radius is 10", () => {
return getCircleArea(10);
});
test("when radius is null", () => {
try {
return getCircleArea(null);
} catch (e) {
return e;
}
});
});
This generates a markdown file documenting how your code behaves in different scenarios:
# getCircleArea
## when radius is 2
```js
getCircleArea(2);
```
Returns:
12.566370614359172;
when radius is 10
getCircleArea(10);
Returns:
314.1592653589793;
when radius is null
getCircleArea(null);
Throws:
TypeError: circleRadius must be a number, received null
See a full example at [./docs/\_circle_area.test.js/circle_area.test.js.md](./docs/_circle_area.test.js/circle_area.test.js.md)
#### snapshotTests Options
```js
await snapshotTests(import.meta.url, fnRegisteringTests, {
throwWhenDiff: true, // Force error on snapshot differences (default: false in local, true in CI)
formatValue: (value) => {}, // Custom formatter for values
trackingConfig: {}, // Track specific resources like network requests or file operations
});
Why Use snapshotTests?
- Assertion-free testing: Simply call your functions and let the snapshots document their behavior
- Self-documenting tests: Markdown files serve as both test validation and documentation
- Visual change reviews: Code changes are reflected in snapshots, making reviews easy
- Side effect tracking: Automatically captures and documents:
takeFileSnapshot(fileUrl)
Captures and compares the state of a specific file.
import { writeFileSync } from "node:fs";
import { takeFileSnapshot } from "@jsenv/snapshot";
const fileTxtUrl = new URL("./file.txt", import.meta.url);
const writeFileTxt = (content) => {
writeFileSync(fileTxtUrl, content);
};
// take snapshot of "./file.txt"
const fileSnapshot = takeFileSnapshot(fileTxtUrl);
writeFileTxt("Hello world");
// compare the state of "./file.txt" with previous version
fileSnapshot.compare();
takeDirectorySnapshot(directoryUrl)
Captures and compares the state of an entire directory.
import { writeFileSync } from "node:fs";
import { takeDirectorySnapshot } from "@jsenv/snapshot";
const directoryUrl = new URL("./dir/", import.meta.url);
const writeManyFiles = () => {
writeFileSync(new URL("./a.txt", directoryUrl), "a");
writeFileSync(new URL("./b.txt", directoryUrl), "b");
};
// take snapshot of "./dir/"
const directorySnapshot = takeDirectorySnapshot(directoryUrl);
writeManyFiles();
// compare the state of "./dir/" with previous version
directorySnapshot.compare();
Stable Snapshots Across Environments
To ensure snapshots remain consistent across different machines and CI environments, @jsenv/snapshot
automatically normalizes fluctuating values:
Fluctuating Value | Stabilized As |
---|---|
Time durations | "Xs" instead of "2.34s" |
Filesystem paths | Platform-independent paths |
Network ports | Removed from URLs |
Random IDs | Consistent placeholder values |
Stack traces | Simplified and normalized |
This ensures your snapshot tests remain stable regardless of when or where they run.
Advanced Examples
- Testing complex assertion behavior: @jsenv/assert/tests/array.test.js.md
- Testing server-side builds with browser execution: @jsenv/core/tests/script_type_module_basic.test.mjs
Compatibility
- Node.js: 16.x and above
- Works with most JavaScript test runners
- Compatible with ESM and CommonJS modules
Contributing
If you encounter unstable snapshots due to fluctuating values not being properly normalized, please open an issue or submit a pull request.