@jsenv/snapshot 2.10.1 → 2.10.3
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.10.
|
|
3
|
+
"version": "2.10.3",
|
|
4
4
|
"description": "Snapshot testing",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": {
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
36
|
"@jsenv/assert": "4.4.1",
|
|
37
|
-
"@jsenv/ast": "6.2.
|
|
37
|
+
"@jsenv/ast": "6.2.17",
|
|
38
38
|
"@jsenv/exception": "1.1.2",
|
|
39
39
|
"@jsenv/humanize": "1.2.8",
|
|
40
40
|
"@jsenv/filesystem": "4.10.2",
|
|
@@ -379,24 +379,22 @@ ${extraUrls.join("\n")}`);
|
|
|
379
379
|
const relativeUrl = urlToRelativeUrl(directoryItemUrl, directoryUrl);
|
|
380
380
|
if (directoryItemStat.isDirectory()) {
|
|
381
381
|
ensurePathnameTrailingSlash(directoryItemUrl);
|
|
382
|
-
if (!shouldVisitDirectory(directoryItemUrl)) {
|
|
382
|
+
if (!shouldVisitDirectory(directoryItemUrl.href)) {
|
|
383
383
|
continue;
|
|
384
384
|
}
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
);
|
|
394
|
-
if (clean) {
|
|
385
|
+
const subdirSnapshot = createDirectorySnapshot(directoryItemUrl, {
|
|
386
|
+
shouldVisitDirectory,
|
|
387
|
+
shouldIncludeFile,
|
|
388
|
+
shouldCompareFileContent,
|
|
389
|
+
clean,
|
|
390
|
+
});
|
|
391
|
+
contentSnapshotNaturalOrder[relativeUrl] = subdirSnapshot;
|
|
392
|
+
if (clean && subdirSnapshot) {
|
|
395
393
|
removeDirectorySync(directoryItemUrl);
|
|
396
394
|
}
|
|
397
395
|
continue;
|
|
398
396
|
}
|
|
399
|
-
if (!shouldIncludeFile(directoryItemUrl)) {
|
|
397
|
+
if (!shouldIncludeFile(directoryItemUrl.href)) {
|
|
400
398
|
continue;
|
|
401
399
|
}
|
|
402
400
|
contentSnapshotNaturalOrder[relativeUrl] =
|
|
@@ -15,6 +15,13 @@ export const createCaptureSideEffects = ({
|
|
|
15
15
|
rootDirectoryUrl,
|
|
16
16
|
}),
|
|
17
17
|
}) => {
|
|
18
|
+
if (executionEffects === false) {
|
|
19
|
+
executionEffects = {
|
|
20
|
+
catch: false,
|
|
21
|
+
return: false,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
18
25
|
const detectors = [];
|
|
19
26
|
if (logEffects) {
|
|
20
27
|
detectors.push(logSideEffects(logEffects === true ? {} : logEffects));
|
|
@@ -283,6 +290,33 @@ export const createCaptureSideEffects = ({
|
|
|
283
290
|
finallyCallbackSet.clear();
|
|
284
291
|
};
|
|
285
292
|
|
|
293
|
+
const onThrowOrReject = (value, isThrow) => {
|
|
294
|
+
if (executionEffects.catch === false) {
|
|
295
|
+
throw value;
|
|
296
|
+
}
|
|
297
|
+
if (typeof executionEffects.catch === "function") {
|
|
298
|
+
executionEffects.catch(value);
|
|
299
|
+
}
|
|
300
|
+
if (isThrow) {
|
|
301
|
+
onCatch(value);
|
|
302
|
+
} else {
|
|
303
|
+
onReject(value);
|
|
304
|
+
}
|
|
305
|
+
};
|
|
306
|
+
const onReturnOrResolve = (value, isReturn) => {
|
|
307
|
+
if (executionEffects.return === false) {
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
if (typeof executionEffects.return === "function") {
|
|
311
|
+
executionEffects.return(value);
|
|
312
|
+
}
|
|
313
|
+
if (isReturn) {
|
|
314
|
+
onReturn(value);
|
|
315
|
+
} else {
|
|
316
|
+
onResolve(value);
|
|
317
|
+
}
|
|
318
|
+
};
|
|
319
|
+
|
|
286
320
|
process.env.CAPTURING_SIDE_EFFECTS = "1";
|
|
287
321
|
functionExecutingCount++;
|
|
288
322
|
let returnedPromise = false;
|
|
@@ -292,34 +326,22 @@ export const createCaptureSideEffects = ({
|
|
|
292
326
|
onReturn(RETURN_PROMISE);
|
|
293
327
|
returnedPromise = valueReturned.then(
|
|
294
328
|
(value) => {
|
|
295
|
-
|
|
329
|
+
onReturnOrResolve(value);
|
|
296
330
|
onFinally();
|
|
297
331
|
return sideEffects;
|
|
298
332
|
},
|
|
299
333
|
(e) => {
|
|
300
|
-
|
|
301
|
-
throw e;
|
|
302
|
-
}
|
|
303
|
-
if (typeof executionEffects.catch === "function") {
|
|
304
|
-
executionEffects.catch(e);
|
|
305
|
-
}
|
|
306
|
-
onReject(e);
|
|
334
|
+
onThrowOrReject(e);
|
|
307
335
|
onFinally();
|
|
308
336
|
return sideEffects;
|
|
309
337
|
},
|
|
310
338
|
);
|
|
311
339
|
return returnedPromise;
|
|
312
340
|
}
|
|
313
|
-
|
|
341
|
+
onReturnOrResolve(valueReturned, true);
|
|
314
342
|
return sideEffects;
|
|
315
343
|
} catch (e) {
|
|
316
|
-
|
|
317
|
-
throw e;
|
|
318
|
-
}
|
|
319
|
-
if (typeof executionEffects.catch === "function") {
|
|
320
|
-
executionEffects.catch(e);
|
|
321
|
-
}
|
|
322
|
-
onCatch(e);
|
|
344
|
+
onThrowOrReject(e, true);
|
|
323
345
|
return sideEffects;
|
|
324
346
|
} finally {
|
|
325
347
|
if (!returnedPromise) {
|
|
@@ -4,13 +4,14 @@
|
|
|
4
4
|
// https://github.com/tschaub/mock-fs/issues/348
|
|
5
5
|
|
|
6
6
|
import {
|
|
7
|
+
comparePathnames,
|
|
7
8
|
removeDirectorySync,
|
|
8
9
|
removeFileSync,
|
|
9
10
|
writeFileSync,
|
|
10
11
|
} from "@jsenv/filesystem";
|
|
11
12
|
import { URL_META } from "@jsenv/url-meta";
|
|
12
13
|
import { ensurePathnameTrailingSlash, yieldAncestorUrls } from "@jsenv/urls";
|
|
13
|
-
import { readFileSync, statSync } from "node:fs";
|
|
14
|
+
import { readdirSync, readFileSync, statSync } from "node:fs";
|
|
14
15
|
import { pathToFileURL } from "node:url";
|
|
15
16
|
import {
|
|
16
17
|
disableHooksWhileCalling,
|
|
@@ -47,7 +48,8 @@ export const spyFilesystemCalls = (
|
|
|
47
48
|
const filesystemStateInfoMap = new Map();
|
|
48
49
|
const fileDescriptorPathMap = new Map();
|
|
49
50
|
const fileRestoreMap = new Map();
|
|
50
|
-
const
|
|
51
|
+
const dirRestoreMap = new Map();
|
|
52
|
+
const onFileMutationDone = (stateBefore, stateAfter) => {
|
|
51
53
|
if (!stateAfter.found) {
|
|
52
54
|
// seems to be possible somehow
|
|
53
55
|
return;
|
|
@@ -56,39 +58,42 @@ export const spyFilesystemCalls = (
|
|
|
56
58
|
// - writing file for the 1st time
|
|
57
59
|
// - updating file content
|
|
58
60
|
// the important part is the file content in the end of the function execution
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
61
|
+
let reason;
|
|
62
|
+
if (!stateBefore.found && stateAfter.found) {
|
|
63
|
+
reason = "created";
|
|
64
|
+
} else if (Buffer.compare(stateBefore.buffer, stateAfter.buffer)) {
|
|
65
|
+
reason = "content_modified";
|
|
66
|
+
} else if (stateBefore.mtimeMs !== stateAfter.mtimeMs) {
|
|
67
|
+
reason = "mtime_modified";
|
|
68
|
+
} else if (stateBefore.url !== stateAfter.url) {
|
|
69
|
+
reason = "moved";
|
|
70
|
+
} else {
|
|
68
71
|
// file is exactly the same
|
|
69
72
|
// function did not have any effect on the file
|
|
70
73
|
return;
|
|
71
74
|
}
|
|
72
|
-
const
|
|
75
|
+
const beforeUrl = stateBefore.url;
|
|
76
|
+
const afterUrl = stateAfter.url;
|
|
77
|
+
const action = getAction(beforeUrl);
|
|
73
78
|
const shouldCompare =
|
|
74
79
|
action === "compare" ||
|
|
75
80
|
action === "compare_presence_only" ||
|
|
76
81
|
action === true;
|
|
77
82
|
if (action === "undo" || shouldCompare) {
|
|
78
|
-
if (undoFilesystemSideEffects && !fileRestoreMap.has(
|
|
83
|
+
if (undoFilesystemSideEffects && !fileRestoreMap.has(beforeUrl)) {
|
|
79
84
|
if (stateBefore.found) {
|
|
80
|
-
fileRestoreMap.set(
|
|
81
|
-
writeFileSync(
|
|
85
|
+
fileRestoreMap.set(beforeUrl, () => {
|
|
86
|
+
writeFileSync(beforeUrl, stateBefore.buffer);
|
|
82
87
|
});
|
|
83
88
|
} else {
|
|
84
|
-
fileRestoreMap.set(
|
|
85
|
-
removeFileSync(
|
|
89
|
+
fileRestoreMap.set(beforeUrl, () => {
|
|
90
|
+
removeFileSync(beforeUrl, { allowUseless: true });
|
|
86
91
|
});
|
|
87
92
|
}
|
|
88
93
|
}
|
|
89
94
|
}
|
|
90
95
|
if (shouldCompare) {
|
|
91
|
-
onWriteFile(
|
|
96
|
+
onWriteFile(afterUrl, stateAfter.buffer, reason);
|
|
92
97
|
}
|
|
93
98
|
// "ignore", false, anything else
|
|
94
99
|
};
|
|
@@ -99,12 +104,16 @@ export const spyFilesystemCalls = (
|
|
|
99
104
|
action === "compare_presence_only" ||
|
|
100
105
|
action === true;
|
|
101
106
|
if (action === "undo" || shouldCompare) {
|
|
102
|
-
if (undoFilesystemSideEffects && !
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
107
|
+
if (undoFilesystemSideEffects && !dirRestoreMap.has(directoryUrl)) {
|
|
108
|
+
dirRestoreMap.set(directoryUrl, () => {
|
|
109
|
+
try {
|
|
110
|
+
const isEmpty = readdirSync(new URL(directoryUrl)).length === 0;
|
|
111
|
+
if (isEmpty) {
|
|
112
|
+
removeDirectorySync(directoryUrl, {
|
|
113
|
+
allowUseless: true,
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
} catch {}
|
|
108
117
|
});
|
|
109
118
|
}
|
|
110
119
|
}
|
|
@@ -115,9 +124,9 @@ export const spyFilesystemCalls = (
|
|
|
115
124
|
};
|
|
116
125
|
const restoreCallbackSet = new Set();
|
|
117
126
|
|
|
118
|
-
const getFileStateWithinHook = (
|
|
127
|
+
const getFileStateWithinHook = (filePath) => {
|
|
119
128
|
return disableHooksWhileCalling(
|
|
120
|
-
() => getFileState(
|
|
129
|
+
() => getFileState(filePath),
|
|
121
130
|
[openHook, closeHook],
|
|
122
131
|
);
|
|
123
132
|
};
|
|
@@ -202,14 +211,13 @@ export const spyFilesystemCalls = (
|
|
|
202
211
|
fileDescriptorPathMap.delete(fileDescriptor);
|
|
203
212
|
return;
|
|
204
213
|
}
|
|
205
|
-
const fileUrl = pathToFileURL(filePath);
|
|
206
214
|
if (buffer) {
|
|
207
|
-
onReadFile(
|
|
215
|
+
onReadFile(filePath);
|
|
208
216
|
}
|
|
209
217
|
fileDescriptorPathMap.delete(fileDescriptor);
|
|
210
218
|
filesystemStateInfoMap.delete(filePath);
|
|
211
|
-
const stateAfter = getFileStateWithinHook(
|
|
212
|
-
|
|
219
|
+
const stateAfter = getFileStateWithinHook(filePath);
|
|
220
|
+
onFileMutationDone(stateBefore, stateAfter);
|
|
213
221
|
},
|
|
214
222
|
};
|
|
215
223
|
},
|
|
@@ -219,12 +227,11 @@ export const spyFilesystemCalls = (
|
|
|
219
227
|
_internalFs,
|
|
220
228
|
"writeFileUtf8",
|
|
221
229
|
(filePath) => {
|
|
222
|
-
const
|
|
223
|
-
const stateBefore = getFileStateWithinHook(fileUrl);
|
|
230
|
+
const stateBefore = getFileStateWithinHook(filePath);
|
|
224
231
|
return {
|
|
225
232
|
return: () => {
|
|
226
|
-
const stateAfter = getFileStateWithinHook(
|
|
227
|
-
|
|
233
|
+
const stateAfter = getFileStateWithinHook(filePath);
|
|
234
|
+
onFileMutationDone(stateBefore, stateAfter);
|
|
228
235
|
},
|
|
229
236
|
};
|
|
230
237
|
},
|
|
@@ -236,12 +243,44 @@ export const spyFilesystemCalls = (
|
|
|
236
243
|
},
|
|
237
244
|
};
|
|
238
245
|
});
|
|
246
|
+
const copyFileHook = hookIntoMethod(
|
|
247
|
+
_internalFs,
|
|
248
|
+
"copyFile",
|
|
249
|
+
(fromPath, toPath) => {
|
|
250
|
+
const stateBefore = getFileStateWithinHook(fromPath);
|
|
251
|
+
return {
|
|
252
|
+
return: () => {
|
|
253
|
+
const stateAfter = getFileStateWithinHook(toPath);
|
|
254
|
+
onFileMutationDone(stateBefore, stateAfter);
|
|
255
|
+
},
|
|
256
|
+
};
|
|
257
|
+
},
|
|
258
|
+
{ execute: METHOD_EXECUTION_NODE_CALLBACK },
|
|
259
|
+
);
|
|
260
|
+
const renameHook = hookIntoMethod(
|
|
261
|
+
_internalFs,
|
|
262
|
+
"rename",
|
|
263
|
+
(fromPath, toPath) => {
|
|
264
|
+
const stateBefore = getFileStateWithinHook(fromPath);
|
|
265
|
+
return {
|
|
266
|
+
return: () => {
|
|
267
|
+
const stateAfter = getFileStateWithinHook(toPath);
|
|
268
|
+
onFileMutationDone(stateBefore, stateAfter);
|
|
269
|
+
},
|
|
270
|
+
};
|
|
271
|
+
},
|
|
272
|
+
{
|
|
273
|
+
execute: METHOD_EXECUTION_NODE_CALLBACK,
|
|
274
|
+
},
|
|
275
|
+
);
|
|
239
276
|
restoreCallbackSet.add(() => {
|
|
240
277
|
mkdirHook.remove();
|
|
241
278
|
openHook.remove();
|
|
242
279
|
closeHook.remove();
|
|
243
280
|
writeFileUtf8Hook.remove();
|
|
244
281
|
unlinkHook.remove();
|
|
282
|
+
copyFileHook.remove();
|
|
283
|
+
renameHook.remove();
|
|
245
284
|
});
|
|
246
285
|
return {
|
|
247
286
|
restore: () => {
|
|
@@ -253,15 +292,29 @@ export const spyFilesystemCalls = (
|
|
|
253
292
|
restore();
|
|
254
293
|
}
|
|
255
294
|
fileRestoreMap.clear();
|
|
295
|
+
const dirUrls = Array.from(dirRestoreMap.keys());
|
|
296
|
+
dirUrls.sort((left, right) => {
|
|
297
|
+
return comparePathnames(
|
|
298
|
+
new URL(left).pathname,
|
|
299
|
+
new URL(right).pathname,
|
|
300
|
+
);
|
|
301
|
+
});
|
|
302
|
+
for (const dirUrl of dirUrls) {
|
|
303
|
+
const restore = dirRestoreMap.get(dirUrl);
|
|
304
|
+
restore();
|
|
305
|
+
}
|
|
306
|
+
dirRestoreMap.clear();
|
|
256
307
|
},
|
|
257
308
|
};
|
|
258
309
|
};
|
|
259
310
|
|
|
260
|
-
const getFileState = (
|
|
311
|
+
const getFileState = (filePath) => {
|
|
312
|
+
const fileUrl = pathToFileURL(filePath);
|
|
261
313
|
try {
|
|
262
|
-
const fileBuffer = readFileSync(
|
|
263
|
-
const { mtimeMs } = statSync(
|
|
314
|
+
const fileBuffer = readFileSync(fileUrl);
|
|
315
|
+
const { mtimeMs } = statSync(fileUrl);
|
|
264
316
|
return {
|
|
317
|
+
url: String(fileUrl),
|
|
265
318
|
found: true,
|
|
266
319
|
mtimeMs,
|
|
267
320
|
buffer: fileBuffer,
|
|
@@ -269,6 +322,7 @@ const getFileState = (file) => {
|
|
|
269
322
|
} catch (e) {
|
|
270
323
|
if (e.code === "ENOENT") {
|
|
271
324
|
return {
|
|
325
|
+
url: String(fileUrl),
|
|
272
326
|
found: false,
|
|
273
327
|
};
|
|
274
328
|
}
|
|
@@ -31,10 +31,7 @@ export const snapshotTests = async (
|
|
|
31
31
|
fnRegisteringTest,
|
|
32
32
|
{
|
|
33
33
|
outFilePattern = "./_[source_filename]/[filename]",
|
|
34
|
-
filesystemActions
|
|
35
|
-
"**": "compare",
|
|
36
|
-
// "**/*.svg": "compare_presence_only",
|
|
37
|
-
},
|
|
34
|
+
filesystemActions,
|
|
38
35
|
rootDirectoryUrl,
|
|
39
36
|
generatedBy = true,
|
|
40
37
|
executionEffects,
|
|
@@ -44,6 +41,7 @@ export const snapshotTests = async (
|
|
|
44
41
|
} = {},
|
|
45
42
|
) => {
|
|
46
43
|
filesystemActions = {
|
|
44
|
+
"**": "compare",
|
|
47
45
|
...filesystemActions,
|
|
48
46
|
"**/*.svg": "compare_presence_only",
|
|
49
47
|
};
|