@vitest/expect 0.27.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +22 -0
- package/README.md +17 -0
- package/dist/index.d.ts +191 -0
- package/dist/index.js +1331 -0
- package/package.json +36 -0
package/dist/index.js
ADDED
@@ -0,0 +1,1331 @@
|
|
1
|
+
import c from 'picocolors';
|
2
|
+
import { stringify, unifiedDiff, isObject, assertTypes } from '@vitest/utils';
|
3
|
+
import { AssertionError, util } from 'chai';
|
4
|
+
import { isMockFunction } from '@vitest/spy';
|
5
|
+
|
6
|
+
const MATCHERS_OBJECT = Symbol.for("matchers-object");
|
7
|
+
const JEST_MATCHERS_OBJECT = Symbol.for("$$jest-matchers-object");
|
8
|
+
const GLOBAL_EXPECT = Symbol.for("expect-global");
|
9
|
+
|
10
|
+
if (!Object.prototype.hasOwnProperty.call(globalThis, MATCHERS_OBJECT)) {
|
11
|
+
const globalState = /* @__PURE__ */ new WeakMap();
|
12
|
+
const matchers = /* @__PURE__ */ Object.create(null);
|
13
|
+
Object.defineProperty(globalThis, MATCHERS_OBJECT, {
|
14
|
+
get: () => globalState
|
15
|
+
});
|
16
|
+
Object.defineProperty(globalThis, JEST_MATCHERS_OBJECT, {
|
17
|
+
configurable: true,
|
18
|
+
get: () => ({
|
19
|
+
state: globalState.get(globalThis[GLOBAL_EXPECT]),
|
20
|
+
matchers
|
21
|
+
})
|
22
|
+
});
|
23
|
+
}
|
24
|
+
const getState = (expect) => globalThis[MATCHERS_OBJECT].get(expect);
|
25
|
+
const setState = (state, expect) => {
|
26
|
+
const map = globalThis[MATCHERS_OBJECT];
|
27
|
+
const current = map.get(expect) || {};
|
28
|
+
Object.assign(current, state);
|
29
|
+
map.set(expect, current);
|
30
|
+
};
|
31
|
+
|
32
|
+
const EXPECTED_COLOR = c.green;
|
33
|
+
const RECEIVED_COLOR = c.red;
|
34
|
+
const INVERTED_COLOR = c.inverse;
|
35
|
+
const BOLD_WEIGHT = c.bold;
|
36
|
+
const DIM_COLOR = c.dim;
|
37
|
+
function matcherHint(matcherName, received = "received", expected = "expected", options = {}) {
|
38
|
+
const {
|
39
|
+
comment = "",
|
40
|
+
isDirectExpectCall = false,
|
41
|
+
isNot = false,
|
42
|
+
promise = "",
|
43
|
+
secondArgument = "",
|
44
|
+
expectedColor = EXPECTED_COLOR,
|
45
|
+
receivedColor = RECEIVED_COLOR,
|
46
|
+
secondArgumentColor = EXPECTED_COLOR
|
47
|
+
} = options;
|
48
|
+
let hint = "";
|
49
|
+
let dimString = "expect";
|
50
|
+
if (!isDirectExpectCall && received !== "") {
|
51
|
+
hint += DIM_COLOR(`${dimString}(`) + receivedColor(received);
|
52
|
+
dimString = ")";
|
53
|
+
}
|
54
|
+
if (promise !== "") {
|
55
|
+
hint += DIM_COLOR(`${dimString}.`) + promise;
|
56
|
+
dimString = "";
|
57
|
+
}
|
58
|
+
if (isNot) {
|
59
|
+
hint += `${DIM_COLOR(`${dimString}.`)}not`;
|
60
|
+
dimString = "";
|
61
|
+
}
|
62
|
+
if (matcherName.includes(".")) {
|
63
|
+
dimString += matcherName;
|
64
|
+
} else {
|
65
|
+
hint += DIM_COLOR(`${dimString}.`) + matcherName;
|
66
|
+
dimString = "";
|
67
|
+
}
|
68
|
+
if (expected === "") {
|
69
|
+
dimString += "()";
|
70
|
+
} else {
|
71
|
+
hint += DIM_COLOR(`${dimString}(`) + expectedColor(expected);
|
72
|
+
if (secondArgument)
|
73
|
+
hint += DIM_COLOR(", ") + secondArgumentColor(secondArgument);
|
74
|
+
dimString = ")";
|
75
|
+
}
|
76
|
+
if (comment !== "")
|
77
|
+
dimString += ` // ${comment}`;
|
78
|
+
if (dimString !== "")
|
79
|
+
hint += DIM_COLOR(dimString);
|
80
|
+
return hint;
|
81
|
+
}
|
82
|
+
const SPACE_SYMBOL = "\xB7";
|
83
|
+
const replaceTrailingSpaces = (text) => text.replace(/\s+$/gm, (spaces) => SPACE_SYMBOL.repeat(spaces.length));
|
84
|
+
const printReceived = (object) => RECEIVED_COLOR(replaceTrailingSpaces(stringify(object)));
|
85
|
+
const printExpected = (value) => EXPECTED_COLOR(replaceTrailingSpaces(stringify(value)));
|
86
|
+
function diff(a, b, options) {
|
87
|
+
return unifiedDiff(stringify(b), stringify(a));
|
88
|
+
}
|
89
|
+
|
90
|
+
var matcherUtils = /*#__PURE__*/Object.freeze({
|
91
|
+
__proto__: null,
|
92
|
+
stringify: stringify,
|
93
|
+
EXPECTED_COLOR: EXPECTED_COLOR,
|
94
|
+
RECEIVED_COLOR: RECEIVED_COLOR,
|
95
|
+
INVERTED_COLOR: INVERTED_COLOR,
|
96
|
+
BOLD_WEIGHT: BOLD_WEIGHT,
|
97
|
+
DIM_COLOR: DIM_COLOR,
|
98
|
+
matcherHint: matcherHint,
|
99
|
+
printReceived: printReceived,
|
100
|
+
printExpected: printExpected,
|
101
|
+
diff: diff
|
102
|
+
});
|
103
|
+
|
104
|
+
function equals(a, b, customTesters, strictCheck) {
|
105
|
+
customTesters = customTesters || [];
|
106
|
+
return eq(a, b, [], [], customTesters, strictCheck ? hasKey : hasDefinedKey);
|
107
|
+
}
|
108
|
+
const functionToString = Function.prototype.toString;
|
109
|
+
function isAsymmetric(obj) {
|
110
|
+
return !!obj && typeof obj === "object" && "asymmetricMatch" in obj && isA("Function", obj.asymmetricMatch);
|
111
|
+
}
|
112
|
+
function hasAsymmetric(obj, seen = /* @__PURE__ */ new Set()) {
|
113
|
+
if (seen.has(obj))
|
114
|
+
return false;
|
115
|
+
seen.add(obj);
|
116
|
+
if (isAsymmetric(obj))
|
117
|
+
return true;
|
118
|
+
if (Array.isArray(obj))
|
119
|
+
return obj.some((i) => hasAsymmetric(i, seen));
|
120
|
+
if (obj instanceof Set)
|
121
|
+
return Array.from(obj).some((i) => hasAsymmetric(i, seen));
|
122
|
+
if (isObject(obj))
|
123
|
+
return Object.values(obj).some((v) => hasAsymmetric(v, seen));
|
124
|
+
return false;
|
125
|
+
}
|
126
|
+
function asymmetricMatch(a, b) {
|
127
|
+
const asymmetricA = isAsymmetric(a);
|
128
|
+
const asymmetricB = isAsymmetric(b);
|
129
|
+
if (asymmetricA && asymmetricB)
|
130
|
+
return void 0;
|
131
|
+
if (asymmetricA)
|
132
|
+
return a.asymmetricMatch(b);
|
133
|
+
if (asymmetricB)
|
134
|
+
return b.asymmetricMatch(a);
|
135
|
+
}
|
136
|
+
function eq(a, b, aStack, bStack, customTesters, hasKey2) {
|
137
|
+
let result = true;
|
138
|
+
const asymmetricResult = asymmetricMatch(a, b);
|
139
|
+
if (asymmetricResult !== void 0)
|
140
|
+
return asymmetricResult;
|
141
|
+
for (let i = 0; i < customTesters.length; i++) {
|
142
|
+
const customTesterResult = customTesters[i](a, b);
|
143
|
+
if (customTesterResult !== void 0)
|
144
|
+
return customTesterResult;
|
145
|
+
}
|
146
|
+
if (a instanceof Error && b instanceof Error)
|
147
|
+
return a.message === b.message;
|
148
|
+
if (Object.is(a, b))
|
149
|
+
return true;
|
150
|
+
if (a === null || b === null)
|
151
|
+
return a === b;
|
152
|
+
const className = Object.prototype.toString.call(a);
|
153
|
+
if (className !== Object.prototype.toString.call(b))
|
154
|
+
return false;
|
155
|
+
switch (className) {
|
156
|
+
case "[object Boolean]":
|
157
|
+
case "[object String]":
|
158
|
+
case "[object Number]":
|
159
|
+
if (typeof a !== typeof b) {
|
160
|
+
return false;
|
161
|
+
} else if (typeof a !== "object" && typeof b !== "object") {
|
162
|
+
return Object.is(a, b);
|
163
|
+
} else {
|
164
|
+
return Object.is(a.valueOf(), b.valueOf());
|
165
|
+
}
|
166
|
+
case "[object Date]":
|
167
|
+
return isNaN(a) && isNaN(b) || +a === +b;
|
168
|
+
case "[object RegExp]":
|
169
|
+
return a.source === b.source && a.flags === b.flags;
|
170
|
+
}
|
171
|
+
if (typeof a !== "object" || typeof b !== "object")
|
172
|
+
return false;
|
173
|
+
if (isDomNode(a) && isDomNode(b))
|
174
|
+
return a.isEqualNode(b);
|
175
|
+
let length = aStack.length;
|
176
|
+
while (length--) {
|
177
|
+
if (aStack[length] === a)
|
178
|
+
return bStack[length] === b;
|
179
|
+
else if (bStack[length] === b)
|
180
|
+
return false;
|
181
|
+
}
|
182
|
+
aStack.push(a);
|
183
|
+
bStack.push(b);
|
184
|
+
if (className === "[object Array]" && a.length !== b.length)
|
185
|
+
return false;
|
186
|
+
const aKeys = keys(a, hasKey2);
|
187
|
+
let key;
|
188
|
+
let size = aKeys.length;
|
189
|
+
if (keys(b, hasKey2).length !== size)
|
190
|
+
return false;
|
191
|
+
while (size--) {
|
192
|
+
key = aKeys[size];
|
193
|
+
result = hasKey2(b, key) && eq(a[key], b[key], aStack, bStack, customTesters, hasKey2);
|
194
|
+
if (!result)
|
195
|
+
return false;
|
196
|
+
}
|
197
|
+
aStack.pop();
|
198
|
+
bStack.pop();
|
199
|
+
return result;
|
200
|
+
}
|
201
|
+
function keys(obj, hasKey2) {
|
202
|
+
const keys2 = [];
|
203
|
+
for (const key in obj) {
|
204
|
+
if (hasKey2(obj, key))
|
205
|
+
keys2.push(key);
|
206
|
+
}
|
207
|
+
return keys2.concat(
|
208
|
+
Object.getOwnPropertySymbols(obj).filter(
|
209
|
+
(symbol) => Object.getOwnPropertyDescriptor(obj, symbol).enumerable
|
210
|
+
)
|
211
|
+
);
|
212
|
+
}
|
213
|
+
function hasDefinedKey(obj, key) {
|
214
|
+
return hasKey(obj, key) && obj[key] !== void 0;
|
215
|
+
}
|
216
|
+
function hasKey(obj, key) {
|
217
|
+
return Object.prototype.hasOwnProperty.call(obj, key);
|
218
|
+
}
|
219
|
+
function isA(typeName, value) {
|
220
|
+
return Object.prototype.toString.apply(value) === `[object ${typeName}]`;
|
221
|
+
}
|
222
|
+
function isDomNode(obj) {
|
223
|
+
return obj !== null && typeof obj === "object" && typeof obj.nodeType === "number" && typeof obj.nodeName === "string" && typeof obj.isEqualNode === "function";
|
224
|
+
}
|
225
|
+
function fnNameFor(func) {
|
226
|
+
if (func.name)
|
227
|
+
return func.name;
|
228
|
+
const matches = functionToString.call(func).match(/^(?:async)?\s*function\s*\*?\s*([\w$]+)\s*\(/);
|
229
|
+
return matches ? matches[1] : "<anonymous>";
|
230
|
+
}
|
231
|
+
function getPrototype(obj) {
|
232
|
+
if (Object.getPrototypeOf)
|
233
|
+
return Object.getPrototypeOf(obj);
|
234
|
+
if (obj.constructor.prototype === obj)
|
235
|
+
return null;
|
236
|
+
return obj.constructor.prototype;
|
237
|
+
}
|
238
|
+
function hasProperty(obj, property) {
|
239
|
+
if (!obj)
|
240
|
+
return false;
|
241
|
+
if (Object.prototype.hasOwnProperty.call(obj, property))
|
242
|
+
return true;
|
243
|
+
return hasProperty(getPrototype(obj), property);
|
244
|
+
}
|
245
|
+
const IS_KEYED_SENTINEL = "@@__IMMUTABLE_KEYED__@@";
|
246
|
+
const IS_SET_SENTINEL = "@@__IMMUTABLE_SET__@@";
|
247
|
+
const IS_ORDERED_SENTINEL = "@@__IMMUTABLE_ORDERED__@@";
|
248
|
+
function isImmutableUnorderedKeyed(maybeKeyed) {
|
249
|
+
return !!(maybeKeyed && maybeKeyed[IS_KEYED_SENTINEL] && !maybeKeyed[IS_ORDERED_SENTINEL]);
|
250
|
+
}
|
251
|
+
function isImmutableUnorderedSet(maybeSet) {
|
252
|
+
return !!(maybeSet && maybeSet[IS_SET_SENTINEL] && !maybeSet[IS_ORDERED_SENTINEL]);
|
253
|
+
}
|
254
|
+
const IteratorSymbol = Symbol.iterator;
|
255
|
+
const hasIterator = (object) => !!(object != null && object[IteratorSymbol]);
|
256
|
+
const iterableEquality = (a, b, aStack = [], bStack = []) => {
|
257
|
+
if (typeof a !== "object" || typeof b !== "object" || Array.isArray(a) || Array.isArray(b) || !hasIterator(a) || !hasIterator(b))
|
258
|
+
return void 0;
|
259
|
+
if (a.constructor !== b.constructor)
|
260
|
+
return false;
|
261
|
+
let length = aStack.length;
|
262
|
+
while (length--) {
|
263
|
+
if (aStack[length] === a)
|
264
|
+
return bStack[length] === b;
|
265
|
+
}
|
266
|
+
aStack.push(a);
|
267
|
+
bStack.push(b);
|
268
|
+
const iterableEqualityWithStack = (a2, b2) => iterableEquality(a2, b2, [...aStack], [...bStack]);
|
269
|
+
if (a.size !== void 0) {
|
270
|
+
if (a.size !== b.size) {
|
271
|
+
return false;
|
272
|
+
} else if (isA("Set", a) || isImmutableUnorderedSet(a)) {
|
273
|
+
let allFound = true;
|
274
|
+
for (const aValue of a) {
|
275
|
+
if (!b.has(aValue)) {
|
276
|
+
let has = false;
|
277
|
+
for (const bValue of b) {
|
278
|
+
const isEqual = equals(aValue, bValue, [iterableEqualityWithStack]);
|
279
|
+
if (isEqual === true)
|
280
|
+
has = true;
|
281
|
+
}
|
282
|
+
if (has === false) {
|
283
|
+
allFound = false;
|
284
|
+
break;
|
285
|
+
}
|
286
|
+
}
|
287
|
+
}
|
288
|
+
aStack.pop();
|
289
|
+
bStack.pop();
|
290
|
+
return allFound;
|
291
|
+
} else if (isA("Map", a) || isImmutableUnorderedKeyed(a)) {
|
292
|
+
let allFound = true;
|
293
|
+
for (const aEntry of a) {
|
294
|
+
if (!b.has(aEntry[0]) || !equals(aEntry[1], b.get(aEntry[0]), [iterableEqualityWithStack])) {
|
295
|
+
let has = false;
|
296
|
+
for (const bEntry of b) {
|
297
|
+
const matchedKey = equals(aEntry[0], bEntry[0], [
|
298
|
+
iterableEqualityWithStack
|
299
|
+
]);
|
300
|
+
let matchedValue = false;
|
301
|
+
if (matchedKey === true) {
|
302
|
+
matchedValue = equals(aEntry[1], bEntry[1], [
|
303
|
+
iterableEqualityWithStack
|
304
|
+
]);
|
305
|
+
}
|
306
|
+
if (matchedValue === true)
|
307
|
+
has = true;
|
308
|
+
}
|
309
|
+
if (has === false) {
|
310
|
+
allFound = false;
|
311
|
+
break;
|
312
|
+
}
|
313
|
+
}
|
314
|
+
}
|
315
|
+
aStack.pop();
|
316
|
+
bStack.pop();
|
317
|
+
return allFound;
|
318
|
+
}
|
319
|
+
}
|
320
|
+
const bIterator = b[IteratorSymbol]();
|
321
|
+
for (const aValue of a) {
|
322
|
+
const nextB = bIterator.next();
|
323
|
+
if (nextB.done || !equals(aValue, nextB.value, [iterableEqualityWithStack]))
|
324
|
+
return false;
|
325
|
+
}
|
326
|
+
if (!bIterator.next().done)
|
327
|
+
return false;
|
328
|
+
aStack.pop();
|
329
|
+
bStack.pop();
|
330
|
+
return true;
|
331
|
+
};
|
332
|
+
const hasPropertyInObject = (object, key) => {
|
333
|
+
const shouldTerminate = !object || typeof object !== "object" || object === Object.prototype;
|
334
|
+
if (shouldTerminate)
|
335
|
+
return false;
|
336
|
+
return Object.prototype.hasOwnProperty.call(object, key) || hasPropertyInObject(Object.getPrototypeOf(object), key);
|
337
|
+
};
|
338
|
+
const isObjectWithKeys = (a) => isObject(a) && !(a instanceof Error) && !Array.isArray(a) && !(a instanceof Date);
|
339
|
+
const subsetEquality = (object, subset) => {
|
340
|
+
const subsetEqualityWithContext = (seenReferences = /* @__PURE__ */ new WeakMap()) => (object2, subset2) => {
|
341
|
+
if (!isObjectWithKeys(subset2))
|
342
|
+
return void 0;
|
343
|
+
return Object.keys(subset2).every((key) => {
|
344
|
+
if (isObjectWithKeys(subset2[key])) {
|
345
|
+
if (seenReferences.has(subset2[key]))
|
346
|
+
return equals(object2[key], subset2[key], [iterableEquality]);
|
347
|
+
seenReferences.set(subset2[key], true);
|
348
|
+
}
|
349
|
+
const result = object2 != null && hasPropertyInObject(object2, key) && equals(object2[key], subset2[key], [
|
350
|
+
iterableEquality,
|
351
|
+
subsetEqualityWithContext(seenReferences)
|
352
|
+
]);
|
353
|
+
seenReferences.delete(subset2[key]);
|
354
|
+
return result;
|
355
|
+
});
|
356
|
+
};
|
357
|
+
return subsetEqualityWithContext()(object, subset);
|
358
|
+
};
|
359
|
+
const typeEquality = (a, b) => {
|
360
|
+
if (a == null || b == null || a.constructor === b.constructor)
|
361
|
+
return void 0;
|
362
|
+
return false;
|
363
|
+
};
|
364
|
+
const arrayBufferEquality = (a, b) => {
|
365
|
+
if (!(a instanceof ArrayBuffer) || !(b instanceof ArrayBuffer))
|
366
|
+
return void 0;
|
367
|
+
const dataViewA = new DataView(a);
|
368
|
+
const dataViewB = new DataView(b);
|
369
|
+
if (dataViewA.byteLength !== dataViewB.byteLength)
|
370
|
+
return false;
|
371
|
+
for (let i = 0; i < dataViewA.byteLength; i++) {
|
372
|
+
if (dataViewA.getUint8(i) !== dataViewB.getUint8(i))
|
373
|
+
return false;
|
374
|
+
}
|
375
|
+
return true;
|
376
|
+
};
|
377
|
+
const sparseArrayEquality = (a, b) => {
|
378
|
+
if (!Array.isArray(a) || !Array.isArray(b))
|
379
|
+
return void 0;
|
380
|
+
const aKeys = Object.keys(a);
|
381
|
+
const bKeys = Object.keys(b);
|
382
|
+
return equals(a, b, [iterableEquality, typeEquality], true) && equals(aKeys, bKeys);
|
383
|
+
};
|
384
|
+
const generateToBeMessage = (deepEqualityName, expected = "#{this}", actual = "#{exp}") => {
|
385
|
+
const toBeMessage = `expected ${expected} to be ${actual} // Object.is equality`;
|
386
|
+
if (["toStrictEqual", "toEqual"].includes(deepEqualityName))
|
387
|
+
return `${toBeMessage}
|
388
|
+
|
389
|
+
If it should pass with deep equality, replace "toBe" with "${deepEqualityName}"
|
390
|
+
|
391
|
+
Expected: ${expected}
|
392
|
+
Received: serializes to the same string
|
393
|
+
`;
|
394
|
+
return toBeMessage;
|
395
|
+
};
|
396
|
+
|
397
|
+
class AsymmetricMatcher {
|
398
|
+
constructor(sample, inverse = false) {
|
399
|
+
this.sample = sample;
|
400
|
+
this.inverse = inverse;
|
401
|
+
this.$$typeof = Symbol.for("jest.asymmetricMatcher");
|
402
|
+
}
|
403
|
+
getMatcherContext(expect) {
|
404
|
+
return {
|
405
|
+
...getState(expect || globalThis[GLOBAL_EXPECT]),
|
406
|
+
equals,
|
407
|
+
isNot: this.inverse,
|
408
|
+
utils: matcherUtils
|
409
|
+
};
|
410
|
+
}
|
411
|
+
}
|
412
|
+
class StringContaining extends AsymmetricMatcher {
|
413
|
+
constructor(sample, inverse = false) {
|
414
|
+
if (!isA("String", sample))
|
415
|
+
throw new Error("Expected is not a string");
|
416
|
+
super(sample, inverse);
|
417
|
+
}
|
418
|
+
asymmetricMatch(other) {
|
419
|
+
const result = isA("String", other) && other.includes(this.sample);
|
420
|
+
return this.inverse ? !result : result;
|
421
|
+
}
|
422
|
+
toString() {
|
423
|
+
return `String${this.inverse ? "Not" : ""}Containing`;
|
424
|
+
}
|
425
|
+
getExpectedType() {
|
426
|
+
return "string";
|
427
|
+
}
|
428
|
+
}
|
429
|
+
class Anything extends AsymmetricMatcher {
|
430
|
+
asymmetricMatch(other) {
|
431
|
+
return other != null;
|
432
|
+
}
|
433
|
+
toString() {
|
434
|
+
return "Anything";
|
435
|
+
}
|
436
|
+
toAsymmetricMatcher() {
|
437
|
+
return "Anything";
|
438
|
+
}
|
439
|
+
}
|
440
|
+
class ObjectContaining extends AsymmetricMatcher {
|
441
|
+
constructor(sample, inverse = false) {
|
442
|
+
super(sample, inverse);
|
443
|
+
}
|
444
|
+
getPrototype(obj) {
|
445
|
+
if (Object.getPrototypeOf)
|
446
|
+
return Object.getPrototypeOf(obj);
|
447
|
+
if (obj.constructor.prototype === obj)
|
448
|
+
return null;
|
449
|
+
return obj.constructor.prototype;
|
450
|
+
}
|
451
|
+
hasProperty(obj, property) {
|
452
|
+
if (!obj)
|
453
|
+
return false;
|
454
|
+
if (Object.prototype.hasOwnProperty.call(obj, property))
|
455
|
+
return true;
|
456
|
+
return this.hasProperty(this.getPrototype(obj), property);
|
457
|
+
}
|
458
|
+
asymmetricMatch(other) {
|
459
|
+
if (typeof this.sample !== "object") {
|
460
|
+
throw new TypeError(
|
461
|
+
`You must provide an object to ${this.toString()}, not '${typeof this.sample}'.`
|
462
|
+
);
|
463
|
+
}
|
464
|
+
let result = true;
|
465
|
+
for (const property in this.sample) {
|
466
|
+
if (!this.hasProperty(other, property) || !equals(this.sample[property], other[property])) {
|
467
|
+
result = false;
|
468
|
+
break;
|
469
|
+
}
|
470
|
+
}
|
471
|
+
return this.inverse ? !result : result;
|
472
|
+
}
|
473
|
+
toString() {
|
474
|
+
return `Object${this.inverse ? "Not" : ""}Containing`;
|
475
|
+
}
|
476
|
+
getExpectedType() {
|
477
|
+
return "object";
|
478
|
+
}
|
479
|
+
}
|
480
|
+
class ArrayContaining extends AsymmetricMatcher {
|
481
|
+
constructor(sample, inverse = false) {
|
482
|
+
super(sample, inverse);
|
483
|
+
}
|
484
|
+
asymmetricMatch(other) {
|
485
|
+
if (!Array.isArray(this.sample)) {
|
486
|
+
throw new TypeError(
|
487
|
+
`You must provide an array to ${this.toString()}, not '${typeof this.sample}'.`
|
488
|
+
);
|
489
|
+
}
|
490
|
+
const result = this.sample.length === 0 || Array.isArray(other) && this.sample.every(
|
491
|
+
(item) => other.some((another) => equals(item, another))
|
492
|
+
);
|
493
|
+
return this.inverse ? !result : result;
|
494
|
+
}
|
495
|
+
toString() {
|
496
|
+
return `Array${this.inverse ? "Not" : ""}Containing`;
|
497
|
+
}
|
498
|
+
getExpectedType() {
|
499
|
+
return "array";
|
500
|
+
}
|
501
|
+
}
|
502
|
+
class Any extends AsymmetricMatcher {
|
503
|
+
constructor(sample) {
|
504
|
+
if (typeof sample === "undefined") {
|
505
|
+
throw new TypeError(
|
506
|
+
"any() expects to be passed a constructor function. Please pass one or use anything() to match any object."
|
507
|
+
);
|
508
|
+
}
|
509
|
+
super(sample);
|
510
|
+
}
|
511
|
+
fnNameFor(func) {
|
512
|
+
if (func.name)
|
513
|
+
return func.name;
|
514
|
+
const functionToString = Function.prototype.toString;
|
515
|
+
const matches = functionToString.call(func).match(/^(?:async)?\s*function\s*\*?\s*([\w$]+)\s*\(/);
|
516
|
+
return matches ? matches[1] : "<anonymous>";
|
517
|
+
}
|
518
|
+
asymmetricMatch(other) {
|
519
|
+
if (this.sample === String)
|
520
|
+
return typeof other == "string" || other instanceof String;
|
521
|
+
if (this.sample === Number)
|
522
|
+
return typeof other == "number" || other instanceof Number;
|
523
|
+
if (this.sample === Function)
|
524
|
+
return typeof other == "function" || other instanceof Function;
|
525
|
+
if (this.sample === Boolean)
|
526
|
+
return typeof other == "boolean" || other instanceof Boolean;
|
527
|
+
if (this.sample === BigInt)
|
528
|
+
return typeof other == "bigint" || other instanceof BigInt;
|
529
|
+
if (this.sample === Symbol)
|
530
|
+
return typeof other == "symbol" || other instanceof Symbol;
|
531
|
+
if (this.sample === Object)
|
532
|
+
return typeof other == "object";
|
533
|
+
return other instanceof this.sample;
|
534
|
+
}
|
535
|
+
toString() {
|
536
|
+
return "Any";
|
537
|
+
}
|
538
|
+
getExpectedType() {
|
539
|
+
if (this.sample === String)
|
540
|
+
return "string";
|
541
|
+
if (this.sample === Number)
|
542
|
+
return "number";
|
543
|
+
if (this.sample === Function)
|
544
|
+
return "function";
|
545
|
+
if (this.sample === Object)
|
546
|
+
return "object";
|
547
|
+
if (this.sample === Boolean)
|
548
|
+
return "boolean";
|
549
|
+
return this.fnNameFor(this.sample);
|
550
|
+
}
|
551
|
+
toAsymmetricMatcher() {
|
552
|
+
return `Any<${this.fnNameFor(this.sample)}>`;
|
553
|
+
}
|
554
|
+
}
|
555
|
+
class StringMatching extends AsymmetricMatcher {
|
556
|
+
constructor(sample, inverse = false) {
|
557
|
+
if (!isA("String", sample) && !isA("RegExp", sample))
|
558
|
+
throw new Error("Expected is not a String or a RegExp");
|
559
|
+
super(new RegExp(sample), inverse);
|
560
|
+
}
|
561
|
+
asymmetricMatch(other) {
|
562
|
+
const result = isA("String", other) && this.sample.test(other);
|
563
|
+
return this.inverse ? !result : result;
|
564
|
+
}
|
565
|
+
toString() {
|
566
|
+
return `String${this.inverse ? "Not" : ""}Matching`;
|
567
|
+
}
|
568
|
+
getExpectedType() {
|
569
|
+
return "string";
|
570
|
+
}
|
571
|
+
}
|
572
|
+
const JestAsymmetricMatchers = (chai, utils) => {
|
573
|
+
utils.addMethod(
|
574
|
+
chai.expect,
|
575
|
+
"anything",
|
576
|
+
() => new Anything()
|
577
|
+
);
|
578
|
+
utils.addMethod(
|
579
|
+
chai.expect,
|
580
|
+
"any",
|
581
|
+
(expected) => new Any(expected)
|
582
|
+
);
|
583
|
+
utils.addMethod(
|
584
|
+
chai.expect,
|
585
|
+
"stringContaining",
|
586
|
+
(expected) => new StringContaining(expected)
|
587
|
+
);
|
588
|
+
utils.addMethod(
|
589
|
+
chai.expect,
|
590
|
+
"objectContaining",
|
591
|
+
(expected) => new ObjectContaining(expected)
|
592
|
+
);
|
593
|
+
utils.addMethod(
|
594
|
+
chai.expect,
|
595
|
+
"arrayContaining",
|
596
|
+
(expected) => new ArrayContaining(expected)
|
597
|
+
);
|
598
|
+
utils.addMethod(
|
599
|
+
chai.expect,
|
600
|
+
"stringMatching",
|
601
|
+
(expected) => new StringMatching(expected)
|
602
|
+
);
|
603
|
+
chai.expect.not = {
|
604
|
+
stringContaining: (expected) => new StringContaining(expected, true),
|
605
|
+
objectContaining: (expected) => new ObjectContaining(expected, true),
|
606
|
+
arrayContaining: (expected) => new ArrayContaining(expected, true),
|
607
|
+
stringMatching: (expected) => new StringMatching(expected, true)
|
608
|
+
};
|
609
|
+
};
|
610
|
+
|
611
|
+
const JestChaiExpect = (chai, utils) => {
|
612
|
+
function def(name, fn) {
|
613
|
+
const addMethod = (n) => {
|
614
|
+
utils.addMethod(chai.Assertion.prototype, n, fn);
|
615
|
+
utils.addMethod(globalThis[JEST_MATCHERS_OBJECT].matchers, n, fn);
|
616
|
+
};
|
617
|
+
if (Array.isArray(name))
|
618
|
+
name.forEach((n) => addMethod(n));
|
619
|
+
else
|
620
|
+
addMethod(name);
|
621
|
+
}
|
622
|
+
["throw", "throws", "Throw"].forEach((m) => {
|
623
|
+
utils.overwriteMethod(chai.Assertion.prototype, m, (_super) => {
|
624
|
+
return function(...args) {
|
625
|
+
const promise = utils.flag(this, "promise");
|
626
|
+
const object = utils.flag(this, "object");
|
627
|
+
const isNot = utils.flag(this, "negate");
|
628
|
+
if (promise === "rejects") {
|
629
|
+
utils.flag(this, "object", () => {
|
630
|
+
throw object;
|
631
|
+
});
|
632
|
+
} else if (promise === "resolves" && typeof object !== "function") {
|
633
|
+
if (!isNot) {
|
634
|
+
const message = utils.flag(this, "message") || "expected promise to throw an error, but it didn't";
|
635
|
+
const error = {
|
636
|
+
showDiff: false
|
637
|
+
};
|
638
|
+
throw new AssertionError(message, error, utils.flag(this, "ssfi"));
|
639
|
+
} else {
|
640
|
+
return;
|
641
|
+
}
|
642
|
+
}
|
643
|
+
_super.apply(this, args);
|
644
|
+
};
|
645
|
+
});
|
646
|
+
});
|
647
|
+
def("withTest", function(test) {
|
648
|
+
utils.flag(this, "vitest-test", test);
|
649
|
+
return this;
|
650
|
+
});
|
651
|
+
def("toEqual", function(expected) {
|
652
|
+
const actual = utils.flag(this, "object");
|
653
|
+
const equal = equals(
|
654
|
+
actual,
|
655
|
+
expected,
|
656
|
+
[iterableEquality]
|
657
|
+
);
|
658
|
+
return this.assert(
|
659
|
+
equal,
|
660
|
+
"expected #{this} to deeply equal #{exp}",
|
661
|
+
"expected #{this} to not deeply equal #{exp}",
|
662
|
+
expected,
|
663
|
+
actual
|
664
|
+
);
|
665
|
+
});
|
666
|
+
def("toStrictEqual", function(expected) {
|
667
|
+
const obj = utils.flag(this, "object");
|
668
|
+
const equal = equals(
|
669
|
+
obj,
|
670
|
+
expected,
|
671
|
+
[
|
672
|
+
iterableEquality,
|
673
|
+
typeEquality,
|
674
|
+
sparseArrayEquality,
|
675
|
+
arrayBufferEquality
|
676
|
+
],
|
677
|
+
true
|
678
|
+
);
|
679
|
+
return this.assert(
|
680
|
+
equal,
|
681
|
+
"expected #{this} to strictly equal #{exp}",
|
682
|
+
"expected #{this} to not strictly equal #{exp}",
|
683
|
+
expected,
|
684
|
+
obj
|
685
|
+
);
|
686
|
+
});
|
687
|
+
def("toBe", function(expected) {
|
688
|
+
const actual = this._obj;
|
689
|
+
const pass = Object.is(actual, expected);
|
690
|
+
let deepEqualityName = "";
|
691
|
+
if (!pass) {
|
692
|
+
const toStrictEqualPass = equals(
|
693
|
+
actual,
|
694
|
+
expected,
|
695
|
+
[
|
696
|
+
iterableEquality,
|
697
|
+
typeEquality,
|
698
|
+
sparseArrayEquality,
|
699
|
+
arrayBufferEquality
|
700
|
+
],
|
701
|
+
true
|
702
|
+
);
|
703
|
+
if (toStrictEqualPass) {
|
704
|
+
deepEqualityName = "toStrictEqual";
|
705
|
+
} else {
|
706
|
+
const toEqualPass = equals(
|
707
|
+
actual,
|
708
|
+
expected,
|
709
|
+
[iterableEquality]
|
710
|
+
);
|
711
|
+
if (toEqualPass)
|
712
|
+
deepEqualityName = "toEqual";
|
713
|
+
}
|
714
|
+
}
|
715
|
+
return this.assert(
|
716
|
+
pass,
|
717
|
+
generateToBeMessage(deepEqualityName),
|
718
|
+
"expected #{this} not to be #{exp} // Object.is equality",
|
719
|
+
expected,
|
720
|
+
actual
|
721
|
+
);
|
722
|
+
});
|
723
|
+
def("toMatchObject", function(expected) {
|
724
|
+
const actual = this._obj;
|
725
|
+
return this.assert(
|
726
|
+
equals(actual, expected, [iterableEquality, subsetEquality]),
|
727
|
+
"expected #{this} to match object #{exp}",
|
728
|
+
"expected #{this} to not match object #{exp}",
|
729
|
+
expected,
|
730
|
+
actual
|
731
|
+
);
|
732
|
+
});
|
733
|
+
def("toMatch", function(expected) {
|
734
|
+
if (typeof expected === "string")
|
735
|
+
return this.include(expected);
|
736
|
+
else
|
737
|
+
return this.match(expected);
|
738
|
+
});
|
739
|
+
def("toContain", function(item) {
|
740
|
+
return this.contain(item);
|
741
|
+
});
|
742
|
+
def("toContainEqual", function(expected) {
|
743
|
+
const obj = utils.flag(this, "object");
|
744
|
+
const index = Array.from(obj).findIndex((item) => {
|
745
|
+
return equals(item, expected);
|
746
|
+
});
|
747
|
+
this.assert(
|
748
|
+
index !== -1,
|
749
|
+
"expected #{this} to deep equally contain #{exp}",
|
750
|
+
"expected #{this} to not deep equally contain #{exp}",
|
751
|
+
expected
|
752
|
+
);
|
753
|
+
});
|
754
|
+
def("toBeTruthy", function() {
|
755
|
+
const obj = utils.flag(this, "object");
|
756
|
+
this.assert(
|
757
|
+
Boolean(obj),
|
758
|
+
"expected #{this} to be truthy",
|
759
|
+
"expected #{this} to not be truthy",
|
760
|
+
obj
|
761
|
+
);
|
762
|
+
});
|
763
|
+
def("toBeFalsy", function() {
|
764
|
+
const obj = utils.flag(this, "object");
|
765
|
+
this.assert(
|
766
|
+
!obj,
|
767
|
+
"expected #{this} to be falsy",
|
768
|
+
"expected #{this} to not be falsy",
|
769
|
+
obj
|
770
|
+
);
|
771
|
+
});
|
772
|
+
def("toBeGreaterThan", function(expected) {
|
773
|
+
const actual = this._obj;
|
774
|
+
assertTypes(actual, "actual", ["number", "bigint"]);
|
775
|
+
assertTypes(expected, "expected", ["number", "bigint"]);
|
776
|
+
return this.assert(
|
777
|
+
actual > expected,
|
778
|
+
`expected ${actual} to be greater than ${expected}`,
|
779
|
+
`expected ${actual} to be not greater than ${expected}`,
|
780
|
+
actual,
|
781
|
+
expected
|
782
|
+
);
|
783
|
+
});
|
784
|
+
def("toBeGreaterThanOrEqual", function(expected) {
|
785
|
+
const actual = this._obj;
|
786
|
+
assertTypes(actual, "actual", ["number", "bigint"]);
|
787
|
+
assertTypes(expected, "expected", ["number", "bigint"]);
|
788
|
+
return this.assert(
|
789
|
+
actual >= expected,
|
790
|
+
`expected ${actual} to be greater than or equal to ${expected}`,
|
791
|
+
`expected ${actual} to be not greater than or equal to ${expected}`,
|
792
|
+
actual,
|
793
|
+
expected
|
794
|
+
);
|
795
|
+
});
|
796
|
+
def("toBeLessThan", function(expected) {
|
797
|
+
const actual = this._obj;
|
798
|
+
assertTypes(actual, "actual", ["number", "bigint"]);
|
799
|
+
assertTypes(expected, "expected", ["number", "bigint"]);
|
800
|
+
return this.assert(
|
801
|
+
actual < expected,
|
802
|
+
`expected ${actual} to be less than ${expected}`,
|
803
|
+
`expected ${actual} to be not less than ${expected}`,
|
804
|
+
actual,
|
805
|
+
expected
|
806
|
+
);
|
807
|
+
});
|
808
|
+
def("toBeLessThanOrEqual", function(expected) {
|
809
|
+
const actual = this._obj;
|
810
|
+
assertTypes(actual, "actual", ["number", "bigint"]);
|
811
|
+
assertTypes(expected, "expected", ["number", "bigint"]);
|
812
|
+
return this.assert(
|
813
|
+
actual <= expected,
|
814
|
+
`expected ${actual} to be less than or equal to ${expected}`,
|
815
|
+
`expected ${actual} to be not less than or equal to ${expected}`,
|
816
|
+
actual,
|
817
|
+
expected
|
818
|
+
);
|
819
|
+
});
|
820
|
+
def("toBeNaN", function() {
|
821
|
+
return this.be.NaN;
|
822
|
+
});
|
823
|
+
def("toBeUndefined", function() {
|
824
|
+
return this.be.undefined;
|
825
|
+
});
|
826
|
+
def("toBeNull", function() {
|
827
|
+
return this.be.null;
|
828
|
+
});
|
829
|
+
def("toBeDefined", function() {
|
830
|
+
const negate = utils.flag(this, "negate");
|
831
|
+
utils.flag(this, "negate", false);
|
832
|
+
if (negate)
|
833
|
+
return this.be.undefined;
|
834
|
+
return this.not.be.undefined;
|
835
|
+
});
|
836
|
+
def("toBeTypeOf", function(expected) {
|
837
|
+
const actual = typeof this._obj;
|
838
|
+
const equal = expected === actual;
|
839
|
+
return this.assert(
|
840
|
+
equal,
|
841
|
+
"expected #{this} to be type of #{exp}",
|
842
|
+
"expected #{this} not to be type of #{exp}",
|
843
|
+
expected,
|
844
|
+
actual
|
845
|
+
);
|
846
|
+
});
|
847
|
+
def("toBeInstanceOf", function(obj) {
|
848
|
+
return this.instanceOf(obj);
|
849
|
+
});
|
850
|
+
def("toHaveLength", function(length) {
|
851
|
+
return this.have.length(length);
|
852
|
+
});
|
853
|
+
def("toHaveProperty", function(...args) {
|
854
|
+
if (Array.isArray(args[0]))
|
855
|
+
args[0] = args[0].map((key) => key.replace(/([.[\]])/g, "\\$1")).join(".");
|
856
|
+
const actual = this._obj;
|
857
|
+
const [propertyName, expected] = args;
|
858
|
+
const getValue = () => {
|
859
|
+
const hasOwn = Object.prototype.hasOwnProperty.call(actual, propertyName);
|
860
|
+
if (hasOwn)
|
861
|
+
return { value: actual[propertyName], exists: true };
|
862
|
+
return utils.getPathInfo(actual, propertyName);
|
863
|
+
};
|
864
|
+
const { value, exists } = getValue();
|
865
|
+
const pass = exists && (args.length === 1 || equals(expected, value));
|
866
|
+
const valueString = args.length === 1 ? "" : ` with value ${utils.objDisplay(expected)}`;
|
867
|
+
return this.assert(
|
868
|
+
pass,
|
869
|
+
`expected #{this} to have property "${propertyName}"${valueString}`,
|
870
|
+
`expected #{this} to not have property "${propertyName}"${valueString}`,
|
871
|
+
actual
|
872
|
+
);
|
873
|
+
});
|
874
|
+
def("toBeCloseTo", function(received, precision = 2) {
|
875
|
+
const expected = this._obj;
|
876
|
+
let pass = false;
|
877
|
+
let expectedDiff = 0;
|
878
|
+
let receivedDiff = 0;
|
879
|
+
if (received === Infinity && expected === Infinity) {
|
880
|
+
pass = true;
|
881
|
+
} else if (received === -Infinity && expected === -Infinity) {
|
882
|
+
pass = true;
|
883
|
+
} else {
|
884
|
+
expectedDiff = 10 ** -precision / 2;
|
885
|
+
receivedDiff = Math.abs(expected - received);
|
886
|
+
pass = receivedDiff < expectedDiff;
|
887
|
+
}
|
888
|
+
return this.assert(
|
889
|
+
pass,
|
890
|
+
`expected #{this} to be close to #{exp}, received difference is ${receivedDiff}, but expected ${expectedDiff}`,
|
891
|
+
`expected #{this} to not be close to #{exp}, received difference is ${receivedDiff}, but expected ${expectedDiff}`,
|
892
|
+
received,
|
893
|
+
expected
|
894
|
+
);
|
895
|
+
});
|
896
|
+
const assertIsMock = (assertion) => {
|
897
|
+
if (!isMockFunction(assertion._obj))
|
898
|
+
throw new TypeError(`${utils.inspect(assertion._obj)} is not a spy or a call to a spy!`);
|
899
|
+
};
|
900
|
+
const getSpy = (assertion) => {
|
901
|
+
assertIsMock(assertion);
|
902
|
+
return assertion._obj;
|
903
|
+
};
|
904
|
+
const ordinalOf = (i) => {
|
905
|
+
const j = i % 10;
|
906
|
+
const k = i % 100;
|
907
|
+
if (j === 1 && k !== 11)
|
908
|
+
return `${i}st`;
|
909
|
+
if (j === 2 && k !== 12)
|
910
|
+
return `${i}nd`;
|
911
|
+
if (j === 3 && k !== 13)
|
912
|
+
return `${i}rd`;
|
913
|
+
return `${i}th`;
|
914
|
+
};
|
915
|
+
const formatCalls = (spy, msg, actualCall) => {
|
916
|
+
msg += c.gray(`
|
917
|
+
|
918
|
+
Received:
|
919
|
+
${spy.mock.calls.map((callArg, i) => {
|
920
|
+
let methodCall = c.bold(` ${ordinalOf(i + 1)} ${spy.getMockName()} call:
|
921
|
+
|
922
|
+
`);
|
923
|
+
if (actualCall)
|
924
|
+
methodCall += unifiedDiff(stringify(callArg), stringify(actualCall), { showLegend: false });
|
925
|
+
else
|
926
|
+
methodCall += stringify(callArg).split("\n").map((line) => ` ${line}`).join("\n");
|
927
|
+
methodCall += "\n";
|
928
|
+
return methodCall;
|
929
|
+
}).join("\n")}`);
|
930
|
+
msg += c.gray(`
|
931
|
+
|
932
|
+
Number of calls: ${c.bold(spy.mock.calls.length)}
|
933
|
+
`);
|
934
|
+
return msg;
|
935
|
+
};
|
936
|
+
const formatReturns = (spy, msg, actualReturn) => {
|
937
|
+
msg += c.gray(`
|
938
|
+
|
939
|
+
Received:
|
940
|
+
${spy.mock.results.map((callReturn, i) => {
|
941
|
+
let methodCall = c.bold(` ${ordinalOf(i + 1)} ${spy.getMockName()} call return:
|
942
|
+
|
943
|
+
`);
|
944
|
+
if (actualReturn)
|
945
|
+
methodCall += unifiedDiff(stringify(callReturn.value), stringify(actualReturn), { showLegend: false });
|
946
|
+
else
|
947
|
+
methodCall += stringify(callReturn).split("\n").map((line) => ` ${line}`).join("\n");
|
948
|
+
methodCall += "\n";
|
949
|
+
return methodCall;
|
950
|
+
}).join("\n")}`);
|
951
|
+
msg += c.gray(`
|
952
|
+
|
953
|
+
Number of calls: ${c.bold(spy.mock.calls.length)}
|
954
|
+
`);
|
955
|
+
return msg;
|
956
|
+
};
|
957
|
+
def(["toHaveBeenCalledTimes", "toBeCalledTimes"], function(number) {
|
958
|
+
const spy = getSpy(this);
|
959
|
+
const spyName = spy.getMockName();
|
960
|
+
const callCount = spy.mock.calls.length;
|
961
|
+
return this.assert(
|
962
|
+
callCount === number,
|
963
|
+
`expected "${spyName}" to be called #{exp} times`,
|
964
|
+
`expected "${spyName}" to not be called #{exp} times`,
|
965
|
+
number,
|
966
|
+
callCount
|
967
|
+
);
|
968
|
+
});
|
969
|
+
def("toHaveBeenCalledOnce", function() {
|
970
|
+
const spy = getSpy(this);
|
971
|
+
const spyName = spy.getMockName();
|
972
|
+
const callCount = spy.mock.calls.length;
|
973
|
+
return this.assert(
|
974
|
+
callCount === 1,
|
975
|
+
`expected "${spyName}" to be called once`,
|
976
|
+
`expected "${spyName}" to not be called once`,
|
977
|
+
1,
|
978
|
+
callCount
|
979
|
+
);
|
980
|
+
});
|
981
|
+
def(["toHaveBeenCalled", "toBeCalled"], function() {
|
982
|
+
const spy = getSpy(this);
|
983
|
+
const spyName = spy.getMockName();
|
984
|
+
const called = spy.mock.calls.length > 0;
|
985
|
+
const isNot = utils.flag(this, "negate");
|
986
|
+
let msg = utils.getMessage(
|
987
|
+
this,
|
988
|
+
[
|
989
|
+
called,
|
990
|
+
`expected "${spyName}" to be called at least once`,
|
991
|
+
`expected "${spyName}" to not be called at all`,
|
992
|
+
true,
|
993
|
+
called
|
994
|
+
]
|
995
|
+
);
|
996
|
+
if (called && isNot)
|
997
|
+
msg += formatCalls(spy, msg);
|
998
|
+
if (called && isNot || !called && !isNot) {
|
999
|
+
const err = new Error(msg);
|
1000
|
+
err.name = "AssertionError";
|
1001
|
+
throw err;
|
1002
|
+
}
|
1003
|
+
});
|
1004
|
+
def(["toHaveBeenCalledWith", "toBeCalledWith"], function(...args) {
|
1005
|
+
const spy = getSpy(this);
|
1006
|
+
const spyName = spy.getMockName();
|
1007
|
+
const pass = spy.mock.calls.some((callArg) => equals(callArg, args, [iterableEquality]));
|
1008
|
+
const isNot = utils.flag(this, "negate");
|
1009
|
+
let msg = utils.getMessage(
|
1010
|
+
this,
|
1011
|
+
[
|
1012
|
+
pass,
|
1013
|
+
`expected "${spyName}" to be called with arguments: #{exp}`,
|
1014
|
+
`expected "${spyName}" to not be called with arguments: #{exp}`,
|
1015
|
+
args
|
1016
|
+
]
|
1017
|
+
);
|
1018
|
+
if (pass && isNot || !pass && !isNot) {
|
1019
|
+
msg += formatCalls(spy, msg, args);
|
1020
|
+
const err = new Error(msg);
|
1021
|
+
err.name = "AssertionError";
|
1022
|
+
throw err;
|
1023
|
+
}
|
1024
|
+
});
|
1025
|
+
def(["toHaveBeenNthCalledWith", "nthCalledWith"], function(times, ...args) {
|
1026
|
+
const spy = getSpy(this);
|
1027
|
+
const spyName = spy.getMockName();
|
1028
|
+
const nthCall = spy.mock.calls[times - 1];
|
1029
|
+
this.assert(
|
1030
|
+
equals(nthCall, args, [iterableEquality]),
|
1031
|
+
`expected ${ordinalOf(times)} "${spyName}" call to have been called with #{exp}`,
|
1032
|
+
`expected ${ordinalOf(times)} "${spyName}" call to not have been called with #{exp}`,
|
1033
|
+
args,
|
1034
|
+
nthCall
|
1035
|
+
);
|
1036
|
+
});
|
1037
|
+
def(["toHaveBeenLastCalledWith", "lastCalledWith"], function(...args) {
|
1038
|
+
const spy = getSpy(this);
|
1039
|
+
const spyName = spy.getMockName();
|
1040
|
+
const lastCall = spy.mock.calls[spy.calls.length - 1];
|
1041
|
+
this.assert(
|
1042
|
+
equals(lastCall, args, [iterableEquality]),
|
1043
|
+
`expected last "${spyName}" call to have been called with #{exp}`,
|
1044
|
+
`expected last "${spyName}" call to not have been called with #{exp}`,
|
1045
|
+
args,
|
1046
|
+
lastCall
|
1047
|
+
);
|
1048
|
+
});
|
1049
|
+
def(["toThrow", "toThrowError"], function(expected) {
|
1050
|
+
if (typeof expected === "string" || typeof expected === "undefined" || expected instanceof RegExp)
|
1051
|
+
return this.throws(expected);
|
1052
|
+
const obj = this._obj;
|
1053
|
+
const promise = utils.flag(this, "promise");
|
1054
|
+
const isNot = utils.flag(this, "negate");
|
1055
|
+
let thrown = null;
|
1056
|
+
if (promise === "rejects") {
|
1057
|
+
thrown = obj;
|
1058
|
+
} else if (promise === "resolves" && typeof obj !== "function") {
|
1059
|
+
if (!isNot) {
|
1060
|
+
const message = utils.flag(this, "message") || "expected promise to throw an error, but it didn't";
|
1061
|
+
const error = {
|
1062
|
+
showDiff: false
|
1063
|
+
};
|
1064
|
+
throw new AssertionError(message, error, utils.flag(this, "ssfi"));
|
1065
|
+
} else {
|
1066
|
+
return;
|
1067
|
+
}
|
1068
|
+
} else {
|
1069
|
+
try {
|
1070
|
+
obj();
|
1071
|
+
} catch (err) {
|
1072
|
+
thrown = err;
|
1073
|
+
}
|
1074
|
+
}
|
1075
|
+
if (typeof expected === "function") {
|
1076
|
+
const name = expected.name || expected.prototype.constructor.name;
|
1077
|
+
return this.assert(
|
1078
|
+
thrown && thrown instanceof expected,
|
1079
|
+
`expected error to be instance of ${name}`,
|
1080
|
+
`expected error not to be instance of ${name}`,
|
1081
|
+
expected,
|
1082
|
+
thrown
|
1083
|
+
);
|
1084
|
+
}
|
1085
|
+
if (expected instanceof Error) {
|
1086
|
+
return this.assert(
|
1087
|
+
thrown && expected.message === thrown.message,
|
1088
|
+
`expected error to have message: ${expected.message}`,
|
1089
|
+
`expected error not to have message: ${expected.message}`,
|
1090
|
+
expected.message,
|
1091
|
+
thrown && thrown.message
|
1092
|
+
);
|
1093
|
+
}
|
1094
|
+
if (typeof expected === "object" && "asymmetricMatch" in expected && typeof expected.asymmetricMatch === "function") {
|
1095
|
+
const matcher = expected;
|
1096
|
+
return this.assert(
|
1097
|
+
thrown && matcher.asymmetricMatch(thrown),
|
1098
|
+
"expected error to match asymmetric matcher",
|
1099
|
+
"expected error not to match asymmetric matcher",
|
1100
|
+
matcher.toString(),
|
1101
|
+
thrown
|
1102
|
+
);
|
1103
|
+
}
|
1104
|
+
throw new Error(`"toThrow" expects string, RegExp, function, Error instance or asymmetric matcher, got "${typeof expected}"`);
|
1105
|
+
});
|
1106
|
+
def(["toHaveReturned", "toReturn"], function() {
|
1107
|
+
const spy = getSpy(this);
|
1108
|
+
const spyName = spy.getMockName();
|
1109
|
+
const calledAndNotThrew = spy.mock.calls.length > 0 && !spy.mock.results.some(({ type }) => type === "throw");
|
1110
|
+
this.assert(
|
1111
|
+
calledAndNotThrew,
|
1112
|
+
`expected "${spyName}" to be successfully called at least once`,
|
1113
|
+
`expected "${spyName}" to not be successfully called`,
|
1114
|
+
calledAndNotThrew,
|
1115
|
+
!calledAndNotThrew
|
1116
|
+
);
|
1117
|
+
});
|
1118
|
+
def(["toHaveReturnedTimes", "toReturnTimes"], function(times) {
|
1119
|
+
const spy = getSpy(this);
|
1120
|
+
const spyName = spy.getMockName();
|
1121
|
+
const successfulReturns = spy.mock.results.reduce((success, { type }) => type === "throw" ? success : ++success, 0);
|
1122
|
+
this.assert(
|
1123
|
+
successfulReturns === times,
|
1124
|
+
`expected "${spyName}" to be successfully called ${times} times`,
|
1125
|
+
`expected "${spyName}" to not be successfully called ${times} times`,
|
1126
|
+
`expected number of returns: ${times}`,
|
1127
|
+
`received number of returns: ${successfulReturns}`
|
1128
|
+
);
|
1129
|
+
});
|
1130
|
+
def(["toHaveReturnedWith", "toReturnWith"], function(value) {
|
1131
|
+
const spy = getSpy(this);
|
1132
|
+
const spyName = spy.getMockName();
|
1133
|
+
const pass = spy.mock.results.some(({ type, value: result }) => type === "return" && equals(value, result));
|
1134
|
+
const isNot = utils.flag(this, "negate");
|
1135
|
+
let msg = utils.getMessage(
|
1136
|
+
this,
|
1137
|
+
[
|
1138
|
+
pass,
|
1139
|
+
`expected "${spyName}" to return with: #{exp} at least once`,
|
1140
|
+
`expected "${spyName}" to not return with: #{exp}`,
|
1141
|
+
value
|
1142
|
+
]
|
1143
|
+
);
|
1144
|
+
if (pass && isNot || !pass && !isNot) {
|
1145
|
+
msg = formatReturns(spy, msg, value);
|
1146
|
+
const err = new Error(msg);
|
1147
|
+
err.name = "AssertionError";
|
1148
|
+
throw err;
|
1149
|
+
}
|
1150
|
+
});
|
1151
|
+
def(["toHaveLastReturnedWith", "lastReturnedWith"], function(value) {
|
1152
|
+
const spy = getSpy(this);
|
1153
|
+
const spyName = spy.getMockName();
|
1154
|
+
const { value: lastResult } = spy.mock.results[spy.returns.length - 1];
|
1155
|
+
const pass = equals(lastResult, value);
|
1156
|
+
this.assert(
|
1157
|
+
pass,
|
1158
|
+
`expected last "${spyName}" call to return #{exp}`,
|
1159
|
+
`expected last "${spyName}" call to not return #{exp}`,
|
1160
|
+
value,
|
1161
|
+
lastResult
|
1162
|
+
);
|
1163
|
+
});
|
1164
|
+
def(["toHaveNthReturnedWith", "nthReturnedWith"], function(nthCall, value) {
|
1165
|
+
const spy = getSpy(this);
|
1166
|
+
const spyName = spy.getMockName();
|
1167
|
+
const isNot = utils.flag(this, "negate");
|
1168
|
+
const { type: callType, value: callResult } = spy.mock.results[nthCall - 1];
|
1169
|
+
const ordinalCall = `${ordinalOf(nthCall)} call`;
|
1170
|
+
if (!isNot && callType === "throw")
|
1171
|
+
chai.assert.fail(`expected ${ordinalCall} to return #{exp}, but instead it threw an error`);
|
1172
|
+
const nthCallReturn = equals(callResult, value);
|
1173
|
+
this.assert(
|
1174
|
+
nthCallReturn,
|
1175
|
+
`expected ${ordinalCall} "${spyName}" call to return #{exp}`,
|
1176
|
+
`expected ${ordinalCall} "${spyName}" call to not return #{exp}`,
|
1177
|
+
value,
|
1178
|
+
callResult
|
1179
|
+
);
|
1180
|
+
});
|
1181
|
+
def("toSatisfy", function(matcher, message) {
|
1182
|
+
return this.be.satisfy(matcher, message);
|
1183
|
+
});
|
1184
|
+
utils.addProperty(chai.Assertion.prototype, "resolves", function __VITEST_RESOLVES__() {
|
1185
|
+
utils.flag(this, "promise", "resolves");
|
1186
|
+
utils.flag(this, "error", new Error("resolves"));
|
1187
|
+
const obj = utils.flag(this, "object");
|
1188
|
+
if (typeof (obj == null ? void 0 : obj.then) !== "function")
|
1189
|
+
throw new TypeError(`You must provide a Promise to expect() when using .resolves, not '${typeof obj}'.`);
|
1190
|
+
const proxy = new Proxy(this, {
|
1191
|
+
get: (target, key, receiver) => {
|
1192
|
+
const result = Reflect.get(target, key, receiver);
|
1193
|
+
if (typeof result !== "function")
|
1194
|
+
return result instanceof chai.Assertion ? proxy : result;
|
1195
|
+
return async (...args) => {
|
1196
|
+
return obj.then(
|
1197
|
+
(value) => {
|
1198
|
+
utils.flag(this, "object", value);
|
1199
|
+
return result.call(this, ...args);
|
1200
|
+
},
|
1201
|
+
(err) => {
|
1202
|
+
throw new Error(`promise rejected "${String(err)}" instead of resolving`);
|
1203
|
+
}
|
1204
|
+
);
|
1205
|
+
};
|
1206
|
+
}
|
1207
|
+
});
|
1208
|
+
return proxy;
|
1209
|
+
});
|
1210
|
+
utils.addProperty(chai.Assertion.prototype, "rejects", function __VITEST_REJECTS__() {
|
1211
|
+
utils.flag(this, "promise", "rejects");
|
1212
|
+
utils.flag(this, "error", new Error("rejects"));
|
1213
|
+
const obj = utils.flag(this, "object");
|
1214
|
+
const wrapper = typeof obj === "function" ? obj() : obj;
|
1215
|
+
if (typeof (wrapper == null ? void 0 : wrapper.then) !== "function")
|
1216
|
+
throw new TypeError(`You must provide a Promise to expect() when using .rejects, not '${typeof wrapper}'.`);
|
1217
|
+
const proxy = new Proxy(this, {
|
1218
|
+
get: (target, key, receiver) => {
|
1219
|
+
const result = Reflect.get(target, key, receiver);
|
1220
|
+
if (typeof result !== "function")
|
1221
|
+
return result instanceof chai.Assertion ? proxy : result;
|
1222
|
+
return async (...args) => {
|
1223
|
+
return wrapper.then(
|
1224
|
+
(value) => {
|
1225
|
+
throw new Error(`promise resolved "${String(value)}" instead of rejecting`);
|
1226
|
+
},
|
1227
|
+
(err) => {
|
1228
|
+
utils.flag(this, "object", err);
|
1229
|
+
return result.call(this, ...args);
|
1230
|
+
}
|
1231
|
+
);
|
1232
|
+
};
|
1233
|
+
}
|
1234
|
+
});
|
1235
|
+
return proxy;
|
1236
|
+
});
|
1237
|
+
};
|
1238
|
+
|
1239
|
+
const isAsyncFunction = (fn) => typeof fn === "function" && fn[Symbol.toStringTag] === "AsyncFunction";
|
1240
|
+
const getMatcherState = (assertion, expect) => {
|
1241
|
+
const obj = assertion._obj;
|
1242
|
+
const isNot = util.flag(assertion, "negate");
|
1243
|
+
const promise = util.flag(assertion, "promise") || "";
|
1244
|
+
const jestUtils = {
|
1245
|
+
...matcherUtils,
|
1246
|
+
iterableEquality,
|
1247
|
+
subsetEquality
|
1248
|
+
};
|
1249
|
+
const matcherState = {
|
1250
|
+
...getState(expect),
|
1251
|
+
isNot,
|
1252
|
+
utils: jestUtils,
|
1253
|
+
promise,
|
1254
|
+
equals,
|
1255
|
+
suppressedErrors: []
|
1256
|
+
};
|
1257
|
+
return {
|
1258
|
+
state: matcherState,
|
1259
|
+
isNot,
|
1260
|
+
obj
|
1261
|
+
};
|
1262
|
+
};
|
1263
|
+
class JestExtendError extends Error {
|
1264
|
+
constructor(message, actual, expected) {
|
1265
|
+
super(message);
|
1266
|
+
this.actual = actual;
|
1267
|
+
this.expected = expected;
|
1268
|
+
}
|
1269
|
+
}
|
1270
|
+
function JestExtendPlugin(expect, matchers) {
|
1271
|
+
return (c, utils) => {
|
1272
|
+
Object.entries(matchers).forEach(([expectAssertionName, expectAssertion]) => {
|
1273
|
+
function expectSyncWrapper(...args) {
|
1274
|
+
const { state, isNot, obj } = getMatcherState(this, expect);
|
1275
|
+
const { pass, message, actual, expected } = expectAssertion.call(state, obj, ...args);
|
1276
|
+
if (pass && isNot || !pass && !isNot)
|
1277
|
+
throw new JestExtendError(message(), actual, expected);
|
1278
|
+
}
|
1279
|
+
async function expectAsyncWrapper(...args) {
|
1280
|
+
const { state, isNot, obj } = getMatcherState(this, expect);
|
1281
|
+
const { pass, message, actual, expected } = await expectAssertion.call(state, obj, ...args);
|
1282
|
+
if (pass && isNot || !pass && !isNot)
|
1283
|
+
throw new JestExtendError(message(), actual, expected);
|
1284
|
+
}
|
1285
|
+
const expectAssertionWrapper = isAsyncFunction(expectAssertion) ? expectAsyncWrapper : expectSyncWrapper;
|
1286
|
+
utils.addMethod(globalThis[JEST_MATCHERS_OBJECT].matchers, expectAssertionName, expectAssertionWrapper);
|
1287
|
+
utils.addMethod(c.Assertion.prototype, expectAssertionName, expectAssertionWrapper);
|
1288
|
+
class CustomMatcher extends AsymmetricMatcher {
|
1289
|
+
constructor(inverse = false, ...sample) {
|
1290
|
+
super(sample, inverse);
|
1291
|
+
}
|
1292
|
+
asymmetricMatch(other) {
|
1293
|
+
const { pass } = expectAssertion.call(
|
1294
|
+
this.getMatcherContext(expect),
|
1295
|
+
other,
|
1296
|
+
...this.sample
|
1297
|
+
);
|
1298
|
+
return this.inverse ? !pass : pass;
|
1299
|
+
}
|
1300
|
+
toString() {
|
1301
|
+
return `${this.inverse ? "not." : ""}${expectAssertionName}`;
|
1302
|
+
}
|
1303
|
+
getExpectedType() {
|
1304
|
+
return "any";
|
1305
|
+
}
|
1306
|
+
toAsymmetricMatcher() {
|
1307
|
+
return `${this.toString()}<${this.sample.map(String).join(", ")}>`;
|
1308
|
+
}
|
1309
|
+
}
|
1310
|
+
Object.defineProperty(expect, expectAssertionName, {
|
1311
|
+
configurable: true,
|
1312
|
+
enumerable: true,
|
1313
|
+
value: (...sample) => new CustomMatcher(false, ...sample),
|
1314
|
+
writable: true
|
1315
|
+
});
|
1316
|
+
Object.defineProperty(expect.not, expectAssertionName, {
|
1317
|
+
configurable: true,
|
1318
|
+
enumerable: true,
|
1319
|
+
value: (...sample) => new CustomMatcher(true, ...sample),
|
1320
|
+
writable: true
|
1321
|
+
});
|
1322
|
+
});
|
1323
|
+
};
|
1324
|
+
}
|
1325
|
+
const JestExtend = (chai, utils) => {
|
1326
|
+
utils.addMethod(chai.expect, "extend", (expect, expects) => {
|
1327
|
+
chai.use(JestExtendPlugin(expect, expects));
|
1328
|
+
});
|
1329
|
+
};
|
1330
|
+
|
1331
|
+
export { Any, Anything, ArrayContaining, AsymmetricMatcher, GLOBAL_EXPECT, JEST_MATCHERS_OBJECT, JestAsymmetricMatchers, JestChaiExpect, JestExtend, MATCHERS_OBJECT, ObjectContaining, StringContaining, StringMatching, arrayBufferEquality, equals, fnNameFor, generateToBeMessage, getState, hasAsymmetric, hasProperty, isA, isAsymmetric, isImmutableUnorderedKeyed, isImmutableUnorderedSet, iterableEquality, setState, sparseArrayEquality, subsetEquality, typeEquality };
|