@jsenv/snapshot 2.8.4 → 2.8.6
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 +5 -5
- package/src/filesystem_snapshot.js +12 -8
- package/src/side_effects/create_capture_side_effects.js +5 -1
- package/src/side_effects/filesystem/filesystem_side_effects.js +18 -16
- package/src/side_effects/filesystem/spy_filesystem_calls.js +51 -26
- package/src/side_effects/render_side_effects.js +13 -17
- package/src/side_effects/snapshot_side_effects.js +52 -50
- package/src/side_effects/snapshot_tests.js +78 -54
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jsenv/snapshot",
|
|
3
|
-
"version": "2.8.
|
|
3
|
+
"version": "2.8.6",
|
|
4
4
|
"description": "Snapshot testing",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": {
|
|
@@ -34,10 +34,10 @@
|
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
36
|
"@jsenv/assert": "4.1.15",
|
|
37
|
-
"@jsenv/ast": "6.2.
|
|
38
|
-
"@jsenv/exception": "1.0.
|
|
39
|
-
"@jsenv/filesystem": "4.9.
|
|
40
|
-
"@jsenv/terminal-recorder": "1.4.
|
|
37
|
+
"@jsenv/ast": "6.2.15",
|
|
38
|
+
"@jsenv/exception": "1.0.2",
|
|
39
|
+
"@jsenv/filesystem": "4.9.10",
|
|
40
|
+
"@jsenv/terminal-recorder": "1.4.4",
|
|
41
41
|
"@jsenv/urls": "2.5.2",
|
|
42
42
|
"@jsenv/utils": "2.1.2",
|
|
43
43
|
"ansi-regex": "6.0.1",
|
|
@@ -180,7 +180,7 @@ export const takeDirectorySnapshot = (
|
|
|
180
180
|
return URL_META.urlChildMayMatch({
|
|
181
181
|
url,
|
|
182
182
|
associations,
|
|
183
|
-
predicate: (meta) =>
|
|
183
|
+
predicate: (meta) => meta.action && meta.action !== "ignore",
|
|
184
184
|
});
|
|
185
185
|
};
|
|
186
186
|
const shouldIncludeFile = (url) => {
|
|
@@ -188,9 +188,13 @@ export const takeDirectorySnapshot = (
|
|
|
188
188
|
url,
|
|
189
189
|
associations,
|
|
190
190
|
});
|
|
191
|
-
return
|
|
191
|
+
return (
|
|
192
|
+
meta.action === true ||
|
|
193
|
+
meta.action === "compare" ||
|
|
194
|
+
meta.action === "compare_presence_only"
|
|
195
|
+
);
|
|
192
196
|
};
|
|
193
|
-
const
|
|
197
|
+
const shouldCompareFileContent = (url) => {
|
|
194
198
|
const meta = URL_META.applyAssociations({
|
|
195
199
|
url,
|
|
196
200
|
associations,
|
|
@@ -200,7 +204,7 @@ export const takeDirectorySnapshot = (
|
|
|
200
204
|
const directorySnapshot = createDirectorySnapshot(directoryUrl, {
|
|
201
205
|
shouldVisitDirectory,
|
|
202
206
|
shouldIncludeFile,
|
|
203
|
-
|
|
207
|
+
shouldCompareFileContent,
|
|
204
208
|
clean: true,
|
|
205
209
|
});
|
|
206
210
|
return {
|
|
@@ -209,7 +213,7 @@ export const takeDirectorySnapshot = (
|
|
|
209
213
|
const nextDirectorySnapshot = createDirectorySnapshot(directoryUrl, {
|
|
210
214
|
shouldVisitDirectory,
|
|
211
215
|
shouldIncludeFile,
|
|
212
|
-
|
|
216
|
+
shouldCompareFileContent,
|
|
213
217
|
});
|
|
214
218
|
directorySnapshot.compare(nextDirectorySnapshot, { throwWhenDiff });
|
|
215
219
|
},
|
|
@@ -237,7 +241,7 @@ export const takeDirectorySnapshot = (
|
|
|
237
241
|
};
|
|
238
242
|
const createDirectorySnapshot = (
|
|
239
243
|
directoryUrl,
|
|
240
|
-
{ shouldVisitDirectory, shouldIncludeFile,
|
|
244
|
+
{ shouldVisitDirectory, shouldIncludeFile, shouldCompareFileContent, clean },
|
|
241
245
|
) => {
|
|
242
246
|
const directorySnapshot = {
|
|
243
247
|
type: "directory",
|
|
@@ -322,7 +326,7 @@ ${extraUrls.join("\n")}`);
|
|
|
322
326
|
// content
|
|
323
327
|
{
|
|
324
328
|
for (const relativeUrl of relativeUrls) {
|
|
325
|
-
if (!
|
|
329
|
+
if (!shouldCompareFileContent(new URL(relativeUrl, directoryUrl))) {
|
|
326
330
|
continue;
|
|
327
331
|
}
|
|
328
332
|
const snapshot = directoryContentSnapshot[relativeUrl];
|
|
@@ -384,7 +388,7 @@ ${extraUrls.join("\n")}`);
|
|
|
384
388
|
{
|
|
385
389
|
shouldVisitDirectory,
|
|
386
390
|
shouldIncludeFile,
|
|
387
|
-
|
|
391
|
+
shouldCompareFileContent,
|
|
388
392
|
clean,
|
|
389
393
|
},
|
|
390
394
|
);
|
|
@@ -4,13 +4,15 @@ import { filesystemSideEffects } from "./filesystem/filesystem_side_effects.js";
|
|
|
4
4
|
import { logSideEffects } from "./log/log_side_effects.js";
|
|
5
5
|
|
|
6
6
|
export const createCaptureSideEffects = ({
|
|
7
|
+
sourceFileUrl,
|
|
7
8
|
logEffects = true,
|
|
8
9
|
filesystemEffects = true,
|
|
10
|
+
filesystemActions,
|
|
9
11
|
rootDirectoryUrl,
|
|
10
12
|
replaceFilesystemWellKnownValues = createReplaceFilesystemWellKnownValues({
|
|
11
13
|
rootDirectoryUrl,
|
|
12
14
|
}),
|
|
13
|
-
}
|
|
15
|
+
}) => {
|
|
14
16
|
const detectors = [];
|
|
15
17
|
if (logEffects) {
|
|
16
18
|
detectors.push(logSideEffects(logEffects === true ? {} : logEffects));
|
|
@@ -20,7 +22,9 @@ export const createCaptureSideEffects = ({
|
|
|
20
22
|
filesystemSideEffectsDetector = filesystemSideEffects(
|
|
21
23
|
filesystemEffects === true ? {} : filesystemEffects,
|
|
22
24
|
{
|
|
25
|
+
sourceFileUrl,
|
|
23
26
|
replaceFilesystemWellKnownValues,
|
|
27
|
+
filesystemActions,
|
|
24
28
|
},
|
|
25
29
|
);
|
|
26
30
|
detectors.push(filesystemSideEffectsDetector);
|
|
@@ -7,17 +7,16 @@ import { groupFileSideEffectsPerDirectory } from "./group_file_side_effects_per_
|
|
|
7
7
|
import { spyFilesystemCalls } from "./spy_filesystem_calls.js";
|
|
8
8
|
|
|
9
9
|
const filesystemSideEffectsOptionsDefault = {
|
|
10
|
-
include: null,
|
|
11
10
|
preserve: false,
|
|
12
11
|
baseDirectory: "",
|
|
13
|
-
|
|
12
|
+
textualFilesInline: false,
|
|
14
13
|
};
|
|
15
14
|
const INLINE_MAX_LINES = 20;
|
|
16
15
|
const INLINE_MAX_LENGTH = 2000;
|
|
17
16
|
|
|
18
17
|
export const filesystemSideEffects = (
|
|
19
18
|
filesystemSideEffectsOptions,
|
|
20
|
-
{ replaceFilesystemWellKnownValues },
|
|
19
|
+
{ sourceFileUrl, filesystemActions, replaceFilesystemWellKnownValues },
|
|
21
20
|
) => {
|
|
22
21
|
filesystemSideEffectsOptions = {
|
|
23
22
|
...filesystemSideEffectsOptionsDefault,
|
|
@@ -42,10 +41,11 @@ export const filesystemSideEffects = (
|
|
|
42
41
|
name: "filesystem",
|
|
43
42
|
setBaseDirectory,
|
|
44
43
|
install: (addSideEffect, { addSkippableHandler, addFinallyCallback }) => {
|
|
45
|
-
let {
|
|
46
|
-
filesystemSideEffectsOptions;
|
|
44
|
+
let { preserve, textualFilesInline } = filesystemSideEffectsOptions;
|
|
47
45
|
if (filesystemSideEffectsOptions.baseDirectory) {
|
|
48
46
|
setBaseDirectory(filesystemSideEffectsOptions.baseDirectory);
|
|
47
|
+
} else if (sourceFileUrl) {
|
|
48
|
+
setBaseDirectory(new URL("./", sourceFileUrl));
|
|
49
49
|
}
|
|
50
50
|
const getUrlRelativeToBase = (url) => {
|
|
51
51
|
if (baseDirectory) {
|
|
@@ -155,7 +155,7 @@ export const filesystemSideEffects = (
|
|
|
155
155
|
if (outDirectoryReason) {
|
|
156
156
|
const outUrlRelativeToCommonDirectory = urlToRelativeUrl(
|
|
157
157
|
text.urlInsideOutDirectory,
|
|
158
|
-
options.
|
|
158
|
+
options.sideEffectMdFileUrl,
|
|
159
159
|
);
|
|
160
160
|
groupMd += `${"#".repeat(2)} ${urlRelativeToCommonDirectory}
|
|
161
161
|
${renderFileContent(
|
|
@@ -193,7 +193,7 @@ ${renderFileContent(
|
|
|
193
193
|
);
|
|
194
194
|
const commonDirectoryOutRelativeUrl = urlToRelativeUrl(
|
|
195
195
|
commonDirectoryOutUrl,
|
|
196
|
-
options.
|
|
196
|
+
options.sideEffectMdFileUrl,
|
|
197
197
|
{ preferRelativeNotation: true },
|
|
198
198
|
);
|
|
199
199
|
return {
|
|
@@ -219,12 +219,14 @@ ${renderFileContent(
|
|
|
219
219
|
const isTextual = CONTENT_TYPE.isTextual(contentType);
|
|
220
220
|
let outDirectoryReason;
|
|
221
221
|
if (isTextual) {
|
|
222
|
-
if (
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
222
|
+
if (textualFilesInline) {
|
|
223
|
+
if (String(buffer).split("\n").length > INLINE_MAX_LINES) {
|
|
224
|
+
outDirectoryReason = "lot_of_lines";
|
|
225
|
+
} else if (buffer.size > INLINE_MAX_LENGTH) {
|
|
226
|
+
outDirectoryReason = "lot_of_chars";
|
|
227
|
+
}
|
|
228
|
+
} else {
|
|
229
|
+
outDirectoryReason = "text";
|
|
228
230
|
}
|
|
229
231
|
} else {
|
|
230
232
|
outDirectoryReason = "binary";
|
|
@@ -240,7 +242,7 @@ ${renderFileContent(
|
|
|
240
242
|
outDirectoryReason,
|
|
241
243
|
},
|
|
242
244
|
render: {
|
|
243
|
-
md: ({
|
|
245
|
+
md: ({ sideEffectMdFileUrl, generateOutFileUrl }) => {
|
|
244
246
|
const urlRelativeToBase = getUrlRelativeToBase(url);
|
|
245
247
|
if (outDirectoryReason) {
|
|
246
248
|
let urlInsideOutDirectory = getUrlInsideOutDirectory(
|
|
@@ -267,7 +269,7 @@ ${renderFileContent(
|
|
|
267
269
|
}
|
|
268
270
|
const outRelativeUrl = urlToRelativeUrl(
|
|
269
271
|
urlInsideOutDirectory,
|
|
270
|
-
|
|
272
|
+
sideEffectMdFileUrl,
|
|
271
273
|
{
|
|
272
274
|
preferRelativeNotation: true,
|
|
273
275
|
},
|
|
@@ -311,7 +313,7 @@ ${renderFileContent(
|
|
|
311
313
|
},
|
|
312
314
|
},
|
|
313
315
|
{
|
|
314
|
-
include,
|
|
316
|
+
include: filesystemActions,
|
|
315
317
|
undoFilesystemSideEffects: !preserve,
|
|
316
318
|
},
|
|
317
319
|
);
|
|
@@ -28,9 +28,20 @@ export const spyFilesystemCalls = (
|
|
|
28
28
|
},
|
|
29
29
|
{ include, undoFilesystemSideEffects } = {},
|
|
30
30
|
) => {
|
|
31
|
-
const
|
|
32
|
-
?
|
|
33
|
-
|
|
31
|
+
const getAction = include
|
|
32
|
+
? (() => {
|
|
33
|
+
const associations = URL_META.resolveAssociations(
|
|
34
|
+
{
|
|
35
|
+
action: include,
|
|
36
|
+
},
|
|
37
|
+
"file:///",
|
|
38
|
+
);
|
|
39
|
+
return (url) => {
|
|
40
|
+
const meta = URL_META.applyAssociations({ url, associations });
|
|
41
|
+
return meta.action;
|
|
42
|
+
};
|
|
43
|
+
})()
|
|
44
|
+
: () => "compare";
|
|
34
45
|
|
|
35
46
|
const _internalFs = process.binding("fs");
|
|
36
47
|
const filesystemStateInfoMap = new Map();
|
|
@@ -58,35 +69,49 @@ export const spyFilesystemCalls = (
|
|
|
58
69
|
// function did not have any effect on the file
|
|
59
70
|
return;
|
|
60
71
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
}
|
|
72
|
+
const action = getAction(fileUrl);
|
|
73
|
+
const shouldCompare =
|
|
74
|
+
action === "compare" ||
|
|
75
|
+
action === "compare_presence_only" ||
|
|
76
|
+
action === true;
|
|
77
|
+
if (action === "undo" || shouldCompare) {
|
|
78
|
+
if (undoFilesystemSideEffects && !fileRestoreMap.has(fileUrl)) {
|
|
79
|
+
if (stateBefore.found) {
|
|
80
|
+
fileRestoreMap.set(fileUrl, () => {
|
|
81
|
+
writeFileSync(fileUrl, stateBefore.buffer);
|
|
82
|
+
});
|
|
83
|
+
} else {
|
|
84
|
+
fileRestoreMap.set(fileUrl, () => {
|
|
85
|
+
removeFileSync(fileUrl, { allowUseless: true });
|
|
86
|
+
});
|
|
87
|
+
}
|
|
73
88
|
}
|
|
74
89
|
}
|
|
75
|
-
|
|
90
|
+
if (shouldCompare) {
|
|
91
|
+
onWriteFile(fileUrl, stateAfter.buffer, reason);
|
|
92
|
+
}
|
|
93
|
+
// "ignore", false, anything else
|
|
76
94
|
};
|
|
77
95
|
const onWriteDirectoryDone = (directoryUrl) => {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
96
|
+
const action = getAction(directoryUrl);
|
|
97
|
+
const shouldCompare =
|
|
98
|
+
action === "compare" ||
|
|
99
|
+
action === "compare_presence_only" ||
|
|
100
|
+
action === true;
|
|
101
|
+
if (action === "undo" || shouldCompare) {
|
|
102
|
+
if (undoFilesystemSideEffects && !fileRestoreMap.has(directoryUrl)) {
|
|
103
|
+
fileRestoreMap.set(directoryUrl, () => {
|
|
104
|
+
removeDirectorySync(directoryUrl, {
|
|
105
|
+
allowUseless: true,
|
|
106
|
+
recursive: true,
|
|
107
|
+
});
|
|
86
108
|
});
|
|
87
|
-
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
if (shouldCompare) {
|
|
112
|
+
onWriteDirectory(directoryUrl);
|
|
88
113
|
}
|
|
89
|
-
|
|
114
|
+
// "ignore", false, anything else
|
|
90
115
|
};
|
|
91
116
|
const restoreCallbackSet = new Set();
|
|
92
117
|
|
|
@@ -37,8 +37,7 @@ export const createBigSizeEffect =
|
|
|
37
37
|
export const renderSideEffects = (
|
|
38
38
|
sideEffects,
|
|
39
39
|
{
|
|
40
|
-
|
|
41
|
-
outDirectoryUrl,
|
|
40
|
+
sideEffectMdFileUrl,
|
|
42
41
|
generateOutFileUrl,
|
|
43
42
|
generatedBy = true,
|
|
44
43
|
titleLevel = 1,
|
|
@@ -86,8 +85,7 @@ export const renderSideEffects = (
|
|
|
86
85
|
markdown += "\n\n";
|
|
87
86
|
}
|
|
88
87
|
markdown += renderOneSideEffect(sideEffect, {
|
|
89
|
-
|
|
90
|
-
outDirectoryUrl,
|
|
88
|
+
sideEffectMdFileUrl,
|
|
91
89
|
generateOutFileUrl,
|
|
92
90
|
rootDirectoryUrl,
|
|
93
91
|
titleLevel,
|
|
@@ -135,8 +133,7 @@ ${" ".repeat(indent)}</sub>`;
|
|
|
135
133
|
const renderOneSideEffect = (
|
|
136
134
|
sideEffect,
|
|
137
135
|
{
|
|
138
|
-
|
|
139
|
-
outDirectoryUrl,
|
|
136
|
+
sideEffectMdFileUrl,
|
|
140
137
|
generateOutFileUrl,
|
|
141
138
|
rootDirectoryUrl,
|
|
142
139
|
titleLevel,
|
|
@@ -155,8 +152,7 @@ const renderOneSideEffect = (
|
|
|
155
152
|
}
|
|
156
153
|
const { md } = sideEffect.render;
|
|
157
154
|
let { label, text } = md({
|
|
158
|
-
|
|
159
|
-
outDirectoryUrl,
|
|
155
|
+
sideEffectMdFileUrl,
|
|
160
156
|
generateOutFileUrl,
|
|
161
157
|
replace,
|
|
162
158
|
rootDirectoryUrl,
|
|
@@ -175,7 +171,7 @@ const renderOneSideEffect = (
|
|
|
175
171
|
}
|
|
176
172
|
text = renderText(text, {
|
|
177
173
|
sideEffect,
|
|
178
|
-
|
|
174
|
+
sideEffectMdFileUrl,
|
|
179
175
|
generateOutFileUrl,
|
|
180
176
|
replace,
|
|
181
177
|
rootDirectoryUrl,
|
|
@@ -212,7 +208,7 @@ const renderText = (
|
|
|
212
208
|
text,
|
|
213
209
|
{
|
|
214
210
|
sideEffect,
|
|
215
|
-
|
|
211
|
+
sideEffectMdFileUrl,
|
|
216
212
|
generateOutFileUrl,
|
|
217
213
|
replace,
|
|
218
214
|
rootDirectoryUrl,
|
|
@@ -229,7 +225,7 @@ const renderText = (
|
|
|
229
225
|
}
|
|
230
226
|
const callSiteRelativeUrl = urlToRelativeUrl(
|
|
231
227
|
callSite.url,
|
|
232
|
-
|
|
228
|
+
sideEffectMdFileUrl,
|
|
233
229
|
{ preferRelativeNotation: true },
|
|
234
230
|
);
|
|
235
231
|
const sourceCodeLinkText = `${callSiteRelativeUrl}:${callSite.line}:${callSite.column}`;
|
|
@@ -265,7 +261,7 @@ const renderText = (
|
|
|
265
261
|
return renderPotentialAnsi(exceptionText, {
|
|
266
262
|
stringType: "error",
|
|
267
263
|
sideEffect,
|
|
268
|
-
|
|
264
|
+
sideEffectMdFileUrl,
|
|
269
265
|
generateOutFileUrl,
|
|
270
266
|
replace,
|
|
271
267
|
});
|
|
@@ -275,7 +271,7 @@ const renderText = (
|
|
|
275
271
|
if (text.type === "console") {
|
|
276
272
|
return renderConsole(text.value, {
|
|
277
273
|
sideEffect,
|
|
278
|
-
|
|
274
|
+
sideEffectMdFileUrl,
|
|
279
275
|
generateOutFileUrl,
|
|
280
276
|
replace,
|
|
281
277
|
});
|
|
@@ -295,12 +291,12 @@ const renderText = (
|
|
|
295
291
|
|
|
296
292
|
export const renderConsole = (
|
|
297
293
|
string,
|
|
298
|
-
{ sideEffect,
|
|
294
|
+
{ sideEffect, sideEffectMdFileUrl, generateOutFileUrl, replace },
|
|
299
295
|
) => {
|
|
300
296
|
return renderPotentialAnsi(string, {
|
|
301
297
|
stringType: "console",
|
|
302
298
|
sideEffect,
|
|
303
|
-
|
|
299
|
+
sideEffectMdFileUrl,
|
|
304
300
|
generateOutFileUrl,
|
|
305
301
|
replace,
|
|
306
302
|
});
|
|
@@ -308,7 +304,7 @@ export const renderConsole = (
|
|
|
308
304
|
|
|
309
305
|
const renderPotentialAnsi = (
|
|
310
306
|
string,
|
|
311
|
-
{ stringType, sideEffect,
|
|
307
|
+
{ stringType, sideEffect, sideEffectMdFileUrl, generateOutFileUrl, replace },
|
|
312
308
|
) => {
|
|
313
309
|
const rawTextBlock = renderMarkdownBlock(
|
|
314
310
|
replace(string, { stringType }),
|
|
@@ -332,7 +328,7 @@ const renderPotentialAnsi = (
|
|
|
332
328
|
);
|
|
333
329
|
svgFileContent = replace(svgFileContent, { fileUrl: svgFileUrl });
|
|
334
330
|
writeFileSync(svgFileUrl, svgFileContent);
|
|
335
|
-
const svgFileRelativeUrl = urlToRelativeUrl(svgFileUrl,
|
|
331
|
+
const svgFileRelativeUrl = urlToRelativeUrl(svgFileUrl, sideEffectMdFileUrl);
|
|
336
332
|
let md = ``;
|
|
337
333
|
md += "\n\n";
|
|
338
334
|
md += renderMarkdownDetails(`${rawTextBlock}`, {
|
|
@@ -1,69 +1,71 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
takeFileSnapshot,
|
|
5
|
-
} from "../filesystem_snapshot.js";
|
|
1
|
+
import { writeFileSync } from "@jsenv/filesystem";
|
|
2
|
+
import { urlToBasename, urlToFilename } from "@jsenv/urls";
|
|
3
|
+
import { takeDirectorySnapshot } from "../filesystem_snapshot.js";
|
|
6
4
|
import { createCaptureSideEffects } from "./create_capture_side_effects.js";
|
|
7
5
|
import { renderSideEffects } from "./render_side_effects.js";
|
|
8
|
-
|
|
6
|
+
/**
|
|
7
|
+
* Generate a markdown file describing code side effects. When executed in CI throw if there is a diff.
|
|
8
|
+
* @param {URL} sourceFileUrl
|
|
9
|
+
* Url where the function is located (import.meta.url)
|
|
10
|
+
* @param {Function} fn
|
|
11
|
+
* Function to snapshot
|
|
12
|
+
* @param {Object} snapshotSideEffectsOptions
|
|
13
|
+
* @param {string|url} snapshotSideEffectsOptions.outFilePattern
|
|
14
|
+
* @param {string|url} snapshotSideEffectsOptions.sideEffectMdFileUrl
|
|
15
|
+
* Where to write the markdown file. Defaults to ./[]
|
|
16
|
+
* @param {string|url} snapshotSideEffectsOptions.rootDirectoryUrl
|
|
17
|
+
* @param {Object|boolean} [snapshotSideEffectsOptions.filesystemEffects]
|
|
18
|
+
* @param {boolean} [snapshotSideEffectsOptions.filesystemEffects.textualFilesInline=false]
|
|
19
|
+
* Put textual files content in the markdown (instead of separate files).
|
|
20
|
+
* Big files will still be put in dedicated files.
|
|
21
|
+
* @param {boolean} [snapshotSideEffectsOptions.filesystemEffects.preserve=false]
|
|
22
|
+
* Preserve filesystem side effect when function ends. By default
|
|
23
|
+
* filesystem effects are undone when function ends
|
|
24
|
+
* @param {url} [snapshotSideEffectsOptions.filesystemEffects.baseDirectory]
|
|
25
|
+
* Urls of filesystem side effects will be relative to this base directory
|
|
26
|
+
* Default to the directory containing @sourceFileUrl
|
|
27
|
+
* @return {Array.<Object>} sideEffects
|
|
28
|
+
*/
|
|
9
29
|
export const snapshotSideEffects = (
|
|
10
30
|
sourceFileUrl,
|
|
11
31
|
fn,
|
|
12
32
|
{
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
sideEffectFilePattern = "./side_effects/[basename].md",
|
|
16
|
-
outFilePattern = "./side_effects/[name]/[filename]",
|
|
17
|
-
generateOutFileUrl,
|
|
18
|
-
outDirectoryUrl,
|
|
33
|
+
sideEffectMdFileUrl,
|
|
34
|
+
outFilePattern = "_[source_filename]/[filename]",
|
|
19
35
|
errorStackHidden,
|
|
36
|
+
throwWhenDiff,
|
|
20
37
|
...captureOptions
|
|
21
38
|
} = {},
|
|
22
39
|
) => {
|
|
23
|
-
const
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
.replaceAll("[
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
generateOutFileUrl = (filename) => {
|
|
44
|
-
const outRelativeUrl = outFilePattern
|
|
45
|
-
.replaceAll("[name]", name)
|
|
46
|
-
.replaceAll("[basename]", basename)
|
|
47
|
-
.replaceAll("[filename]", filename);
|
|
48
|
-
const outFileUrl = new URL(outRelativeUrl, new URL("./", sourceFileUrl))
|
|
49
|
-
.href;
|
|
50
|
-
return outFileUrl;
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
const sideEffectFileSnapshot = takeFileSnapshot(sideEffectFileUrl);
|
|
40
|
+
const sourceName = urlToBasename(sourceFileUrl, true);
|
|
41
|
+
const sourceBasename = urlToBasename(sourceFileUrl);
|
|
42
|
+
const sourceFilename = urlToFilename(sourceFileUrl);
|
|
43
|
+
const generateOutFileUrl = (filename) => {
|
|
44
|
+
const outRelativeUrl = outFilePattern
|
|
45
|
+
.replaceAll("[source_name]", sourceName)
|
|
46
|
+
.replaceAll("[source_basename]", sourceBasename)
|
|
47
|
+
.replaceAll("[source_filename]", sourceFilename)
|
|
48
|
+
.replaceAll("[filename]", filename);
|
|
49
|
+
const outFileUrl = new URL(outRelativeUrl, new URL("./", sourceFileUrl))
|
|
50
|
+
.href;
|
|
51
|
+
return outFileUrl;
|
|
52
|
+
};
|
|
53
|
+
const outDirectoryUrl = generateOutFileUrl("");
|
|
54
|
+
sideEffectMdFileUrl =
|
|
55
|
+
sideEffectMdFileUrl || generateOutFileUrl(`${sourceFilename}.md`);
|
|
56
|
+
const captureSideEffects = createCaptureSideEffects({
|
|
57
|
+
...captureOptions,
|
|
58
|
+
sourceFileUrl,
|
|
59
|
+
});
|
|
55
60
|
const outDirectorySnapshot = takeDirectorySnapshot(outDirectoryUrl);
|
|
56
61
|
const onSideEffects = (sideEffects) => {
|
|
57
62
|
const sideEffectFileContent = renderSideEffects(sideEffects, {
|
|
58
|
-
|
|
59
|
-
outDirectoryUrl,
|
|
63
|
+
sideEffectMdFileUrl,
|
|
60
64
|
generateOutFileUrl,
|
|
61
65
|
errorStackHidden,
|
|
62
66
|
});
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
});
|
|
66
|
-
outDirectorySnapshot.compare();
|
|
67
|
+
writeFileSync(sideEffectMdFileUrl, sideEffectFileContent);
|
|
68
|
+
outDirectorySnapshot.compare(throwWhenDiff);
|
|
67
69
|
};
|
|
68
70
|
const returnValue = captureSideEffects(fn);
|
|
69
71
|
if (returnValue && typeof returnValue.then === "function") {
|
|
@@ -1,30 +1,40 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
takeFileSnapshot,
|
|
5
|
-
} from "../filesystem_snapshot.js";
|
|
1
|
+
import { writeFileSync } from "@jsenv/filesystem";
|
|
2
|
+
import { urlToBasename, urlToFilename, urlToRelativeUrl } from "@jsenv/urls";
|
|
3
|
+
import { takeDirectorySnapshot } from "../filesystem_snapshot.js";
|
|
6
4
|
import { getCallerLocation } from "../get_caller_location.js";
|
|
7
5
|
import { createCaptureSideEffects } from "./create_capture_side_effects.js";
|
|
8
6
|
import { renderSideEffects, renderSmallLink } from "./render_side_effects.js";
|
|
9
7
|
|
|
10
8
|
/**
|
|
11
|
-
* Generate a markdown file describing
|
|
12
|
-
* @param {URL}
|
|
9
|
+
* Generate a markdown file describing test(s) side effects. When executed in CI throw if there is a diff.
|
|
10
|
+
* @param {URL} sourceFileUrl
|
|
13
11
|
* @param {Function} fnRegisteringTest
|
|
14
12
|
* @param {Object} snapshotTestsOptions
|
|
15
|
-
* @param {string|url} snapshotTestsOptions.
|
|
13
|
+
* @param {string|url} snapshotTestsOptions.outFilePattern
|
|
16
14
|
* @param {string|url} snapshotTestsOptions.rootDirectoryUrl
|
|
17
|
-
* @
|
|
15
|
+
* @param {Object} [snapshotTestsOptions.filesystemActions]
|
|
16
|
+
* Control what to do when there is a file side effect
|
|
17
|
+
* "compare", "compare_presence_only", "undo", "ignore"
|
|
18
|
+
* @param {Object|boolean} [snapshotTestsOptions.filesystemEffects]
|
|
19
|
+
* @param {boolean} [snapshotTestsOptions.filesystemEffects.textualFilesInline=false]
|
|
20
|
+
* Put textual files content in the markdown (instead of separate files).
|
|
21
|
+
* Big files will still be put in dedicated files.
|
|
22
|
+
* @param {boolean} [snapshotTestsOptions.filesystemEffects.preserve=false]
|
|
23
|
+
* Preserve filesystem side effect when function ends. By default
|
|
24
|
+
* filesystem effects are undone when function ends
|
|
25
|
+
* @param {url} [snapshotTestsOptions.filesystemEffects.baseDirectory]
|
|
26
|
+
* Urls of filesystem side effects will be relative to this base directory
|
|
27
|
+
* Default to the directory containing @sourceFileUrl
|
|
18
28
|
*/
|
|
19
29
|
export const snapshotTests = async (
|
|
20
|
-
|
|
30
|
+
sourceFileUrl,
|
|
21
31
|
fnRegisteringTest,
|
|
22
32
|
{
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
33
|
+
outFilePattern = "./_[source_filename]/[filename]",
|
|
34
|
+
filesystemActions = {
|
|
35
|
+
"**": "compare",
|
|
36
|
+
// "**/*.svg": "compare_presence_only",
|
|
37
|
+
},
|
|
28
38
|
rootDirectoryUrl,
|
|
29
39
|
generatedBy = true,
|
|
30
40
|
linkToSource = true,
|
|
@@ -36,15 +46,29 @@ export const snapshotTests = async (
|
|
|
36
46
|
throwWhenDiff = process.env.CI,
|
|
37
47
|
} = {},
|
|
38
48
|
) => {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
49
|
+
filesystemActions = {
|
|
50
|
+
...filesystemActions,
|
|
51
|
+
"**/*.svg": "presence_only",
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const sourceName = urlToBasename(sourceFileUrl, true);
|
|
55
|
+
const sourceBasename = urlToBasename(sourceFileUrl);
|
|
56
|
+
const sourceFilename = urlToFilename(sourceFileUrl);
|
|
57
|
+
const generateOutFileUrl = (outFilename) => {
|
|
58
|
+
const outFileRelativeUrl = outFilePattern
|
|
59
|
+
.replaceAll("[source_name]", sourceName)
|
|
60
|
+
.replaceAll("[source_basename]", sourceBasename)
|
|
61
|
+
.replaceAll("[source_filename]", sourceFilename)
|
|
62
|
+
.replaceAll("[filename]", outFilename);
|
|
63
|
+
const outFileUrl = new URL(outFileRelativeUrl, sourceFileUrl).href;
|
|
64
|
+
return outFileUrl;
|
|
65
|
+
};
|
|
66
|
+
const outDirectoryUrl = generateOutFileUrl("");
|
|
67
|
+
const outDirectorySnapshot = takeDirectorySnapshot(
|
|
68
|
+
outDirectoryUrl,
|
|
69
|
+
filesystemActions,
|
|
70
|
+
);
|
|
71
|
+
const sideEffectMdFileUrl = generateOutFileUrl(`${sourceFilename}.md`);
|
|
48
72
|
|
|
49
73
|
const dirUrlMap = new Map();
|
|
50
74
|
const sideEffectsMap = new Map();
|
|
@@ -66,12 +90,14 @@ export const snapshotTests = async (
|
|
|
66
90
|
|
|
67
91
|
const activeTestMap = onlyTestMap.size ? onlyTestMap : testMap;
|
|
68
92
|
const captureSideEffects = createCaptureSideEffects({
|
|
93
|
+
sourceFileUrl,
|
|
69
94
|
rootDirectoryUrl,
|
|
70
95
|
logEffects,
|
|
71
96
|
filesystemEffects,
|
|
97
|
+
filesystemActions,
|
|
72
98
|
});
|
|
73
99
|
let markdown = "";
|
|
74
|
-
markdown += `# ${
|
|
100
|
+
markdown += `# ${sourceName}`;
|
|
75
101
|
if (generatedBy) {
|
|
76
102
|
let generatedByLink = renderSmallLink(
|
|
77
103
|
{
|
|
@@ -81,8 +107,8 @@ export const snapshotTests = async (
|
|
|
81
107
|
{
|
|
82
108
|
prefix: "Generated by ",
|
|
83
109
|
suffix:
|
|
84
|
-
linkToSource &&
|
|
85
|
-
? generateExecutingLink(
|
|
110
|
+
linkToSource && sourceFileUrl
|
|
111
|
+
? generateExecutingLink(sourceFileUrl, sideEffectMdFileUrl)
|
|
86
112
|
: "",
|
|
87
113
|
},
|
|
88
114
|
);
|
|
@@ -90,15 +116,7 @@ export const snapshotTests = async (
|
|
|
90
116
|
markdown += generatedByLink;
|
|
91
117
|
}
|
|
92
118
|
|
|
93
|
-
const
|
|
94
|
-
"[test_name]",
|
|
95
|
-
testName,
|
|
96
|
-
);
|
|
97
|
-
const outDirectoryUrl = new URL(outDirectoryRelativeUrl, testFileUrl);
|
|
98
|
-
const outDirectorySnapshot = takeDirectorySnapshot(outDirectoryUrl, {
|
|
99
|
-
"**/*": true,
|
|
100
|
-
"**/*.svg": "presence_only",
|
|
101
|
-
});
|
|
119
|
+
const scenarioDirs = [];
|
|
102
120
|
for (const [scenario, { fn, callSite }] of activeTestMap) {
|
|
103
121
|
markdown += "\n\n";
|
|
104
122
|
markdown += `## ${scenario}`;
|
|
@@ -109,21 +127,15 @@ export const snapshotTests = async (
|
|
|
109
127
|
});
|
|
110
128
|
sideEffectsMap.set(scenario, sideEffects);
|
|
111
129
|
const testScenario = asValidFilename(scenario);
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
.replaceAll("[test_basename]", testBasename)
|
|
116
|
-
.replaceAll("[test_scenario]", testScenario)
|
|
117
|
-
.replaceAll("[filename]", filename);
|
|
118
|
-
const outFileUrl = new URL(outFileRelativeUrl, testFileUrl).href;
|
|
119
|
-
return outFileUrl;
|
|
130
|
+
scenarioDirs.push(testScenario);
|
|
131
|
+
const generateScenarioOutFileUrl = (outfilename) => {
|
|
132
|
+
return generateOutFileUrl(`${testScenario}/${outfilename}`);
|
|
120
133
|
};
|
|
121
|
-
const
|
|
122
|
-
dirUrlMap.set(scenario,
|
|
134
|
+
const scenarioOutDirectoryUrl = generateScenarioOutFileUrl("");
|
|
135
|
+
dirUrlMap.set(scenario, scenarioOutDirectoryUrl);
|
|
123
136
|
const sideEffectsMarkdown = renderSideEffects(sideEffects, {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
generateOutFileUrl,
|
|
137
|
+
sideEffectMdFileUrl,
|
|
138
|
+
generateOutFileUrl: generateScenarioOutFileUrl,
|
|
127
139
|
generatedBy: false,
|
|
128
140
|
titleLevel: 3,
|
|
129
141
|
errorStackHidden,
|
|
@@ -131,11 +143,23 @@ export const snapshotTests = async (
|
|
|
131
143
|
});
|
|
132
144
|
markdown += sideEffectsMarkdown;
|
|
133
145
|
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
146
|
+
// if (sideEffectFilePattern === "./side_effects/[filename]/[filename].md") {
|
|
147
|
+
// const scenarioParentDirUrl = new URL("./", sideEffectFileUrl);
|
|
148
|
+
// const dirContent = readDirectorySync(scenarioParentDirUrl);
|
|
149
|
+
// for (const entry of dirContent) {
|
|
150
|
+
// const entryUrl = new URL(entry, scenarioParentDirUrl);
|
|
151
|
+
// if (!readEntryStatSync(entryUrl).isDirectory()) {
|
|
152
|
+
// continue;
|
|
153
|
+
// }
|
|
154
|
+
// if (scenarioDirs.includes(entry)) {
|
|
155
|
+
// continue;
|
|
156
|
+
// }
|
|
157
|
+
// removeDirectorySync(entryUrl, {
|
|
158
|
+
// recursive: true,
|
|
159
|
+
// });
|
|
160
|
+
// }
|
|
161
|
+
// }
|
|
162
|
+
writeFileSync(sideEffectMdFileUrl, markdown);
|
|
139
163
|
outDirectorySnapshot.compare(throwWhenDiff);
|
|
140
164
|
|
|
141
165
|
return { dirUrlMap, sideEffectsMap };
|