@jsenv/snapshot 2.6.10 → 2.7.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.6.10",
3
+ "version": "2.7.0",
4
4
  "description": "Snapshot testing",
5
5
  "license": "MIT",
6
6
  "author": {
@@ -21,7 +21,7 @@ export const replaceFluctuatingValues = (
21
21
  stringType,
22
22
  rootDirectoryUrl,
23
23
  fileUrl,
24
- removeAnsi = true,
24
+ preserveAnsi,
25
25
  // for unit test
26
26
  replaceFilesystemWellKnownValues = createReplaceFilesystemWellKnownValues({
27
27
  rootDirectoryUrl,
@@ -51,7 +51,7 @@ export const replaceFluctuatingValues = (
51
51
  if (stringType === "filesystem") {
52
52
  return replaceFilesystemWellKnownValues(value);
53
53
  }
54
- if (removeAnsi) {
54
+ if (!preserveAnsi) {
55
55
  value = stripAnsi(value);
56
56
  }
57
57
  value = replaceFilesystemWellKnownValues(value, {
@@ -55,19 +55,19 @@ export const filesystemSideEffects = (
55
55
  }
56
56
  return url;
57
57
  };
58
- const getUrlInsideOutDirectory = (url, outDirectoryUrl) => {
58
+ const getUrlInsideOutDirectory = (url, generateOutFileUrl) => {
59
59
  if (baseDirectory) {
60
60
  if (
61
61
  url.href === baseDirectory.href ||
62
62
  urlIsInsideOf(url, baseDirectory)
63
63
  ) {
64
64
  const outRelativeUrl = urlToRelativeUrl(url, baseDirectory);
65
- return new URL(outRelativeUrl, outDirectoryUrl);
65
+ return generateOutFileUrl(outRelativeUrl);
66
66
  }
67
67
  }
68
68
  // otherwise we replace the url with well known
69
69
  const toRelativeUrl = replaceFilesystemWellKnownValues(url);
70
- return new URL(toRelativeUrl, outDirectoryUrl);
70
+ return generateOutFileUrl(toRelativeUrl);
71
71
  };
72
72
 
73
73
  addSkippableHandler((sideEffect) => {
@@ -189,7 +189,7 @@ ${renderFileContent(
189
189
  if (allFilesInsideOutDirectory) {
190
190
  const commonDirectoryOutUrl = getUrlInsideOutDirectory(
191
191
  commonDirectoryUrl,
192
- options.outDirectoryUrl,
192
+ options.generateOutFileUrl,
193
193
  );
194
194
  const commonDirectoryOutRelativeUrl = urlToRelativeUrl(
195
195
  commonDirectoryOutUrl,
@@ -240,15 +240,15 @@ ${renderFileContent(
240
240
  outDirectoryReason,
241
241
  },
242
242
  render: {
243
- md: ({ sideEffectFileUrl, outDirectoryUrl }) => {
243
+ md: ({ sideEffectFileUrl, generateOutFileUrl }) => {
244
244
  const urlRelativeToBase = getUrlRelativeToBase(url);
245
245
  if (outDirectoryReason) {
246
- const urlInsideOutDirectory = getUrlInsideOutDirectory(
246
+ let urlInsideOutDirectory = getUrlInsideOutDirectory(
247
247
  url,
248
- outDirectoryUrl,
248
+ generateOutFileUrl,
249
249
  );
250
250
  if (writeFileSideEffect.counter) {
251
- setUrlBasename(
251
+ urlInsideOutDirectory = setUrlBasename(
252
252
  urlInsideOutDirectory,
253
253
  (basename) =>
254
254
  `${basename}_${writeFileSideEffect.counter}`,
@@ -37,6 +37,10 @@ export const spyFilesystemCalls = (
37
37
  const fileDescriptorPathMap = new Map();
38
38
  const fileRestoreMap = new Map();
39
39
  const onWriteFileDone = (fileUrl, stateBefore, stateAfter) => {
40
+ if (!stateAfter.found) {
41
+ // not suppoed to happen but could apparently
42
+ return;
43
+ }
40
44
  // we use same type because we don't want to differentiate between
41
45
  // - writing file for the 1st time
42
46
  // - updating file content
@@ -1,7 +1,7 @@
1
1
  import { createException } from "@jsenv/exception";
2
2
  import { writeFileSync } from "@jsenv/filesystem";
3
3
  import { renderTerminalSvg } from "@jsenv/terminal-recorder";
4
- import { urlToBasename, urlToExtension, urlToRelativeUrl } from "@jsenv/urls";
4
+ import { urlToExtension, urlToRelativeUrl } from "@jsenv/urls";
5
5
  import ansiRegex from "ansi-regex";
6
6
  import { replaceFluctuatingValues } from "../replace_fluctuating_values.js";
7
7
 
@@ -39,6 +39,7 @@ export const renderSideEffects = (
39
39
  {
40
40
  sideEffectFileUrl,
41
41
  outDirectoryUrl,
42
+ generateOutFileUrl,
42
43
  generatedBy = true,
43
44
  titleLevel = 1,
44
45
  getBigSizeEffect = createBigSizeEffect({
@@ -86,6 +87,7 @@ export const renderSideEffects = (
86
87
  markdown += renderOneSideEffect(sideEffect, {
87
88
  sideEffectFileUrl,
88
89
  outDirectoryUrl,
90
+ generateOutFileUrl,
89
91
  rootDirectoryUrl,
90
92
  titleLevel,
91
93
  getBigSizeEffect,
@@ -133,6 +135,7 @@ const renderOneSideEffect = (
133
135
  {
134
136
  sideEffectFileUrl,
135
137
  outDirectoryUrl,
138
+ generateOutFileUrl,
136
139
  rootDirectoryUrl,
137
140
  titleLevel,
138
141
  getBigSizeEffect,
@@ -151,6 +154,7 @@ const renderOneSideEffect = (
151
154
  let { label, text } = md({
152
155
  sideEffectFileUrl,
153
156
  outDirectoryUrl,
157
+ generateOutFileUrl,
154
158
  replace,
155
159
  rootDirectoryUrl,
156
160
  lastSideEffectNumber,
@@ -159,7 +163,7 @@ const renderOneSideEffect = (
159
163
  text = renderText(text, {
160
164
  sideEffect,
161
165
  sideEffectFileUrl,
162
- outDirectoryUrl,
166
+ generateOutFileUrl,
163
167
  replace,
164
168
  rootDirectoryUrl,
165
169
  errorStackHidden,
@@ -200,7 +204,7 @@ const renderText = (
200
204
  {
201
205
  sideEffect,
202
206
  sideEffectFileUrl,
203
- outDirectoryUrl,
207
+ generateOutFileUrl,
204
208
  replace,
205
209
  rootDirectoryUrl,
206
210
  errorStackHidden,
@@ -242,25 +246,25 @@ const renderText = (
242
246
  typeof value.stack === "string")
243
247
  ) {
244
248
  onRenderError();
249
+ // return renderMarkdownBlock(text.value.stack);
245
250
  const exception = createException(text.value, { rootDirectoryUrl });
246
251
  const exceptionText = errorStackHidden
247
252
  ? `${exception.name}: ${exception.message}`
248
253
  : exception.stack || exception.message || exception;
249
- const potentialAnsi = renderPotentialAnsi(exceptionText, {
254
+ return renderPotentialAnsi(exceptionText, {
255
+ stringType: "error",
250
256
  sideEffect,
251
257
  sideEffectFileUrl,
252
- outDirectoryUrl,
258
+ generateOutFileUrl,
253
259
  replace,
254
260
  });
255
- if (potentialAnsi) {
256
- return potentialAnsi;
257
- }
258
- return renderMarkdownBlock(
259
- replace(exceptionText, { stringType: "error" }),
260
- );
261
261
  }
262
+
262
263
  return renderMarkdownBlock(
263
- replace(JSON.stringify(value, null, " "), { stringType: "json" }),
264
+ replace(
265
+ typeof value === "string" ? value : JSON.stringify(value, null, " "),
266
+ { stringType: "json" },
267
+ ),
264
268
  "js",
265
269
  );
266
270
  }
@@ -268,7 +272,7 @@ const renderText = (
268
272
  return renderConsole(text.value, {
269
273
  sideEffect,
270
274
  sideEffectFileUrl,
271
- outDirectoryUrl,
275
+ generateOutFileUrl,
272
276
  replace,
273
277
  });
274
278
  }
@@ -288,47 +292,51 @@ const renderText = (
288
292
 
289
293
  export const renderConsole = (
290
294
  string,
291
- { sideEffect, sideEffectFileUrl, outDirectoryUrl, replace },
295
+ { sideEffect, sideEffectFileUrl, generateOutFileUrl, replace },
292
296
  ) => {
293
- const potentialAnsi = renderPotentialAnsi(string, {
297
+ return renderPotentialAnsi(string, {
298
+ stringType: "console",
294
299
  sideEffect,
295
300
  sideEffectFileUrl,
296
- outDirectoryUrl,
301
+ generateOutFileUrl,
297
302
  replace,
298
303
  });
299
- if (potentialAnsi) {
300
- return potentialAnsi;
301
- }
302
- return renderMarkdownBlock(
303
- replace(string, { stringType: "console" }),
304
- "console",
305
- );
306
304
  };
307
305
 
308
306
  const renderPotentialAnsi = (
309
307
  string,
310
- { sideEffect, sideEffectFileUrl, outDirectoryUrl, replace },
308
+ { stringType, sideEffect, sideEffectFileUrl, generateOutFileUrl, replace },
311
309
  ) => {
312
- if (!ansiRegex().test(string)) {
313
- return null;
314
- }
315
- let svgFilename = urlToBasename(outDirectoryUrl);
316
- svgFilename += `_${sideEffect.code}`;
317
- if (sideEffect.counter) {
318
- svgFilename += `_${sideEffect.counter}`;
310
+ const rawTextBlock = renderMarkdownBlock(
311
+ replace(string, { stringType }),
312
+ "console",
313
+ );
314
+ // for assert we want ideally hummm
315
+ // colored in details block?
316
+ const includesAnsi = ansiRegex().test(string);
317
+ if (!includesAnsi) {
318
+ return rawTextBlock;
319
319
  }
320
- svgFilename += ".svg";
321
- const svgFileUrl = new URL(`./${svgFilename}`, outDirectoryUrl);
322
- let svgFileContent = renderTerminalSvg(string, {
323
- head: false,
324
- paddingTop: 10,
325
- paddingBottom: 10,
326
- });
320
+ const svgFilename = `${sideEffect.code}${sideEffect.counter ? `_${sideEffect.counter}` : ""}.svg`;
321
+ const svgFileUrl = generateOutFileUrl(svgFilename);
322
+ let svgFileContent = renderTerminalSvg(
323
+ replace(string, { stringType: "console", preserveAnsi: true }),
324
+ {
325
+ head: false,
326
+ paddingTop: 10,
327
+ paddingBottom: 10,
328
+ },
329
+ );
327
330
  svgFileContent = replace(svgFileContent, { fileUrl: svgFileUrl });
328
331
  writeFileSync(svgFileUrl, svgFileContent);
329
332
  const svgFileRelativeUrl = urlToRelativeUrl(svgFileUrl, sideEffectFileUrl);
330
- return `![img](${svgFileRelativeUrl})`;
331
- // we will write a svg file
333
+ let md = `![img](${svgFileRelativeUrl})`;
334
+ md += "\n\n";
335
+ md += renderMarkdownDetails(`${rawTextBlock}`, {
336
+ summary: "see without style",
337
+ });
338
+ md += "\n";
339
+ return md;
332
340
  };
333
341
 
334
342
  export const renderFileContent = (text, { sideEffect, replace }) => {
@@ -11,16 +11,19 @@ export const snapshotSideEffects = (
11
11
  fn,
12
12
  {
13
13
  sideEffectFileUrl,
14
- sideEffectFilePattern = "./output/<basename>.md",
14
+ sideEffectFilePattern = "./output/[basename].md",
15
+ outDirectoryPattern = "./output/[basename]/",
16
+ outFilePattern = "./output/[basename]/[filename]",
17
+ generateOutFileUrl,
15
18
  outDirectoryUrl,
16
19
  errorStackHidden,
17
20
  ...captureOptions
18
21
  } = {},
19
22
  ) => {
23
+ const basename = urlToBasename(sourceFileUrl, true);
20
24
  if (sideEffectFileUrl === undefined) {
21
- const basename = urlToBasename(sourceFileUrl, true);
22
25
  const sideEffectFileRelativeUrl = sideEffectFilePattern.replaceAll(
23
- "<basename>",
26
+ "[basename]",
24
27
  basename,
25
28
  );
26
29
  sideEffectFileUrl = new URL(sideEffectFileRelativeUrl, sourceFileUrl);
@@ -30,17 +33,30 @@ export const snapshotSideEffects = (
30
33
 
31
34
  const captureSideEffects = createCaptureSideEffects(captureOptions);
32
35
  if (outDirectoryUrl === undefined) {
33
- outDirectoryUrl = new URL(
34
- `./${urlToBasename(sideEffectFileUrl)}/`,
35
- sideEffectFileUrl,
36
+ const outDirectoryRelativeUrl = outDirectoryPattern.replaceAll(
37
+ "[basename]",
38
+ basename,
36
39
  );
40
+ outDirectoryUrl = new URL(outDirectoryRelativeUrl, sideEffectFileUrl);
37
41
  }
42
+ if (generateOutFileUrl === undefined) {
43
+ generateOutFileUrl = (filename) => {
44
+ const outRelativeUrl = outFilePattern
45
+ .replaceAll("[basename]", basename)
46
+ .replaceAll("[filename]", filename);
47
+ const outFileUrl = new URL(outRelativeUrl, new URL("./", sourceFileUrl))
48
+ .href;
49
+ return outFileUrl;
50
+ };
51
+ }
52
+
38
53
  const sideEffectFileSnapshot = takeFileSnapshot(sideEffectFileUrl);
39
54
  const outDirectorySnapshot = takeDirectorySnapshot(outDirectoryUrl);
40
55
  const onSideEffects = (sideEffects) => {
41
56
  const sideEffectFileContent = renderSideEffects(sideEffects, {
42
57
  sideEffectFileUrl,
43
58
  outDirectoryUrl,
59
+ generateOutFileUrl,
44
60
  errorStackHidden,
45
61
  });
46
62
  sideEffectFileSnapshot.update(sideEffectFileContent, {
@@ -9,7 +9,7 @@ import { renderSideEffects, renderSmallLink } from "./render_side_effects.js";
9
9
 
10
10
  /**
11
11
  * Generate a markdown file describing all test side effects. When executed in CI throw if there is a diff.
12
- * @param {URL} sourceFileUrl
12
+ * @param {URL} testFileUrl
13
13
  * @param {Function} fnRegisteringTest
14
14
  * @param {Object} snapshotTestsOptions
15
15
  * @param {string|url} snapshotTestsOptions.sideEffectFileUrl
@@ -17,11 +17,14 @@ import { renderSideEffects, renderSmallLink } from "./render_side_effects.js";
17
17
  * @return {Array.<Object>} sideEffects
18
18
  */
19
19
  export const snapshotTests = async (
20
- sourceFileUrl,
20
+ testFileUrl,
21
21
  fnRegisteringTest,
22
22
  {
23
+ testName = urlToBasename(testFileUrl, true),
23
24
  sideEffectFileUrl,
24
- sideEffectFilePattern = "./<basename>/<basename>.md",
25
+ sideEffectFilePattern = "./output/[test_name].md",
26
+ outDirectoryPattern = "./output/[test_name]/",
27
+ outFilePattern = "./output/[test_scenario]/[filename]",
25
28
  rootDirectoryUrl,
26
29
  generatedBy = true,
27
30
  linkToSource = true,
@@ -33,14 +36,13 @@ export const snapshotTests = async (
33
36
  } = {},
34
37
  ) => {
35
38
  if (sideEffectFileUrl === undefined) {
36
- const basename = urlToBasename(sourceFileUrl, true);
37
39
  const sideEffectFileRelativeUrl = sideEffectFilePattern.replaceAll(
38
- "<basename>",
39
- basename,
40
+ "[test_name]",
41
+ testName,
40
42
  );
41
- sideEffectFileUrl = new URL(sideEffectFileRelativeUrl, sourceFileUrl);
43
+ sideEffectFileUrl = new URL(sideEffectFileRelativeUrl, testFileUrl);
42
44
  } else {
43
- sideEffectFileUrl = new URL(sideEffectFileUrl, sourceFileUrl);
45
+ sideEffectFileUrl = new URL(sideEffectFileUrl, testFileUrl);
44
46
  }
45
47
 
46
48
  const testMap = new Map();
@@ -70,8 +72,8 @@ export const snapshotTests = async (
70
72
  {
71
73
  prefix: "Generated by ",
72
74
  suffix:
73
- linkToSource && sourceFileUrl
74
- ? generateExecutingLink(sourceFileUrl, sideEffectFileUrl)
75
+ linkToSource && testFileUrl
76
+ ? generateExecutingLink(testFileUrl, sideEffectFileUrl)
75
77
  : "",
76
78
  },
77
79
  );
@@ -87,19 +89,29 @@ export const snapshotTests = async (
87
89
  callSite: linkToEachSource ? callSite : undefined,
88
90
  baseDirectory: String(new URL("./", callSite.url)),
89
91
  });
90
- const outDirectoryUrl = new URL(
91
- `./${asValidFilename(scenario)}/`,
92
- sideEffectFileUrl,
93
- );
92
+ const testScenario = asValidFilename(scenario);
93
+ let outDirectoryRelativeUrl = outDirectoryPattern
94
+ .replaceAll("[test_name]", testName)
95
+ .replaceAll("[test_scenario]", testScenario);
96
+ const outDirectoryUrl = new URL(outDirectoryRelativeUrl, testFileUrl);
94
97
  const outDirectorySnapshot = takeDirectorySnapshot(outDirectoryUrl, {
95
98
  pattern: {
96
99
  "**/*": true,
97
100
  "**/*.svg": "presence_only",
98
101
  },
99
102
  });
103
+ const generateOutFileUrl = (filename) => {
104
+ const outFileRelativeUrl = outFilePattern
105
+ .replaceAll("[test_name]", testName)
106
+ .replaceAll("[test_scenario]", testScenario)
107
+ .replaceAll("[filename]", filename);
108
+ const outFileUrl = new URL(outFileRelativeUrl, testFileUrl).href;
109
+ return outFileUrl;
110
+ };
100
111
  const sideEffectsMarkdown = renderSideEffects(sideEffects, {
101
112
  sideEffectFileUrl,
102
113
  outDirectoryUrl,
114
+ generateOutFileUrl,
103
115
  generatedBy: false,
104
116
  titleLevel: 3,
105
117
  errorStackHidden,