@jsenv/snapshot 2.9.4 → 2.9.5

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.9.4",
3
+ "version": "2.9.5",
4
4
  "description": "Snapshot testing",
5
5
  "license": "MIT",
6
6
  "author": {
@@ -35,7 +35,7 @@
35
35
  "dependencies": {
36
36
  "@jsenv/assert": "4.4.0",
37
37
  "@jsenv/ast": "6.2.16",
38
- "@jsenv/exception": "1.1.0",
38
+ "@jsenv/exception": "1.1.1",
39
39
  "@jsenv/filesystem": "4.10.0",
40
40
  "@jsenv/terminal-recorder": "1.4.4",
41
41
  "@jsenv/urls": "2.5.2",
package/readme.md ADDED
@@ -0,0 +1,149 @@
1
+ # snapshot
2
+
3
+ [![npm package](https://img.shields.io/npm/v/@jsenv/snapshot.svg?logo=npm&label=package)](https://www.npmjs.com/package/@jsenv/snapshot)
4
+
5
+ A tool to generate snapshots during tests.
6
+
7
+ ## A word on snapshot testing
8
+
9
+ Snapshot testing consists into:
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
17
+
18
+ This force code execution to produce the same snapshots. Meaning that code being tested still behave as expected.
19
+
20
+ ## How it works
21
+
22
+ `@jsenv/snapshot` behaves as follow:
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.
25
+
26
+ For all next code executions, snapshots are compared and
27
+
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)
30
+
31
+ > Every function accepts a `throwWhenDiff` param to throw even if runned locally. You can also set `process.env.CI` before executing your code.
32
+
33
+ ## takeFileSnapshot(fileUrl)
34
+
35
+ The code below ensure `writeFileTxt` write `content` into "./file.txt".
36
+ Changing that behaviour would fail snapshot comparison.
37
+
38
+ ```js
39
+ import { writeFileSync } from "node:fs";
40
+ import { takeFileSnapshot } from "@jsenv/snapshot";
41
+
42
+ const fileTxtUrl = new URL("./file.txt", import.meta.url);
43
+ const writeFileTxt = (content) => {
44
+ writeFileSync(writeFileTxt, content);
45
+ };
46
+
47
+ // take snapshot of "./file.txt"
48
+ const fileSnapshot = takeFileSnapshot(fileTxtUrl);
49
+ writeFileTxt("Hello world");
50
+ // compare the state of "./file.txt" with previous version
51
+ fileSnapshot.compare();
52
+ ```
53
+
54
+ ## takeDirectorySnapshot(directoryUrl)
55
+
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.
58
+
59
+ ```js
60
+ import { writeFileSync } from "node:fs";
61
+ import { takeDirectorySnapshot } from "@jsenv/snapshot";
62
+
63
+ const directoryUrl = new URL("./dir/", import.meta.url);
64
+ const writeManyFiles = () => {
65
+ writeFileSync(new URL("./a.txt", directoryUrl), "a");
66
+ writeFileSync(new URL("./b.txt", directoryUrl), "b");
67
+ };
68
+
69
+ // take snapshot of "./dir/"
70
+ const directorySnapshot = takeDirectorySnapshot(directoryUrl);
71
+ writeFileTxt(directoryUrl);
72
+ // compare the state of "./dir/" with previous version
73
+ directorySnapshot.compare();
74
+ ```
75
+
76
+ ## snapshotTests(testFileUrl, fnRegistertingTests, options)
77
+
78
+ This function is wonderful:
79
+
80
+ ```js
81
+ import { snapshotTests } from "@jsenv/snapshot";
82
+
83
+ const getCircleArea = (circleRadius) => {
84
+ if (isNaN(circleRadius)) {
85
+ throw new TypeError(
86
+ `circleRadius must be a number, received ${circleRadius}`,
87
+ );
88
+ }
89
+ return circleRadius * circleRadius * Math.PI;
90
+ };
91
+
92
+ await snapshotTests(import.meta.url, ({ test }) => {
93
+ test("when radius is 2", () => {
94
+ return getCircleArea(2);
95
+ });
96
+
97
+ test("when radius is 10", () => {
98
+ return getCircleArea(10);
99
+ });
100
+
101
+ test("when radius is null", () => {
102
+ return getCircleArea(null);
103
+ });
104
+ });
105
+ ```
106
+
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)
111
+
112
+ Why is it so wonderful?
113
+
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
118
+
119
+ There is a few more very helpul things hapenning:
120
+
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
+ - Fluctuating values are replaced with stable values, see [#Fluctuating values replacement](#fluctuating-values-replacement)
124
+
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).
129
+
130
+ ### Fluctuating values replacement
131
+
132
+ Snapshots will be generated on your machine or the machine of an other contributor, then on the CI.
133
+
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.
135
+
136
+ - time
137
+ - operating system
138
+ - filesystem location
139
+ - available ressources,
140
+ - and so on...
141
+
142
+ To ensure the snapshot generated is not influenced, all fluctuating values are replaced with stable values.
143
+
144
+ - Things like "2s" becomes "Xs"
145
+ - Filesystem urls dynamic parts are replaced
146
+ - Port in https urls is removed
147
+ - and so on...
148
+
149
+ If something is fluctuating and makes your snapshot testing unstable, you can open an issue or create a pull request.
@@ -68,7 +68,7 @@ export const replaceFluctuatingValues = (
68
68
  // );
69
69
  return string;
70
70
  };
71
- const replaceThings = (string) => {
71
+ const replaceThings = (string, { shouldReplaceDurations } = {}) => {
72
72
  if (stringType === "filesystem") {
73
73
  return replaceFilesystemWellKnownValues(string);
74
74
  }
@@ -79,7 +79,9 @@ export const replaceFluctuatingValues = (
79
79
  willBeWrittenOnFilesystem: false,
80
80
  });
81
81
  string = replaceHttpUrls(string);
82
- string = replaceDurations(string);
82
+ if (shouldReplaceDurations !== false) {
83
+ string = replaceDurations(string);
84
+ }
83
85
  string = replaceSizes(string);
84
86
  return string;
85
87
  };
@@ -113,13 +115,17 @@ export const replaceFluctuatingValues = (
113
115
  if (attributes) {
114
116
  for (const name of Object.keys(attributes)) {
115
117
  const attributeValue = attributes[name];
118
+ let newValue;
116
119
  if (name === "timestamp") {
117
- attributes[name] = "[timestamp]";
120
+ newValue = "[timestamp]";
118
121
  } else if (name === "time") {
119
- attributes[name] = "[time]";
122
+ newValue = "[time]";
120
123
  } else {
121
- attributes[name] = replaceThings(attributeValue);
124
+ newValue = replaceThings(attributeValue, {
125
+ shouldReplaceDurations: name !== "style",
126
+ });
122
127
  }
128
+ attributes[name] = newValue;
123
129
  }
124
130
  setHtmlNodeAttributes(node, attributes);
125
131
  }
package/README.md DELETED
@@ -1,7 +0,0 @@
1
- # snapshot [![npm package](https://img.shields.io/npm/v/@jsenv/snapshot.svg?logo=npm&label=package)](https://www.npmjs.com/package/@jsenv/snapshot)
2
-
3
- ## takeDirectorySnapshot
4
-
5
- TODO
6
-
7
- ## takeFileSnapshot