vitest 4.1.5 → 5.0.0-beta.2

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.
Files changed (80) hide show
  1. package/LICENSE.md +7 -0
  2. package/dist/browser.d.ts +9 -9
  3. package/dist/browser.js +4 -4
  4. package/dist/chunks/{base.RR7zL1h0.js → base.Opc_YHkk.js} +10 -11
  5. package/dist/chunks/browser.d.BUhkKcDl.d.ts +899 -0
  6. package/dist/chunks/{cac.DJJmV0dT.js → cac.8N4bOkkB.js} +23 -11
  7. package/dist/chunks/{cli-api.Cjt90eJu.js → cli-api.B0RFke2g.js} +5799 -353
  8. package/dist/chunks/{config.d.A1h_Y6Jt.d.ts → config.d.D91DHYaD.d.ts} +11 -3
  9. package/dist/chunks/{console.3WNpx0tS.js → console.B3IRP8fX.js} +3 -1
  10. package/dist/chunks/{constants.CPYnjOGj.js → constants.-juJ8b_4.js} +1 -1
  11. package/dist/chunks/{coverage.d.BZtK59WP.d.ts → coverage.d.g2xbl2sP.d.ts} +4 -0
  12. package/dist/chunks/{creator.DgVhQm5q.js → creator.BqL2U_x4.js} +1 -1
  13. package/dist/chunks/{defaults.9aQKnqFk.js → defaults.szbHWQun.js} +4 -2
  14. package/dist/chunks/environment.d-DOJxxZV9.d.DOJxxZV9.d.ts +17 -0
  15. package/dist/chunks/general.d.DFAHgpC2.d.ts +247 -0
  16. package/dist/chunks/{global.d.DVsSRdQ5.d.ts → global.d.DhbKSQoV.d.ts} +4 -5
  17. package/dist/chunks/{globals.Dj1TGiMC.js → globals.EHmmu0nC.js} +15 -14
  18. package/dist/chunks/{index.DXx9Dtk7.js → index.CViWo__T.js} +5 -5
  19. package/dist/chunks/{startVitestModuleRunner.bRl2_oI_.js → index.CbgUM9E5.js} +731 -5
  20. package/dist/chunks/{test.DNmyFkvJ.js → index.D_7-4CaB.js} +2659 -14
  21. package/dist/chunks/{init-forks.UV3ZQGQH.js → init-forks.DMge3WTt.js} +1 -1
  22. package/dist/chunks/{init-threads.D3eCsY76.js → init-threads.eIoyCTon.js} +1 -1
  23. package/dist/chunks/{init.D98-gwRW.js → init.BVd7SaCA.js} +3 -5
  24. package/dist/chunks/{nativeModuleMocker.BRN2oBJd.js → nativeModuleMocker.DKpFw0pk.js} +3 -2
  25. package/dist/chunks/{index.BCY_7LL2.js → nativeModuleRunner.BOeMnHl4.js} +43 -12
  26. package/dist/chunks/node.CwFbQqI1.js +47 -0
  27. package/dist/chunks/{reporters.d.CEnv6XRv.d.ts → plugin.d.cIKZEZ16.d.ts} +306 -19
  28. package/dist/chunks/plugins.DrsmdUE2.js +37 -0
  29. package/dist/chunks/{rpc.MzXet3jl.js → rpc.DFRWVnRh.js} +16 -1
  30. package/dist/chunks/{rpc.d.B_8sPU0w.d.ts → rpc.d.7JZuxZ8u.d.ts} +19 -3
  31. package/dist/chunks/{setup-common.DYx3LtFI.js → setup-common.Hpq30zVk.js} +7 -3
  32. package/dist/chunks/{utils.BS4fH3nR.js → utils.DKODp04v.js} +3 -4
  33. package/dist/chunks/{vm.DVLYObm9.js → vm.2okbRRME.js} +6 -6
  34. package/dist/chunks/{worker.d.ZpHpO4yb.d.ts → worker.d.Bu1kXGw4.d.ts} +3 -3
  35. package/dist/cli.js +2 -2
  36. package/dist/config.cjs +4 -2
  37. package/dist/config.d.ts +21 -18
  38. package/dist/config.js +2 -2
  39. package/dist/index.d.ts +84 -22
  40. package/dist/index.js +15 -13
  41. package/dist/module-evaluator.d.ts +5 -3
  42. package/dist/module-evaluator.js +1 -1
  43. package/dist/node.d.ts +114 -19
  44. package/dist/node.js +21 -26
  45. package/dist/runtime.d.ts +40 -4
  46. package/dist/runtime.js +5 -6
  47. package/dist/{chunks/traces.DT5aQ62U.js → traces.js} +1 -1
  48. package/dist/worker.d.ts +5 -5
  49. package/dist/worker.js +21 -23
  50. package/dist/workers/forks.js +21 -23
  51. package/dist/workers/runVmTests.js +17 -16
  52. package/dist/workers/threads.js +21 -23
  53. package/dist/workers/vmForks.js +7 -9
  54. package/dist/workers/vmThreads.js +7 -9
  55. package/package.json +21 -38
  56. package/dist/chunks/benchmark.CX_oY03V.js +0 -40
  57. package/dist/chunks/benchmark.d.DAaHLpsq.d.ts +0 -24
  58. package/dist/chunks/browser.d.BcoexmFG.d.ts +0 -62
  59. package/dist/chunks/coverage.DM_a_rWm.js +0 -1087
  60. package/dist/chunks/evaluatedModules.Dg1zASAC.js +0 -17
  61. package/dist/chunks/index.DC7d2Pf8.js +0 -729
  62. package/dist/chunks/index.DdgEv5B1.js +0 -42
  63. package/dist/chunks/index.UpGiHP7g.js +0 -4255
  64. package/dist/chunks/nativeModuleRunner.BIakptoF.js +0 -36
  65. package/dist/chunks/node.COQbm6gK.js +0 -14
  66. package/dist/chunks/plugin.d.BM2TCi12.d.ts +0 -38
  67. package/dist/chunks/suite.d.udJtyAgw.d.ts +0 -10
  68. package/dist/chunks/traces.d.D2T_R8rx.d.ts +0 -60
  69. package/dist/coverage.d.ts +0 -123
  70. package/dist/coverage.js +0 -27
  71. package/dist/environments.d.ts +0 -22
  72. package/dist/environments.js +0 -5
  73. package/dist/reporters.d.ts +0 -27
  74. package/dist/reporters.js +0 -26
  75. package/dist/runners.d.ts +0 -70
  76. package/dist/runners.js +0 -19
  77. package/dist/snapshot.d.ts +0 -9
  78. package/dist/snapshot.js +0 -6
  79. package/dist/suite.d.ts +0 -5
  80. package/dist/suite.js +0 -8
@@ -1,18 +1,1594 @@
1
- import { getCurrentTest, updateTask, createTaskCollector, getCurrentSuite, getHooks, getFn } from '@vitest/runner';
2
- import { assertTypes, createSimpleStackTrace, createDefer } from '@vitest/utils/helpers';
3
- import { getSafeTimers, delay } from '@vitest/utils/timers';
4
- import { a as getBenchOptions, g as getBenchFn } from './benchmark.CX_oY03V.js';
1
+ import { TestSyntaxError, getCurrentTest, getCurrentSuite, updateTask, createTaskCollector, getHooks, getFn, afterAll, afterEach, aroundAll, aroundEach, beforeAll, beforeEach, describe, it, onTestFailed, onTestFinished, recordArtifact, suite, test } from '@vitest/runner';
5
2
  import { i as isChildProcess, w as waitForImportsToResolve, r as resetModules, g as getWorkerState } from './utils.BX5Fg8C4.js';
6
- import { chai, wrapAssertion, recordAsyncExpect, createAssertionMessage, equals, iterableEquality, subsetEquality, JestExtend, JestChaiExpect, ChaiStyleAssertions, JestAsymmetricMatchers, GLOBAL_EXPECT, ASYMMETRIC_MATCHERS_OBJECT, getState, setState, addCustomEqualityTesters, customMatchers } from '@vitest/expect';
7
- import { getNames, getTests, getTestName, createChainable, matchesTags } from '@vitest/runner/utils';
3
+ import { getSafeTimers, delay } from '@vitest/utils/timers';
4
+ import { isMockFunction, fn, spyOn, restoreAllMocks, resetAllMocks, clearAllMocks } from '@vitest/spy';
5
+ import { getType, isObject, noop, assertTypes, ordinal, createSimpleStackTrace, getCallLastIndex, createDefer } from '@vitest/utils/helpers';
6
+ import { positionToOffset, offsetToLineNumber, lineSplitRE } from '@vitest/utils/offset';
7
+ import { parseSingleStack, parseErrorStacktrace } from '@vitest/utils/source-map';
8
+ import { c as commonjsGlobal, g as getDefaultExportFromCjs } from './_commonjsHelpers.D26ty3Ew.js';
9
+ import { R as RealDate, b as resetDate, m as mockDate, r as rpc, V as VitestEvaluatedModules } from './rpc.DFRWVnRh.js';
10
+ import * as chai from 'chai';
11
+ import { use, util } from 'chai';
12
+ import { getNames, createChainable, getTests, getTestName, matchesTags } from '@vitest/runner/utils';
8
13
  import { processError } from '@vitest/utils/error';
14
+ import { g as getSerializers, a as addSerializer } from './plugins.DrsmdUE2.js';
15
+ import { format } from '@vitest/pretty-format';
16
+ import { printDiffOrStringify, diff } from '@vitest/utils/diff';
17
+ import { stringify, inspect } from '@vitest/utils/display';
18
+ import c from 'tinyrainbow';
9
19
  import { normalize } from 'pathe';
