@vitest/snapshot 0.30.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/dist/index.js ADDED
@@ -0,0 +1,940 @@
1
+ import { join, dirname } from 'pathe';
2
+ import { plugins, format } from 'pretty-format';
3
+ import 'util';
4
+
5
+ var naturalCompare$1 = {exports: {}};
6
+
7
+ /*
8
+ * @version 1.4.0
9
+ * @date 2015-10-26
10
+ * @stability 3 - Stable
11
+ * @author Lauri Rooden (https://github.com/litejs/natural-compare-lite)
12
+ * @license MIT License
13
+ */
14
+
15
+
16
+ var naturalCompare = function(a, b) {
17
+ var i, codeA
18
+ , codeB = 1
19
+ , posA = 0
20
+ , posB = 0
21
+ , alphabet = String.alphabet;
22
+
23
+ function getCode(str, pos, code) {
24
+ if (code) {
25
+ for (i = pos; code = getCode(str, i), code < 76 && code > 65;) ++i;
26
+ return +str.slice(pos - 1, i)
27
+ }
28
+ code = alphabet && alphabet.indexOf(str.charAt(pos));
29
+ return code > -1 ? code + 76 : ((code = str.charCodeAt(pos) || 0), code < 45 || code > 127) ? code
30
+ : code < 46 ? 65 // -
31
+ : code < 48 ? code - 1
32
+ : code < 58 ? code + 18 // 0-9
33
+ : code < 65 ? code - 11
34
+ : code < 91 ? code + 11 // A-Z
35
+ : code < 97 ? code - 37
36
+ : code < 123 ? code + 5 // a-z
37
+ : code - 63
38
+ }
39
+
40
+
41
+ if ((a+="") != (b+="")) for (;codeB;) {
42
+ codeA = getCode(a, posA++);
43
+ codeB = getCode(b, posB++);
44
+
45
+ if (codeA < 76 && codeB < 76 && codeA > 66 && codeB > 66) {
46
+ codeA = getCode(a, posA, posA);
47
+ codeB = getCode(b, posB, posA = i);
48
+ posB = i;
49
+ }
50
+
51
+ if (codeA != codeB) return (codeA < codeB) ? -1 : 1
52
+ }
53
+ return 0
54
+ };
55
+
56
+ try {
57
+ naturalCompare$1.exports = naturalCompare;
58
+ } catch (e) {
59
+ String.naturalCompare = naturalCompare;
60
+ }
61
+
62
+ function notNullish(v) {
63
+ return v != null;
64
+ }
65
+ function isPrimitive(value) {
66
+ return value === null || typeof value !== "function" && typeof value !== "object";
67
+ }
68
+ function isObject(item) {
69
+ return item != null && typeof item === "object" && !Array.isArray(item);
70
+ }
71
+ function getCallLastIndex(code) {
72
+ let charIndex = -1;
73
+ let inString = null;
74
+ let startedBracers = 0;
75
+ let endedBracers = 0;
76
+ let beforeChar = null;
77
+ while (charIndex <= code.length) {
78
+ beforeChar = code[charIndex];
79
+ charIndex++;
80
+ const char = code[charIndex];
81
+ const isCharString = char === '"' || char === "'" || char === "`";
82
+ if (isCharString && beforeChar !== "\\") {
83
+ if (inString === char)
84
+ inString = null;
85
+ else if (!inString)
86
+ inString = char;
87
+ }
88
+ if (!inString) {
89
+ if (char === "(")
90
+ startedBracers++;
91
+ if (char === ")")
92
+ endedBracers++;
93
+ }
94
+ if (startedBracers && endedBracers && startedBracers === endedBracers)
95
+ return charIndex;
96
+ }
97
+ return null;
98
+ }
99
+
100
+ let getPromiseValue = () => 'Promise{…}';
101
+ try {
102
+ const { getPromiseDetails, kPending, kRejected } = process.binding('util');
103
+ if (Array.isArray(getPromiseDetails(Promise.resolve()))) {
104
+ getPromiseValue = (value, options) => {
105
+ const [state, innerValue] = getPromiseDetails(value);
106
+ if (state === kPending) {
107
+ return 'Promise{<pending>}'
108
+ }
109
+ return `Promise${state === kRejected ? '!' : ''}{${options.inspect(innerValue, options)}}`
110
+ };
111
+ }
112
+ } catch (notNode) {
113
+ /* ignore */
114
+ }
115
+
116
+ /* !
117
+ * loupe
118
+ * Copyright(c) 2013 Jake Luer <jake@alogicalparadox.com>
119
+ * MIT Licensed
120
+ */
121
+ let nodeInspect = false;
122
+ try {
123
+ // eslint-disable-next-line global-require
124
+ const nodeUtil = require('util');
125
+ nodeInspect = nodeUtil.inspect ? nodeUtil.inspect.custom : false;
126
+ } catch (noNodeInspect) {
127
+ nodeInspect = false;
128
+ }
129
+
130
+ function normalizeWindowsPath(input = "") {
131
+ if (!input || !input.includes("\\")) {
132
+ return input;
133
+ }
134
+ return input.replace(/\\/g, "/");
135
+ }
136
+ const _IS_ABSOLUTE_RE = /^[/\\](?![/\\])|^[/\\]{2}(?!\.)|^[A-Za-z]:[/\\]/;
137
+ function cwd() {
138
+ if (typeof process !== "undefined") {
139
+ return process.cwd().replace(/\\/g, "/");
140
+ }
141
+ return "/";
142
+ }
143
+ const resolve = function(...arguments_) {
144
+ arguments_ = arguments_.map((argument) => normalizeWindowsPath(argument));
145
+ let resolvedPath = "";
146
+ let resolvedAbsolute = false;
147
+ for (let index = arguments_.length - 1; index >= -1 && !resolvedAbsolute; index--) {
148
+ const path = index >= 0 ? arguments_[index] : cwd();
149
+ if (!path || path.length === 0) {
150
+ continue;
151
+ }
152
+ resolvedPath = `${path}/${resolvedPath}`;
153
+ resolvedAbsolute = isAbsolute(path);
154
+ }
155
+ resolvedPath = normalizeString(resolvedPath, !resolvedAbsolute);
156
+ if (resolvedAbsolute && !isAbsolute(resolvedPath)) {
157
+ return `/${resolvedPath}`;
158
+ }
159
+ return resolvedPath.length > 0 ? resolvedPath : ".";
160
+ };
161
+ function normalizeString(path, allowAboveRoot) {
162
+ let res = "";
163
+ let lastSegmentLength = 0;
164
+ let lastSlash = -1;
165
+ let dots = 0;
166
+ let char = null;
167
+ for (let index = 0; index <= path.length; ++index) {
168
+ if (index < path.length) {
169
+ char = path[index];
170
+ } else if (char === "/") {
171
+ break;
172
+ } else {
173
+ char = "/";
174
+ }
175
+ if (char === "/") {
176
+ if (lastSlash === index - 1 || dots === 1)
177
+ ;
178
+ else if (dots === 2) {
179
+ if (res.length < 2 || lastSegmentLength !== 2 || res[res.length - 1] !== "." || res[res.length - 2] !== ".") {
180
+ if (res.length > 2) {
181
+ const lastSlashIndex = res.lastIndexOf("/");
182
+ if (lastSlashIndex === -1) {
183
+ res = "";
184
+ lastSegmentLength = 0;
185
+ } else {
186
+ res = res.slice(0, lastSlashIndex);
187
+ lastSegmentLength = res.length - 1 - res.lastIndexOf("/");
188
+ }
189
+ lastSlash = index;
190
+ dots = 0;
191
+ continue;
192
+ } else if (res.length > 0) {
193
+ res = "";
194
+ lastSegmentLength = 0;
195
+ lastSlash = index;
196
+ dots = 0;
197
+ continue;
198
+ }
199
+ }
200
+ if (allowAboveRoot) {
201
+ res += res.length > 0 ? "/.." : "..";
202
+ lastSegmentLength = 2;
203
+ }
204
+ } else {
205
+ if (res.length > 0) {
206
+ res += `/${path.slice(lastSlash + 1, index)}`;
207
+ } else {
208
+ res = path.slice(lastSlash + 1, index);
209
+ }
210
+ lastSegmentLength = index - lastSlash - 1;
211
+ }
212
+ lastSlash = index;
213
+ dots = 0;
214
+ } else if (char === "." && dots !== -1) {
215
+ ++dots;
216
+ } else {
217
+ dots = -1;
218
+ }
219
+ }
220
+ return res;
221
+ }
222
+ const isAbsolute = function(p) {
223
+ return _IS_ABSOLUTE_RE.test(p);
224
+ };
225
+ const lineSplitRE = /\r?\n/;
226
+ const stackIgnorePatterns = [
227
+ "node:internal",
228
+ /\/packages\/\w+\/dist\//,
229
+ /\/@vitest\/\w+\/dist\//,
230
+ "/vitest/dist/",
231
+ "/vitest/src/",
232
+ "/vite-node/dist/",
233
+ "/vite-node/src/",
234
+ "/node_modules/chai/",
235
+ "/node_modules/tinypool/",
236
+ "/node_modules/tinyspy/"
237
+ ];
238
+ function extractLocation(urlLike) {
239
+ if (!urlLike.includes(":"))
240
+ return [urlLike];
241
+ const regExp = /(.+?)(?::(\d+))?(?::(\d+))?$/;
242
+ const parts = regExp.exec(urlLike.replace(/[()]/g, ""));
243
+ if (!parts)
244
+ return [urlLike];
245
+ return [parts[1], parts[2] || void 0, parts[3] || void 0];
246
+ }
247
+ function parseSingleStack(raw) {
248
+ let line = raw.trim();
249
+ if (line.includes("(eval "))
250
+ line = line.replace(/eval code/g, "eval").replace(/(\(eval at [^()]*)|(,.*$)/g, "");
251
+ let sanitizedLine = line.replace(/^\s+/, "").replace(/\(eval code/g, "(").replace(/^.*?\s+/, "");
252
+ const location = sanitizedLine.match(/ (\(.+\)$)/);
253
+ sanitizedLine = location ? sanitizedLine.replace(location[0], "") : sanitizedLine;
254
+ const [url, lineNumber, columnNumber] = extractLocation(location ? location[1] : sanitizedLine);
255
+ let method = location && sanitizedLine || "";
256
+ let file = url && ["eval", "<anonymous>"].includes(url) ? void 0 : url;
257
+ if (!file || !lineNumber || !columnNumber)
258
+ return null;
259
+ if (method.startsWith("async "))
260
+ method = method.slice(6);
261
+ if (file.startsWith("file://"))
262
+ file = file.slice(7);
263
+ file = resolve(file);
264
+ return {
265
+ method,
266
+ file,
267
+ line: parseInt(lineNumber),
268
+ column: parseInt(columnNumber)
269
+ };
270
+ }
271
+ function parseStacktrace(stack, ignore = stackIgnorePatterns) {
272
+ const stackFrames = stack.split("\n").map((raw) => {
273
+ const stack2 = parseSingleStack(raw);
274
+ if (!stack2 || ignore.length && ignore.some((p) => stack2.file.match(p)))
275
+ return null;
276
+ return stack2;
277
+ }).filter(notNullish);
278
+ return stackFrames;
279
+ }
280
+ function parseErrorStacktrace(e, ignore = stackIgnorePatterns) {
281
+ if (!e || isPrimitive(e))
282
+ return [];
283
+ if (e.stacks)
284
+ return e.stacks;
285
+ const stackStr = e.stack || e.stackStr || "";
286
+ const stackFrames = parseStacktrace(stackStr, ignore);
287
+ e.stacks = stackFrames;
288
+ return stackFrames;
289
+ }
290
+ function positionToOffset(source, lineNumber, columnNumber) {
291
+ const lines = source.split(lineSplitRE);
292
+ const nl = /\r\n/.test(source) ? 2 : 1;
293
+ let start = 0;
294
+ if (lineNumber > lines.length)
295
+ return source.length;
296
+ for (let i = 0; i < lineNumber - 1; i++)
297
+ start += lines[i].length + nl;
298
+ return start + columnNumber;
299
+ }
300
+ function offsetToLineNumber(source, offset) {
301
+ if (offset > source.length) {
302
+ throw new Error(
303
+ `offset is longer than source length! offset ${offset} > length ${source.length}`
304
+ );
305
+ }
306
+ const lines = source.split(lineSplitRE);
307
+ const nl = /\r\n/.test(source) ? 2 : 1;
308
+ let counted = 0;
309
+ let line = 0;
310
+ for (; line < lines.length; line++) {
311
+ const lineLength = lines[line].length + nl;
312
+ if (counted + lineLength >= offset)
313
+ break;
314
+ counted += lineLength;
315
+ }
316
+ return line + 1;
317
+ }
318
+
319
+ const serialize$1 = (val, config, indentation, depth, refs, printer) => {
320
+ const name = val.getMockName();
321
+ const nameString = name === "vi.fn()" ? "" : ` ${name}`;
322
+ let callsString = "";
323
+ if (val.mock.calls.length !== 0) {
324
+ const indentationNext = indentation + config.indent;
325
+ callsString = ` {${config.spacingOuter}${indentationNext}"calls": ${printer(val.mock.calls, config, indentationNext, depth, refs)}${config.min ? ", " : ","}${config.spacingOuter}${indentationNext}"results": ${printer(val.mock.results, config, indentationNext, depth, refs)}${config.min ? "" : ","}${config.spacingOuter}${indentation}}`;
326
+ }
327
+ return `[MockFunction${nameString}]${callsString}`;
328
+ };
329
+ const test = (val) => val && !!val._isMockFunction;
330
+ const plugin = { serialize: serialize$1, test };
331
+
332
+ const {
333
+ DOMCollection,
334
+ DOMElement,
335
+ Immutable,
336
+ ReactElement,
337
+ ReactTestComponent,
338
+ AsymmetricMatcher
339
+ } = plugins;
340
+ let PLUGINS = [
341
+ ReactTestComponent,
342
+ ReactElement,
343
+ DOMElement,
344
+ DOMCollection,
345
+ Immutable,
346
+ AsymmetricMatcher,
347
+ plugin
348
+ ];
349
+ function addSerializer(plugin) {
350
+ PLUGINS = [plugin].concat(PLUGINS);
351
+ }
352
+ function getSerializers() {
353
+ return PLUGINS;
354
+ }
355
+
356
+ function testNameToKey(testName, count) {
357
+ return `${testName} ${count}`;
358
+ }
359
+ function keyToTestName(key) {
360
+ if (!/ \d+$/.test(key))
361
+ throw new Error("Snapshot keys must end with a number.");
362
+ return key.replace(/ \d+$/, "");
363
+ }
364
+ function getSnapshotData(content, options) {
365
+ const update = options.updateSnapshot;
366
+ const data = /* @__PURE__ */ Object.create(null);
367
+ let snapshotContents = "";
368
+ let dirty = false;
369
+ if (content != null) {
370
+ try {
371
+ snapshotContents = content;
372
+ const populate = new Function("exports", snapshotContents);
373
+ populate(data);
374
+ } catch {
375
+ }
376
+ }
377
+ const isInvalid = snapshotContents;
378
+ if ((update === "all" || update === "new") && isInvalid)
379
+ dirty = true;
380
+ return { data, dirty };
381
+ }
382
+ function addExtraLineBreaks(string) {
383
+ return string.includes("\n") ? `
384
+ ${string}
385
+ ` : string;
386
+ }
387
+ function removeExtraLineBreaks(string) {
388
+ return string.length > 2 && string.startsWith("\n") && string.endsWith("\n") ? string.slice(1, -1) : string;
389
+ }
390
+ const escapeRegex = true;
391
+ const printFunctionName = false;
392
+ function serialize(val, indent = 2, formatOverrides = {}) {
393
+ return normalizeNewlines(
394
+ format(val, {
395
+ escapeRegex,
396
+ indent,
397
+ plugins: getSerializers(),
398
+ printFunctionName,
399
+ ...formatOverrides
400
+ })
401
+ );
402
+ }
403
+ function escapeBacktickString(str) {
404
+ return str.replace(/`|\\|\${/g, "\\$&");
405
+ }
406
+ function printBacktickString(str) {
407
+ return `\`${escapeBacktickString(str)}\``;
408
+ }
409
+ async function ensureDirectoryExists(environment, filePath) {
410
+ try {
411
+ await environment.prepareDirectory(join(dirname(filePath)));
412
+ } catch {
413
+ }
414
+ }
415
+ function normalizeNewlines(string) {
416
+ return string.replace(/\r\n|\r/g, "\n");
417
+ }
418
+ async function saveSnapshotFile(environment, snapshotData, snapshotPath) {
419
+ const snapshots = Object.keys(snapshotData).sort(naturalCompare$1.exports).map(
420
+ (key) => `exports[${printBacktickString(key)}] = ${printBacktickString(normalizeNewlines(snapshotData[key]))};`
421
+ );
422
+ const content = `${environment.getHeader()}
423
+
424
+ ${snapshots.join("\n\n")}
425
+ `;
426
+ const oldContent = await environment.readSnapshotFile(snapshotPath);
427
+ const skipWriting = oldContent && oldContent === content;
428
+ if (skipWriting)
429
+ return;
430
+ await ensureDirectoryExists(environment, snapshotPath);
431
+ await environment.saveSnapshotFile(
432
+ snapshotPath,
433
+ content
434
+ );
435
+ }
436
+ function prepareExpected(expected) {
437
+ function findStartIndent() {
438
+ var _a, _b;
439
+ const matchObject = /^( +)}\s+$/m.exec(expected || "");
440
+ const objectIndent = (_a = matchObject == null ? void 0 : matchObject[1]) == null ? void 0 : _a.length;
441
+ if (objectIndent)
442
+ return objectIndent;
443
+ const matchText = /^\n( +)"/.exec(expected || "");
444
+ return ((_b = matchText == null ? void 0 : matchText[1]) == null ? void 0 : _b.length) || 0;
445
+ }
446
+ const startIndent = findStartIndent();
447
+ let expectedTrimmed = expected == null ? void 0 : expected.trim();
448
+ if (startIndent) {
449
+ expectedTrimmed = expectedTrimmed == null ? void 0 : expectedTrimmed.replace(new RegExp(`^${" ".repeat(startIndent)}`, "gm"), "").replace(/ +}$/, "}");
450
+ }
451
+ return expectedTrimmed;
452
+ }
453
+ function deepMergeArray(target = [], source = []) {
454
+ const mergedOutput = Array.from(target);
455
+ source.forEach((sourceElement, index) => {
456
+ const targetElement = mergedOutput[index];
457
+ if (Array.isArray(target[index])) {
458
+ mergedOutput[index] = deepMergeArray(target[index], sourceElement);
459
+ } else if (isObject(targetElement)) {
460
+ mergedOutput[index] = deepMergeSnapshot(target[index], sourceElement);
461
+ } else {
462
+ mergedOutput[index] = sourceElement;
463
+ }
464
+ });
465
+ return mergedOutput;
466
+ }
467
+ function deepMergeSnapshot(target, source) {
468
+ if (isObject(target) && isObject(source)) {
469
+ const mergedOutput = { ...target };
470
+ Object.keys(source).forEach((key) => {
471
+ if (isObject(source[key]) && !source[key].$$typeof) {
472
+ if (!(key in target))
473
+ Object.assign(mergedOutput, { [key]: source[key] });
474
+ else
475
+ mergedOutput[key] = deepMergeSnapshot(target[key], source[key]);
476
+ } else if (Array.isArray(source[key])) {
477
+ mergedOutput[key] = deepMergeArray(target[key], source[key]);
478
+ } else {
479
+ Object.assign(mergedOutput, { [key]: source[key] });
480
+ }
481
+ });
482
+ return mergedOutput;
483
+ } else if (Array.isArray(target) && Array.isArray(source)) {
484
+ return deepMergeArray(target, source);
485
+ }
486
+ return target;
487
+ }
488
+
489
+ async function saveInlineSnapshots(environment, snapshots) {
490
+ const MagicString = (await import('magic-string')).default;
491
+ const files = new Set(snapshots.map((i) => i.file));
492
+ await Promise.all(Array.from(files).map(async (file) => {
493
+ const snaps = snapshots.filter((i) => i.file === file);
494
+ const code = await environment.readSnapshotFile(file);
495
+ const s = new MagicString(code);
496
+ for (const snap of snaps) {
497
+ const index = positionToOffset(code, snap.line, snap.column);
498
+ replaceInlineSnap(code, s, index, snap.snapshot);
499
+ }
500
+ const transformed = s.toString();
501
+ if (transformed !== code)
502
+ await environment.saveSnapshotFile(file, transformed);
503
+ }));
504
+ }
505
+ const startObjectRegex = /(?:toMatchInlineSnapshot|toThrowErrorMatchingInlineSnapshot)\s*\(\s*(?:\/\*[\S\s]*\*\/\s*|\/\/.*\s+)*\s*({)/m;
506
+ function replaceObjectSnap(code, s, index, newSnap) {
507
+ code = code.slice(index);
508
+ const startMatch = startObjectRegex.exec(code);
509
+ if (!startMatch)
510
+ return false;
511
+ code = code.slice(startMatch.index);
512
+ const charIndex = getCallLastIndex(code);
513
+ if (charIndex === null)
514
+ return false;
515
+ s.appendLeft(index + startMatch.index + charIndex, `, ${prepareSnapString(newSnap, code, index)}`);
516
+ return true;
517
+ }
518
+ function prepareSnapString(snap, source, index) {
519
+ const lineNumber = offsetToLineNumber(source, index);
520
+ const line = source.split(lineSplitRE)[lineNumber - 1];
521
+ const indent = line.match(/^\s*/)[0] || "";
522
+ const indentNext = indent.includes(" ") ? `${indent} ` : `${indent} `;
523
+ const lines = snap.trim().replace(/\\/g, "\\\\").split(/\n/g);
524
+ const isOneline = lines.length <= 1;
525
+ const quote = isOneline ? "'" : "`";
526
+ if (isOneline)
527
+ return `'${lines.join("\n").replace(/'/g, "\\'")}'`;
528
+ else
529
+ return `${quote}
530
+ ${lines.map((i) => i ? indentNext + i : "").join("\n").replace(/`/g, "\\`").replace(/\${/g, "\\${")}
531
+ ${indent}${quote}`;
532
+ }
533
+ const startRegex = /(?:toMatchInlineSnapshot|toThrowErrorMatchingInlineSnapshot)\s*\(\s*(?:\/\*[\S\s]*\*\/\s*|\/\/.*\s+)*\s*[\w_$]*(['"`\)])/m;
534
+ function replaceInlineSnap(code, s, index, newSnap) {
535
+ const startMatch = startRegex.exec(code.slice(index));
536
+ if (!startMatch)
537
+ return replaceObjectSnap(code, s, index, newSnap);
538
+ const quote = startMatch[1];
539
+ const startIndex = index + startMatch.index + startMatch[0].length;
540
+ const snapString = prepareSnapString(newSnap, code, index);
541
+ if (quote === ")") {
542
+ s.appendRight(startIndex - 1, snapString);
543
+ return true;
544
+ }
545
+ const quoteEndRE = new RegExp(`(?:^|[^\\\\])${quote}`);
546
+ const endMatch = quoteEndRE.exec(code.slice(startIndex));
547
+ if (!endMatch)
548
+ return false;
549
+ const endIndex = startIndex + endMatch.index + endMatch[0].length;
550
+ s.overwrite(startIndex - 1, endIndex, snapString);
551
+ return true;
552
+ }
553
+ const INDENTATION_REGEX = /^([^\S\n]*)\S/m;
554
+ function stripSnapshotIndentation(inlineSnapshot) {
555
+ const match = inlineSnapshot.match(INDENTATION_REGEX);
556
+ if (!match || !match[1]) {
557
+ return inlineSnapshot;
558
+ }
559
+ const indentation = match[1];
560
+ const lines = inlineSnapshot.split(/\n/g);
561
+ if (lines.length <= 2) {
562
+ return inlineSnapshot;
563
+ }
564
+ if (lines[0].trim() !== "" || lines[lines.length - 1].trim() !== "") {
565
+ return inlineSnapshot;
566
+ }
567
+ for (let i = 1; i < lines.length - 1; i++) {
568
+ if (lines[i] !== "") {
569
+ if (lines[i].indexOf(indentation) !== 0) {
570
+ return inlineSnapshot;
571
+ }
572
+ lines[i] = lines[i].substring(indentation.length);
573
+ }
574
+ }
575
+ lines[lines.length - 1] = "";
576
+ inlineSnapshot = lines.join("\n");
577
+ return inlineSnapshot;
578
+ }
579
+
580
+ async function saveRawSnapshots(environment, snapshots) {
581
+ await Promise.all(snapshots.map(async (snap) => {
582
+ if (!snap.readonly)
583
+ await environment.saveSnapshotFile(snap.file, snap.snapshot);
584
+ }));
585
+ }
586
+
587
+ class SnapshotState {
588
+ constructor(testFilePath, snapshotPath, snapshotContent, options) {
589
+ this.testFilePath = testFilePath;
590
+ this.snapshotPath = snapshotPath;
591
+ const { data, dirty } = getSnapshotData(
592
+ snapshotContent,
593
+ options
594
+ );
595
+ this._fileExists = snapshotContent != null;
596
+ this._initialData = data;
597
+ this._snapshotData = data;
598
+ this._dirty = dirty;
599
+ this._inlineSnapshots = [];
600
+ this._rawSnapshots = [];
601
+ this._uncheckedKeys = new Set(Object.keys(this._snapshotData));
602
+ this._counters = /* @__PURE__ */ new Map();
603
+ this.expand = options.expand || false;
604
+ this.added = 0;
605
+ this.matched = 0;
606
+ this.unmatched = 0;
607
+ this._updateSnapshot = options.updateSnapshot;
608
+ this.updated = 0;
609
+ this._snapshotFormat = {
610
+ printBasicPrototype: false,
611
+ ...options.snapshotFormat
612
+ };
613
+ this._environment = options.snapshotEnvironment;
614
+ }
615
+ static async create(testFilePath, options) {
616
+ const snapshotPath = await options.snapshotEnvironment.resolvePath(testFilePath);
617
+ const content = await options.snapshotEnvironment.readSnapshotFile(snapshotPath);
618
+ return new SnapshotState(testFilePath, snapshotPath, content, options);
619
+ }
620
+ get environment() {
621
+ return this._environment;
622
+ }
623
+ markSnapshotsAsCheckedForTest(testName) {
624
+ this._uncheckedKeys.forEach((uncheckedKey) => {
625
+ if (keyToTestName(uncheckedKey) === testName)
626
+ this._uncheckedKeys.delete(uncheckedKey);
627
+ });
628
+ }
629
+ _inferInlineSnapshotStack(stacks) {
630
+ const promiseIndex = stacks.findIndex((i) => i.method.match(/__VITEST_(RESOLVES|REJECTS)__/));
631
+ if (promiseIndex !== -1)
632
+ return stacks[promiseIndex + 3];
633
+ const stackIndex = stacks.findIndex((i) => i.method.includes("__INLINE_SNAPSHOT__"));
634
+ return stackIndex !== -1 ? stacks[stackIndex + 2] : null;
635
+ }
636
+ _addSnapshot(key, receivedSerialized, options) {
637
+ this._dirty = true;
638
+ if (options.isInline) {
639
+ const stacks = parseErrorStacktrace(options.error || new Error("snapshot"), []);
640
+ const stack = this._inferInlineSnapshotStack(stacks);
641
+ if (!stack) {
642
+ throw new Error(
643
+ `@vitest/snapshot: Couldn't infer stack frame for inline snapshot.
644
+ ${JSON.stringify(stacks)}`
645
+ );
646
+ }
647
+ stack.column--;
648
+ this._inlineSnapshots.push({
649
+ snapshot: receivedSerialized,
650
+ ...stack
651
+ });
652
+ } else if (options.rawSnapshot) {
653
+ this._rawSnapshots.push({
654
+ ...options.rawSnapshot,
655
+ snapshot: receivedSerialized
656
+ });
657
+ } else {
658
+ this._snapshotData[key] = receivedSerialized;
659
+ }
660
+ }
661
+ clear() {
662
+ this._snapshotData = this._initialData;
663
+ this._counters = /* @__PURE__ */ new Map();
664
+ this.added = 0;
665
+ this.matched = 0;
666
+ this.unmatched = 0;
667
+ this.updated = 0;
668
+ this._dirty = false;
669
+ }
670
+ async save() {
671
+ const hasExternalSnapshots = Object.keys(this._snapshotData).length;
672
+ const hasInlineSnapshots = this._inlineSnapshots.length;
673
+ const hasRawSnapshots = this._rawSnapshots.length;
674
+ const isEmpty = !hasExternalSnapshots && !hasInlineSnapshots && !hasRawSnapshots;
675
+ const status = {
676
+ deleted: false,
677
+ saved: false
678
+ };
679
+ if ((this._dirty || this._uncheckedKeys.size) && !isEmpty) {
680
+ if (hasExternalSnapshots) {
681
+ await saveSnapshotFile(this._environment, this._snapshotData, this.snapshotPath);
682
+ this._fileExists = true;
683
+ }
684
+ if (hasInlineSnapshots)
685
+ await saveInlineSnapshots(this._environment, this._inlineSnapshots);
686
+ if (hasRawSnapshots)
687
+ await saveRawSnapshots(this._environment, this._rawSnapshots);
688
+ status.saved = true;
689
+ } else if (!hasExternalSnapshots && this._fileExists) {
690
+ if (this._updateSnapshot === "all") {
691
+ await this._environment.removeSnapshotFile(this.snapshotPath);
692
+ this._fileExists = false;
693
+ }
694
+ status.deleted = true;
695
+ }
696
+ return status;
697
+ }
698
+ getUncheckedCount() {
699
+ return this._uncheckedKeys.size || 0;
700
+ }
701
+ getUncheckedKeys() {
702
+ return Array.from(this._uncheckedKeys);
703
+ }
704
+ removeUncheckedKeys() {
705
+ if (this._updateSnapshot === "all" && this._uncheckedKeys.size) {
706
+ this._dirty = true;
707
+ this._uncheckedKeys.forEach((key) => delete this._snapshotData[key]);
708
+ this._uncheckedKeys.clear();
709
+ }
710
+ }
711
+ match({
712
+ testName,
713
+ received,
714
+ key,
715
+ inlineSnapshot,
716
+ isInline,
717
+ error,
718
+ rawSnapshot
719
+ }) {
720
+ this._counters.set(testName, (this._counters.get(testName) || 0) + 1);
721
+ const count = Number(this._counters.get(testName));
722
+ if (!key)
723
+ key = testNameToKey(testName, count);
724
+ if (!(isInline && this._snapshotData[key] !== void 0))
725
+ this._uncheckedKeys.delete(key);
726
+ let receivedSerialized = rawSnapshot && typeof received === "string" ? received : serialize(received, void 0, this._snapshotFormat);
727
+ if (!rawSnapshot)
728
+ receivedSerialized = addExtraLineBreaks(receivedSerialized);
729
+ const expected = isInline ? inlineSnapshot : rawSnapshot ? rawSnapshot.content : this._snapshotData[key];
730
+ const expectedTrimmed = prepareExpected(expected);
731
+ const pass = expectedTrimmed === prepareExpected(receivedSerialized);
732
+ const hasSnapshot = expected !== void 0;
733
+ const snapshotIsPersisted = isInline || this._fileExists || rawSnapshot && rawSnapshot.content != null;
734
+ if (pass && !isInline && !rawSnapshot) {
735
+ this._snapshotData[key] = receivedSerialized;
736
+ }
737
+ if (hasSnapshot && this._updateSnapshot === "all" || (!hasSnapshot || !snapshotIsPersisted) && (this._updateSnapshot === "new" || this._updateSnapshot === "all")) {
738
+ if (this._updateSnapshot === "all") {
739
+ if (!pass) {
740
+ if (hasSnapshot)
741
+ this.updated++;
742
+ else
743
+ this.added++;
744
+ this._addSnapshot(key, receivedSerialized, { error, isInline, rawSnapshot });
745
+ } else {
746
+ this.matched++;
747
+ }
748
+ } else {
749
+ this._addSnapshot(key, receivedSerialized, { error, isInline, rawSnapshot });
750
+ this.added++;
751
+ }
752
+ return {
753
+ actual: "",
754
+ count,
755
+ expected: "",
756
+ key,
757
+ pass: true
758
+ };
759
+ } else {
760
+ if (!pass) {
761
+ this.unmatched++;
762
+ return {
763
+ actual: removeExtraLineBreaks(receivedSerialized),
764
+ count,
765
+ expected: expectedTrimmed !== void 0 ? removeExtraLineBreaks(expectedTrimmed) : void 0,
766
+ key,
767
+ pass: false
768
+ };
769
+ } else {
770
+ this.matched++;
771
+ return {
772
+ actual: "",
773
+ count,
774
+ expected: "",
775
+ key,
776
+ pass: true
777
+ };
778
+ }
779
+ }
780
+ }
781
+ async pack() {
782
+ const snapshot = {
783
+ filepath: this.testFilePath,
784
+ added: 0,
785
+ fileDeleted: false,
786
+ matched: 0,
787
+ unchecked: 0,
788
+ uncheckedKeys: [],
789
+ unmatched: 0,
790
+ updated: 0
791
+ };
792
+ const uncheckedCount = this.getUncheckedCount();
793
+ const uncheckedKeys = this.getUncheckedKeys();
794
+ if (uncheckedCount)
795
+ this.removeUncheckedKeys();
796
+ const status = await this.save();
797
+ snapshot.fileDeleted = status.deleted;
798
+ snapshot.added = this.added;
799
+ snapshot.matched = this.matched;
800
+ snapshot.unmatched = this.unmatched;
801
+ snapshot.updated = this.updated;
802
+ snapshot.unchecked = !status.deleted ? uncheckedCount : 0;
803
+ snapshot.uncheckedKeys = Array.from(uncheckedKeys);
804
+ return snapshot;
805
+ }
806
+ }
807
+
808
+ function createMismatchError(message, actual, expected) {
809
+ const error = new Error(message);
810
+ Object.defineProperty(error, "actual", {
811
+ value: actual,
812
+ enumerable: true,
813
+ configurable: true,
814
+ writable: true
815
+ });
816
+ Object.defineProperty(error, "expected", {
817
+ value: expected,
818
+ enumerable: true,
819
+ configurable: true,
820
+ writable: true
821
+ });
822
+ return error;
823
+ }
824
+ class SnapshotClient {
825
+ constructor(Service = SnapshotState) {
826
+ this.Service = Service;
827
+ this.snapshotStateMap = /* @__PURE__ */ new Map();
828
+ }
829
+ async setTest(filepath, name, options) {
830
+ var _a;
831
+ this.filepath = filepath;
832
+ this.name = name;
833
+ if (((_a = this.snapshotState) == null ? void 0 : _a.testFilePath) !== filepath) {
834
+ this.resetCurrent();
835
+ if (!this.getSnapshotState(filepath)) {
836
+ this.snapshotStateMap.set(
837
+ filepath,
838
+ await this.Service.create(
839
+ filepath,
840
+ options
841
+ )
842
+ );
843
+ }
844
+ this.snapshotState = this.getSnapshotState(filepath);
845
+ }
846
+ }
847
+ getSnapshotState(filepath) {
848
+ return this.snapshotStateMap.get(filepath);
849
+ }
850
+ clearTest() {
851
+ this.filepath = void 0;
852
+ this.name = void 0;
853
+ }
854
+ skipTestSnapshots(name) {
855
+ var _a;
856
+ (_a = this.snapshotState) == null ? void 0 : _a.markSnapshotsAsCheckedForTest(name);
857
+ }
858
+ /**
859
+ * Should be overridden by the consumer.
860
+ *
861
+ * Vitest checks equality with @vitest/expect.
862
+ */
863
+ equalityCheck(received, expected) {
864
+ return received === expected;
865
+ }
866
+ assert(options) {
867
+ const {
868
+ filepath = this.filepath,
869
+ name = this.name,
870
+ message,
871
+ isInline = false,
872
+ properties,
873
+ inlineSnapshot,
874
+ error,
875
+ errorMessage,
876
+ rawSnapshot
877
+ } = options;
878
+ let { received } = options;
879
+ if (!filepath)
880
+ throw new Error("Snapshot cannot be used outside of test");
881
+ if (typeof properties === "object") {
882
+ if (typeof received !== "object" || !received)
883
+ throw new Error("Received value must be an object when the matcher has properties");
884
+ try {
885
+ const pass2 = this.equalityCheck(received, properties);
886
+ if (!pass2)
887
+ throw createMismatchError("Snapshot properties mismatched", received, properties);
888
+ else
889
+ received = deepMergeSnapshot(received, properties);
890
+ } catch (err) {
891
+ err.message = errorMessage || "Snapshot mismatched";
892
+ throw err;
893
+ }
894
+ }
895
+ const testName = [
896
+ name,
897
+ ...message ? [message] : []
898
+ ].join(" > ");
899
+ const snapshotState = this.getSnapshotState(filepath);
900
+ const { actual, expected, key, pass } = snapshotState.match({
901
+ testName,
902
+ received,
903
+ isInline,
904
+ error,
905
+ inlineSnapshot,
906
+ rawSnapshot
907
+ });
908
+ if (!pass)
909
+ throw createMismatchError(`Snapshot \`${key || "unknown"}\` mismatched`, actual == null ? void 0 : actual.trim(), expected == null ? void 0 : expected.trim());
910
+ }
911
+ async assertRaw(options) {
912
+ if (!options.rawSnapshot)
913
+ throw new Error("Raw snapshot is required");
914
+ const {
915
+ filepath = this.filepath,
916
+ rawSnapshot
917
+ } = options;
918
+ if (rawSnapshot.content == null) {
919
+ if (!filepath)
920
+ throw new Error("Snapshot cannot be used outside of test");
921
+ const snapshotState = this.getSnapshotState(filepath);
922
+ options.filepath || (options.filepath = filepath);
923
+ rawSnapshot.file = await snapshotState.environment.resolveRawPath(filepath, rawSnapshot.file);
924
+ rawSnapshot.content = await snapshotState.environment.readSnapshotFile(rawSnapshot.file) || void 0;
925
+ }
926
+ return this.assert(options);
927
+ }
928
+ async resetCurrent() {
929
+ if (!this.snapshotState)
930
+ return null;
931
+ const result = await this.snapshotState.pack();
932
+ this.snapshotState = void 0;
933
+ return result;
934
+ }
935
+ clear() {
936
+ this.snapshotStateMap.clear();
937
+ }
938
+ }
939
+
940
+ export { SnapshotClient, SnapshotState, addSerializer, getSerializers, stripSnapshotIndentation };