@jsenv/snapshot 2.14.1 → 2.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jsenv/snapshot",
3
- "version": "2.14.1",
3
+ "version": "2.15.0",
4
4
  "description": "Snapshot testing",
5
5
  "license": "MIT",
6
6
  "author": {
@@ -31,13 +31,13 @@
31
31
  ],
32
32
  "sideEffects": false,
33
33
  "dependencies": {
34
- "@jsenv/assert": "4.4.5",
35
- "@jsenv/ast": "6.6.7",
34
+ "@jsenv/assert": "4.4.6",
35
+ "@jsenv/ast": "6.6.9",
36
36
  "@jsenv/exception": "1.1.7",
37
- "@jsenv/humanize": "1.4.0",
38
- "@jsenv/filesystem": "4.14.6",
39
- "@jsenv/terminal-recorder": "1.5.10",
40
- "@jsenv/urls": "2.7.1",
37
+ "@jsenv/humanize": "1.5.0",
38
+ "@jsenv/filesystem": "4.15.0",
39
+ "@jsenv/terminal-recorder": "1.5.14",
40
+ "@jsenv/urls": "2.7.2",
41
41
  "@jsenv/utils": "2.3.0",
42
42
  "ansi-regex": "6.1.0",
43
43
  "pixelmatch": "7.1.0",
package/readme.md CHANGED
@@ -2,38 +2,37 @@
2
2
 
3
3
  [![npm package](https://img.shields.io/npm/v/@jsenv/snapshot.svg?logo=npm&label=package)](https://www.npmjs.com/package/@jsenv/snapshot)
4
4
 
5
- A tool to generate snapshots during tests.
5
+ A powerful snapshot testing tool for JavaScript applications.
6
6
 
7
- ## A word on snapshot testing
7
+ ## Introduction to Snapshot Testing
8
8
 
9
- Snapshot testing consists into:
9
+ Snapshot testing is a technique that:
10
10
 
11
- 1. Making code execution produce file(s). They are called snapshots.
12
- 2. Make further code execution follow these steps:
13
- 1. Read existing snapshot
14
- 2. Execute code
15
- 3. Read new snapshot
16
- 4. Compare the two snapshots and throw if there is a diff
11
+ 1. Captures the output of code execution into files (snapshots)
12
+ 2. Validates future code changes by:
13
+ - Reading the existing snapshot
14
+ - Executing the code
15
+ - Generating a new snapshot
16
+ - Comparing the two snapshots and reporting differences
17
17
 
18
- This force code execution to produce the same snapshots. Meaning that code being tested still behave as expected.
18
+ This approach ensures your code continues to behave as expected by verifying its outputs remain consistent over time.
19
19
 
20
- ## How it works
20
+ ## How `@jsenv/snapshot` Works
21
21
 
22
- `@jsenv/snapshot` behaves as follow:
22
+ When running tests:
23
23
 
24
- When there is no snapshot(s), the snapshot won't be compared. It happens the very first time you generate snapshots or because all snapshot files have been removed for some reason.
24
+ - **First run**: If no snapshot exists, one will be generated without comparison
25
+ - **Subsequent runs**: Snapshots are compared with the following behavior:
26
+ - In CI environments (`process.env.CI` is set): An error is thrown if differences are detected
27
+ - Locally: No error is thrown, allowing you to review changes with tools like git diff
25
28
 
26
- For all next code executions, snapshots are compared and
29
+ > **Note**: All functions accept a throwWhenDiff parameter to force errors even in local environments.
27
30
 
28
- - An error is thrown when `process.env.CI` is set (code is executed in CI).
29
- - Otherwise nothing special happens (it's your job to review eventual diff in the snapshots, using `git diff` for example)
31
+ ## API Reference
30
32
 
31
- > Every function accepts a `throwWhenDiff` param to throw even if runned locally. You can also set `process.env.CI` before executing your code.
33
+ ### takeFileSnapshot(fileUrl)
32
34
 
33
- ## takeFileSnapshot(fileUrl)
34
-
35
- The code below ensure `writeFileTxt` write `content` into "./file.txt".
36
- Changing that behaviour would fail snapshot comparison.
35
+ Captures and compares the state of a specific file.
37
36
 
38
37
  ```js
39
38
  import { writeFileSync } from "node:fs";
@@ -51,10 +50,9 @@ writeFileTxt("Hello world");
51
50
  fileSnapshot.compare();
52
51
  ```
53
52
 
54
- ## takeDirectorySnapshot(directoryUrl)
53
+ ### takeDirectorySnapshot(directoryUrl)
55
54
 
56
- The code below ensure `writeManyFiles` always write twos file: "./dir/a.txt" and "./dir/b.txt" with the content "a" and "b".
57
- Changing that behaviour would fail snapshot comparison.
55
+ Captures and compares the state of an entire directory.
58
56
 
59
57
  ```js
60
58
  import { writeFileSync } from "node:fs";
@@ -73,9 +71,9 @@ writeFileTxt(directoryUrl);
73
71
  directorySnapshot.compare();
74
72
  ```
75
73
 
76
- ## snapshotTests(testFileUrl, fnRegistertingTests, options)
74
+ ### snapshotTests(testFileUrl, fnRegistertingTests, options)
77
75
 
78
- This function is wonderful:
76
+ The most powerful feature of this library - creates readable markdown snapshots of test executions.
79
77
 
80
78
  ```js
81
79
  import { snapshotTests } from "@jsenv/snapshot";
@@ -104,46 +102,38 @@ await snapshotTests(import.meta.url, ({ test }) => {
104
102
  });
105
103
  ```
106
104
 
107
- The code above is executing `getCircleArea` and produces a markdown files describing how it goes.
108
- This markdown will be compared with any previous version ensuring `getCircleArea` still behave as expected.
109
-
110
- See the markdown at [./docs/\_circle_area.test.js/circle_area.test.js.md](./docs/_circle_area.test.js/circle_area.test.js.md)
105
+ This generates a markdown file documenting how your code behaves in different scenarios.
106
+ See an example at [./docs/\_circle_area.test.js/circle_area.test.js.md](./docs/_circle_area.test.js/circle_area.test.js.md)
111
107
 
112
- Why is it so wonderful?
108
+ ## Why Use snapshotTests?
113
109
 
114
- - You don't have to assert anything, you just call the function
115
- - The markdown files can be reviewed to ensure it is what you expect
116
- - The markdown files can be used as documentation
117
- - Changes in the source code would be reflected in the markdown making it easy to review
110
+ - **Assertion-free testing**: Simply call your functions and let the snapshots document their behavior
111
+ - **Self-documenting tests**: Markdown files serve as both test validation and documentation
112
+ - **Visual change reviews**: Code changes are reflected in snapshots, making reviews easy
113
+ - **Side effect tracking**: Automatically captures and documents:
118
114
 
119
- There is a few more very helpul things hapenning:
115
+ - Console logs [example](./docs/_log.test.js/log.test.js.md)
116
+ - Filesystem operations [example](./docs/_filesystem.test.js/filesystem.test.js.md)
117
+ - And more
120
118
 
121
- - Log side effects are catched, see [./docs/\_logs.test.js/log.test.js.md](./docs/_log.test.js/log.test.js.md)
122
- - Filesystem side effects are catched and undone, see [./docs/\_filesystem.test.js/filesystem.test.js.md](./docs/_filesystem.test.js/filesystem.test.js.md)
123
119
  - Fluctuating values are replaced with stable values, see [#Fluctuating values replacement](#fluctuating-values-replacement)
124
120
 
125
- Advanced examples:
126
-
127
- - Tests how `assert` throw in many different ways: [@jsenv/assert/tests/array.test.js.md](../assert/tests/_array.test.js/array.test.js.md).
128
- - Tests generating build files, starting a server and executing build files in a browser: [@jsenv/core/tests/script_type_module_basic.test.mjs](../../../tests/build/basics/script_type_module_basic/_script_type_module_basic.test.mjs/script_type_module_basic.test.mjs.md).
121
+ ## Stable Snapshots Across Environments
129
122
 
130
- ### Fluctuating values replacement
123
+ To ensure snapshots remain consistent across different machines and CI environments, `@jsenv/snapshot` automatically normalizes fluctuating values:
131
124
 
132
- Snapshots will be generated on your machine or the machine of an other contributor, then on the CI.
125
+ - Time values like "2s" become "Xs"
126
+ - Filesystem paths are standardized
127
+ - Network ports in URLs are removed
128
+ - And much more...
133
129
 
134
- Each execution will happen in a different context. This context influence behaviour of the code and as a consequence might change the snapshot being generated.
130
+ This ensures your snapshot tests remain stable regardless of when or where they run.
135
131
 
136
- - time
137
- - operating system
138
- - filesystem location
139
- - available ressources,
140
- - and so on...
132
+ ## Advanced Examples
141
133
 
142
- To ensure the snapshot generated is not influenced, all fluctuating values are replaced with stable values.
134
+ - Testing complex assertion behavior: [@jsenv/assert/tests/array.test.js.md](../assert/tests/_array.test.js/array.test.js.md)
135
+ - Testing server-side builds with browser execution: [@jsenv/core/tests/script_type_module_basic.test.mjs](../../../tests/build/basics/script_type_module_basic/_script_type_module_basic.test.mjs/script_type_module_basic.test.mjs.md)
143
136
 
144
- - Things like "2s" becomes "Xs"
145
- - Filesystem urls dynamic parts are replaced
146
- - Port in https urls is removed
147
- - and so on...
137
+ ## Contributing
148
138
 
149
- If something is fluctuating and makes your snapshot testing unstable, you can open an issue or create a pull request.
139
+ If you encounter unstable snapshots due to fluctuating values not being properly normalized, please open an issue or submit a pull request.
@@ -9,6 +9,8 @@ const executionEffectsDefault = {
9
9
  return: true,
10
10
  };
11
11
 
12
+ let currentCapture = false;
13
+
12
14
  export const createCaptureSideEffects = ({
13
15
  sourceFileUrl,
14
16
  logEffects = true,
@@ -50,6 +52,7 @@ export const createCaptureSideEffects = ({
50
52
  replaceFilesystemWellKnownValues,
51
53
  };
52
54
  let functionExecutingCount = 0;
55
+ let ignored = false;
53
56
  const capture = (fn, { callSite, baseDirectory } = {}) => {
54
57
  const unicodeSupported = UNICODE.supported;
55
58
  const ansiSupported = ANSI.supported;
@@ -74,6 +77,7 @@ export const createCaptureSideEffects = ({
74
77
  };
75
78
  const onSideEffectRemoved = () => {};
76
79
  const addSideEffect = (sideEffect) => {
80
+ if (ignored) return null;
77
81
  sideEffects.push(sideEffect);
78
82
  onSideEffectAdded(sideEffect);
79
83
  return sideEffect;
@@ -286,6 +290,7 @@ export const createCaptureSideEffects = ({
286
290
  });
287
291
  };
288
292
  const onFinally = () => {
293
+ currentCapture = null;
289
294
  delete process.env.CAPTURING_SIDE_EFFECTS;
290
295
  UNICODE.supported = unicodeSupported;
291
296
  ANSI.supported = ansiSupported;
@@ -326,6 +331,13 @@ export const createCaptureSideEffects = ({
326
331
  process.env.CAPTURING_SIDE_EFFECTS = "1";
327
332
  functionExecutingCount++;
328
333
  let returnedPromise = false;
334
+ currentCapture = {
335
+ ignoreWhile: (fn) => {
336
+ ignored = true;
337
+ fn();
338
+ ignored = false;
339
+ },
340
+ };
329
341
  try {
330
342
  const valueReturned = fn();
331
343
  if (valueReturned && typeof valueReturned.then === "function") {
@@ -355,7 +367,17 @@ export const createCaptureSideEffects = ({
355
367
  }
356
368
  }
357
369
  };
370
+ capture.ignoreSideEffects = ignoreSideEffects;
371
+
358
372
  return capture;
359
373
  };
360
374
 
375
+ export const ignoreSideEffects = (fn) => {
376
+ if (!currentCapture) {
377
+ console.warn(`ignoreSideEffects called outside of captureSideEffects`);
378
+ return;
379
+ }
380
+ currentCapture.ignoreWhile(fn);
381
+ };
382
+
361
383
  const RETURN_PROMISE = {};
@@ -2,7 +2,10 @@ import { writeFileSync } from "@jsenv/filesystem";
2
2
  import { urlToBasename, urlToFilename, urlToRelativeUrl } from "@jsenv/urls";
3
3
  import { takeDirectorySnapshot } from "../filesystem_snapshot.js";
4
4
  import { getCallerLocation } from "../get_caller_location.js";
5
- import { createCaptureSideEffects } from "./create_capture_side_effects.js";
5
+ import {
6
+ createCaptureSideEffects,
7
+ ignoreSideEffects,
8
+ } from "./create_capture_side_effects.js";
6
9
  import { renderSideEffects, renderSmallLink } from "./render_side_effects.js";
7
10
 
8
11
  /**
@@ -235,11 +238,12 @@ export const snapshotTests = async (
235
238
  // snapshotTests.prefConfigure(options)
236
239
  // snapshotTests(import.meta.url, ({ test }) => { })
237
240
  // which are equivalent
238
-
239
241
  snapshotTests.prefConfigure = (options) => {
240
242
  preconfiguredOptions = options;
241
243
  };
242
244
 
245
+ snapshotTests.ignoreSideEffects = ignoreSideEffects;
246
+
243
247
  // see https://github.com/parshap/node-sanitize-filename/blob/master/index.js
244
248
  const asValidFilename = (string) => {
245
249
  return string