10
- import { fn, spyOn, restoreAllMocks, resetAllMocks, clearAllMocks, isMockFunction } from '@vitest/spy';
11
- import '@vitest/utils/offset';
12
- import { parseSingleStack } from '@vitest/utils/source-map';
13
- import { c as commonjsGlobal } from './_commonjsHelpers.D26ty3Ew.js';
14
- import { R as RealDate, b as resetDate, m as mockDate, r as rpc } from './rpc.MzXet3jl.js';
15
- import { addSerializer, stripSnapshotIndentation, SnapshotClient } from '@vitest/snapshot';
20
+ import { expectTypeOf } from 'expect-type';
21
+
22
+ const ChaiStyleAssertions = (chai, utils) => {
23
+ function defProperty(name, delegateTo) {
24
+ utils.addProperty(chai.Assertion.prototype, name, function() {
25
+ const jestMethod = chai.Assertion.prototype[delegateTo];
26
+ if (!jestMethod) throw new Error(`Cannot delegate to ${String(delegateTo)}: method not found. Ensure JestChaiExpect plugin is loaded first.`);
27
+ return jestMethod.call(this);
28
+ });
29
+ }
30
+ function defPropertyWithArgs(name, delegateTo, ...args) {
31
+ utils.addProperty(chai.Assertion.prototype, name, function() {
32
+ const jestMethod = chai.Assertion.prototype[delegateTo];
33
+ if (!jestMethod) throw new Error(`Cannot delegate to ${String(delegateTo)}: method not found. Ensure JestChaiExpect plugin is loaded first.`);
34
+ return jestMethod.call(this, ...args);
35
+ });
36
+ }
37
+ function defMethod(name, delegateTo) {
38
+ utils.addMethod(chai.Assertion.prototype, name, function(...args) {
39
+ const jestMethod = chai.Assertion.prototype[delegateTo];
40
+ if (!jestMethod) throw new Error(`Cannot delegate to ${String(delegateTo)}: method not found. Ensure JestChaiExpect plugin is loaded first.`);
41
+ return jestMethod.call(this, ...args);
42
+ });
43
+ }
44
+ // API to (somewhat) mirror sinon-chai
45
+ // https://github.com/chaijs/sinon-chai
46
+ defProperty("called", "toHaveBeenCalled");
47
+ defProperty("calledOnce", "toHaveBeenCalledOnce");
48
+ defPropertyWithArgs("calledTwice", "toHaveBeenCalledTimes", 2);
49
+ defPropertyWithArgs("calledThrice", "toHaveBeenCalledTimes", 3);
50
+ defMethod("callCount", "toHaveBeenCalledTimes");
51
+ defMethod("calledWith", "toHaveBeenCalledWith");
52
+ defMethod("calledOnceWith", "toHaveBeenCalledExactlyOnceWith");
53
+ defMethod("lastCalledWith", "toHaveBeenLastCalledWith");
54
+ defMethod("nthCalledWith", "toHaveBeenNthCalledWith");
55
+ defMethod("returned", "toHaveReturned");
56
+ defMethod("returnedWith", "toHaveReturnedWith");
57
+ defMethod("returnedTimes", "toHaveReturnedTimes");
58
+ defMethod("lastReturnedWith", "toHaveLastReturnedWith");
59
+ defMethod("nthReturnedWith", "toHaveNthReturnedWith");
60
+ defMethod("calledBefore", "toHaveBeenCalledBefore");
61
+ defMethod("calledAfter", "toHaveBeenCalledAfter");
62
+ // TODO: implement
63
+ // defMethod('thrown', 'toHaveThrown')
64
+ };
65
+
66
+ const MATCHERS_OBJECT = Symbol.for("matchers-object");
67
+ const JEST_MATCHERS_OBJECT = Symbol.for("$$jest-matchers-object");
68
+ const GLOBAL_EXPECT = Symbol.for("expect-global");
69
+ const ASYMMETRIC_MATCHERS_OBJECT = Symbol.for("asymmetric-matchers-object");
70
+
71
+ // selectively ported from https://github.com/jest-community/jest-extended
72
+ const customMatchers = {
73
+ toSatisfy(actual, expected, message) {
74
+ const { printReceived, printExpected, matcherHint } = this.utils;
75
+ const pass = expected(actual);
76
+ return {
77
+ pass,
78
+ message: () => pass ? `\
79
+ ${matcherHint(".not.toSatisfy", "received", "")}
80
+
81
+ Expected value to not satisfy:
82
+ ${message || printExpected(expected)}
83
+ Received:
84
+ ${printReceived(actual)}` : `\
85
+ ${matcherHint(".toSatisfy", "received", "")}
86
+
87
+ Expected value to satisfy:
88
+ ${message || printExpected(expected)}
89
+
90
+ Received:
91
+ ${printReceived(actual)}`
92
+ };
93
+ },
94
+ toBeOneOf(actual, expected) {
95
+ const { equals, customTesters } = this;
96
+ const { printReceived, printExpected, matcherHint } = this.utils;
97
+ let pass;
98
+ if (Array.isArray(expected)) pass = expected.length === 0 || expected.some((item) => equals(item, actual, customTesters));
99
+ else if (expected instanceof Set) pass = expected.size === 0 || expected.has(actual) || [...expected].some((item) => equals(item, actual, customTesters));
100
+ else throw new TypeError(`You must provide an array or set to ${matcherHint(".toBeOneOf")}, not '${typeof expected}'.`);
101
+ return {
102
+ pass,
103
+ message: () => pass ? `\
104
+ ${matcherHint(".not.toBeOneOf", "received", "")}
105
+
106
+ Expected value to not be one of:
107
+ ${printExpected(expected)}
108
+ Received:
109
+ ${printReceived(actual)}` : `\
110
+ ${matcherHint(".toBeOneOf", "received", "")}
111
+
112
+ Expected value to be one of:
113
+ ${printExpected(expected)}
114
+
115
+ Received:
116
+ ${printReceived(actual)}`
117
+ };
118
+ }
119
+ };
120
+
121
+ const EXPECTED_COLOR = c.green;
122
+ const RECEIVED_COLOR = c.red;
123
+ const INVERTED_COLOR = c.inverse;
124
+ const BOLD_WEIGHT = c.bold;
125
+ const DIM_COLOR = c.dim;
126
+ function matcherHint(matcherName, received = "received", expected = "expected", options = {}) {
127
+ const { comment = "", isDirectExpectCall = false, isNot = false, promise = "", secondArgument = "", expectedColor = EXPECTED_COLOR, receivedColor = RECEIVED_COLOR, secondArgumentColor = EXPECTED_COLOR } = options;
128
+ let hint = "";
129
+ let dimString = "expect";
130
+ if (!isDirectExpectCall && received !== "") {
131
+ hint += DIM_COLOR(`${dimString}(`) + receivedColor(received);
132
+ dimString = ")";
133
+ }
134
+ if (promise !== "") {
135
+ hint += DIM_COLOR(`${dimString}.`) + promise;
136
+ dimString = "";
137
+ }
138
+ if (isNot) {
139
+ hint += `${DIM_COLOR(`${dimString}.`)}not`;
140
+ dimString = "";
141
+ }
142
+ if (matcherName.includes("."))
143
+ // Old format: for backward compatibility,
144
+ // especially without promise or isNot options
145
+ dimString += matcherName;
146
+ else {
147
+ // New format: omit period from matcherName arg
148
+ hint += DIM_COLOR(`${dimString}.`) + matcherName;
149
+ dimString = "";
150
+ }
151
+ if (expected === "") dimString += "()";
152
+ else {
153
+ hint += DIM_COLOR(`${dimString}(`) + expectedColor(expected);
154
+ if (secondArgument) hint += DIM_COLOR(", ") + secondArgumentColor(secondArgument);
155
+ dimString = ")";
156
+ }
157
+ if (comment !== "") dimString += ` // ${comment}`;
158
+ if (dimString !== "") hint += DIM_COLOR(dimString);
159
+ return hint;
160
+ }
161
+ const SPACE_SYMBOL = "·";
162
+ // Instead of inverse highlight which now implies a change,
163
+ // replace common spaces with middle dot at the end of any line.
164
+ function replaceTrailingSpaces(text) {
165
+ return text.replace(/\s+$/gm, (spaces) => SPACE_SYMBOL.repeat(spaces.length));
166
+ }
167
+ function printReceived(object) {
168
+ return RECEIVED_COLOR(replaceTrailingSpaces(stringify(object)));
169
+ }
170
+ function printExpected(value) {
171
+ return EXPECTED_COLOR(replaceTrailingSpaces(stringify(value)));
172
+ }
173
+ function getMatcherUtils() {
174
+ return {
175
+ EXPECTED_COLOR,
176
+ RECEIVED_COLOR,
177
+ INVERTED_COLOR,
178
+ BOLD_WEIGHT,
179
+ DIM_COLOR,
180
+ diff,
181
+ matcherHint,
182
+ printReceived,
183
+ printExpected,
184
+ printDiffOrStringify,
185
+ printWithType
186
+ };
187
+ }
188
+ function printWithType(name, value, print) {
189
+ const type = getType(value);
190
+ return (type !== "null" && type !== "undefined" ? `${name} has type: ${type}\n` : "") + `${name} has value: ${print(value)}`;
191
+ }
192
+ function addCustomEqualityTesters(newTesters) {
193
+ if (!Array.isArray(newTesters)) throw new TypeError(`expect.customEqualityTesters: Must be set to an array of Testers. Was given "${getType(newTesters)}"`);
194
+ globalThis[JEST_MATCHERS_OBJECT].customEqualityTesters.push(...newTesters);
195
+ }
196
+ function getCustomEqualityTesters() {
197
+ return globalThis[JEST_MATCHERS_OBJECT].customEqualityTesters;
198
+ }
199
+
200
+ // Extracted out of jasmine 2.5.2
201
+ function equals(a, b, customTesters, strictCheck) {
202
+ customTesters = customTesters || [];
203
+ return eq(a, b, [], [], customTesters, strictCheck ? hasKey : hasDefinedKey);
204
+ }
205
+ function isAsymmetric(obj) {
206
+ return !!obj && typeof obj === "object" && "asymmetricMatch" in obj && isA("Function", obj.asymmetricMatch);
207
+ }
208
+ function asymmetricMatch(a, b, customTesters) {
209
+ const asymmetricA = isAsymmetric(a);
210
+ const asymmetricB = isAsymmetric(b);
211
+ if (asymmetricA && asymmetricB) return;
212
+ if (asymmetricA) return a.asymmetricMatch(b, customTesters);
213
+ if (asymmetricB) return b.asymmetricMatch(a, customTesters);
214
+ }
215
+ // https://github.com/jestjs/jest/blob/905bcbced3d40cdf7aadc4cdf6fb731c4bb3dbe3/packages/expect-utils/src/utils.ts#L509
216
+ function isError(value) {
217
+ if (typeof Error.isError === "function") return Error.isError(value);
218
+ switch (Object.prototype.toString.call(value)) {
219
+ case "[object Error]":
220
+ case "[object Exception]":
221
+ case "[object DOMException]": return true;
222
+ default: return value instanceof Error;
223
+ }
224
+ }
225
+ // Equality function lovingly adapted from isEqual in
226
+ // [Underscore](http://underscorejs.org)
227
+ function eq(a, b, aStack, bStack, customTesters, hasKey) {
228
+ let result = true;
229
+ const asymmetricResult = asymmetricMatch(a, b, customTesters);
230
+ if (asymmetricResult !== void 0) return asymmetricResult;
231
+ const testerContext = { equals };
232
+ for (let i = 0; i < customTesters.length; i++) {
233
+ const customTesterResult = customTesters[i].call(testerContext, a, b, customTesters);
234
+ if (customTesterResult !== void 0) return customTesterResult;
235
+ }
236
+ if (typeof URL === "function" && a instanceof URL && b instanceof URL) return a.href === b.href;
237
+ if (Object.is(a, b)) return true;
238
+ // A strict comparison is necessary because `null == undefined`.
239
+ if (a === null || b === null) return a === b;
240
+ const className = Object.prototype.toString.call(a);
241
+ if (className !== Object.prototype.toString.call(b)) return false;
242
+ switch (className) {
243
+ case "[object Boolean]":
244
+ case "[object String]":
245
+ case "[object Number]": if (typeof a !== typeof b)
246
+ // One is a primitive, one a `new Primitive()`
247
+ return false;
248
+ else if (typeof a !== "object" && typeof b !== "object")
249
+ // both are proper primitives
250
+ return Object.is(a, b);
251
+ else
252
+ // both are `new Primitive()`s
253
+ return Object.is(a.valueOf(), b.valueOf());
254
+ case "[object Date]": {
255
+ const numA = +a;
256
+ const numB = +b;
257
+ // Coerce dates to numeric primitive values. Dates are compared by their
258
+ // millisecond representations. Note that invalid dates with millisecond representations
259
+ // of `NaN` are equivalent.
260
+ return numA === numB || Number.isNaN(numA) && Number.isNaN(numB);
261
+ }
262
+ case "[object RegExp]": return a.source === b.source && a.flags === b.flags;
263
+ case "[object Temporal.Instant]":
264
+ case "[object Temporal.ZonedDateTime]":
265
+ case "[object Temporal.PlainDateTime]":
266
+ case "[object Temporal.PlainDate]":
267
+ case "[object Temporal.PlainTime]":
268
+ case "[object Temporal.PlainYearMonth]":
269
+ case "[object Temporal.PlainMonthDay]": return a.equals(b);
270
+ case "[object Temporal.Duration]": return a.toString() === b.toString();
271
+ }
272
+ if (typeof a !== "object" || typeof b !== "object") return false;
273
+ // Use DOM3 method isEqualNode (IE>=9)
274
+ if (isDomNode(a) && isDomNode(b)) return a.isEqualNode(b);
275
+ // Used to detect circular references.
276
+ let length = aStack.length;
277
+ while (length--)
278
+ // Linear search. Performance is inversely proportional to the number of
279
+ // unique nested structures.
280
+ // circular references at same depth are equal
281
+ // circular reference is not equal to non-circular one
282
+ if (aStack[length] === a) return bStack[length] === b;
283
+ else if (bStack[length] === b) return false;
284
+ // Add the first object to the stack of traversed objects.
285
+ aStack.push(a);
286
+ bStack.push(b);
287
+ // Recursively compare objects and arrays.
288
+ // Compare array lengths to determine if a deep comparison is necessary.
289
+ if (className === "[object Array]" && a.length !== b.length) return false;
290
+ if (isError(a) && isError(b)) try {
291
+ return isErrorEqual(a, b, aStack, bStack, customTesters, hasKey);
292
+ } finally {
293
+ aStack.pop();
294
+ bStack.pop();
295
+ }
296
+ // Deep compare objects.
297
+ const aKeys = keys(a, hasKey);
298
+ let key;
299
+ let size = aKeys.length;
300
+ // Ensure that both objects contain the same number of properties before comparing deep equality.
301
+ if (keys(b, hasKey).length !== size) return false;
302
+ while (size--) {
303
+ key = aKeys[size];
304
+ // Deep compare each member
305
+ result = hasKey(b, key) && eq(a[key], b[key], aStack, bStack, customTesters, hasKey);
306
+ if (!result) return false;
307
+ }
308
+ // Remove the first object from the stack of traversed objects.
309
+ aStack.pop();
310
+ bStack.pop();
311
+ return result;
312
+ }
313
+ function isErrorEqual(a, b, aStack, bStack, customTesters, hasKey) {
314
+ // https://nodejs.org/docs/latest-v22.x/api/assert.html#comparison-details
315
+ // - [[Prototype]] of objects are compared using the === operator.
316
+ // - Only enumerable "own" properties are considered.
317
+ // - Error names, messages, causes, and errors are always compared, even if these are not enumerable properties. errors is also compared.
318
+ let result = Object.prototype.toString.call(a) === Object.prototype.toString.call(b) && a.name === b.name && a.message === b.message;
319
+ // check Error.cause asymmetrically
320
+ if (typeof b.cause !== "undefined") result &&= eq(a.cause, b.cause, aStack, bStack, customTesters, hasKey);
321
+ // AggregateError.errors
322
+ if (a instanceof AggregateError && b instanceof AggregateError) result &&= eq(a.errors, b.errors, aStack, bStack, customTesters, hasKey);
323
+ // spread to compare enumerable properties
324
+ result &&= eq({ ...a }, { ...b }, aStack, bStack, customTesters, hasKey);
325
+ return result;
326
+ }
327
+ function keys(obj, hasKey) {
328
+ const keys = [];
329
+ for (const key in obj) if (hasKey(obj, key)) keys.push(key);
330
+ return keys.concat(Object.getOwnPropertySymbols(obj).filter((symbol) => Object.getOwnPropertyDescriptor(obj, symbol).enumerable));
331
+ }
332
+ function hasDefinedKey(obj, key) {
333
+ return hasKey(obj, key) && obj[key] !== void 0;
334
+ }
335
+ function hasKey(obj, key) {
336
+ return Object.hasOwn(obj, key);
337
+ }
338
+ function isA(typeName, value) {
339
+ return Object.prototype.toString.apply(value) === `[object ${typeName}]`;
340
+ }
341
+ function isDomNode(obj) {
342
+ return obj !== null && typeof obj === "object" && "nodeType" in obj && typeof obj.nodeType === "number" && "nodeName" in obj && typeof obj.nodeName === "string" && "isEqualNode" in obj && typeof obj.isEqualNode === "function";
343
+ }
344
+ // SENTINEL constants are from https://github.com/facebook/immutable-js
345
+ const IS_KEYED_SENTINEL = "@@__IMMUTABLE_KEYED__@@";
346
+ const IS_SET_SENTINEL = "@@__IMMUTABLE_SET__@@";
347
+ const IS_LIST_SENTINEL = "@@__IMMUTABLE_LIST__@@";
348
+ const IS_ORDERED_SENTINEL = "@@__IMMUTABLE_ORDERED__@@";
349
+ const IS_RECORD_SYMBOL = "@@__IMMUTABLE_RECORD__@@";
350
+ function isImmutableUnorderedKeyed(maybeKeyed) {
351
+ return !!(maybeKeyed && maybeKeyed[IS_KEYED_SENTINEL] && !maybeKeyed[IS_ORDERED_SENTINEL]);
352
+ }
353
+ function isImmutableUnorderedSet(maybeSet) {
354
+ return !!(maybeSet && maybeSet[IS_SET_SENTINEL] && !maybeSet[IS_ORDERED_SENTINEL]);
355
+ }
356
+ function isObjectLiteral(source) {
357
+ return source != null && typeof source === "object" && !Array.isArray(source);
358
+ }
359
+ function isImmutableList(source) {
360
+ return Boolean(source && isObjectLiteral(source) && source[IS_LIST_SENTINEL]);
361
+ }
362
+ function isImmutableOrderedKeyed(source) {
363
+ return Boolean(source && isObjectLiteral(source) && source[IS_KEYED_SENTINEL] && source[IS_ORDERED_SENTINEL]);
364
+ }
365
+ function isImmutableOrderedSet(source) {
366
+ return Boolean(source && isObjectLiteral(source) && source[IS_SET_SENTINEL] && source[IS_ORDERED_SENTINEL]);
367
+ }
368
+ function isImmutableRecord(source) {
369
+ return Boolean(source && isObjectLiteral(source) && source[IS_RECORD_SYMBOL]);
370
+ }
371
+ /**
372
+ * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
373
+ *
374
+ * This source code is licensed under the MIT license found in the
375
+ * LICENSE file in the root directory of this source tree.
376
+ *
377
+ */
378
+ const IteratorSymbol = Symbol.iterator;
379
+ function hasIterator(object) {
380
+ return !!(object != null && object[IteratorSymbol]);
381
+ }
382
+ function iterableEquality(a, b, customTesters = [], aStack = [], bStack = []) {
383
+ if (typeof a !== "object" || typeof b !== "object" || Array.isArray(a) || Array.isArray(b) || !hasIterator(a) || !hasIterator(b)) return;
384
+ if (a.constructor !== b.constructor) return false;
385
+ let length = aStack.length;
386
+ while (length--)
387
+ // Linear search. Performance is inversely proportional to the number of
388
+ // unique nested structures.
389
+ // circular references at same depth are equal
390
+ // circular reference is not equal to non-circular one
391
+ if (aStack[length] === a) return bStack[length] === b;
392
+ aStack.push(a);
393
+ bStack.push(b);
394
+ const filteredCustomTesters = [...customTesters.filter((t) => t !== iterableEquality), iterableEqualityWithStack];
395
+ function iterableEqualityWithStack(a, b) {
396
+ return iterableEquality(a, b, [...customTesters], [...aStack], [...bStack]);
397
+ }
398
+ if (a.size !== void 0) {
399
+ if (a.size !== b.size) return false;
400
+ else if (isA("Set", a) || isImmutableUnorderedSet(a)) {
401
+ let allFound = true;
402
+ for (const aValue of a) if (!b.has(aValue)) {
403
+ let has = false;
404
+ for (const bValue of b) if (equals(aValue, bValue, filteredCustomTesters) === true) has = true;
405
+ if (has === false) {
406
+ allFound = false;
407
+ break;
408
+ }
409
+ }
410
+ // Remove the first value from the stack of traversed values.
411
+ aStack.pop();
412
+ bStack.pop();
413
+ return allFound;
414
+ } else if (isA("Map", a) || isImmutableUnorderedKeyed(a)) {
415
+ let allFound = true;
416
+ for (const aEntry of a) if (!b.has(aEntry[0]) || !equals(aEntry[1], b.get(aEntry[0]), filteredCustomTesters)) {
417
+ let has = false;
418
+ for (const bEntry of b) {
419
+ const matchedKey = equals(aEntry[0], bEntry[0], filteredCustomTesters);
420
+ let matchedValue = false;
421
+ if (matchedKey === true) matchedValue = equals(aEntry[1], bEntry[1], filteredCustomTesters);
422
+ if (matchedValue === true) has = true;
423
+ }
424
+ if (has === false) {
425
+ allFound = false;
426
+ break;
427
+ }
428
+ }
429
+ // Remove the first value from the stack of traversed values.
430
+ aStack.pop();
431
+ bStack.pop();
432
+ return allFound;
433
+ }
434
+ }
435
+ const bIterator = b[IteratorSymbol]();
436
+ for (const aValue of a) {
437
+ const nextB = bIterator.next();
438
+ if (nextB.done || !equals(aValue, nextB.value, filteredCustomTesters)) return false;
439
+ }
440
+ if (!bIterator.next().done) return false;
441
+ if (!isImmutableList(a) && !isImmutableOrderedKeyed(a) && !isImmutableOrderedSet(a) && !isImmutableRecord(a)) {
442
+ if (!equals(Object.entries(a), Object.entries(b), filteredCustomTesters)) return false;
443
+ }
444
+ // Remove the first value from the stack of traversed values.
445
+ aStack.pop();
446
+ bStack.pop();
447
+ return true;
448
+ }
449
+ /**
450
+ * Checks if `hasOwnProperty(object, key)` up the prototype chain, stopping at `Object.prototype`.
451
+ */
452
+ function hasPropertyInObject(object, key) {
453
+ if (!object || typeof object !== "object" || object === Object.prototype) return false;
454
+ return Object.hasOwn(object, key) || hasPropertyInObject(Object.getPrototypeOf(object), key);
455
+ }
456
+ function isObjectWithKeys(a) {
457
+ return isObject(a) && !isError(a) && !Array.isArray(a) && !(a instanceof Date) && !(a instanceof Set) && !(a instanceof Map);
458
+ }
459
+ function subsetEquality(object, subset, customTesters = []) {
460
+ const filteredCustomTesters = customTesters.filter((t) => t !== subsetEquality);
461
+ // subsetEquality needs to keep track of the references
462
+ // it has already visited to avoid infinite loops in case
463
+ // there are circular references in the subset passed to it.
464
+ const subsetEqualityWithContext = (seenReferences = /* @__PURE__ */ new WeakMap()) => (object, subset) => {
465
+ if (!isObjectWithKeys(subset)) return;
466
+ return Object.keys(subset).every((key) => {
467
+ if (subset[key] != null && typeof subset[key] === "object") {
468
+ if (seenReferences.has(subset[key])) return equals(object[key], subset[key], filteredCustomTesters);
469
+ seenReferences.set(subset[key], true);
470
+ }
471
+ const result = object != null && hasPropertyInObject(object, key) && equals(object[key], subset[key], [...filteredCustomTesters, subsetEqualityWithContext(seenReferences)]);
472
+ // The main goal of using seenReference is to avoid circular node on tree.
473
+ // It will only happen within a parent and its child, not a node and nodes next to it (same level)
474
+ // We should keep the reference for a parent and its child only
475
+ // Thus we should delete the reference immediately so that it doesn't interfere
476
+ // other nodes within the same level on tree.
477
+ seenReferences.delete(subset[key]);
478
+ return result;
479
+ });
480
+ };
481
+ return subsetEqualityWithContext()(object, subset);
482
+ }
483
+ function typeEquality(a, b) {
484
+ if (a == null || b == null || a.constructor === b.constructor) return;
485
+ return false;
486
+ }
487
+ function arrayBufferEquality(a, b) {
488
+ let dataViewA = a;
489
+ let dataViewB = b;
490
+ if (!(a instanceof DataView && b instanceof DataView)) {
491
+ if (!(a instanceof ArrayBuffer) || !(b instanceof ArrayBuffer)) return;
492
+ try {
493
+ dataViewA = new DataView(a);
494
+ dataViewB = new DataView(b);
495
+ } catch {
496
+ return;
497
+ }
498
+ }
499
+ // Buffers are not equal when they do not have the same byte length
500
+ if (dataViewA.byteLength !== dataViewB.byteLength) return false;
501
+ // Check if every byte value is equal to each other
502
+ for (let i = 0; i < dataViewA.byteLength; i++) if (dataViewA.getUint8(i) !== dataViewB.getUint8(i)) return false;
503
+ return true;
504
+ }
505
+ function sparseArrayEquality(a, b, customTesters = []) {
506
+ if (!Array.isArray(a) || !Array.isArray(b)) return;
507
+ // A sparse array [, , 1] will have keys ["2"] whereas [undefined, undefined, 1] will have keys ["0", "1", "2"]
508
+ const aKeys = Object.keys(a);
509
+ const bKeys = Object.keys(b);
510
+ return equals(a, b, customTesters.filter((t) => t !== sparseArrayEquality), true) && equals(aKeys, bKeys);
511
+ }
512
+ function generateToBeMessage(deepEqualityName, expected = "#{this}", actual = "#{exp}") {
513
+ const toBeMessage = `expected ${expected} to be ${actual} // Object.is equality`;
514
+ if (["toStrictEqual", "toEqual"].includes(deepEqualityName)) return `${toBeMessage}\n\nIf it should pass with deep equality, replace "toBe" with "${deepEqualityName}"\n\nExpected: ${expected}\nReceived: serializes to the same string\n`;
515
+ return toBeMessage;
516
+ }
517
+ function pluralize(word, count) {
518
+ return `${count} ${word}${count === 1 ? "" : "s"}`;
519
+ }
520
+ function getObjectKeys(object) {
521
+ return [...Object.keys(object), ...Object.getOwnPropertySymbols(object).filter((s) => Object.getOwnPropertyDescriptor(object, s)?.enumerable)];
522
+ }
523
+ function getObjectSubset(object, subset, customTesters) {
524
+ let stripped = 0;
525
+ const getObjectSubsetWithContext = (seenReferences = /* @__PURE__ */ new WeakMap()) => (object, subset) => {
526
+ if (Array.isArray(object)) {
527
+ if (Array.isArray(subset) && subset.length === object.length)
528
+ // The map method returns correct subclass of subset.
529
+ return subset.map((sub, i) => getObjectSubsetWithContext(seenReferences)(object[i], sub));
530
+ } else if (object instanceof Date) return object;
531
+ else if (isObject(object) && isObject(subset)) {
532
+ if (equals(object, subset, [
533
+ ...customTesters,
534
+ iterableEquality,
535
+ subsetEquality
536
+ ]))
537
+ // return "expected" subset to avoid showing irrelevant toMatchObject diff
538
+ return subset;
539
+ const trimmed = {};
540
+ seenReferences.set(object, trimmed);
541
+ // preserve constructor for toMatchObject diff
542
+ if (typeof object.constructor === "function" && typeof object.constructor.name === "string") Object.defineProperty(trimmed, "constructor", {
543
+ enumerable: false,
544
+ value: object.constructor
545
+ });
546
+ for (const key of getObjectKeys(object)) if (hasPropertyInObject(subset, key)) trimmed[key] = seenReferences.has(object[key]) ? seenReferences.get(object[key]) : getObjectSubsetWithContext(seenReferences)(object[key], subset[key]);
547
+ else if (!seenReferences.has(object[key])) {
548
+ stripped += 1;
549
+ if (isObject(object[key])) stripped += getObjectKeys(object[key]).length;
550
+ getObjectSubsetWithContext(seenReferences)(object[key], subset[key]);
551
+ }
552
+ if (getObjectKeys(trimmed).length > 0) return trimmed;
553
+ }
554
+ return object;
555
+ };
556
+ return {
557
+ subset: getObjectSubsetWithContext()(object, subset),
558
+ stripped
559
+ };
560
+ }
561
+ /**
562
+ * Detects if an object is a Standard Schema V1 compatible schema
563
+ */
564
+ function isStandardSchema(obj) {
565
+ return !!obj && (typeof obj === "object" || typeof obj === "function") && obj["~standard"] && typeof obj["~standard"].validate === "function";
566
+ }
567
+
568
+ if (!Object.hasOwn(globalThis, MATCHERS_OBJECT)) {
569
+ const globalState = /* @__PURE__ */ new WeakMap();
570
+ const matchers = Object.create(null);
571
+ const customEqualityTesters = [];
572
+ const asymmetricMatchers = Object.create(null);
573
+ Object.defineProperty(globalThis, MATCHERS_OBJECT, { get: () => globalState });
574
+ Object.defineProperty(globalThis, JEST_MATCHERS_OBJECT, {
575
+ configurable: true,
576
+ get: () => ({
577
+ state: globalState.get(globalThis[GLOBAL_EXPECT]),
578
+ matchers,
579
+ customEqualityTesters
580
+ })
581
+ });
582
+ Object.defineProperty(globalThis, ASYMMETRIC_MATCHERS_OBJECT, { get: () => asymmetricMatchers });
583
+ }
584
+ function getState(expect) {
585
+ return globalThis[MATCHERS_OBJECT].get(expect);
586
+ }
587
+ function setState(state, expect) {
588
+ const map = globalThis[MATCHERS_OBJECT];
589
+ const current = map.get(expect) || {};
590
+ // so it keeps getters from `testPath`
591
+ const results = Object.defineProperties(current, {
592
+ ...Object.getOwnPropertyDescriptors(current),
593
+ ...Object.getOwnPropertyDescriptors(state)
594
+ });
595
+ map.set(expect, results);
596
+ }
597
+
598
+ class AsymmetricMatcher {
599
+ // should have "jest" to be compatible with its ecosystem
600
+ $$typeof = Symbol.for("jest.asymmetricMatcher");
601
+ constructor(sample, inverse = false) {
602
+ this.sample = sample;
603
+ this.inverse = inverse;
604
+ }
605
+ getMatcherContext(expect) {
606
+ return {
607
+ ...getState(expect || globalThis[GLOBAL_EXPECT]),
608
+ equals,
609
+ isNot: this.inverse,
610
+ customTesters: getCustomEqualityTesters(),
611
+ utils: {
612
+ ...getMatcherUtils(),
613
+ diff,
614
+ stringify,
615
+ iterableEquality,
616
+ subsetEquality
617
+ }
618
+ };
619
+ }
620
+ }
621
+ // implement custom chai/loupe inspect for better AssertionError.message formatting
622
+ // https://github.com/chaijs/loupe/blob/9b8a6deabcd50adc056a64fb705896194710c5c6/src/index.ts#L29
623
+ // @ts-expect-error computed properties is not supported when isolatedDeclarations is enabled
624
+ // FIXME: https://github.com/microsoft/TypeScript/issues/61068
625
+ AsymmetricMatcher.prototype[Symbol.for("chai/inspect")] = function(options) {
626
+ // minimal pretty-format with simple manual truncation
627
+ const result = stringify(this, options.depth, { min: true });
628
+ if (result.length <= options.truncate) return result;
629
+ return `${this.toString()}{…}`;
630
+ };
631
+ class StringContaining extends AsymmetricMatcher {
632
+ constructor(sample, inverse = false) {
633
+ if (!isA("String", sample)) throw new Error("Expected is not a string");
634
+ super(sample, inverse);
635
+ }
636
+ asymmetricMatch(other) {
637
+ const result = isA("String", other) && other.includes(this.sample);
638
+ return this.inverse ? !result : result;
639
+ }
640
+ toString() {
641
+ return `String${this.inverse ? "Not" : ""}Containing`;
642
+ }
643
+ getExpectedType() {
644
+ return "string";
645
+ }
646
+ }
647
+ class Anything extends AsymmetricMatcher {
648
+ asymmetricMatch(other) {
649
+ return other != null;
650
+ }
651
+ toString() {
652
+ return "Anything";
653
+ }
654
+ toAsymmetricMatcher() {
655
+ return "Anything";
656
+ }
657
+ }
658
+ class ObjectContaining extends AsymmetricMatcher {
659
+ constructor(sample, inverse = false) {
660
+ super(sample, inverse);
661
+ }
662
+ getPrototype(obj) {
663
+ if (Object.getPrototypeOf) return Object.getPrototypeOf(obj);
664
+ if (obj.constructor.prototype === obj) return null;
665
+ return obj.constructor.prototype;
666
+ }
667
+ hasProperty(obj, property) {
668
+ if (!obj) return false;
669
+ if (Object.hasOwn(obj, property)) return true;
670
+ return this.hasProperty(this.getPrototype(obj), property);
671
+ }
672
+ getProperties(obj) {
673
+ return [...Object.keys(obj), ...Object.getOwnPropertySymbols(obj).filter((s) => Object.getOwnPropertyDescriptor(obj, s)?.enumerable)];
674
+ }
675
+ asymmetricMatch(other, customTesters) {
676
+ if (typeof this.sample !== "object") throw new TypeError(`You must provide an object to ${this.toString()}, not '${typeof this.sample}'.`);
677
+ let result = true;
678
+ const properties = this.getProperties(this.sample);
679
+ for (const property of properties) {
680
+ if (!this.hasProperty(other, property)) {
681
+ result = false;
682
+ break;
683
+ }
684
+ const value = this.sample[property];
685
+ const otherValue = other[property];
686
+ if (!equals(value, otherValue, customTesters)) {
687
+ result = false;
688
+ break;
689
+ }
690
+ }
691
+ return this.inverse ? !result : result;
692
+ }
693
+ toString() {
694
+ return `Object${this.inverse ? "Not" : ""}Containing`;
695
+ }
696
+ getExpectedType() {
697
+ return "object";
698
+ }
699
+ }
700
+ class ArrayContaining extends AsymmetricMatcher {
701
+ constructor(sample, inverse = false) {
702
+ super(sample, inverse);
703
+ }
704
+ asymmetricMatch(other, customTesters) {
705
+ if (!Array.isArray(this.sample)) throw new TypeError(`You must provide an array to ${this.toString()}, not '${typeof this.sample}'.`);
706
+ const result = this.sample.length === 0 || Array.isArray(other) && this.sample.every((item) => other.some((another) => equals(item, another, customTesters)));
707
+ return this.inverse ? !result : result;
708
+ }
709
+ toString() {
710
+ return `Array${this.inverse ? "Not" : ""}Containing`;
711
+ }
712
+ getExpectedType() {
713
+ return "array";
714
+ }
715
+ }
716
+ class Any extends AsymmetricMatcher {
717
+ constructor(sample) {
718
+ if (typeof sample === "undefined") throw new TypeError("any() expects to be passed a constructor function. Please pass one or use anything() to match any object.");
719
+ super(sample);
720
+ }
721
+ fnNameFor(func) {
722
+ if (func.name) return func.name;
723
+ const matches = Function.prototype.toString.call(func).match(/^(?:async)?\s*function\s*(?:\*\s*)?([\w$]+)\s*\(/);
724
+ return matches ? matches[1] : "<anonymous>";
725
+ }
726
+ asymmetricMatch(other) {
727
+ if (this.sample === String) return typeof other == "string" || other instanceof String;
728
+ if (this.sample === Number) return typeof other == "number" || other instanceof Number;
729
+ if (this.sample === Function) return typeof other == "function" || typeof other === "function";
730
+ if (this.sample === Boolean) return typeof other == "boolean" || other instanceof Boolean;
731
+ if (this.sample === BigInt) return typeof other == "bigint" || other instanceof BigInt;
732
+ if (this.sample === Symbol) return typeof other == "symbol" || other instanceof Symbol;
733
+ if (this.sample === Object) return typeof other == "object";
734
+ return other instanceof this.sample;
735
+ }
736
+ toString() {
737
+ return "Any";
738
+ }
739
+ getExpectedType() {
740
+ if (this.sample === String) return "string";
741
+ if (this.sample === Number) return "number";
742
+ if (this.sample === Function) return "function";
743
+ if (this.sample === Object) return "object";
744
+ if (this.sample === Boolean) return "boolean";
745
+ return this.fnNameFor(this.sample);
746
+ }
747
+ toAsymmetricMatcher() {
748
+ return `Any<${this.fnNameFor(this.sample)}>`;
749
+ }
750
+ }
751
+ class StringMatching extends AsymmetricMatcher {
752
+ constructor(sample, inverse = false) {
753
+ if (!isA("String", sample) && !isA("RegExp", sample)) throw new Error("Expected is not a String or a RegExp");
754
+ super(new RegExp(sample), inverse);
755
+ }
756
+ asymmetricMatch(other) {
757
+ const result = isA("String", other) && this.sample.test(other);
758
+ return this.inverse ? !result : result;
759
+ }
760
+ toString() {
761
+ return `String${this.inverse ? "Not" : ""}Matching`;
762
+ }
763
+ getExpectedType() {
764
+ return "string";
765
+ }
766
+ }
767
+ class CloseTo extends AsymmetricMatcher {
768
+ precision;
769
+ constructor(sample, precision = 2, inverse = false) {
770
+ if (!isA("Number", sample)) throw new Error("Expected is not a Number");
771
+ if (!isA("Number", precision)) throw new Error("Precision is not a Number");
772
+ super(sample);
773
+ this.inverse = inverse;
774
+ this.precision = precision;
775
+ }
776
+ asymmetricMatch(other) {
777
+ if (!isA("Number", other)) return false;
778
+ let result = false;
779
+ if (other === Number.POSITIVE_INFINITY && this.sample === Number.POSITIVE_INFINITY) result = true;
780
+ else if (other === Number.NEGATIVE_INFINITY && this.sample === Number.NEGATIVE_INFINITY) result = true;
781
+ else result = Math.abs(this.sample - other) < 10 ** -this.precision / 2;
782
+ return this.inverse ? !result : result;
783
+ }
784
+ toString() {
785
+ return `Number${this.inverse ? "Not" : ""}CloseTo`;
786
+ }
787
+ getExpectedType() {
788
+ return "number";
789
+ }
790
+ toAsymmetricMatcher() {
791
+ return [
792
+ this.toString(),
793
+ this.sample,
794
+ `(${pluralize("digit", this.precision)})`
795
+ ].join(" ");
796
+ }
797
+ }
798
+ class SchemaMatching extends AsymmetricMatcher {
799
+ result;
800
+ constructor(sample, inverse = false) {
801
+ if (!isStandardSchema(sample)) throw new TypeError("SchemaMatching expected to receive a Standard Schema.");
802
+ super(sample, inverse);
803
+ }
804
+ asymmetricMatch(other) {
805
+ const result = this.sample["~standard"].validate(other);
806
+ // Check if the result is a Promise (async validation)
807
+ if (result instanceof Promise) throw new TypeError("Async schema validation is not supported in asymmetric matchers.");
808
+ this.result = result;
809
+ const pass = !this.result.issues || this.result.issues.length === 0;
810
+ return this.inverse ? !pass : pass;
811
+ }
812
+ toString() {
813
+ return `Schema${this.inverse ? "Not" : ""}Matching`;
814
+ }
815
+ getExpectedType() {
816
+ return "object";
817
+ }
818
+ toAsymmetricMatcher() {
819
+ const { utils } = this.getMatcherContext();
820
+ if ((this.result?.issues || []).length > 0) return `${this.toString()} ${utils.stringify(this.result, void 0, { printBasicPrototype: false })}`;
821
+ return this.toString();
822
+ }
823
+ }
824
+ const JestAsymmetricMatchers = (chai, utils) => {
825
+ utils.addMethod(chai.expect, "anything", () => new Anything());
826
+ utils.addMethod(chai.expect, "any", (expected) => new Any(expected));
827
+ utils.addMethod(chai.expect, "stringContaining", (expected) => new StringContaining(expected));
828
+ utils.addMethod(chai.expect, "objectContaining", (expected) => new ObjectContaining(expected));
829
+ utils.addMethod(chai.expect, "arrayContaining", (expected) => new ArrayContaining(expected));
830
+ utils.addMethod(chai.expect, "stringMatching", (expected) => new StringMatching(expected));
831
+ utils.addMethod(chai.expect, "closeTo", (expected, precision) => new CloseTo(expected, precision));
832
+ utils.addMethod(chai.expect, "schemaMatching", (expected) => new SchemaMatching(expected));
833
+ // defineProperty does not work
834
+ chai.expect.not = {
835
+ stringContaining: (expected) => new StringContaining(expected, true),
836
+ objectContaining: (expected) => new ObjectContaining(expected, true),
837
+ arrayContaining: (expected) => new ArrayContaining(expected, true),
838
+ stringMatching: (expected) => new StringMatching(expected, true),
839
+ closeTo: (expected, precision) => new CloseTo(expected, precision, true),
840
+ schemaMatching: (expected) => new SchemaMatching(expected, true)
841
+ };
842
+ };
843
+
844
+ function createAssertionMessage(util, assertion, hasArgs) {
845
+ const soft = util.flag(assertion, "soft") ? ".soft" : "";
846
+ const not = util.flag(assertion, "negate") ? "not." : "";
847
+ const name = `${util.flag(assertion, "_name")}(${hasArgs ? "expected" : ""})`;
848
+ const promiseName = util.flag(assertion, "promise");
849
+ return `expect${soft}(actual)${promiseName ? `.${promiseName}` : ""}.${not}${name}`;
850
+ }
851
+ function recordAsyncExpect(_test, promise, assertion, error, isSoft) {
852
+ const test = _test;
853
+ // record promise for test, that resolves before test ends
854
+ if (test && promise instanceof Promise) {
855
+ // if promise is explicitly awaited, remove it from the list
856
+ promise = promise.finally(() => {
857
+ if (!test.promises) return;
858
+ const index = test.promises.indexOf(promise);
859
+ if (index !== -1) test.promises.splice(index, 1);
860
+ });
861
+ // record promise
862
+ if (!test.promises) test.promises = [];
863
+ // setup `expect.soft` handler here instead of `wrapAssertion`
864
+ // to avoid double error tracking while keeping non-await promise detection.
865
+ if (isSoft) promise = promise.then(noop, (err) => {
866
+ handleTestError(test, err);
867
+ });
868
+ test.promises.push(promise);
869
+ let resolved = false;
870
+ test.onFinished ??= [];
871
+ test.onFinished.push(() => {
872
+ if (!resolved) {
873
+ const stack = (globalThis.__vitest_worker__?.onFilterStackTrace || ((s) => s || ""))(error.stack);
874
+ console.warn([
875
+ `Promise returned by \`${assertion}\` was not awaited. `,
876
+ "Vitest currently auto-awaits hanging assertions at the end of the test, but this will cause the test to fail in the next Vitest major. ",
877
+ "Please remember to await the assertion.\n",
878
+ stack
879
+ ].join(""));
880
+ }
881
+ });
882
+ return {
883
+ then(onFulfilled, onRejected) {
884
+ resolved = true;
885
+ return promise.then(onFulfilled, onRejected);
886
+ },
887
+ catch(onRejected) {
888
+ resolved = true;
889
+ return promise.catch(onRejected);
890
+ },
891
+ finally(onFinally) {
892
+ resolved = true;
893
+ return promise.finally(onFinally);
894
+ },
895
+ [Symbol.toStringTag]: "Promise"
896
+ };
897
+ }
898
+ return promise;
899
+ }
900
+ function handleTestError(test, err) {
901
+ test.result ||= { state: "fail" };
902
+ test.result.state = "fail";
903
+ test.result.errors ||= [];
904
+ test.result.errors.push(processError(err));
905
+ }
906
+ /** wrap assertion function to support `expect.soft` and provide assertion name as `_name` */
907
+ function wrapAssertion(utils, name, fn) {
908
+ return function(...args) {
909
+ // private
910
+ if (name !== "withTest") utils.flag(this, "_name", name);
911
+ if (!utils.flag(this, "soft"))
912
+ // avoid WebKit's proper tail call to preserve stacktrace offset for inline snapshot
913
+ // https://webkit.org/blog/6240/ecmascript-6-proper-tail-calls-in-webkit
914
+ try {
915
+ return fn.apply(this, args);
916
+ } finally {}
917
+ const test = utils.flag(this, "vitest-test");
918
+ if (!test) throw new Error("expect.soft() can only be used inside a test");
919
+ try {
920
+ const result = fn.apply(this, args);
921
+ if (result && typeof result === "object" && typeof result.then === "function") return result.then(noop, (err) => {
922
+ handleTestError(test, err);
923
+ });
924
+ return result;
925
+ } catch (err) {
926
+ handleTestError(test, err);
927
+ }
928
+ };
929
+ }
930
+
931
+ // Jest Expect Compact
932
+ const JestChaiExpect = (chai, utils) => {
933
+ const { AssertionError } = chai;
934
+ const customTesters = getCustomEqualityTesters();
935
+ function def(name, fn) {
936
+ const addMethod = (n) => {
937
+ const softWrapper = wrapAssertion(utils, n, fn);
938
+ utils.addMethod(chai.Assertion.prototype, n, softWrapper);
939
+ utils.addMethod(globalThis[JEST_MATCHERS_OBJECT].matchers, n, softWrapper);
940
+ };
941
+ if (Array.isArray(name)) name.forEach((n) => addMethod(n));
942
+ else addMethod(name);
943
+ }
944
+ [
945
+ "throw",
946
+ "throws",
947
+ "Throw"
948
+ ].forEach((m) => {
949
+ utils.overwriteMethod(chai.Assertion.prototype, m, (_super) => {
950
+ return function(...args) {
951
+ const promise = utils.flag(this, "promise");
952
+ const object = utils.flag(this, "object");
953
+ const isNot = utils.flag(this, "negate");
954
+ if (promise === "rejects") utils.flag(this, "object", () => {
955
+ throw object;
956
+ });
957
+ else if (promise === "resolves" && typeof object !== "function") if (!isNot) throw new AssertionError(utils.flag(this, "message") || "expected promise to throw an error, but it didn't", { showDiff: false }, utils.flag(this, "ssfi"));
958
+ else return;
959
+ _super.apply(this, args);
960
+ };
961
+ });
962
+ });
963
+ // @ts-expect-error @internal
964
+ def("withTest", function(test) {
965
+ utils.flag(this, "vitest-test", test);
966
+ return this;
967
+ });
968
+ def("toEqual", function(expected) {
969
+ const actual = utils.flag(this, "object");
970
+ const equal = equals(actual, expected, [...customTesters, iterableEquality]);
971
+ return this.assert(equal, "expected #{this} to deeply equal #{exp}", "expected #{this} to not deeply equal #{exp}", expected, actual);
972
+ });
973
+ def("toStrictEqual", function(expected) {
974
+ const obj = utils.flag(this, "object");
975
+ const equal = equals(obj, expected, [
976
+ ...customTesters,
977
+ iterableEquality,
978
+ typeEquality,
979
+ sparseArrayEquality,
980
+ arrayBufferEquality
981
+ ], true);
982
+ return this.assert(equal, "expected #{this} to strictly equal #{exp}", "expected #{this} to not strictly equal #{exp}", expected, obj);
983
+ });
984
+ def("toBe", function(expected) {
985
+ const actual = this._obj;
986
+ const pass = Object.is(actual, expected);
987
+ let deepEqualityName = "";
988
+ if (!pass) {
989
+ if (equals(actual, expected, [
990
+ ...customTesters,
991
+ iterableEquality,
992
+ typeEquality,
993
+ sparseArrayEquality,
994
+ arrayBufferEquality
995
+ ], true)) deepEqualityName = "toStrictEqual";
996
+ else if (equals(actual, expected, [...customTesters, iterableEquality])) deepEqualityName = "toEqual";
997
+ }
998
+ return this.assert(pass, generateToBeMessage(deepEqualityName), "expected #{this} not to be #{exp} // Object.is equality", expected, actual);
999
+ });
1000
+ def("toMatchObject", function(expected) {
1001
+ const actual = this._obj;
1002
+ const pass = equals(actual, expected, [
1003
+ ...customTesters,
1004
+ iterableEquality,
1005
+ subsetEquality
1006
+ ]);
1007
+ const isNot = utils.flag(this, "negate");
1008
+ const { subset: actualSubset, stripped } = getObjectSubset(actual, expected, customTesters);
1009
+ if (pass && isNot || !pass && !isNot) {
1010
+ const msg = utils.getMessage(this, [
1011
+ pass,
1012
+ "expected #{this} to match object #{exp}",
1013
+ "expected #{this} to not match object #{exp}",
1014
+ expected,
1015
+ actualSubset,
1016
+ false
1017
+ ]);
1018
+ throw new AssertionError(stripped === 0 ? msg : `${msg}\n(${stripped} matching ${stripped === 1 ? "property" : "properties"} omitted from actual)`, {
1019
+ showDiff: true,
1020
+ expected,
1021
+ actual: actualSubset
1022
+ });
1023
+ }
1024
+ });
1025
+ def("toMatch", function(expected) {
1026
+ const actual = this._obj;
1027
+ if (typeof actual !== "string") throw new TypeError(`.toMatch() expects to receive a string, but got ${typeof actual}`);
1028
+ return this.assert(typeof expected === "string" ? actual.includes(expected) : actual.match(expected), `expected #{this} to match #{exp}`, `expected #{this} not to match #{exp}`, expected, actual);
1029
+ });
1030
+ def("toContain", function(item) {
1031
+ const actual = this._obj;
1032
+ if (typeof Node !== "undefined" && actual instanceof Node) {
1033
+ if (!(item instanceof Node)) throw new TypeError(`toContain() expected a DOM node as the argument, but got ${typeof item}`);
1034
+ return this.assert(actual.contains(item), "expected #{this} to contain element #{exp}", "expected #{this} not to contain element #{exp}", item, actual);
1035
+ }
1036
+ if (typeof DOMTokenList !== "undefined" && actual instanceof DOMTokenList) {
1037
+ assertTypes(item, "class name", ["string"]);
1038
+ const expectedClassList = utils.flag(this, "negate") ? actual.value.replace(item, "").trim() : `${actual.value} ${item}`;
1039
+ return this.assert(actual.contains(item), `expected "${actual.value}" to contain "${item}"`, `expected "${actual.value}" not to contain "${item}"`, expectedClassList, actual.value);
1040
+ }
1041
+ // handle simple case on our own using `this.assert` to include diff in error message
1042
+ if (typeof actual === "string" && typeof item === "string") return this.assert(actual.includes(item), `expected #{this} to contain #{exp}`, `expected #{this} not to contain #{exp}`, item, actual);
1043
+ // make "actual" indexable to have compatibility with jest
1044
+ if (actual != null && typeof actual !== "string") utils.flag(this, "object", Array.from(actual));
1045
+ return this.contain(item);
1046
+ });
1047
+ def("toContainEqual", function(expected) {
1048
+ const obj = utils.flag(this, "object");
1049
+ const index = Array.from(obj).findIndex((item) => {
1050
+ return equals(item, expected, customTesters);
1051
+ });
1052
+ this.assert(index !== -1, "expected #{this} to deep equally contain #{exp}", "expected #{this} to not deep equally contain #{exp}", expected);
1053
+ });
1054
+ def("toBeTruthy", function() {
1055
+ const obj = utils.flag(this, "object");
1056
+ this.assert(Boolean(obj), "expected #{this} to be truthy", "expected #{this} to not be truthy", true, obj);
1057
+ });
1058
+ def("toBeFalsy", function() {
1059
+ const obj = utils.flag(this, "object");
1060
+ this.assert(!obj, "expected #{this} to be falsy", "expected #{this} to not be falsy", false, obj);
1061
+ });
1062
+ def("toBeGreaterThan", function(expected) {
1063
+ const actual = this._obj;
1064
+ assertTypes(actual, "actual", ["number", "bigint"]);
1065
+ assertTypes(expected, "expected", ["number", "bigint"]);
1066
+ return this.assert(actual > expected, `expected ${actual} to be greater than ${expected}`, `expected ${actual} to be not greater than ${expected}`, expected, actual, false);
1067
+ });
1068
+ def("toBeGreaterThanOrEqual", function(expected) {
1069
+ const actual = this._obj;
1070
+ assertTypes(actual, "actual", ["number", "bigint"]);
1071
+ assertTypes(expected, "expected", ["number", "bigint"]);
1072
+ return this.assert(actual >= expected, `expected ${actual} to be greater than or equal to ${expected}`, `expected ${actual} to be not greater than or equal to ${expected}`, expected, actual, false);
1073
+ });
1074
+ def("toBeLessThan", function(expected) {
1075
+ const actual = this._obj;
1076
+ assertTypes(actual, "actual", ["number", "bigint"]);
1077
+ assertTypes(expected, "expected", ["number", "bigint"]);
1078
+ return this.assert(actual < expected, `expected ${actual} to be less than ${expected}`, `expected ${actual} to be not less than ${expected}`, expected, actual, false);
1079
+ });
1080
+ def("toBeLessThanOrEqual", function(expected) {
1081
+ const actual = this._obj;
1082
+ assertTypes(actual, "actual", ["number", "bigint"]);
1083
+ assertTypes(expected, "expected", ["number", "bigint"]);
1084
+ return this.assert(actual <= expected, `expected ${actual} to be less than or equal to ${expected}`, `expected ${actual} to be not less than or equal to ${expected}`, expected, actual, false);
1085
+ });
1086
+ def("toBeNaN", function() {
1087
+ const obj = utils.flag(this, "object");
1088
+ this.assert(Number.isNaN(obj), "expected #{this} to be NaN", "expected #{this} not to be NaN", NaN, obj);
1089
+ });
1090
+ def("toBeUndefined", function() {
1091
+ const obj = utils.flag(this, "object");
1092
+ this.assert(void 0 === obj, "expected #{this} to be undefined", "expected #{this} not to be undefined", void 0, obj);
1093
+ });
1094
+ def("toBeNull", function() {
1095
+ const obj = utils.flag(this, "object");
1096
+ this.assert(obj === null, "expected #{this} to be null", "expected #{this} not to be null", null, obj);
1097
+ });
1098
+ def("toBeNullable", function() {
1099
+ const obj = utils.flag(this, "object");
1100
+ this.assert(obj == null, "expected #{this} to be nullish", "expected #{this} not to be nullish", null, obj);
1101
+ });
1102
+ def("toBeDefined", function() {
1103
+ const obj = utils.flag(this, "object");
1104
+ this.assert(typeof obj !== "undefined", "expected #{this} to be defined", "expected #{this} to be undefined", obj);
1105
+ });
1106
+ def("toBeTypeOf", function(expected) {
1107
+ const actual = typeof this._obj;
1108
+ const equal = expected === actual;
1109
+ return this.assert(equal, "expected #{this} to be type of #{exp}", "expected #{this} not to be type of #{exp}", expected, actual);
1110
+ });
1111
+ def("toBeInstanceOf", function(obj) {
1112
+ return this.instanceOf(obj);
1113
+ });
1114
+ def("toHaveLength", function(length) {
1115
+ return this.have.length(length);
1116
+ });
1117
+ // destructuring, because it checks `arguments` inside, and value is passing as `undefined`
1118
+ def("toHaveProperty", function(...args) {
1119
+ if (Array.isArray(args[0])) args[0] = args[0].map((key) => String(key).replace(/([.[\]])/g, "\\$1")).join(".");
1120
+ const actual = this._obj;
1121
+ const [propertyName, expected] = args;
1122
+ const getValue = () => {
1123
+ if (Object.hasOwn(actual, propertyName)) return {
1124
+ value: actual[propertyName],
1125
+ exists: true
1126
+ };
1127
+ return utils.getPathInfo(actual, propertyName);
1128
+ };
1129
+ const { value, exists } = getValue();
1130
+ const pass = exists && (args.length === 1 || equals(expected, value, customTesters));
1131
+ const valueString = args.length === 1 ? "" : ` with value ${inspect(expected, { truncate: 40 })}`;
1132
+ return this.assert(pass, `expected #{this} to have property "${propertyName}"${valueString}`, `expected #{this} to not have property "${propertyName}"${valueString}`, expected, exists ? value : void 0);
1133
+ });
1134
+ def("toBeCloseTo", function(received, precision = 2) {
1135
+ const expected = this._obj;
1136
+ let pass = false;
1137
+ let expectedDiff = 0;
1138
+ let receivedDiff = 0;
1139
+ if (received === Number.POSITIVE_INFINITY && expected === Number.POSITIVE_INFINITY) pass = true;
1140
+ else if (received === Number.NEGATIVE_INFINITY && expected === Number.NEGATIVE_INFINITY) pass = true;
1141
+ else {
1142
+ expectedDiff = 10 ** -precision / 2;
1143
+ receivedDiff = Math.abs(expected - received);
1144
+ pass = receivedDiff < expectedDiff;
1145
+ }
1146
+ return this.assert(pass, `expected #{this} to be close to #{exp}, received difference is ${receivedDiff}, but expected ${expectedDiff}`, `expected #{this} to not be close to #{exp}, received difference is ${receivedDiff}, but expected ${expectedDiff}`, received, expected, false);
1147
+ });
1148
+ function assertIsMock(assertion) {
1149
+ if (!isMockFunction(assertion._obj)) throw new TypeError(`${utils.inspect(assertion._obj)} is not a spy or a call to a spy!`);
1150
+ }
1151
+ function getSpy(assertion) {
1152
+ assertIsMock(assertion);
1153
+ return assertion._obj;
1154
+ }
1155
+ def(["toHaveBeenCalledTimes", "toBeCalledTimes"], function(number) {
1156
+ const spy = getSpy(this);
1157
+ const spyName = spy.getMockName();
1158
+ const callCount = spy.mock.calls.length;
1159
+ return this.assert(callCount === number, `expected "${spyName}" to be called #{exp} times, but got ${callCount} times`, `expected "${spyName}" to not be called #{exp} times`, number, callCount, false);
1160
+ });
1161
+ def("toHaveBeenCalledOnce", function() {
1162
+ const spy = getSpy(this);
1163
+ const spyName = spy.getMockName();
1164
+ const callCount = spy.mock.calls.length;
1165
+ return this.assert(callCount === 1, `expected "${spyName}" to be called once, but got ${callCount} times`, `expected "${spyName}" to not be called once`, 1, callCount, false);
1166
+ });
1167
+ def(["toHaveBeenCalled", "toBeCalled"], function() {
1168
+ const spy = getSpy(this);
1169
+ const spyName = spy.getMockName();
1170
+ const callCount = spy.mock.calls.length;
1171
+ const called = callCount > 0;
1172
+ const isNot = utils.flag(this, "negate");
1173
+ let msg = utils.getMessage(this, [
1174
+ called,
1175
+ `expected "${spyName}" to be called at least once`,
1176
+ `expected "${spyName}" to not be called at all, but actually been called ${callCount} times`,
1177
+ true,
1178
+ called
1179
+ ]);
1180
+ if (called && isNot) msg = formatCalls(spy, msg);
1181
+ if (called && isNot || !called && !isNot) throw new AssertionError(msg);
1182
+ });
1183
+ // manually compare array elements since `jestEquals` cannot
1184
+ // apply asymmetric matcher to `undefined` array element.
1185
+ function equalsArgumentArray(a, b) {
1186
+ return a.length === b.length && a.every((aItem, i) => equals(aItem, b[i], [...customTesters, iterableEquality]));
1187
+ }
1188
+ def(["toHaveBeenCalledWith", "toBeCalledWith"], function(...args) {
1189
+ const spy = getSpy(this);
1190
+ const spyName = spy.getMockName();
1191
+ const pass = spy.mock.calls.some((callArg) => equalsArgumentArray(callArg, args));
1192
+ const isNot = utils.flag(this, "negate");
1193
+ const msg = utils.getMessage(this, [
1194
+ pass,
1195
+ `expected "${spyName}" to be called with arguments: #{exp}`,
1196
+ `expected "${spyName}" to not be called with arguments: #{exp}`,
1197
+ args
1198
+ ]);
1199
+ if (pass && isNot || !pass && !isNot) throw new AssertionError(formatCalls(spy, msg, args));
1200
+ });
1201
+ def("toHaveBeenCalledExactlyOnceWith", function(...args) {
1202
+ const spy = getSpy(this);
1203
+ const spyName = spy.getMockName();
1204
+ const callCount = spy.mock.calls.length;
1205
+ const pass = spy.mock.calls.some((callArg) => equalsArgumentArray(callArg, args)) && callCount === 1;
1206
+ const isNot = utils.flag(this, "negate");
1207
+ const msg = utils.getMessage(this, [
1208
+ pass,
1209
+ `expected "${spyName}" to be called once with arguments: #{exp}`,
1210
+ `expected "${spyName}" to not be called once with arguments: #{exp}`,
1211
+ args
1212
+ ]);
1213
+ if (pass && isNot || !pass && !isNot) throw new AssertionError(formatCalls(spy, msg, args));
1214
+ });
1215
+ def("toHaveBeenNthCalledWith", function(times, ...args) {
1216
+ const spy = getSpy(this);
1217
+ const spyName = spy.getMockName();
1218
+ const nthCall = spy.mock.calls[times - 1];
1219
+ const callCount = spy.mock.calls.length;
1220
+ const isCalled = times <= callCount;
1221
+ this.assert(nthCall && equalsArgumentArray(nthCall, args), `expected ${ordinal(times)} "${spyName}" call to have been called with #{exp}${isCalled ? `` : `, but called only ${callCount} times`}`, `expected ${ordinal(times)} "${spyName}" call to not have been called with #{exp}`, args, nthCall, isCalled);
1222
+ });
1223
+ def("toHaveBeenLastCalledWith", function(...args) {
1224
+ const spy = getSpy(this);
1225
+ const spyName = spy.getMockName();
1226
+ const lastCall = spy.mock.calls.at(-1);
1227
+ this.assert(lastCall && equalsArgumentArray(lastCall, args), `expected last "${spyName}" call to have been called with #{exp}`, `expected last "${spyName}" call to not have been called with #{exp}`, args, lastCall);
1228
+ });
1229
+ /**
1230
+ * Used for `toHaveBeenCalledBefore` and `toHaveBeenCalledAfter` to determine if the expected spy was called before the result spy.
1231
+ */
1232
+ function isSpyCalledBeforeAnotherSpy(beforeSpy, afterSpy, failIfNoFirstInvocation) {
1233
+ const beforeInvocationCallOrder = beforeSpy.mock.invocationCallOrder;
1234
+ const afterInvocationCallOrder = afterSpy.mock.invocationCallOrder;
1235
+ if (beforeInvocationCallOrder.length === 0) return !failIfNoFirstInvocation;
1236
+ if (afterInvocationCallOrder.length === 0) return false;
1237
+ return beforeInvocationCallOrder[0] < afterInvocationCallOrder[0];
1238
+ }
1239
+ def(["toHaveBeenCalledBefore"], function(resultSpy, failIfNoFirstInvocation = true) {
1240
+ const expectSpy = getSpy(this);
1241
+ if (!isMockFunction(resultSpy)) throw new TypeError(`${utils.inspect(resultSpy)} is not a spy or a call to a spy`);
1242
+ this.assert(isSpyCalledBeforeAnotherSpy(expectSpy, resultSpy, failIfNoFirstInvocation), `expected "${expectSpy.getMockName()}" to have been called before "${resultSpy.getMockName()}"`, `expected "${expectSpy.getMockName()}" to not have been called before "${resultSpy.getMockName()}"`, resultSpy, expectSpy);
1243
+ });
1244
+ def(["toHaveBeenCalledAfter"], function(resultSpy, failIfNoFirstInvocation = true) {
1245
+ const expectSpy = getSpy(this);
1246
+ if (!isMockFunction(resultSpy)) throw new TypeError(`${utils.inspect(resultSpy)} is not a spy or a call to a spy`);
1247
+ this.assert(isSpyCalledBeforeAnotherSpy(resultSpy, expectSpy, failIfNoFirstInvocation), `expected "${expectSpy.getMockName()}" to have been called after "${resultSpy.getMockName()}"`, `expected "${expectSpy.getMockName()}" to not have been called after "${resultSpy.getMockName()}"`, resultSpy, expectSpy);
1248
+ });
1249
+ def(["toThrow", "toThrowError"], function(expected) {
1250
+ if (typeof expected === "string" || typeof expected === "undefined" || expected instanceof RegExp) return this.throws(expected);
1251
+ const obj = this._obj;
1252
+ const promise = utils.flag(this, "promise");
1253
+ const isNot = utils.flag(this, "negate");
1254
+ let thrown = null;
1255
+ if (promise === "rejects") thrown = obj;
1256
+ else if (promise === "resolves" && typeof obj !== "function") if (!isNot) throw new AssertionError(utils.flag(this, "message") || "expected promise to throw an error, but it didn't", { showDiff: false }, utils.flag(this, "ssfi"));
1257
+ else return;
1258
+ else {
1259
+ let isThrow = false;
1260
+ try {
1261
+ obj();
1262
+ } catch (err) {
1263
+ isThrow = true;
1264
+ thrown = err;
1265
+ }
1266
+ if (!isThrow && !isNot) throw new AssertionError(utils.flag(this, "message") || "expected function to throw an error, but it didn't", { showDiff: false }, utils.flag(this, "ssfi"));
1267
+ }
1268
+ if (typeof expected === "function") {
1269
+ const name = expected.name || expected.prototype.constructor.name;
1270
+ return this.assert(thrown && thrown instanceof expected, `expected error to be instance of ${name}`, `expected error not to be instance of ${name}`, expected, thrown);
1271
+ }
1272
+ if (isError(expected)) {
1273
+ const equal = equals(thrown, expected, [...customTesters, iterableEquality]);
1274
+ return this.assert(equal, "expected a thrown error to be #{exp}", "expected a thrown error not to be #{exp}", expected, thrown);
1275
+ }
1276
+ if (typeof expected === "object" && "asymmetricMatch" in expected && typeof expected.asymmetricMatch === "function") {
1277
+ const matcher = expected;
1278
+ return this.assert(thrown && matcher.asymmetricMatch(thrown), "expected error to match asymmetric matcher", "expected error not to match asymmetric matcher", matcher, thrown);
1279
+ }
1280
+ const equal = equals(thrown, expected, [...customTesters, iterableEquality]);
1281
+ return this.assert(equal, "expected a thrown value to equal #{exp}", "expected a thrown value not to equal #{exp}", expected, thrown);
1282
+ });
1283
+ [{
1284
+ name: "toHaveResolved",
1285
+ condition: (spy) => spy.mock.settledResults.length > 0 && spy.mock.settledResults.some(({ type }) => type === "fulfilled"),
1286
+ action: "resolved"
1287
+ }, {
1288
+ name: ["toHaveReturned", "toReturn"],
1289
+ condition: (spy) => spy.mock.calls.length > 0 && spy.mock.results.some(({ type }) => type !== "throw"),
1290
+ action: "called"
1291
+ }].forEach(({ name, condition, action }) => {
1292
+ def(name, function() {
1293
+ const spy = getSpy(this);
1294
+ const spyName = spy.getMockName();
1295
+ const pass = condition(spy);
1296
+ this.assert(pass, `expected "${spyName}" to be successfully ${action} at least once`, `expected "${spyName}" to not be successfully ${action}`, pass, !pass, false);
1297
+ });
1298
+ });
1299
+ [{
1300
+ name: "toHaveResolvedTimes",
1301
+ condition: (spy, times) => spy.mock.settledResults.reduce((s, { type }) => type === "fulfilled" ? ++s : s, 0) === times,
1302
+ action: "resolved"
1303
+ }, {
1304
+ name: ["toHaveReturnedTimes", "toReturnTimes"],
1305
+ condition: (spy, times) => spy.mock.results.reduce((s, { type }) => type === "throw" ? s : ++s, 0) === times,
1306
+ action: "called"
1307
+ }].forEach(({ name, condition, action }) => {
1308
+ def(name, function(times) {
1309
+ const spy = getSpy(this);
1310
+ const spyName = spy.getMockName();
1311
+ const pass = condition(spy, times);
1312
+ this.assert(pass, `expected "${spyName}" to be successfully ${action} ${times} times`, `expected "${spyName}" to not be successfully ${action} ${times} times`, `expected resolved times: ${times}`, `received resolved times: ${pass}`, false);
1313
+ });
1314
+ });
1315
+ [{
1316
+ name: "toHaveResolvedWith",
1317
+ condition: (spy, value) => spy.mock.settledResults.some(({ type, value: result }) => type === "fulfilled" && equals(value, result)),
1318
+ action: "resolve"
1319
+ }, {
1320
+ name: ["toHaveReturnedWith", "toReturnWith"],
1321
+ condition: (spy, value) => spy.mock.results.some(({ type, value: result }) => type === "return" && equals(value, result)),
1322
+ action: "return"
1323
+ }].forEach(({ name, condition, action }) => {
1324
+ def(name, function(value) {
1325
+ const spy = getSpy(this);
1326
+ const pass = condition(spy, value);
1327
+ const isNot = utils.flag(this, "negate");
1328
+ if (pass && isNot || !pass && !isNot) {
1329
+ const spyName = spy.getMockName();
1330
+ const msg = utils.getMessage(this, [
1331
+ pass,
1332
+ `expected "${spyName}" to ${action} with: #{exp} at least once`,
1333
+ `expected "${spyName}" to not ${action} with: #{exp}`,
1334
+ value
1335
+ ]);
1336
+ throw new AssertionError(formatReturns(spy, action === "return" ? spy.mock.results : spy.mock.settledResults, msg, value));
1337
+ }
1338
+ });
1339
+ });
1340
+ [{
1341
+ name: "toHaveLastResolvedWith",
1342
+ condition: (spy, value) => {
1343
+ const result = spy.mock.settledResults.at(-1);
1344
+ return Boolean(result && result.type === "fulfilled" && equals(result.value, value));
1345
+ },
1346
+ action: "resolve"
1347
+ }, {
1348
+ name: "toHaveLastReturnedWith",
1349
+ condition: (spy, value) => {
1350
+ const result = spy.mock.results.at(-1);
1351
+ return Boolean(result && result.type === "return" && equals(result.value, value));
1352
+ },
1353
+ action: "return"
1354
+ }].forEach(({ name, condition, action }) => {
1355
+ def(name, function(value) {
1356
+ const spy = getSpy(this);
1357
+ const result = (action === "return" ? spy.mock.results : spy.mock.settledResults).at(-1);
1358
+ const spyName = spy.getMockName();
1359
+ this.assert(condition(spy, value), `expected last "${spyName}" call to ${action} #{exp}`, `expected last "${spyName}" call to not ${action} #{exp}`, value, result?.value);
1360
+ });
1361
+ });
1362
+ [{
1363
+ name: "toHaveNthResolvedWith",
1364
+ condition: (spy, index, value) => {
1365
+ const result = spy.mock.settledResults[index - 1];
1366
+ return result && result.type === "fulfilled" && equals(result.value, value);
1367
+ },
1368
+ action: "resolve"
1369
+ }, {
1370
+ name: "toHaveNthReturnedWith",
1371
+ condition: (spy, index, value) => {
1372
+ const result = spy.mock.results[index - 1];
1373
+ return result && result.type === "return" && equals(result.value, value);
1374
+ },
1375
+ action: "return"
1376
+ }].forEach(({ name, condition, action }) => {
1377
+ def(name, function(nthCall, value) {
1378
+ const spy = getSpy(this);
1379
+ const spyName = spy.getMockName();
1380
+ const result = (action === "return" ? spy.mock.results : spy.mock.settledResults)[nthCall - 1];
1381
+ const ordinalCall = `${ordinal(nthCall)} call`;
1382
+ this.assert(condition(spy, nthCall, value), `expected ${ordinalCall} "${spyName}" call to ${action} #{exp}`, `expected ${ordinalCall} "${spyName}" call to not ${action} #{exp}`, value, result?.value);
1383
+ });
1384
+ });
1385
+ // @ts-expect-error @internal
1386
+ def("withContext", function(context) {
1387
+ for (const key in context) utils.flag(this, key, context[key]);
1388
+ return this;
1389
+ });
1390
+ utils.addProperty(chai.Assertion.prototype, "resolves", function __VITEST_RESOLVES__() {
1391
+ const error = /* @__PURE__ */ new Error("resolves");
1392
+ utils.flag(this, "promise", "resolves");
1393
+ utils.flag(this, "error", error);
1394
+ const test = utils.flag(this, "vitest-test");
1395
+ const obj = utils.flag(this, "object");
1396
+ if (utils.flag(this, "poll")) throw new SyntaxError(`expect.poll() is not supported in combination with .resolves`);
1397
+ if (typeof obj?.then !== "function") throw new TypeError(`You must provide a Promise to expect() when using .resolves, not '${typeof obj}'.`);
1398
+ const proxy = new Proxy(this, { get: (target, key, receiver) => {
1399
+ const result = Reflect.get(target, key, receiver);
1400
+ if (typeof result !== "function") return result instanceof chai.Assertion ? proxy : result;
1401
+ return (...args) => {
1402
+ utils.flag(this, "_name", key);
1403
+ return recordAsyncExpect(test, Promise.resolve(obj).then((value) => {
1404
+ utils.flag(this, "object", value);
1405
+ return result.call(this, ...args);
1406
+ }, (err) => {
1407
+ const _error = new AssertionError(`promise rejected "${utils.inspect(err)}" instead of resolving`, { showDiff: false });
1408
+ _error.cause = err;
1409
+ throw _error;
1410
+ }).catch((err) => {
1411
+ if (isError(err) && error.stack) err.stack = error.stack.replace(error.message, err.message);
1412
+ throw err;
1413
+ }), createAssertionMessage(utils, this, !!args.length), error, utils.flag(this, "soft"));
1414
+ };
1415
+ } });
1416
+ return proxy;
1417
+ });
1418
+ utils.addProperty(chai.Assertion.prototype, "rejects", function __VITEST_REJECTS__() {
1419
+ const error = /* @__PURE__ */ new Error("rejects");
1420
+ utils.flag(this, "promise", "rejects");
1421
+ utils.flag(this, "error", error);
1422
+ const test = utils.flag(this, "vitest-test");
1423
+ const obj = utils.flag(this, "object");
1424
+ const wrapper = typeof obj === "function" ? obj() : obj;
1425
+ if (utils.flag(this, "poll")) throw new SyntaxError(`expect.poll() is not supported in combination with .rejects`);
1426
+ if (typeof wrapper?.then !== "function") throw new TypeError(`You must provide a Promise to expect() when using .rejects, not '${typeof wrapper}'.`);
1427
+ const proxy = new Proxy(this, { get: (target, key, receiver) => {
1428
+ const result = Reflect.get(target, key, receiver);
1429
+ if (typeof result !== "function") return result instanceof chai.Assertion ? proxy : result;
1430
+ return (...args) => {
1431
+ utils.flag(this, "_name", key);
1432
+ return recordAsyncExpect(test, Promise.resolve(wrapper).then((value) => {
1433
+ throw new AssertionError(`promise resolved "${utils.inspect(value)}" instead of rejecting`, {
1434
+ showDiff: true,
1435
+ expected: /* @__PURE__ */ new Error("rejected promise"),
1436
+ actual: value
1437
+ });
1438
+ }, (err) => {
1439
+ utils.flag(this, "object", err);
1440
+ return result.call(this, ...args);
1441
+ }).catch((err) => {
1442
+ if (isError(err) && error.stack) err.stack = error.stack.replace(error.message, err.message);
1443
+ throw err;
1444
+ }), createAssertionMessage(utils, this, !!args.length), error, utils.flag(this, "soft"));
1445
+ };
1446
+ } });
1447
+ return proxy;
1448
+ });
1449
+ };
1450
+ function formatCalls(spy, msg, showActualCall) {
1451
+ if (spy.mock.calls.length) msg += c.gray(`\n\nReceived:\n\n${spy.mock.calls.map((callArg, i) => {
1452
+ let methodCall = c.bold(` ${ordinal(i + 1)} ${spy.getMockName()} call:\n\n`);
1453
+ if (showActualCall) methodCall += diff(showActualCall, callArg, { omitAnnotationLines: true });
1454
+ else methodCall += stringify(callArg).split("\n").map((line) => ` ${line}`).join("\n");
1455
+ methodCall += "\n";
1456
+ return methodCall;
1457
+ }).join("\n")}`);
1458
+ msg += c.gray(`\n\nNumber of calls: ${c.bold(spy.mock.calls.length)}\n`);
1459
+ return msg;
1460
+ }
1461
+ function formatReturns(spy, results, msg, showActualReturn) {
1462
+ if (results.length) msg += c.gray(`\n\nReceived:\n\n${results.map((callReturn, i) => {
1463
+ let methodCall = c.bold(` ${ordinal(i + 1)} ${spy.getMockName()} call return:\n\n`);
1464
+ if (showActualReturn) methodCall += diff(showActualReturn, callReturn.value, { omitAnnotationLines: true });
1465
+ else methodCall += stringify(callReturn).split("\n").map((line) => ` ${line}`).join("\n");
1466
+ methodCall += "\n";
1467
+ return methodCall;
1468
+ }).join("\n")}`);
1469
+ msg += c.gray(`\n\nNumber of calls: ${c.bold(spy.mock.calls.length)}\n`);
1470
+ return msg;
1471
+ }
1472
+
1473
+ function getMatcherState(assertion, expect) {
1474
+ const obj = assertion._obj;
1475
+ const isNot = util.flag(assertion, "negate");
1476
+ const promise = util.flag(assertion, "promise") || "";
1477
+ const customMessage = util.flag(assertion, "message");
1478
+ const jestUtils = {
1479
+ ...getMatcherUtils(),
1480
+ diff,
1481
+ stringify,
1482
+ iterableEquality,
1483
+ subsetEquality
1484
+ };
1485
+ let task = util.flag(assertion, "vitest-test");
1486
+ const currentTestName = task?.fullTestName ?? "";
1487
+ if (task?.type !== "test") task = void 0;
1488
+ const matcherState = {
1489
+ ...getState(expect),
1490
+ currentTestName,
1491
+ customTesters: getCustomEqualityTesters(),
1492
+ isNot,
1493
+ utils: jestUtils,
1494
+ promise,
1495
+ equals,
1496
+ suppressedErrors: [],
1497
+ soft: util.flag(assertion, "soft"),
1498
+ poll: util.flag(assertion, "poll"),
1499
+ assertion
1500
+ };
1501
+ Object.assign(matcherState, { task });
1502
+ return {
1503
+ state: matcherState,
1504
+ isNot,
1505
+ obj,
1506
+ customMessage
1507
+ };
1508
+ }
1509
+ class JestExtendError extends Error {
1510
+ constructor(message, actual, expected, __vitest_error_context__) {
1511
+ super(message);
1512
+ this.actual = actual;
1513
+ this.expected = expected;
1514
+ this.__vitest_error_context__ = __vitest_error_context__;
1515
+ }
1516
+ }
1517
+ function JestExtendPlugin(c, expect, matchers) {
1518
+ return (_, utils) => {
1519
+ Object.entries(matchers).forEach(([expectAssertionName, expectAssertion]) => {
1520
+ function __VITEST_EXTEND_ASSERTION__(...args) {
1521
+ const { state, isNot, obj, customMessage } = getMatcherState(this, expect);
1522
+ const result = expectAssertion.call(state, obj, ...args);
1523
+ if (result && typeof result === "object" && typeof result.then === "function") return result.then(({ pass, message, actual, expected, meta }) => {
1524
+ if (pass && isNot || !pass && !isNot) throw new JestExtendError((customMessage ? `${customMessage}: ` : "") + message(), actual, expected, {
1525
+ assertionName: expectAssertionName,
1526
+ meta
1527
+ });
1528
+ });
1529
+ const { pass, message, actual, expected, meta } = result;
1530
+ if (pass && isNot || !pass && !isNot) throw new JestExtendError((customMessage ? `${customMessage}: ` : "") + message(), actual, expected, {
1531
+ assertionName: expectAssertionName,
1532
+ meta
1533
+ });
1534
+ }
1535
+ const softWrapper = wrapAssertion(utils, expectAssertionName, __VITEST_EXTEND_ASSERTION__);
1536
+ utils.addMethod(globalThis[JEST_MATCHERS_OBJECT].matchers, expectAssertionName, softWrapper);
1537
+ utils.addMethod(c.Assertion.prototype, expectAssertionName, softWrapper);
1538
+ // `expect.poll()` inspects the installed Chai assertion method,
1539
+ // so copy the internal marker from the original matcher function.
1540
+ // this is only for domain snapshot matchers for now.
1541
+ if (expectAssertion.__vitest_poll_takeover__) {
1542
+ const addedMethod = c.Assertion.prototype[expectAssertionName];
1543
+ Object.defineProperty(addedMethod, "__vitest_poll_takeover__", { value: true });
1544
+ }
1545
+ class CustomMatcher extends AsymmetricMatcher {
1546
+ constructor(inverse = false, ...sample) {
1547
+ super(sample, inverse);
1548
+ }
1549
+ asymmetricMatch(other) {
1550
+ const { pass } = expectAssertion.call(this.getMatcherContext(expect), other, ...this.sample);
1551
+ return this.inverse ? !pass : pass;
1552
+ }
1553
+ toString() {
1554
+ return `${this.inverse ? "not." : ""}${expectAssertionName}`;
1555
+ }
1556
+ getExpectedType() {
1557
+ return "any";
1558
+ }
1559
+ toAsymmetricMatcher() {
1560
+ return `${this.toString()}<${this.sample.map((item) => stringify(item)).join(", ")}>`;
1561
+ }
1562
+ }
1563
+ const customMatcher = (...sample) => new CustomMatcher(false, ...sample);
1564
+ Object.defineProperty(expect, expectAssertionName, {
1565
+ configurable: true,
1566
+ enumerable: true,
1567
+ value: customMatcher,
1568
+ writable: true
1569
+ });
1570
+ Object.defineProperty(expect.not, expectAssertionName, {
1571
+ configurable: true,
1572
+ enumerable: true,
1573
+ value: (...sample) => new CustomMatcher(true, ...sample),
1574
+ writable: true
1575
+ });
1576
+ // keep track of asymmetric matchers on global so that it can be copied over to local context's `expect`.
1577
+ // note that the negated variant is automatically shared since it's assigned on the single `expect.not` object.
1578
+ Object.defineProperty(globalThis[ASYMMETRIC_MATCHERS_OBJECT], expectAssertionName, {
1579
+ configurable: true,
1580
+ enumerable: true,
1581
+ value: customMatcher,
1582
+ writable: true
1583
+ });
1584
+ });
1585
+ };
1586
+ }
1587
+ const JestExtend = (chai, utils) => {
1588
+ utils.addMethod(chai.expect, "extend", (expect, expects) => {
1589
+ use(JestExtendPlugin(chai, expect, expects));
1590
+ });
1591
+ };
16
1592
 
17
1593
  var fakeTimersSrc = {};
18
1594
 
@@ -3838,6 +5414,1006 @@ function copyStackTrace(target, source) {
3838
5414
  return target;
3839
5415
  }
3840
5416
 
5417
+ var naturalCompare$1 = {exports: {}};
5418
+
5419
+ var hasRequiredNaturalCompare;
5420
+
5421
+ function requireNaturalCompare () {
5422
+ if (hasRequiredNaturalCompare) return naturalCompare$1.exports;
5423
+ hasRequiredNaturalCompare = 1;
5424
+ /*
5425
+ * @version 1.4.0
5426
+ * @date 2015-10-26
5427
+ * @stability 3 - Stable
5428
+ * @author Lauri Rooden (https://github.com/litejs/natural-compare-lite)
5429
+ * @license MIT License
5430
+ */
5431
+
5432
+
5433
+ var naturalCompare = function(a, b) {
5434
+ var i, codeA
5435
+ , codeB = 1
5436
+ , posA = 0
5437
+ , posB = 0
5438
+ , alphabet = String.alphabet;
5439
+
5440
+ function getCode(str, pos, code) {
5441
+ if (code) {
5442
+ for (i = pos; code = getCode(str, i), code < 76 && code > 65;) ++i;
5443
+ return +str.slice(pos - 1, i)
5444
+ }
5445
+ code = alphabet && alphabet.indexOf(str.charAt(pos));
5446
+ return code > -1 ? code + 76 : ((code = str.charCodeAt(pos) || 0), code < 45 || code > 127) ? code
5447
+ : code < 46 ? 65 // -
5448
+ : code < 48 ? code - 1
5449
+ : code < 58 ? code + 18 // 0-9
5450
+ : code < 65 ? code - 11
5451
+ : code < 91 ? code + 11 // A-Z
5452
+ : code < 97 ? code - 37
5453
+ : code < 123 ? code + 5 // a-z
5454
+ : code - 63
5455
+ }
5456
+
5457
+
5458
+ if ((a+="") != (b+="")) for (;codeB;) {
5459
+ codeA = getCode(a, posA++);
5460
+ codeB = getCode(b, posB++);
5461
+
5462
+ if (codeA < 76 && codeB < 76 && codeA > 66 && codeB > 66) {
5463
+ codeA = getCode(a, posA, posA);
5464
+ codeB = getCode(b, posB, posA = i);
5465
+ posB = i;
5466
+ }
5467
+
5468
+ if (codeA != codeB) return (codeA < codeB) ? -1 : 1
5469
+ }
5470
+ return 0
5471
+ };
5472
+
5473
+ try {
5474
+ naturalCompare$1.exports = naturalCompare;
5475
+ } catch (e) {
5476
+ String.naturalCompare = naturalCompare;
5477
+ }
5478
+ return naturalCompare$1.exports;
5479
+ }
5480
+
5481
+ var naturalCompareExports = requireNaturalCompare();
5482
+ var naturalCompare = /*@__PURE__*/getDefaultExportFromCjs(naturalCompareExports);
5483
+
5484
+ // TODO: rewrite and clean up
5485
+ function testNameToKey(testName, count) {
5486
+ return `${testName} ${count}`;
5487
+ }
5488
+ function keyToTestName(key) {
5489
+ if (!/ \d+$/.test(key)) throw new Error("Snapshot keys must end with a number.");
5490
+ return key.replace(/ \d+$/, "");
5491
+ }
5492
+ function getSnapshotData(content, options) {
5493
+ const update = options.updateSnapshot;
5494
+ const data = Object.create(null);
5495
+ let snapshotContents = "";
5496
+ let dirty = false;
5497
+ if (content != null) try {
5498
+ snapshotContents = content;
5499
+ new Function("exports", snapshotContents)(data);
5500
+ } catch {}
5501
+ // if (update === 'none' && isInvalid)
5502
+ // throw validationResult
5503
+ if ((update === "all" || update === "new") && snapshotContents) dirty = true;
5504
+ return {
5505
+ data,
5506
+ dirty
5507
+ };
5508
+ }
5509
+ // Add extra line breaks at beginning and end of multiline snapshot
5510
+ // to make the content easier to read.
5511
+ function addExtraLineBreaks(string) {
5512
+ return string.includes("\n") ? `\n${string}\n` : string;
5513
+ }
5514
+ // Remove extra line breaks at beginning and end of multiline snapshot.
5515
+ // Instead of trim, which can remove additional newlines or spaces
5516
+ // at beginning or end of the content from a custom serializer.
5517
+ function removeExtraLineBreaks(string) {
5518
+ return string.length > 2 && string[0] === "\n" && string.endsWith("\n") ? string.slice(1, -1) : string;
5519
+ }
5520
+ // export const removeLinesBeforeExternalMatcherTrap = (stack: string): string => {
5521
+ // const lines = stack.split('\n')
5522
+ // for (let i = 0; i < lines.length; i += 1) {
5523
+ // // It's a function name specified in `packages/expect/src/index.ts`
5524
+ // // for external custom matchers.
5525
+ // if (lines[i].includes('__EXTERNAL_MATCHER_TRAP__'))
5526
+ // return lines.slice(i + 1).join('\n')
5527
+ // }
5528
+ // return stack
5529
+ // }
5530
+ const escapeRegex = true;
5531
+ const printFunctionName = false;
5532
+ function serialize(val, indent = 2, formatOverrides = {}) {
5533
+ return normalizeNewlines(format(val, {
5534
+ escapeRegex,
5535
+ indent,
5536
+ plugins: getSerializers(),
5537
+ printFunctionName,
5538
+ ...formatOverrides
5539
+ }));
5540
+ }
5541
+ function escapeBacktickString(str) {
5542
+ return str.replace(/`|\\|\$\{/g, "\\$&");
5543
+ }
5544
+ function printBacktickString(str) {
5545
+ return `\`${escapeBacktickString(str)}\``;
5546
+ }
5547
+ function normalizeNewlines(string) {
5548
+ return string.replace(/\r\n|\r/g, "\n");
5549
+ }
5550
+ async function saveSnapshotFile(environment, snapshotData, snapshotPath) {
5551
+ const snapshots = Object.keys(snapshotData).sort(naturalCompare).map((key) => `exports[${printBacktickString(key)}] = ${printBacktickString(normalizeNewlines(snapshotData[key]))};`);
5552
+ const content = `${environment.getHeader()}\n\n${snapshots.join("\n\n")}\n`;
5553
+ const oldContent = await environment.readSnapshotFile(snapshotPath);
5554
+ if (oldContent != null && oldContent === content) return;
5555
+ await environment.saveSnapshotFile(snapshotPath, content);
5556
+ }
5557
+ function deepMergeArray(target = [], source = []) {
5558
+ const mergedOutput = Array.from(target);
5559
+ source.forEach((sourceElement, index) => {
5560
+ const targetElement = mergedOutput[index];
5561
+ if (Array.isArray(target[index])) mergedOutput[index] = deepMergeArray(target[index], sourceElement);
5562
+ else if (isObject(targetElement)) mergedOutput[index] = deepMergeSnapshot(target[index], sourceElement);
5563
+ else
5564
+ // Source does not exist in target or target is primitive and cannot be deep merged
5565
+ mergedOutput[index] = sourceElement;
5566
+ });
5567
+ return mergedOutput;
5568
+ }
5569
+ /**
5570
+ * Deep merge, but considers asymmetric matchers. Unlike base util's deep merge,
5571
+ * will merge any object-like instance.
5572
+ * Compatible with Jest's snapshot matcher. Should not be used outside of snapshot.
5573
+ *
5574
+ * @example
5575
+ * ```ts
5576
+ * toMatchSnapshot({
5577
+ * name: expect.stringContaining('text')
5578
+ * })
5579
+ * ```
5580
+ */
5581
+ function deepMergeSnapshot(target, source) {
5582
+ if (isObject(target) && isObject(source)) {
5583
+ const mergedOutput = { ...target };
5584
+ Object.keys(source).forEach((key) => {
5585
+ if (isObject(source[key]) && !source[key].$$typeof) if (!(key in target)) Object.assign(mergedOutput, { [key]: source[key] });
5586
+ else mergedOutput[key] = deepMergeSnapshot(target[key], source[key]);
5587
+ else if (Array.isArray(source[key])) mergedOutput[key] = deepMergeArray(target[key], source[key]);
5588
+ else Object.assign(mergedOutput, { [key]: source[key] });
5589
+ });
5590
+ return mergedOutput;
5591
+ } else if (Array.isArray(target) && Array.isArray(source)) return deepMergeArray(target, source);
5592
+ return target;
5593
+ }
5594
+ class DefaultMap extends Map {
5595
+ constructor(defaultFn, entries) {
5596
+ super(entries);
5597
+ this.defaultFn = defaultFn;
5598
+ }
5599
+ get(key) {
5600
+ if (!this.has(key)) this.set(key, this.defaultFn(key));
5601
+ return super.get(key);
5602
+ }
5603
+ }
5604
+ class CounterMap extends DefaultMap {
5605
+ constructor() {
5606
+ super(() => 0);
5607
+ }
5608
+ // compat for jest-image-snapshot https://github.com/vitest-dev/vitest/issues/7322
5609
+ // `valueOf` and `Snapshot.added` setter allows
5610
+ // snapshotState.added = snapshotState.added + 1
5611
+ // to function as
5612
+ // snapshotState.added.total_ = snapshotState.added.total() + 1
5613
+ _total;
5614
+ valueOf() {
5615
+ return this._total = this.total();
5616
+ }
5617
+ increment(key) {
5618
+ if (typeof this._total !== "undefined") this._total++;
5619
+ this.set(key, this.get(key) + 1);
5620
+ }
5621
+ total() {
5622
+ if (typeof this._total !== "undefined") return this._total;
5623
+ let total = 0;
5624
+ for (const x of this.values()) total += x;
5625
+ return total;
5626
+ }
5627
+ }
5628
+ /* @__NO_SIDE_EFFECTS__ */
5629
+ function memo(fn) {
5630
+ const cache = /* @__PURE__ */ new Map();
5631
+ return (arg) => {
5632
+ if (!cache.has(arg)) cache.set(arg, fn(arg));
5633
+ return cache.get(arg);
5634
+ };
5635
+ }
5636
+
5637
+ async function saveInlineSnapshots(environment, snapshots) {
5638
+ const MagicString = (await import('magic-string')).default;
5639
+ const files = new Set(snapshots.map((i) => i.file));
5640
+ await Promise.all(Array.from(files).map(async (file) => {
5641
+ const snaps = snapshots.filter((i) => i.file === file);
5642
+ const code = await environment.readSnapshotFile(file);
5643
+ if (code == null) throw new Error(`cannot read ${file} when saving inline snapshot`);
5644
+ const s = new MagicString(code);
5645
+ for (const snap of snaps) replaceInlineSnap(code, s, positionToOffset(code, snap.line, snap.column), snap.snapshot, snap.assertionName);
5646
+ const transformed = s.toString();
5647
+ if (transformed !== code) await environment.saveSnapshotFile(file, transformed);
5648
+ }));
5649
+ }
5650
+ const defaultStartObjectRegex = /(?:toMatchInlineSnapshot|toThrowErrorMatchingInlineSnapshot)\s*\(\s*(?:\/\*[\s\S]*\*\/\s*|\/\/.*(?:[\n\r\u2028\u2029]\s*|[\t\v\f \xA0\u1680\u2000-\u200A\u202F\u205F\u3000\uFEFF]))*\{/;
5651
+ function escapeRegExp(s) {
5652
+ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
5653
+ }
5654
+ const buildStartObjectRegex = memo((assertionName) => {
5655
+ const replaced = defaultStartObjectRegex.source.replace("toMatchInlineSnapshot|toThrowErrorMatchingInlineSnapshot", escapeRegExp(assertionName));
5656
+ return new RegExp(replaced);
5657
+ });
5658
+ function replaceObjectSnap(code, s, index, newSnap, assertionName) {
5659
+ let _code = code.slice(index);
5660
+ const startMatch = (assertionName ? buildStartObjectRegex(assertionName) : defaultStartObjectRegex).exec(_code);
5661
+ if (!startMatch) return false;
5662
+ _code = _code.slice(startMatch.index);
5663
+ let callEnd = getCallLastIndex(_code);
5664
+ if (callEnd === null) return false;
5665
+ callEnd += index + startMatch.index;
5666
+ const shapeEnd = getObjectShapeEndIndex(code, index + startMatch.index + startMatch[0].length);
5667
+ const snap = `, ${prepareSnapString(newSnap, code, index)}`;
5668
+ if (shapeEnd === callEnd)
5669
+ // toMatchInlineSnapshot({ foo: expect.any(String) })
5670
+ s.appendLeft(callEnd, snap);
5671
+ else
5672
+ // toMatchInlineSnapshot({ foo: expect.any(String) }, ``)
5673
+ s.overwrite(shapeEnd, callEnd, snap);
5674
+ return true;
5675
+ }
5676
+ function getObjectShapeEndIndex(code, index) {
5677
+ let startBraces = 1;
5678
+ let endBraces = 0;
5679
+ while (startBraces !== endBraces && index < code.length) {
5680
+ const s = code[index++];
5681
+ if (s === "{") startBraces++;
5682
+ else if (s === "}") endBraces++;
5683
+ }
5684
+ return index;
5685
+ }
5686
+ function prepareSnapString(snap, source, index) {
5687
+ const lineNumber = offsetToLineNumber(source, index);
5688
+ const indent = source.split(lineSplitRE)[lineNumber - 1].match(/^\s*/)[0] || "";
5689
+ const indentNext = indent.includes(" ") ? `${indent}\t` : `${indent} `;
5690
+ const lines = snap.trim().replace(/\\/g, "\\\\").split(/\n/g);
5691
+ const isOneline = lines.length <= 1;
5692
+ const quote = "`";
5693
+ if (isOneline) return `${quote}${lines.join("\n").replace(/`/g, "\\`").replace(/\$\{/g, "\\${")}${quote}`;
5694
+ return `${quote}\n${lines.map((i) => i ? indentNext + i : "").join("\n").replace(/`/g, "\\`").replace(/\$\{/g, "\\${")}\n${indent}${quote}`;
5695
+ }
5696
+ const defaultMethodNames = ["toMatchInlineSnapshot", "toThrowErrorMatchingInlineSnapshot"];
5697
+ // on webkit, the line number is at the end of the method, not at the start
5698
+ function getCodeStartingAtIndex(code, index, methodNames) {
5699
+ for (const name of methodNames) {
5700
+ const adjusted = index - name.length;
5701
+ if (adjusted >= 0 && code.slice(adjusted, index) === name) return {
5702
+ code: code.slice(adjusted),
5703
+ index: adjusted
5704
+ };
5705
+ }
5706
+ return {
5707
+ code: code.slice(index),
5708
+ index
5709
+ };
5710
+ }
5711
+ const defaultStartRegex = /(?:toMatchInlineSnapshot|toThrowErrorMatchingInlineSnapshot)\s*\(\s*(?:\/\*[\s\S]*\*\/\s*|\/\/.*(?:[\n\r\u2028\u2029]\s*|[\t\v\f \xA0\u1680\u2000-\u200A\u202F\u205F\u3000\uFEFF]))*[\w$]*(['"`)])/;
5712
+ const buildStartRegex = memo((assertionName) => {
5713
+ const replaced = defaultStartRegex.source.replace("toMatchInlineSnapshot|toThrowErrorMatchingInlineSnapshot", escapeRegExp(assertionName));
5714
+ return new RegExp(replaced);
5715
+ });
5716
+ function replaceInlineSnap(code, s, currentIndex, newSnap, assertionName) {
5717
+ const { code: codeStartingAtIndex, index } = getCodeStartingAtIndex(code, currentIndex, assertionName ? [assertionName] : defaultMethodNames);
5718
+ const startMatch = (assertionName ? buildStartRegex(assertionName) : defaultStartRegex).exec(codeStartingAtIndex);
5719
+ const firstKeywordMatch = (assertionName ? new RegExp(escapeRegExp(assertionName)) : /toMatchInlineSnapshot|toThrowErrorMatchingInlineSnapshot/).exec(codeStartingAtIndex);
5720
+ if (!startMatch || startMatch.index !== firstKeywordMatch?.index) return replaceObjectSnap(code, s, index, newSnap, assertionName);
5721
+ const quote = startMatch[1];
5722
+ const startIndex = index + startMatch.index + startMatch[0].length;
5723
+ const snapString = prepareSnapString(newSnap, code, index);
5724
+ if (quote === ")") {
5725
+ s.appendRight(startIndex - 1, snapString);
5726
+ return true;
5727
+ }
5728
+ const endMatch = new RegExp(`(?:^|[^\\\\])${quote}`).exec(code.slice(startIndex));
5729
+ if (!endMatch) return false;
5730
+ const endIndex = startIndex + endMatch.index + endMatch[0].length;
5731
+ s.overwrite(startIndex - 1, endIndex, snapString);
5732
+ return true;
5733
+ }
5734
+ const INDENTATION_REGEX = /^([^\S\n]*)\S/m;
5735
+ function stripSnapshotIndentation(inlineSnapshot) {
5736
+ // Find indentation if exists.
5737
+ const match = inlineSnapshot.match(INDENTATION_REGEX);
5738
+ if (!match || !match[1])
5739
+ // No indentation.
5740
+ return inlineSnapshot;
5741
+ const indentation = match[1];
5742
+ const lines = inlineSnapshot.split(/\n/g);
5743
+ if (lines.length <= 2)
5744
+ // Must be at least 3 lines.
5745
+ return inlineSnapshot;
5746
+ if (lines[0].trim() !== "" || lines.at(-1)?.trim() !== "")
5747
+ // If not blank first and last lines, abort.
5748
+ return inlineSnapshot;
5749
+ for (let i = 1; i < lines.length - 1; i++) if (lines[i] !== "") {
5750
+ if (lines[i].indexOf(indentation) !== 0)
5751
+ // All lines except first and last should either be blank or have the same
5752
+ // indent as the first line (or more). If this isn't the case we don't
5753
+ // want to touch the snapshot at all.
5754
+ return inlineSnapshot;
5755
+ lines[i] = lines[i].substring(indentation.length);
5756
+ }
5757
+ // Last line is a special case because it won't have the same indent as others
5758
+ // but may still have been given some indent to line up.
5759
+ lines[lines.length - 1] = "";
5760
+ // Return inline snapshot, now at indent 0.
5761
+ inlineSnapshot = lines.join("\n");
5762
+ return inlineSnapshot;
5763
+ }
5764
+
5765
+ async function saveRawSnapshots(environment, snapshots) {
5766
+ await Promise.all(snapshots.map(async (snap) => {
5767
+ if (!snap.readonly) await environment.saveSnapshotFile(snap.file, snap.snapshot);
5768
+ }));
5769
+ }
5770
+
5771
+ function isSameStackPosition(x, y) {
5772
+ return x.file === y.file && x.column === y.column && x.line === y.line;
5773
+ }
5774
+ class SnapshotState {
5775
+ _counters = new CounterMap();
5776
+ _dirty;
5777
+ _updateSnapshot;
5778
+ _snapshotData;
5779
+ _initialData;
5780
+ _inlineSnapshots;
5781
+ _inlineSnapshotStacks;
5782
+ _testIdToKeys = new DefaultMap(() => []);
5783
+ _rawSnapshots;
5784
+ _uncheckedKeys;
5785
+ _snapshotFormat;
5786
+ _environment;
5787
+ _fileExists;
5788
+ expand;
5789
+ // getter/setter for jest-image-snapshot compat
5790
+ // https://github.com/vitest-dev/vitest/issues/7322
5791
+ _added = new CounterMap();
5792
+ _matched = new CounterMap();
5793
+ _unmatched = new CounterMap();
5794
+ _updated = new CounterMap();
5795
+ get added() {
5796
+ return this._added;
5797
+ }
5798
+ set added(value) {
5799
+ this._added._total = value;
5800
+ }
5801
+ get matched() {
5802
+ return this._matched;
5803
+ }
5804
+ set matched(value) {
5805
+ this._matched._total = value;
5806
+ }
5807
+ get unmatched() {
5808
+ return this._unmatched;
5809
+ }
5810
+ set unmatched(value) {
5811
+ this._unmatched._total = value;
5812
+ }
5813
+ get updated() {
5814
+ return this._updated;
5815
+ }
5816
+ set updated(value) {
5817
+ this._updated._total = value;
5818
+ }
5819
+ constructor(testFilePath, snapshotPath, snapshotContent, options) {
5820
+ this.testFilePath = testFilePath;
5821
+ this.snapshotPath = snapshotPath;
5822
+ const { data, dirty } = getSnapshotData(snapshotContent, options);
5823
+ this._fileExists = snapshotContent != null;
5824
+ this._initialData = { ...data };
5825
+ this._snapshotData = { ...data };
5826
+ this._dirty = dirty;
5827
+ this._inlineSnapshots = [];
5828
+ this._inlineSnapshotStacks = [];
5829
+ this._rawSnapshots = [];
5830
+ this._uncheckedKeys = new Set(Object.keys(this._snapshotData));
5831
+ this.expand = options.expand || false;
5832
+ this._updateSnapshot = options.updateSnapshot;
5833
+ this._snapshotFormat = {
5834
+ printBasicPrototype: false,
5835
+ escapeString: false,
5836
+ maxOutputLength: 2 ** 27,
5837
+ ...options.snapshotFormat
5838
+ };
5839
+ this._environment = options.snapshotEnvironment;
5840
+ }
5841
+ static async create(testFilePath, options) {
5842
+ const snapshotPath = await options.snapshotEnvironment.resolvePath(testFilePath);
5843
+ return new SnapshotState(testFilePath, snapshotPath, await options.snapshotEnvironment.readSnapshotFile(snapshotPath), options);
5844
+ }
5845
+ get snapshotUpdateState() {
5846
+ return this._updateSnapshot;
5847
+ }
5848
+ get environment() {
5849
+ return this._environment;
5850
+ }
5851
+ markSnapshotsAsCheckedForTest(testName) {
5852
+ this._uncheckedKeys.forEach((uncheckedKey) => {
5853
+ // skip snapshots with following keys
5854
+ // testName n
5855
+ // testName > xxx n (this is for toMatchSnapshot("xxx") API)
5856
+ if (/ \d+$| > /.test(uncheckedKey.slice(testName.length))) this._uncheckedKeys.delete(uncheckedKey);
5857
+ });
5858
+ }
5859
+ clearTest(testId) {
5860
+ // clear inline
5861
+ this._inlineSnapshots = this._inlineSnapshots.filter((s) => s.testId !== testId);
5862
+ this._inlineSnapshotStacks = this._inlineSnapshotStacks.filter((s) => s.testId !== testId);
5863
+ // clear file
5864
+ for (const key of this._testIdToKeys.get(testId)) {
5865
+ const name = keyToTestName(key);
5866
+ const count = this._counters.get(name);
5867
+ if (count > 0) {
5868
+ if (key in this._snapshotData || key in this._initialData) this._snapshotData[key] = this._initialData[key];
5869
+ this._counters.set(name, count - 1);
5870
+ }
5871
+ }
5872
+ this._testIdToKeys.delete(testId);
5873
+ // clear stats
5874
+ this.added.delete(testId);
5875
+ this.updated.delete(testId);
5876
+ this.matched.delete(testId);
5877
+ this.unmatched.delete(testId);
5878
+ }
5879
+ _inferInlineSnapshotStack(stacks) {
5880
+ // if called inside resolves/rejects, stacktrace is different
5881
+ const promiseIndex = stacks.findIndex((i) => i.method.match(/__VITEST_(RESOLVES|REJECTS)__/));
5882
+ if (promiseIndex !== -1) return stacks[promiseIndex + 3];
5883
+ // support poll + inline snapshot
5884
+ const pollChainIndex = stacks.findIndex((i) => i.method.match(/__VITEST_POLL_CHAIN__/));
5885
+ if (pollChainIndex !== -1) return stacks[pollChainIndex + 1];
5886
+ // inline snapshot function can be named __INLINE_SNAPSHOT_OFFSET_<n>__
5887
+ // to specify a custom stack offset
5888
+ for (let i = 0; i < stacks.length; i++) {
5889
+ const match = stacks[i].method.match(/__INLINE_SNAPSHOT_OFFSET_(\d+)__/);
5890
+ if (match) return stacks[i + Number(match[1])] ?? null;
5891
+ }
5892
+ // custom matcher registered via expect.extend() — the wrapper function
5893
+ // in jest-extend.ts is named __VITEST_EXTEND_ASSERTION__
5894
+ const customMatcherIndex = stacks.findIndex((i) => i.method.includes("__VITEST_EXTEND_ASSERTION__"));
5895
+ if (customMatcherIndex !== -1) return stacks[customMatcherIndex + 3] ?? null;
5896
+ // inline snapshot function is called __INLINE_SNAPSHOT__
5897
+ // in integrations/snapshot/chai.ts
5898
+ const stackIndex = stacks.findIndex((i) => i.method.includes("__INLINE_SNAPSHOT__"));
5899
+ return stackIndex !== -1 ? stacks[stackIndex + 2] : null;
5900
+ }
5901
+ _addSnapshot(key, receivedSerialized, options) {
5902
+ this._dirty = true;
5903
+ if (options.stack) this._inlineSnapshots.push({
5904
+ ...options.stack,
5905
+ snapshot: receivedSerialized,
5906
+ testId: options.testId,
5907
+ assertionName: options.assertionName
5908
+ });
5909
+ else if (options.rawSnapshot) this._rawSnapshots.push({
5910
+ ...options.rawSnapshot,
5911
+ snapshot: receivedSerialized
5912
+ });
5913
+ else this._snapshotData[key] = receivedSerialized;
5914
+ }
5915
+ _resolveKey(testId, testName, key) {
5916
+ this._counters.increment(testName);
5917
+ const count = this._counters.get(testName);
5918
+ if (!key) key = testNameToKey(testName, count);
5919
+ this._testIdToKeys.get(testId).push(key);
5920
+ return {
5921
+ key,
5922
+ count
5923
+ };
5924
+ }
5925
+ _resolveInlineStack(options) {
5926
+ const { testId, snapshot, assertionName, error } = options;
5927
+ const stacks = parseErrorStacktrace(error, { ignoreStackEntries: [] });
5928
+ const _stack = this._inferInlineSnapshotStack(stacks);
5929
+ if (!_stack) {
5930
+ const message = stacks.map((s) => ` ${s.file}:${s.line}:${s.column}${s.method ? ` (${s.method})` : ""}`).join("\n");
5931
+ throw new Error(`@vitest/snapshot: Couldn't infer stack frame for inline snapshot.\n${message}`);
5932
+ }
5933
+ const stack = this.environment.processStackTrace?.(_stack) || _stack;
5934
+ // removing 1 column, because source map points to the wrong
5935
+ // location for js files, but `column-1` points to the same in both js/ts
5936
+ // https://github.com/vitejs/vite/issues/8657
5937
+ stack.column--;
5938
+ // reject multiple inline snapshots at the same location if snapshot is different
5939
+ const snapshotsWithSameStack = this._inlineSnapshotStacks.filter((s) => isSameStackPosition(s, stack));
5940
+ if (snapshotsWithSameStack.length > 0) {
5941
+ // ensure only one snapshot will be written at the same location
5942
+ this._inlineSnapshots = this._inlineSnapshots.filter((s) => !isSameStackPosition(s, stack));
5943
+ const differentSnapshot = snapshotsWithSameStack.find((s) => s.snapshot !== snapshot);
5944
+ if (differentSnapshot) throw Object.assign(/* @__PURE__ */ new Error(`${assertionName} with different snapshots cannot be called at the same location`), {
5945
+ actual: snapshot,
5946
+ expected: differentSnapshot.snapshot
5947
+ });
5948
+ }
5949
+ this._inlineSnapshotStacks.push({
5950
+ ...stack,
5951
+ testId,
5952
+ snapshot
5953
+ });
5954
+ return stack;
5955
+ }
5956
+ _reconcile(opts) {
5957
+ // These are the conditions on when to write snapshots:
5958
+ // * There's no snapshot file in a non-CI environment.
5959
+ // * There is a snapshot file and we decided to update the snapshot.
5960
+ // * There is a snapshot file, but it doesn't have this snapshot.
5961
+ // These are the conditions on when not to write snapshots:
5962
+ // * The update flag is set to 'none'.
5963
+ // * There's no snapshot file or a file without this snapshot on a CI environment.
5964
+ if (opts.hasSnapshot && this._updateSnapshot === "all" || (!opts.hasSnapshot || !opts.snapshotIsPersisted) && (this._updateSnapshot === "new" || this._updateSnapshot === "all")) {
5965
+ if (this._updateSnapshot === "all") if (!opts.pass) {
5966
+ if (opts.hasSnapshot) this.updated.increment(opts.testId);
5967
+ else this.added.increment(opts.testId);
5968
+ this._addSnapshot(opts.key, opts.addValue, {
5969
+ stack: opts.stack,
5970
+ testId: opts.testId,
5971
+ rawSnapshot: opts.rawSnapshot,
5972
+ assertionName: opts.assertionName
5973
+ });
5974
+ } else this.matched.increment(opts.testId);
5975
+ else {
5976
+ this._addSnapshot(opts.key, opts.addValue, {
5977
+ stack: opts.stack,
5978
+ testId: opts.testId,
5979
+ rawSnapshot: opts.rawSnapshot,
5980
+ assertionName: opts.assertionName
5981
+ });
5982
+ this.added.increment(opts.testId);
5983
+ }
5984
+ return {
5985
+ actual: "",
5986
+ count: opts.count,
5987
+ expected: "",
5988
+ key: opts.key,
5989
+ pass: true
5990
+ };
5991
+ } else if (!opts.pass) {
5992
+ this.unmatched.increment(opts.testId);
5993
+ return {
5994
+ actual: opts.actualDisplay,
5995
+ count: opts.count,
5996
+ expected: opts.expectedDisplay,
5997
+ key: opts.key,
5998
+ pass: false
5999
+ };
6000
+ } else {
6001
+ this.matched.increment(opts.testId);
6002
+ return {
6003
+ actual: "",
6004
+ count: opts.count,
6005
+ expected: "",
6006
+ key: opts.key,
6007
+ pass: true
6008
+ };
6009
+ }
6010
+ }
6011
+ async save() {
6012
+ const hasExternalSnapshots = Object.keys(this._snapshotData).length;
6013
+ const hasInlineSnapshots = this._inlineSnapshots.length;
6014
+ const hasRawSnapshots = this._rawSnapshots.length;
6015
+ const isEmpty = !hasExternalSnapshots && !hasInlineSnapshots && !hasRawSnapshots;
6016
+ const status = {
6017
+ deleted: false,
6018
+ saved: false
6019
+ };
6020
+ if ((this._dirty || this._uncheckedKeys.size) && !isEmpty) {
6021
+ if (hasExternalSnapshots) {
6022
+ await saveSnapshotFile(this._environment, this._snapshotData, this.snapshotPath);
6023
+ this._fileExists = true;
6024
+ }
6025
+ if (hasInlineSnapshots) await saveInlineSnapshots(this._environment, this._inlineSnapshots);
6026
+ if (hasRawSnapshots) await saveRawSnapshots(this._environment, this._rawSnapshots);
6027
+ status.saved = true;
6028
+ } else if (!hasExternalSnapshots && this._fileExists) {
6029
+ if (this._updateSnapshot === "all") {
6030
+ await this._environment.removeSnapshotFile(this.snapshotPath);
6031
+ this._fileExists = false;
6032
+ }
6033
+ status.deleted = true;
6034
+ }
6035
+ return status;
6036
+ }
6037
+ getUncheckedCount() {
6038
+ return this._uncheckedKeys.size || 0;
6039
+ }
6040
+ getUncheckedKeys() {
6041
+ return Array.from(this._uncheckedKeys);
6042
+ }
6043
+ removeUncheckedKeys() {
6044
+ if (this._updateSnapshot === "all" && this._uncheckedKeys.size) {
6045
+ this._dirty = true;
6046
+ this._uncheckedKeys.forEach((key) => delete this._snapshotData[key]);
6047
+ this._uncheckedKeys.clear();
6048
+ }
6049
+ }
6050
+ probeExpectedSnapshot(options) {
6051
+ const count = this._counters.get(options.testName) + 1;
6052
+ const key = testNameToKey(options.testName, count);
6053
+ return {
6054
+ key,
6055
+ count,
6056
+ data: options?.isInline ? options.inlineSnapshot : this._snapshotData[key],
6057
+ markAsChecked: () => {
6058
+ this._counters.increment(options.testName);
6059
+ this._testIdToKeys.get(options.testId).push(key);
6060
+ this._uncheckedKeys.delete(key);
6061
+ }
6062
+ };
6063
+ }
6064
+ match({ testId, testName, received, key, inlineSnapshot, isInline, error, rawSnapshot, assertionName }) {
6065
+ const resolved = this._resolveKey(testId, testName, key);
6066
+ key = resolved.key;
6067
+ const count = resolved.count;
6068
+ // Do not mark the snapshot as "checked" if the snapshot is inline and
6069
+ // there's an external snapshot. This way the external snapshot can be
6070
+ // removed with `--updateSnapshot`.
6071
+ if (!(isInline && this._snapshotData[key] !== void 0)) this._uncheckedKeys.delete(key);
6072
+ let receivedSerialized = rawSnapshot && typeof received === "string" ? received : serialize(received, void 0, this._snapshotFormat);
6073
+ if (!rawSnapshot) receivedSerialized = addExtraLineBreaks(receivedSerialized);
6074
+ if (rawSnapshot) {
6075
+ // normalize EOL when snapshot contains CRLF but received is LF
6076
+ if (rawSnapshot.content && rawSnapshot.content.match(/\r\n/) && !receivedSerialized.match(/\r\n/)) rawSnapshot.content = normalizeNewlines(rawSnapshot.content);
6077
+ }
6078
+ const expected = isInline ? inlineSnapshot : rawSnapshot ? rawSnapshot.content : this._snapshotData[key];
6079
+ const expectedTrimmed = rawSnapshot ? expected : expected?.trim();
6080
+ const pass = expectedTrimmed === (rawSnapshot ? receivedSerialized : receivedSerialized.trim());
6081
+ const hasSnapshot = expected !== void 0;
6082
+ const snapshotIsPersisted = isInline || this._fileExists || rawSnapshot && rawSnapshot.content != null;
6083
+ if (pass && !isInline && !rawSnapshot)
6084
+ // When the file is re-saved (because other snapshots changed), the JS
6085
+ // round-trip can lose proper escaping. Refresh in-memory data with the
6086
+ // freshly serialized string so the file is written correctly.
6087
+ // _reconcile does not write _snapshotData on pass, so this is the only
6088
+ // place it gets refreshed. Domain snapshots skip this because the stored
6089
+ // value may contain match patterns that differ from the received output.
6090
+ this._snapshotData[key] = receivedSerialized;
6091
+ const stack = isInline ? this._resolveInlineStack({
6092
+ testId,
6093
+ snapshot: receivedSerialized,
6094
+ assertionName: assertionName || "toMatchInlineSnapshot",
6095
+ error: error || /* @__PURE__ */ new Error("snapshot")
6096
+ }) : void 0;
6097
+ return this._reconcile({
6098
+ testId,
6099
+ key,
6100
+ count,
6101
+ pass,
6102
+ hasSnapshot,
6103
+ snapshotIsPersisted: !!snapshotIsPersisted,
6104
+ addValue: receivedSerialized,
6105
+ actualDisplay: rawSnapshot ? receivedSerialized : removeExtraLineBreaks(receivedSerialized),
6106
+ expectedDisplay: expectedTrimmed !== void 0 ? rawSnapshot ? expectedTrimmed : removeExtraLineBreaks(expectedTrimmed) : void 0,
6107
+ stack,
6108
+ rawSnapshot,
6109
+ assertionName
6110
+ });
6111
+ }
6112
+ processDomainSnapshot({ testId, received, expectedSnapshot, matchResult, isInline, error, assertionName }) {
6113
+ const stack = isInline ? this._resolveInlineStack({
6114
+ testId,
6115
+ snapshot: received,
6116
+ assertionName,
6117
+ error: error || /* @__PURE__ */ new Error("STACK_TRACE_ERROR")
6118
+ }) : void 0;
6119
+ const actualResolved = matchResult?.resolved ?? received;
6120
+ const expectedResolved = matchResult?.expected ?? expectedSnapshot.data;
6121
+ return this._reconcile({
6122
+ testId,
6123
+ key: expectedSnapshot.key,
6124
+ count: expectedSnapshot.count,
6125
+ pass: matchResult?.pass ?? false,
6126
+ hasSnapshot: expectedSnapshot.data !== void 0,
6127
+ snapshotIsPersisted: isInline ? true : this._fileExists,
6128
+ addValue: actualResolved,
6129
+ actualDisplay: removeExtraLineBreaks(actualResolved),
6130
+ expectedDisplay: expectedResolved !== void 0 ? removeExtraLineBreaks(expectedResolved) : void 0,
6131
+ stack,
6132
+ assertionName
6133
+ });
6134
+ }
6135
+ async pack() {
6136
+ const snapshot = {
6137
+ filepath: this.testFilePath,
6138
+ added: 0,
6139
+ fileDeleted: false,
6140
+ matched: 0,
6141
+ unchecked: 0,
6142
+ uncheckedKeys: [],
6143
+ unmatched: 0,
6144
+ updated: 0
6145
+ };
6146
+ const uncheckedCount = this.getUncheckedCount();
6147
+ const uncheckedKeys = this.getUncheckedKeys();
6148
+ if (uncheckedCount) this.removeUncheckedKeys();
6149
+ const status = await this.save();
6150
+ snapshot.fileDeleted = status.deleted;
6151
+ snapshot.added = this.added.total();
6152
+ snapshot.matched = this.matched.total();
6153
+ snapshot.unmatched = this.unmatched.total();
6154
+ snapshot.updated = this.updated.total();
6155
+ snapshot.unchecked = !status.deleted ? uncheckedCount : 0;
6156
+ snapshot.uncheckedKeys = Array.from(uncheckedKeys);
6157
+ return snapshot;
6158
+ }
6159
+ }
6160
+
6161
+ function createMismatchError(message, expand, actual, expected) {
6162
+ const error = new Error(message);
6163
+ Object.defineProperty(error, "actual", {
6164
+ value: actual,
6165
+ enumerable: true,
6166
+ configurable: true,
6167
+ writable: true
6168
+ });
6169
+ Object.defineProperty(error, "expected", {
6170
+ value: expected,
6171
+ enumerable: true,
6172
+ configurable: true,
6173
+ writable: true
6174
+ });
6175
+ Object.defineProperty(error, "diffOptions", { value: { expand } });
6176
+ return error;
6177
+ }
6178
+ class SnapshotClient {
6179
+ snapshotStateMap = /* @__PURE__ */ new Map();
6180
+ constructor(options = {}) {
6181
+ this.options = options;
6182
+ }
6183
+ async setup(filepath, options) {
6184
+ if (this.snapshotStateMap.has(filepath)) return;
6185
+ this.snapshotStateMap.set(filepath, await SnapshotState.create(filepath, options));
6186
+ }
6187
+ async finish(filepath) {
6188
+ const result = await this.getSnapshotState(filepath).pack();
6189
+ this.snapshotStateMap.delete(filepath);
6190
+ return result;
6191
+ }
6192
+ skipTest(filepath, testName) {
6193
+ this.getSnapshotState(filepath).markSnapshotsAsCheckedForTest(testName);
6194
+ }
6195
+ clearTest(filepath, testId) {
6196
+ this.getSnapshotState(filepath).clearTest(testId);
6197
+ }
6198
+ getSnapshotState(filepath) {
6199
+ const state = this.snapshotStateMap.get(filepath);
6200
+ if (!state) throw new Error(`The snapshot state for '${filepath}' is not found. Did you call 'SnapshotClient.setup()'?`);
6201
+ return state;
6202
+ }
6203
+ match(options) {
6204
+ const { filepath, name, testId = name, message, isInline = false, properties, inlineSnapshot, error, errorMessage, rawSnapshot, assertionName } = options;
6205
+ let { received } = options;
6206
+ if (!filepath) throw new Error("Snapshot cannot be used outside of test");
6207
+ const snapshotState = this.getSnapshotState(filepath);
6208
+ const testName = [name, ...message ? [message] : []].join(" > ");
6209
+ // Probe first so we can mark as checked even on early return
6210
+ const expectedSnapshot = snapshotState.probeExpectedSnapshot({
6211
+ testName,
6212
+ testId,
6213
+ isInline,
6214
+ inlineSnapshot
6215
+ });
6216
+ if (typeof properties === "object") {
6217
+ if (typeof received !== "object" || !received) {
6218
+ expectedSnapshot.markAsChecked();
6219
+ throw new Error("Received value must be an object when the matcher has properties");
6220
+ }
6221
+ let propertiesPass;
6222
+ try {
6223
+ propertiesPass = this.options.isEqual?.(received, properties) ?? false;
6224
+ } catch (err) {
6225
+ expectedSnapshot.markAsChecked();
6226
+ throw err;
6227
+ }
6228
+ if (!propertiesPass) {
6229
+ expectedSnapshot.markAsChecked();
6230
+ return {
6231
+ pass: false,
6232
+ message: () => errorMessage || "Snapshot properties mismatched",
6233
+ actual: received,
6234
+ expected: properties
6235
+ };
6236
+ }
6237
+ received = deepMergeSnapshot(received, properties);
6238
+ }
6239
+ const { actual, expected, key, pass } = snapshotState.match({
6240
+ testId,
6241
+ testName,
6242
+ received,
6243
+ isInline,
6244
+ error,
6245
+ inlineSnapshot,
6246
+ rawSnapshot,
6247
+ assertionName
6248
+ });
6249
+ return {
6250
+ pass,
6251
+ message: () => `Snapshot \`${key || "unknown"}\` mismatched`,
6252
+ actual: rawSnapshot ? actual : actual?.trim(),
6253
+ expected: rawSnapshot ? expected : expected?.trim()
6254
+ };
6255
+ }
6256
+ assert(options) {
6257
+ const result = this.match(options);
6258
+ if (!result.pass) {
6259
+ const snapshotState = this.getSnapshotState(options.filepath);
6260
+ throw createMismatchError(result.message(), snapshotState.expand, result.actual, result.expected);
6261
+ }
6262
+ }
6263
+ matchDomain(options) {
6264
+ const { received, filepath, name, testId = name, message, adapter, isInline = false, inlineSnapshot, error } = options;
6265
+ if (!filepath) throw new Error("Snapshot cannot be used outside of test");
6266
+ const captured = adapter.capture(received);
6267
+ const rendered = adapter.render(captured);
6268
+ const snapshotState = this.getSnapshotState(filepath);
6269
+ const testName = [name, ...message ? [message] : []].join(" > ");
6270
+ const expectedSnapshot = snapshotState.probeExpectedSnapshot({
6271
+ testName,
6272
+ testId,
6273
+ isInline,
6274
+ inlineSnapshot
6275
+ });
6276
+ expectedSnapshot.markAsChecked();
6277
+ const matchResult = expectedSnapshot.data !== void 0 ? adapter.match(captured, adapter.parseExpected(expectedSnapshot.data)) : void 0;
6278
+ const { actual, expected, key, pass } = snapshotState.processDomainSnapshot({
6279
+ testId,
6280
+ received: rendered,
6281
+ expectedSnapshot,
6282
+ matchResult,
6283
+ isInline,
6284
+ error,
6285
+ assertionName: options.assertionName
6286
+ });
6287
+ return {
6288
+ pass,
6289
+ message: () => `Snapshot \`${key}\` mismatched`,
6290
+ actual: actual?.trim(),
6291
+ expected: expected?.trim()
6292
+ };
6293
+ }
6294
+ async pollMatchDomain(options) {
6295
+ const { poll, filepath, name, testId = name, message, adapter, isInline = false, inlineSnapshot, error, timeout = 1e3, interval = 50 } = options;
6296
+ if (!filepath) throw new Error("Snapshot cannot be used outside of test");
6297
+ const snapshotState = this.getSnapshotState(filepath);
6298
+ const testName = [name, ...message ? [message] : []].join(" > ");
6299
+ const expectedSnapshot = snapshotState.probeExpectedSnapshot({
6300
+ testName,
6301
+ testId,
6302
+ isInline,
6303
+ inlineSnapshot
6304
+ });
6305
+ const reference = expectedSnapshot.data !== void 0 && snapshotState.snapshotUpdateState !== "all" ? adapter.parseExpected(expectedSnapshot.data) : void 0;
6306
+ const stableResult = await getStableSnapshot({
6307
+ adapter,
6308
+ poll,
6309
+ interval,
6310
+ timedOut: timeout > 0 ? new Promise((r) => setTimeout(r, timeout)) : void 0,
6311
+ match: reference ? (captured) => adapter.match(captured, reference).pass : void 0
6312
+ });
6313
+ expectedSnapshot.markAsChecked();
6314
+ if (stableResult?.rendered === void 0) {
6315
+ // the original caller `expect.poll` later manipulates error via `throwWithCause`,
6316
+ // so here we can directly throw `lastPollError` if exists.
6317
+ if (stableResult?.lastPollError) throw stableResult.lastPollError;
6318
+ return {
6319
+ pass: false,
6320
+ message: () => `poll() did not produce a stable snapshot within the timeout`
6321
+ };
6322
+ }
6323
+ // TODO: should `all` mode ignore parse error?
6324
+ // Sielently hiding the error and creating snaphsot full scratch isn't good either.
6325
+ // Users can fix or purge the broken snapshot manually and that decision affects how domain snapshot gets updated.
6326
+ const matchResult = expectedSnapshot.data !== void 0 ? adapter.match(stableResult.captured, adapter.parseExpected(expectedSnapshot.data)) : void 0;
6327
+ const { actual, expected, key, pass } = snapshotState.processDomainSnapshot({
6328
+ testId,
6329
+ received: stableResult.rendered,
6330
+ expectedSnapshot,
6331
+ matchResult,
6332
+ isInline,
6333
+ error,
6334
+ assertionName: options.assertionName
6335
+ });
6336
+ return {
6337
+ pass,
6338
+ message: () => `Snapshot \`${key}\` mismatched`,
6339
+ actual: actual?.trim(),
6340
+ expected: expected?.trim()
6341
+ };
6342
+ }
6343
+ async assertRaw(options) {
6344
+ if (!options.rawSnapshot) throw new Error("Raw snapshot is required");
6345
+ const { filepath, rawSnapshot } = options;
6346
+ if (rawSnapshot.content == null) {
6347
+ if (!filepath) throw new Error("Snapshot cannot be used outside of test");
6348
+ const snapshotState = this.getSnapshotState(filepath);
6349
+ // save the filepath, so it don't lose even if the await make it out-of-context
6350
+ options.filepath ||= filepath;
6351
+ // resolve and read the raw snapshot file
6352
+ rawSnapshot.file = await snapshotState.environment.resolveRawPath(filepath, rawSnapshot.file);
6353
+ rawSnapshot.content = await snapshotState.environment.readSnapshotFile(rawSnapshot.file) ?? void 0;
6354
+ }
6355
+ return this.assert(options);
6356
+ }
6357
+ clear() {
6358
+ this.snapshotStateMap.clear();
6359
+ }
6360
+ }
6361
+ /**
6362
+ * Polls repeatedly until the value reaches a stable state.
6363
+ *
6364
+ * Compares consecutive rendered outputs from the current session —
6365
+ * when two consecutive polls produce the same rendered string,
6366
+ * the value is considered stable.
6367
+ *
6368
+ * Every `await` (poll call, interval delay) races against `timedOut`
6369
+ * so that hanging polls and delays are interrupted.
6370
+ */
6371
+ async function getStableSnapshot({ adapter, poll, interval, timedOut, match }) {
6372
+ let lastRendered;
6373
+ let lastPollError;
6374
+ let lastStable;
6375
+ while (true) {
6376
+ try {
6377
+ const pollResult = await raceWith(Promise.resolve(poll()), timedOut);
6378
+ if (!pollResult.ok) break;
6379
+ const captured = adapter.capture(pollResult.value);
6380
+ const rendered = adapter.render(captured);
6381
+ if (lastRendered !== void 0 && rendered === lastRendered) {
6382
+ lastStable = {
6383
+ captured,
6384
+ rendered
6385
+ };
6386
+ if (!match || match(captured)) break;
6387
+ } else {
6388
+ lastRendered = rendered;
6389
+ lastStable = void 0;
6390
+ }
6391
+ } catch (pollError) {
6392
+ // poll() threw — reset stability baseline and retry
6393
+ lastRendered = void 0;
6394
+ lastStable = void 0;
6395
+ lastPollError = pollError;
6396
+ }
6397
+ if (!(await raceWith(new Promise((r) => setTimeout(r, interval)), timedOut)).ok) break;
6398
+ }
6399
+ return {
6400
+ ...lastStable,
6401
+ lastPollError
6402
+ };
6403
+ }
6404
+ /** Type-safe `Promise.race` — tells you which promise won. */
6405
+ function raceWith(promise, other) {
6406
+ const left = promise.then((value) => ({
6407
+ ok: true,
6408
+ value
6409
+ }));
6410
+ if (!other) return left;
6411
+ return Promise.race([left, other.then((value) => ({
6412
+ ok: false,
6413
+ value
6414
+ }))]);
6415
+ }
6416
+
3841
6417
  let _client;
3842
6418
  function getSnapshotClient() {
3843
6419
  if (!_client) _client = new SnapshotClient({ isEqual: (received, expected) => {
@@ -3873,6 +6449,7 @@ function getAssertionName(assertion) {
3873
6449
  function getTest(obj) {
3874
6450
  const test = chai.util.flag(obj, "vitest-test");
3875
6451
  if (!test) throw new Error(`'${getAssertionName(obj)}' cannot be used without test context`);
6452
+ if (test.fails) throw new TestSyntaxError(`'${getAssertionName(obj)}' cannot be used with 'test.fails'`);
3876
6453
  return test;
3877
6454
  }
3878
6455
  function validateAssertion(assertion) {
@@ -3936,7 +6513,7 @@ function toMatchDomainSnapshotImpl(opts) {
3936
6513
  const assertionName = getAssertionName(assertion);
3937
6514
  const test = getTest(assertion);
3938
6515
  let { inlineSnapshot } = opts;
3939
- if (inlineSnapshot) inlineSnapshot = stripSnapshotIndentation(inlineSnapshot);
6516
+ if (inlineSnapshot !== void 0) inlineSnapshot = stripSnapshotIndentation(inlineSnapshot);
3940
6517
  const pollFn = chai.util.flag(assertion, "_poll.fn");
3941
6518
  if (pollFn) return getSnapshotClient().pollMatchDomain({
3942
6519
  poll: pollFn,
@@ -4165,6 +6742,40 @@ function inject(key) {
4165
6742
  return getWorkerState().providedContext[key];
4166
6743
  }
4167
6744
 
6745
+ const benchFns = /* @__PURE__ */ new WeakMap();
6746
+ const benchOptsMap = /* @__PURE__ */ new WeakMap();
6747
+ function getBenchOptions(key) {
6748
+ return benchOptsMap.get(key);
6749
+ }
6750
+ function getBenchFn(key) {
6751
+ return benchFns.get(key);
6752
+ }
6753
+ const bench = createBenchmark(function(name, fn = noop, options = {}) {
6754
+ if (getWorkerState().config.mode !== "benchmark") throw new Error("`bench()` is only available in benchmark mode.");
6755
+ const task = getCurrentSuite().task(formatName(name), {
6756
+ ...this,
6757
+ meta: { benchmark: true }
6758
+ });
6759
+ benchFns.set(task, fn);
6760
+ benchOptsMap.set(task, options);
6761
+ // vitest runner sets mode to `todo` if handler is not passed down
6762
+ // but we store handler separately
6763
+ if (!this.todo && task.mode === "todo") task.mode = "run";
6764
+ });
6765
+ function createBenchmark(fn) {
6766
+ const benchmark = createChainable([
6767
+ "skip",
6768
+ "only",
6769
+ "todo"
6770
+ ], fn);
6771
+ benchmark.skipIf = (condition) => condition ? benchmark.skip : benchmark;
6772
+ benchmark.runIf = (condition) => condition ? benchmark : benchmark.skip;
6773
+ return benchmark;
6774
+ }
6775
+ function formatName(name) {
6776
+ return typeof name === "string" ? name : typeof name === "function" ? name.name || "<anonymous>" : String(name);
6777
+ }
6778
+
4168
6779
  function createBenchmarkResult(name) {
4169
6780
  return {
4170
6781
  name,
@@ -4427,4 +7038,38 @@ function clearModuleMocks(config) {
4427
7038
  if (unstubGlobals) vi.unstubAllGlobals();
4428
7039
  }
4429
7040
 
4430
- export { NodeBenchmarkRunner as N, Snapshots as S, TestRunner as T, assert as a, vitest as b, createExpect as c, globalExpect as g, inject as i, should as s, vi as v };
7041
+ const assertType = function assertType() {};
7042
+
7043
+ var index = /*#__PURE__*/Object.freeze({
7044
+ __proto__: null,
7045
+ BenchmarkRunner: NodeBenchmarkRunner,
7046
+ EvaluatedModules: VitestEvaluatedModules,
7047
+ Snapshots: Snapshots,
7048
+ TestRunner: TestRunner,
7049
+ afterAll: afterAll,
7050
+ afterEach: afterEach,
7051
+ aroundAll: aroundAll,
7052
+ aroundEach: aroundEach,
7053
+ assert: assert,
7054
+ assertType: assertType,
7055
+ beforeAll: beforeAll,
7056
+ beforeEach: beforeEach,
7057
+ bench: bench,
7058
+ chai: chai,
7059
+ createExpect: createExpect,
7060
+ describe: describe,
7061
+ expect: globalExpect,
7062
+ expectTypeOf: expectTypeOf,
7063
+ inject: inject,
7064
+ it: it,
7065
+ onTestFailed: onTestFailed,
7066
+ onTestFinished: onTestFinished,
7067
+ recordArtifact: recordArtifact,
7068
+ should: should,
7069
+ suite: suite,
7070
+ test: test,
7071
+ vi: vi,
7072
+ vitest: vitest
7073
+ });
7074
+
7075
+ export { NodeBenchmarkRunner as N, Snapshots as S, TestRunner as T, assert as a, assertType as b, bench as c, createExpect as d, inject as e, vitest as f, globalExpect as g, index as i, should as s, vi as v };