@vitest/expect
Advanced tools
Comparing version 2.0.0-beta.10 to 2.0.0-beta.11
1134
dist/index.js
@@ -11,3 +11,5 @@ import { getType, getColors, stringify, isObject, assertTypes } from '@vitest/utils'; | ||
const GLOBAL_EXPECT = Symbol.for("expect-global"); | ||
const ASYMMETRIC_MATCHERS_OBJECT = Symbol.for("asymmetric-matchers-object"); | ||
const ASYMMETRIC_MATCHERS_OBJECT = Symbol.for( | ||
"asymmetric-matchers-object" | ||
); | ||
@@ -87,10 +89,13 @@ if (!Object.prototype.hasOwnProperty.call(globalThis, MATCHERS_OBJECT)) { | ||
hint += DIM_COLOR(`${dimString}(`) + expectedColor(expected); | ||
if (secondArgument) | ||
if (secondArgument) { | ||
hint += DIM_COLOR(", ") + secondArgumentColor(secondArgument); | ||
} | ||
dimString = ")"; | ||
} | ||
if (comment !== "") | ||
if (comment !== "") { | ||
dimString += ` // ${comment}`; | ||
if (dimString !== "") | ||
} | ||
if (dimString !== "") { | ||
hint += DIM_COLOR(dimString); | ||
} | ||
return hint; | ||
@@ -138,13 +143,18 @@ } | ||
function hasAsymmetric(obj, seen = /* @__PURE__ */ new Set()) { | ||
if (seen.has(obj)) | ||
if (seen.has(obj)) { | ||
return false; | ||
} | ||
seen.add(obj); | ||
if (isAsymmetric(obj)) | ||
if (isAsymmetric(obj)) { | ||
return true; | ||
if (Array.isArray(obj)) | ||
} | ||
if (Array.isArray(obj)) { | ||
return obj.some((i) => hasAsymmetric(i, seen)); | ||
if (obj instanceof Set) | ||
} | ||
if (obj instanceof Set) { | ||
return Array.from(obj).some((i) => hasAsymmetric(i, seen)); | ||
if (isObject(obj)) | ||
} | ||
if (isObject(obj)) { | ||
return Object.values(obj).some((v) => hasAsymmetric(v, seen)); | ||
} | ||
return false; | ||
@@ -155,8 +165,11 @@ } | ||
const asymmetricB = isAsymmetric(b); | ||
if (asymmetricA && asymmetricB) | ||
if (asymmetricA && asymmetricB) { | ||
return void 0; | ||
if (asymmetricA) | ||
} | ||
if (asymmetricA) { | ||
return a.asymmetricMatch(b); | ||
if (asymmetricB) | ||
} | ||
if (asymmetricB) { | ||
return b.asymmetricMatch(a); | ||
} | ||
} | ||
@@ -166,21 +179,33 @@ function eq(a, b, aStack, bStack, customTesters, hasKey2) { | ||
const asymmetricResult = asymmetricMatch(a, b); | ||
if (asymmetricResult !== void 0) | ||
if (asymmetricResult !== void 0) { | ||
return asymmetricResult; | ||
} | ||
const testerContext = { equals }; | ||
for (let i = 0; i < customTesters.length; i++) { | ||
const customTesterResult = customTesters[i].call(testerContext, a, b, customTesters); | ||
if (customTesterResult !== void 0) | ||
const customTesterResult = customTesters[i].call( | ||
testerContext, | ||
a, | ||
b, | ||
customTesters | ||
); | ||
if (customTesterResult !== void 0) { | ||
return customTesterResult; | ||
} | ||
} | ||
if (a instanceof Error && b instanceof Error) | ||
if (a instanceof Error && b instanceof Error) { | ||
return a.message === b.message; | ||
if (typeof URL === "function" && a instanceof URL && b instanceof URL) | ||
} | ||
if (typeof URL === "function" && a instanceof URL && b instanceof URL) { | ||
return a.href === b.href; | ||
if (Object.is(a, b)) | ||
} | ||
if (Object.is(a, b)) { | ||
return true; | ||
if (a === null || b === null) | ||
} | ||
if (a === null || b === null) { | ||
return a === b; | ||
} | ||
const className = Object.prototype.toString.call(a); | ||
if (className !== Object.prototype.toString.call(b)) | ||
if (className !== Object.prototype.toString.call(b)) { | ||
return false; | ||
} | ||
switch (className) { | ||
@@ -205,27 +230,33 @@ case "[object Boolean]": | ||
} | ||
if (typeof a !== "object" || typeof b !== "object") | ||
if (typeof a !== "object" || typeof b !== "object") { | ||
return false; | ||
if (isDomNode(a) && isDomNode(b)) | ||
} | ||
if (isDomNode(a) && isDomNode(b)) { | ||
return a.isEqualNode(b); | ||
} | ||
let length = aStack.length; | ||
while (length--) { | ||
if (aStack[length] === a) | ||
if (aStack[length] === a) { | ||
return bStack[length] === b; | ||
else if (bStack[length] === b) | ||
} else if (bStack[length] === b) { | ||
return false; | ||
} | ||
} | ||
aStack.push(a); | ||
bStack.push(b); | ||
if (className === "[object Array]" && a.length !== b.length) | ||
if (className === "[object Array]" && a.length !== b.length) { | ||
return false; | ||
} | ||
const aKeys = keys(a, hasKey2); | ||
let key; | ||
let size = aKeys.length; | ||
if (keys(b, hasKey2).length !== size) | ||
if (keys(b, hasKey2).length !== size) { | ||
return false; | ||
} | ||
while (size--) { | ||
key = aKeys[size]; | ||
result = hasKey2(b, key) && eq(a[key], b[key], aStack, bStack, customTesters, hasKey2); | ||
if (!result) | ||
if (!result) { | ||
return false; | ||
} | ||
} | ||
@@ -239,4 +270,5 @@ aStack.pop(); | ||
for (const key in obj) { | ||
if (hasKey2(obj, key)) | ||
if (hasKey2(obj, key)) { | ||
keys2.push(key); | ||
} | ||
} | ||
@@ -262,4 +294,5 @@ return keys2.concat( | ||
function fnNameFor(func) { | ||
if (func.name) | ||
if (func.name) { | ||
return func.name; | ||
} | ||
const matches = functionToString.call(func).match(/^(?:async)?\s*function\s*(?:\*\s*)?([\w$]+)\s*\(/); | ||
@@ -269,13 +302,17 @@ return matches ? matches[1] : "<anonymous>"; | ||
function getPrototype(obj) { | ||
if (Object.getPrototypeOf) | ||
if (Object.getPrototypeOf) { | ||
return Object.getPrototypeOf(obj); | ||
if (obj.constructor.prototype === obj) | ||
} | ||
if (obj.constructor.prototype === obj) { | ||
return null; | ||
} | ||
return obj.constructor.prototype; | ||
} | ||
function hasProperty(obj, property) { | ||
if (!obj) | ||
if (!obj) { | ||
return false; | ||
if (Object.prototype.hasOwnProperty.call(obj, property)) | ||
} | ||
if (Object.prototype.hasOwnProperty.call(obj, property)) { | ||
return true; | ||
} | ||
return hasProperty(getPrototype(obj), property); | ||
@@ -321,8 +358,10 @@ } | ||
} | ||
if (a.constructor !== b.constructor) | ||
if (a.constructor !== b.constructor) { | ||
return false; | ||
} | ||
let length = aStack.length; | ||
while (length--) { | ||
if (aStack[length] === a) | ||
if (aStack[length] === a) { | ||
return bStack[length] === b; | ||
} | ||
} | ||
@@ -336,9 +375,3 @@ aStack.push(a); | ||
function iterableEqualityWithStack(a2, b2) { | ||
return iterableEquality( | ||
a2, | ||
b2, | ||
[...customTesters], | ||
[...aStack], | ||
[...bStack] | ||
); | ||
return iterableEquality(a2, b2, [...customTesters], [...aStack], [...bStack]); | ||
} | ||
@@ -355,4 +388,5 @@ if (a.size !== void 0) { | ||
const isEqual = equals(aValue, bValue, filteredCustomTesters); | ||
if (isEqual === true) | ||
if (isEqual === true) { | ||
has = true; | ||
} | ||
} | ||
@@ -374,8 +408,18 @@ if (has === false) { | ||
for (const bEntry of b) { | ||
const matchedKey = equals(aEntry[0], bEntry[0], filteredCustomTesters); | ||
const matchedKey = equals( | ||
aEntry[0], | ||
bEntry[0], | ||
filteredCustomTesters | ||
); | ||
let matchedValue = false; | ||
if (matchedKey === true) | ||
matchedValue = equals(aEntry[1], bEntry[1], filteredCustomTesters); | ||
if (matchedValue === true) | ||
if (matchedKey === true) { | ||
matchedValue = equals( | ||
aEntry[1], | ||
bEntry[1], | ||
filteredCustomTesters | ||
); | ||
} | ||
if (matchedValue === true) { | ||
has = true; | ||
} | ||
} | ||
@@ -400,9 +444,11 @@ if (has === false) { | ||
} | ||
if (!bIterator.next().done) | ||
if (!bIterator.next().done) { | ||
return false; | ||
} | ||
if (!isImmutableList(a) && !isImmutableOrderedKeyed(a) && !isImmutableOrderedSet(a) && !isImmutableRecord(a)) { | ||
const aEntries = Object.entries(a); | ||
const bEntries = Object.entries(b); | ||
if (!equals(aEntries, bEntries)) | ||
if (!equals(aEntries, bEntries)) { | ||
return false; | ||
} | ||
} | ||
@@ -415,4 +461,5 @@ aStack.pop(); | ||
const shouldTerminate = !object || typeof object !== "object" || object === Object.prototype; | ||
if (shouldTerminate) | ||
if (shouldTerminate) { | ||
return false; | ||
} | ||
return Object.prototype.hasOwnProperty.call(object, key) || hasPropertyInObject(Object.getPrototypeOf(object), key); | ||
@@ -424,10 +471,14 @@ } | ||
function subsetEquality(object, subset, customTesters = []) { | ||
const filteredCustomTesters = customTesters.filter((t) => t !== subsetEquality); | ||
const filteredCustomTesters = customTesters.filter( | ||
(t) => t !== subsetEquality | ||
); | ||
const subsetEqualityWithContext = (seenReferences = /* @__PURE__ */ new WeakMap()) => (object2, subset2) => { | ||
if (!isObjectWithKeys(subset2)) | ||
if (!isObjectWithKeys(subset2)) { | ||
return void 0; | ||
} | ||
return Object.keys(subset2).every((key) => { | ||
if (subset2[key] != null && typeof subset2[key] === "object") { | ||
if (seenReferences.has(subset2[key])) | ||
if (seenReferences.has(subset2[key])) { | ||
return equals(object2[key], subset2[key], filteredCustomTesters); | ||
} | ||
seenReferences.set(subset2[key], true); | ||
@@ -446,4 +497,5 @@ } | ||
function typeEquality(a, b) { | ||
if (a == null || b == null || a.constructor === b.constructor) | ||
if (a == null || b == null || a.constructor === b.constructor) { | ||
return void 0; | ||
} | ||
return false; | ||
@@ -455,4 +507,5 @@ } | ||
if (!(a instanceof DataView && b instanceof DataView)) { | ||
if (!(a instanceof ArrayBuffer) || !(b instanceof ArrayBuffer)) | ||
if (!(a instanceof ArrayBuffer) || !(b instanceof ArrayBuffer)) { | ||
return void 0; | ||
} | ||
try { | ||
@@ -465,7 +518,9 @@ dataViewA = new DataView(a); | ||
} | ||
if (dataViewA.byteLength !== dataViewB.byteLength) | ||
if (dataViewA.byteLength !== dataViewB.byteLength) { | ||
return false; | ||
} | ||
for (let i = 0; i < dataViewA.byteLength; i++) { | ||
if (dataViewA.getUint8(i) !== dataViewB.getUint8(i)) | ||
if (dataViewA.getUint8(i) !== dataViewB.getUint8(i)) { | ||
return false; | ||
} | ||
} | ||
@@ -475,7 +530,10 @@ return true; | ||
function sparseArrayEquality(a, b, customTesters = []) { | ||
if (!Array.isArray(a) || !Array.isArray(b)) | ||
if (!Array.isArray(a) || !Array.isArray(b)) { | ||
return void 0; | ||
} | ||
const aKeys = Object.keys(a); | ||
const bKeys = Object.keys(b); | ||
const filteredCustomTesters = customTesters.filter((t) => t !== sparseArrayEquality); | ||
const filteredCustomTesters = customTesters.filter( | ||
(t) => t !== sparseArrayEquality | ||
); | ||
return equals(a, b, filteredCustomTesters, true) && equals(aKeys, bKeys); | ||
@@ -485,3 +543,3 @@ } | ||
const toBeMessage = `expected ${expected} to be ${actual} // Object.is equality`; | ||
if (["toStrictEqual", "toEqual"].includes(deepEqualityName)) | ||
if (["toStrictEqual", "toEqual"].includes(deepEqualityName)) { | ||
return `${toBeMessage} | ||
@@ -494,2 +552,3 @@ | ||
`; | ||
} | ||
return toBeMessage; | ||
@@ -534,14 +593,22 @@ } | ||
if (hasPropertyInObject(subset2, key)) { | ||
trimmed[key] = seenReferences.has(object2[key]) ? seenReferences.get(object2[key]) : getObjectSubsetWithContext(seenReferences)(object2[key], subset2[key]); | ||
trimmed[key] = seenReferences.has(object2[key]) ? seenReferences.get(object2[key]) : getObjectSubsetWithContext(seenReferences)( | ||
object2[key], | ||
subset2[key] | ||
); | ||
} else { | ||
if (!seenReferences.has(object2[key])) { | ||
stripped += 1; | ||
if (isObject(object2[key])) | ||
if (isObject(object2[key])) { | ||
stripped += getObjectKeys(object2[key]).length; | ||
getObjectSubsetWithContext(seenReferences)(object2[key], subset2[key]); | ||
} | ||
getObjectSubsetWithContext(seenReferences)( | ||
object2[key], | ||
subset2[key] | ||
); | ||
} | ||
} | ||
} | ||
if (getObjectKeys(trimmed).length > 0) | ||
if (getObjectKeys(trimmed).length > 0) { | ||
return trimmed; | ||
} | ||
} | ||
@@ -579,4 +646,5 @@ return object2; | ||
const result = stringify(this, options.depth, { min: true }); | ||
if (result.length <= options.truncate) | ||
if (result.length <= options.truncate) { | ||
return result; | ||
} | ||
return `${this.toString()}{\u2026}`; | ||
@@ -587,4 +655,5 @@ } | ||
constructor(sample, inverse = false) { | ||
if (!isA("String", sample)) | ||
if (!isA("String", sample)) { | ||
throw new Error("Expected is not a string"); | ||
} | ||
super(sample, inverse); | ||
@@ -619,13 +688,17 @@ } | ||
getPrototype(obj) { | ||
if (Object.getPrototypeOf) | ||
if (Object.getPrototypeOf) { | ||
return Object.getPrototypeOf(obj); | ||
if (obj.constructor.prototype === obj) | ||
} | ||
if (obj.constructor.prototype === obj) { | ||
return null; | ||
} | ||
return obj.constructor.prototype; | ||
} | ||
hasProperty(obj, property) { | ||
if (!obj) | ||
if (!obj) { | ||
return false; | ||
if (Object.prototype.hasOwnProperty.call(obj, property)) | ||
} | ||
if (Object.prototype.hasOwnProperty.call(obj, property)) { | ||
return true; | ||
} | ||
return this.hasProperty(this.getPrototype(obj), property); | ||
@@ -642,3 +715,7 @@ } | ||
for (const property in this.sample) { | ||
if (!this.hasProperty(other, property) || !equals(this.sample[property], other[property], matcherContext.customTesters)) { | ||
if (!this.hasProperty(other, property) || !equals( | ||
this.sample[property], | ||
other[property], | ||
matcherContext.customTesters | ||
)) { | ||
result = false; | ||
@@ -669,3 +746,5 @@ break; | ||
const result = this.sample.length === 0 || Array.isArray(other) && this.sample.every( | ||
(item) => other.some((another) => equals(item, another, matcherContext.customTesters)) | ||
(item) => other.some( | ||
(another) => equals(item, another, matcherContext.customTesters) | ||
) | ||
); | ||
@@ -691,4 +770,5 @@ return this.inverse ? !result : result; | ||
fnNameFor(func) { | ||
if (func.name) | ||
if (func.name) { | ||
return func.name; | ||
} | ||
const functionToString = Function.prototype.toString; | ||
@@ -699,16 +779,23 @@ const matches = functionToString.call(func).match(/^(?:async)?\s*function\s*(?:\*\s*)?([\w$]+)\s*\(/); | ||
asymmetricMatch(other) { | ||
if (this.sample === String) | ||
if (this.sample === String) { | ||
return typeof other == "string" || other instanceof String; | ||
if (this.sample === Number) | ||
} | ||
if (this.sample === Number) { | ||
return typeof other == "number" || other instanceof Number; | ||
if (this.sample === Function) | ||
} | ||
if (this.sample === Function) { | ||
return typeof other == "function" || other instanceof Function; | ||
if (this.sample === Boolean) | ||
} | ||
if (this.sample === Boolean) { | ||
return typeof other == "boolean" || other instanceof Boolean; | ||
if (this.sample === BigInt) | ||
} | ||
if (this.sample === BigInt) { | ||
return typeof other == "bigint" || other instanceof BigInt; | ||
if (this.sample === Symbol) | ||
} | ||
if (this.sample === Symbol) { | ||
return typeof other == "symbol" || other instanceof Symbol; | ||
if (this.sample === Object) | ||
} | ||
if (this.sample === Object) { | ||
return typeof other == "object"; | ||
} | ||
return other instanceof this.sample; | ||
@@ -720,12 +807,17 @@ } | ||
getExpectedType() { | ||
if (this.sample === String) | ||
if (this.sample === String) { | ||
return "string"; | ||
if (this.sample === Number) | ||
} | ||
if (this.sample === Number) { | ||
return "number"; | ||
if (this.sample === Function) | ||
} | ||
if (this.sample === Function) { | ||
return "function"; | ||
if (this.sample === Object) | ||
} | ||
if (this.sample === Object) { | ||
return "object"; | ||
if (this.sample === Boolean) | ||
} | ||
if (this.sample === Boolean) { | ||
return "boolean"; | ||
} | ||
return this.fnNameFor(this.sample); | ||
@@ -739,4 +831,5 @@ } | ||
constructor(sample, inverse = false) { | ||
if (!isA("String", sample) && !isA("RegExp", sample)) | ||
if (!isA("String", sample) && !isA("RegExp", sample)) { | ||
throw new Error("Expected is not a String or a RegExp"); | ||
} | ||
super(new RegExp(sample), inverse); | ||
@@ -758,6 +851,8 @@ } | ||
constructor(sample, precision = 2, inverse = false) { | ||
if (!isA("Number", sample)) | ||
if (!isA("Number", sample)) { | ||
throw new Error("Expected is not a Number"); | ||
if (!isA("Number", precision)) | ||
} | ||
if (!isA("Number", precision)) { | ||
throw new Error("Precision is not a Number"); | ||
} | ||
super(sample); | ||
@@ -768,4 +863,5 @@ this.inverse = inverse; | ||
asymmetricMatch(other) { | ||
if (!isA("Number", other)) | ||
if (!isA("Number", other)) { | ||
return false; | ||
} | ||
let result = false; | ||
@@ -796,14 +892,6 @@ if (other === Number.POSITIVE_INFINITY && this.sample === Number.POSITIVE_INFINITY) { | ||
const JestAsymmetricMatchers = (chai, utils) => { | ||
utils.addMethod(chai.expect, "anything", () => new Anything()); | ||
utils.addMethod(chai.expect, "any", (expected) => new Any(expected)); | ||
utils.addMethod( | ||
chai.expect, | ||
"anything", | ||
() => new Anything() | ||
); | ||
utils.addMethod( | ||
chai.expect, | ||
"any", | ||
(expected) => new Any(expected) | ||
); | ||
utils.addMethod( | ||
chai.expect, | ||
"stringContaining", | ||
@@ -845,7 +933,9 @@ (expected) => new StringContaining(expected) | ||
const index = test.promises.indexOf(promise); | ||
if (index !== -1) | ||
if (index !== -1) { | ||
test.promises.splice(index, 1); | ||
} | ||
}); | ||
if (!test.promises) | ||
if (!test.promises) { | ||
test.promises = []; | ||
} | ||
test.promises.push(promise); | ||
@@ -858,7 +948,9 @@ } | ||
var _a; | ||
if (!utils.flag(this, "soft")) | ||
if (!utils.flag(this, "soft")) { | ||
return fn.apply(this, args); | ||
} | ||
const test = utils.flag(this, "vitest-test"); | ||
if (!test) | ||
if (!test) { | ||
throw new Error("expect.soft() can only be used inside a test"); | ||
} | ||
try { | ||
@@ -883,8 +975,13 @@ return fn.apply(this, args); | ||
utils.addMethod(chai.Assertion.prototype, n, softWrapper); | ||
utils.addMethod(globalThis[JEST_MATCHERS_OBJECT].matchers, n, softWrapper); | ||
utils.addMethod( | ||
globalThis[JEST_MATCHERS_OBJECT].matchers, | ||
n, | ||
softWrapper | ||
); | ||
}; | ||
if (Array.isArray(name)) | ||
if (Array.isArray(name)) { | ||
name.forEach((n) => addMethod(n)); | ||
else | ||
} else { | ||
addMethod(name); | ||
} | ||
} | ||
@@ -922,7 +1019,6 @@ ["throw", "throws", "Throw"].forEach((m) => { | ||
const actual = utils.flag(this, "object"); | ||
const equal = equals( | ||
actual, | ||
expected, | ||
[...customTesters, iterableEquality] | ||
); | ||
const equal = equals(actual, expected, [ | ||
...customTesters, | ||
iterableEquality | ||
]); | ||
return this.assert( | ||
@@ -978,9 +1074,9 @@ equal, | ||
} else { | ||
const toEqualPass = equals( | ||
actual, | ||
expected, | ||
[...customTesters, iterableEquality] | ||
); | ||
if (toEqualPass) | ||
const toEqualPass = equals(actual, expected, [ | ||
...customTesters, | ||
iterableEquality | ||
]); | ||
if (toEqualPass) { | ||
deepEqualityName = "toEqual"; | ||
} | ||
} | ||
@@ -998,20 +1094,28 @@ } | ||
const actual = this._obj; | ||
const pass = equals(actual, expected, [...customTesters, iterableEquality, subsetEquality]); | ||
const pass = equals(actual, expected, [ | ||
...customTesters, | ||
iterableEquality, | ||
subsetEquality | ||
]); | ||
const isNot = utils.flag(this, "negate"); | ||
const { subset: actualSubset, stripped } = getObjectSubset(actual, expected); | ||
const { subset: actualSubset, stripped } = getObjectSubset( | ||
actual, | ||
expected | ||
); | ||
if (pass && isNot || !pass && !isNot) { | ||
const msg = utils.getMessage( | ||
this, | ||
[ | ||
pass, | ||
"expected #{this} to match object #{exp}", | ||
"expected #{this} to not match object #{exp}", | ||
expected, | ||
actualSubset, | ||
false | ||
] | ||
); | ||
const msg = utils.getMessage(this, [ | ||
pass, | ||
"expected #{this} to match object #{exp}", | ||
"expected #{this} to not match object #{exp}", | ||
expected, | ||
actualSubset, | ||
false | ||
]); | ||
const message = stripped === 0 ? msg : `${msg} | ||
(${stripped} matching ${stripped === 1 ? "property" : "properties"} omitted from actual)`; | ||
throw new AssertionError(message, { showDiff: true, expected, actual: actualSubset }); | ||
throw new AssertionError(message, { | ||
showDiff: true, | ||
expected, | ||
actual: actualSubset | ||
}); | ||
} | ||
@@ -1021,4 +1125,7 @@ }); | ||
const actual = this._obj; | ||
if (typeof actual !== "string") | ||
throw new TypeError(`.toMatch() expects to receive a string, but got ${typeof actual}`); | ||
if (typeof actual !== "string") { | ||
throw new TypeError( | ||
`.toMatch() expects to receive a string, but got ${typeof actual}` | ||
); | ||
} | ||
return this.assert( | ||
@@ -1035,4 +1142,7 @@ typeof expected === "string" ? actual.includes(expected) : actual.match(expected), | ||
if (typeof Node !== "undefined" && actual instanceof Node) { | ||
if (!(item instanceof Node)) | ||
throw new TypeError(`toContain() expected a DOM node as the argument, but got ${typeof item}`); | ||
if (!(item instanceof Node)) { | ||
throw new TypeError( | ||
`toContain() expected a DOM node as the argument, but got ${typeof item}` | ||
); | ||
} | ||
return this.assert( | ||
@@ -1067,4 +1177,5 @@ actual.contains(item), | ||
} | ||
if (actual != null && typeof actual !== "string") | ||
if (actual != null && typeof actual !== "string") { | ||
utils.flag(this, "object", Array.from(actual)); | ||
} | ||
return this.contain(item); | ||
@@ -1168,17 +1279,21 @@ }); | ||
utils.flag(this, "negate", false); | ||
if (negate) | ||
if (negate) { | ||
return this.be.undefined; | ||
} | ||
return this.not.be.undefined; | ||
}); | ||
def("toBeTypeOf", function(expected) { | ||
const actual = typeof this._obj; | ||
const equal = expected === actual; | ||
return this.assert( | ||
equal, | ||
"expected #{this} to be type of #{exp}", | ||
"expected #{this} not to be type of #{exp}", | ||
expected, | ||
actual | ||
); | ||
}); | ||
def( | ||
"toBeTypeOf", | ||
function(expected) { | ||
const actual = typeof this._obj; | ||
const equal = expected === actual; | ||
return this.assert( | ||
equal, | ||
"expected #{this} to be type of #{exp}", | ||
"expected #{this} not to be type of #{exp}", | ||
expected, | ||
actual | ||
); | ||
} | ||
); | ||
def("toBeInstanceOf", function(obj) { | ||
@@ -1190,24 +1305,32 @@ return this.instanceOf(obj); | ||
}); | ||
def("toHaveProperty", function(...args) { | ||
if (Array.isArray(args[0])) | ||
args[0] = args[0].map((key) => String(key).replace(/([.[\]])/g, "\\$1")).join("."); | ||
const actual = this._obj; | ||
const [propertyName, expected] = args; | ||
const getValue = () => { | ||
const hasOwn = Object.prototype.hasOwnProperty.call(actual, propertyName); | ||
if (hasOwn) | ||
return { value: actual[propertyName], exists: true }; | ||
return utils.getPathInfo(actual, propertyName); | ||
}; | ||
const { value, exists } = getValue(); | ||
const pass = exists && (args.length === 1 || equals(expected, value, customTesters)); | ||
const valueString = args.length === 1 ? "" : ` with value ${utils.objDisplay(expected)}`; | ||
return this.assert( | ||
pass, | ||
`expected #{this} to have property "${propertyName}"${valueString}`, | ||
`expected #{this} to not have property "${propertyName}"${valueString}`, | ||
expected, | ||
exists ? value : void 0 | ||
); | ||
}); | ||
def( | ||
"toHaveProperty", | ||
function(...args) { | ||
if (Array.isArray(args[0])) { | ||
args[0] = args[0].map((key) => String(key).replace(/([.[\]])/g, "\\$1")).join("."); | ||
} | ||
const actual = this._obj; | ||
const [propertyName, expected] = args; | ||
const getValue = () => { | ||
const hasOwn = Object.prototype.hasOwnProperty.call( | ||
actual, | ||
propertyName | ||
); | ||
if (hasOwn) { | ||
return { value: actual[propertyName], exists: true }; | ||
} | ||
return utils.getPathInfo(actual, propertyName); | ||
}; | ||
const { value, exists } = getValue(); | ||
const pass = exists && (args.length === 1 || equals(expected, value, customTesters)); | ||
const valueString = args.length === 1 ? "" : ` with value ${utils.objDisplay(expected)}`; | ||
return this.assert( | ||
pass, | ||
`expected #{this} to have property "${propertyName}"${valueString}`, | ||
`expected #{this} to not have property "${propertyName}"${valueString}`, | ||
expected, | ||
exists ? value : void 0 | ||
); | ||
} | ||
); | ||
def("toBeCloseTo", function(received, precision = 2) { | ||
@@ -1237,4 +1360,7 @@ const expected = this._obj; | ||
const assertIsMock = (assertion) => { | ||
if (!isMockFunction(assertion._obj)) | ||
throw new TypeError(`${utils.inspect(assertion._obj)} is not a spy or a call to a spy!`); | ||
if (!isMockFunction(assertion._obj)) { | ||
throw new TypeError( | ||
`${utils.inspect(assertion._obj)} is not a spy or a call to a spy!` | ||
); | ||
} | ||
}; | ||
@@ -1248,8 +1374,11 @@ const getSpy = (assertion) => { | ||
const k = i % 100; | ||
if (j === 1 && k !== 11) | ||
if (j === 1 && k !== 11) { | ||
return `${i}st`; | ||
if (j === 2 && k !== 12) | ||
} | ||
if (j === 2 && k !== 12) { | ||
return `${i}nd`; | ||
if (j === 3 && k !== 13) | ||
} | ||
if (j === 3 && k !== 13) { | ||
return `${i}rd`; | ||
} | ||
return `${i}th`; | ||
@@ -1259,3 +1388,4 @@ }; | ||
if (spy.mock.calls) { | ||
msg += c().gray(` | ||
msg += c().gray( | ||
` | ||
@@ -1265,21 +1395,30 @@ Received: | ||
${spy.mock.calls.map((callArg, i) => { | ||
let methodCall = c().bold(` ${ordinalOf(i + 1)} ${spy.getMockName()} call: | ||
let methodCall = c().bold( | ||
` ${ordinalOf(i + 1)} ${spy.getMockName()} call: | ||
`); | ||
if (showActualCall) | ||
methodCall += diff(showActualCall, callArg, { omitAnnotationLines: true }); | ||
else | ||
methodCall += stringify(callArg).split("\n").map((line) => ` ${line}`).join("\n"); | ||
methodCall += "\n"; | ||
return methodCall; | ||
}).join("\n")}`); | ||
` | ||
); | ||
if (showActualCall) { | ||
methodCall += diff(showActualCall, callArg, { | ||
omitAnnotationLines: true | ||
}); | ||
} else { | ||
methodCall += stringify(callArg).split("\n").map((line) => ` ${line}`).join("\n"); | ||
} | ||
methodCall += "\n"; | ||
return methodCall; | ||
}).join("\n")}` | ||
); | ||
} | ||
msg += c().gray(` | ||
msg += c().gray( | ||
` | ||
Number of calls: ${c().bold(spy.mock.calls.length)} | ||
`); | ||
` | ||
); | ||
return msg; | ||
}; | ||
const formatReturns = (spy, results, msg, showActualReturn) => { | ||
msg += c().gray(` | ||
msg += c().gray( | ||
` | ||
@@ -1289,16 +1428,24 @@ Received: | ||
${results.map((callReturn, i) => { | ||
let methodCall = c().bold(` ${ordinalOf(i + 1)} ${spy.getMockName()} call return: | ||
let methodCall = c().bold( | ||
` ${ordinalOf(i + 1)} ${spy.getMockName()} call return: | ||
`); | ||
if (showActualReturn) | ||
methodCall += diff(showActualReturn, callReturn.value, { omitAnnotationLines: true }); | ||
else | ||
methodCall += stringify(callReturn).split("\n").map((line) => ` ${line}`).join("\n"); | ||
methodCall += "\n"; | ||
return methodCall; | ||
}).join("\n")}`); | ||
msg += c().gray(` | ||
` | ||
); | ||
if (showActualReturn) { | ||
methodCall += diff(showActualReturn, callReturn.value, { | ||
omitAnnotationLines: true | ||
}); | ||
} else { | ||
methodCall += stringify(callReturn).split("\n").map((line) => ` ${line}`).join("\n"); | ||
} | ||
methodCall += "\n"; | ||
return methodCall; | ||
}).join("\n")}` | ||
); | ||
msg += c().gray( | ||
` | ||
Number of calls: ${c().bold(spy.mock.calls.length)} | ||
`); | ||
` | ||
); | ||
return msg; | ||
@@ -1338,16 +1485,15 @@ }; | ||
const isNot = utils.flag(this, "negate"); | ||
let msg = utils.getMessage( | ||
this, | ||
[ | ||
called, | ||
`expected "${spyName}" to be called at least once`, | ||
`expected "${spyName}" to not be called at all, but actually been called ${callCount} times`, | ||
true, | ||
called | ||
] | ||
); | ||
if (called && isNot) | ||
let msg = utils.getMessage(this, [ | ||
called, | ||
`expected "${spyName}" to be called at least once`, | ||
`expected "${spyName}" to not be called at all, but actually been called ${callCount} times`, | ||
true, | ||
called | ||
]); | ||
if (called && isNot) { | ||
msg = formatCalls(spy, msg); | ||
if (called && isNot || !called && !isNot) | ||
} | ||
if (called && isNot || !called && !isNot) { | ||
throw new AssertionError(msg); | ||
} | ||
}); | ||
@@ -1357,109 +1503,125 @@ def(["toHaveBeenCalledWith", "toBeCalledWith"], function(...args) { | ||
const spyName = spy.getMockName(); | ||
const pass = spy.mock.calls.some((callArg) => equals(callArg, args, [...customTesters, iterableEquality])); | ||
const pass = spy.mock.calls.some( | ||
(callArg) => equals(callArg, args, [...customTesters, iterableEquality]) | ||
); | ||
const isNot = utils.flag(this, "negate"); | ||
const msg = utils.getMessage( | ||
this, | ||
[ | ||
pass, | ||
`expected "${spyName}" to be called with arguments: #{exp}`, | ||
`expected "${spyName}" to not be called with arguments: #{exp}`, | ||
args | ||
] | ||
); | ||
if (pass && isNot || !pass && !isNot) | ||
const msg = utils.getMessage(this, [ | ||
pass, | ||
`expected "${spyName}" to be called with arguments: #{exp}`, | ||
`expected "${spyName}" to not be called with arguments: #{exp}`, | ||
args | ||
]); | ||
if (pass && isNot || !pass && !isNot) { | ||
throw new AssertionError(formatCalls(spy, msg, args)); | ||
} | ||
}); | ||
def(["toHaveBeenNthCalledWith", "nthCalledWith"], function(times, ...args) { | ||
const spy = getSpy(this); | ||
const spyName = spy.getMockName(); | ||
const nthCall = spy.mock.calls[times - 1]; | ||
const callCount = spy.mock.calls.length; | ||
const isCalled = times <= callCount; | ||
this.assert( | ||
equals(nthCall, args, [...customTesters, iterableEquality]), | ||
`expected ${ordinalOf(times)} "${spyName}" call to have been called with #{exp}${isCalled ? `` : `, but called only ${callCount} times`}`, | ||
`expected ${ordinalOf(times)} "${spyName}" call to not have been called with #{exp}`, | ||
args, | ||
nthCall, | ||
isCalled | ||
); | ||
}); | ||
def(["toHaveBeenLastCalledWith", "lastCalledWith"], function(...args) { | ||
const spy = getSpy(this); | ||
const spyName = spy.getMockName(); | ||
const lastCall = spy.mock.calls[spy.mock.calls.length - 1]; | ||
this.assert( | ||
equals(lastCall, args, [...customTesters, iterableEquality]), | ||
`expected last "${spyName}" call to have been called with #{exp}`, | ||
`expected last "${spyName}" call to not have been called with #{exp}`, | ||
args, | ||
lastCall | ||
); | ||
}); | ||
def(["toThrow", "toThrowError"], function(expected) { | ||
if (typeof expected === "string" || typeof expected === "undefined" || expected instanceof RegExp) | ||
return this.throws(expected); | ||
const obj = this._obj; | ||
const promise = utils.flag(this, "promise"); | ||
const isNot = utils.flag(this, "negate"); | ||
let thrown = null; | ||
if (promise === "rejects") { | ||
thrown = obj; | ||
} else if (promise === "resolves" && typeof obj !== "function") { | ||
if (!isNot) { | ||
const message = utils.flag(this, "message") || "expected promise to throw an error, but it didn't"; | ||
const error = { | ||
showDiff: false | ||
}; | ||
throw new AssertionError(message, error, utils.flag(this, "ssfi")); | ||
def( | ||
["toHaveBeenNthCalledWith", "nthCalledWith"], | ||
function(times, ...args) { | ||
const spy = getSpy(this); | ||
const spyName = spy.getMockName(); | ||
const nthCall = spy.mock.calls[times - 1]; | ||
const callCount = spy.mock.calls.length; | ||
const isCalled = times <= callCount; | ||
this.assert( | ||
equals(nthCall, args, [...customTesters, iterableEquality]), | ||
`expected ${ordinalOf( | ||
times | ||
)} "${spyName}" call to have been called with #{exp}${isCalled ? `` : `, but called only ${callCount} times`}`, | ||
`expected ${ordinalOf( | ||
times | ||
)} "${spyName}" call to not have been called with #{exp}`, | ||
args, | ||
nthCall, | ||
isCalled | ||
); | ||
} | ||
); | ||
def( | ||
["toHaveBeenLastCalledWith", "lastCalledWith"], | ||
function(...args) { | ||
const spy = getSpy(this); | ||
const spyName = spy.getMockName(); | ||
const lastCall = spy.mock.calls[spy.mock.calls.length - 1]; | ||
this.assert( | ||
equals(lastCall, args, [...customTesters, iterableEquality]), | ||
`expected last "${spyName}" call to have been called with #{exp}`, | ||
`expected last "${spyName}" call to not have been called with #{exp}`, | ||
args, | ||
lastCall | ||
); | ||
} | ||
); | ||
def( | ||
["toThrow", "toThrowError"], | ||
function(expected) { | ||
if (typeof expected === "string" || typeof expected === "undefined" || expected instanceof RegExp) { | ||
return this.throws(expected); | ||
} | ||
const obj = this._obj; | ||
const promise = utils.flag(this, "promise"); | ||
const isNot = utils.flag(this, "negate"); | ||
let thrown = null; | ||
if (promise === "rejects") { | ||
thrown = obj; | ||
} else if (promise === "resolves" && typeof obj !== "function") { | ||
if (!isNot) { | ||
const message = utils.flag(this, "message") || "expected promise to throw an error, but it didn't"; | ||
const error = { | ||
showDiff: false | ||
}; | ||
throw new AssertionError(message, error, utils.flag(this, "ssfi")); | ||
} else { | ||
return; | ||
} | ||
} else { | ||
return; | ||
let isThrow = false; | ||
try { | ||
obj(); | ||
} catch (err) { | ||
isThrow = true; | ||
thrown = err; | ||
} | ||
if (!isThrow && !isNot) { | ||
const message = utils.flag(this, "message") || "expected function to throw an error, but it didn't"; | ||
const error = { | ||
showDiff: false | ||
}; | ||
throw new AssertionError(message, error, utils.flag(this, "ssfi")); | ||
} | ||
} | ||
} else { | ||
let isThrow = false; | ||
try { | ||
obj(); | ||
} catch (err) { | ||
isThrow = true; | ||
thrown = err; | ||
if (typeof expected === "function") { | ||
const name = expected.name || expected.prototype.constructor.name; | ||
return this.assert( | ||
thrown && thrown instanceof expected, | ||
`expected error to be instance of ${name}`, | ||
`expected error not to be instance of ${name}`, | ||
expected, | ||
thrown | ||
); | ||
} | ||
if (!isThrow && !isNot) { | ||
const message = utils.flag(this, "message") || "expected function to throw an error, but it didn't"; | ||
const error = { | ||
showDiff: false | ||
}; | ||
throw new AssertionError(message, error, utils.flag(this, "ssfi")); | ||
if (expected instanceof Error) { | ||
return this.assert( | ||
thrown && expected.message === thrown.message, | ||
`expected error to have message: ${expected.message}`, | ||
`expected error not to have message: ${expected.message}`, | ||
expected.message, | ||
thrown && thrown.message | ||
); | ||
} | ||
} | ||
if (typeof expected === "function") { | ||
const name = expected.name || expected.prototype.constructor.name; | ||
return this.assert( | ||
thrown && thrown instanceof expected, | ||
`expected error to be instance of ${name}`, | ||
`expected error not to be instance of ${name}`, | ||
expected, | ||
thrown | ||
if (typeof expected === "object" && "asymmetricMatch" in expected && typeof expected.asymmetricMatch === "function") { | ||
const matcher = expected; | ||
return this.assert( | ||
thrown && matcher.asymmetricMatch(thrown), | ||
"expected error to match asymmetric matcher", | ||
"expected error not to match asymmetric matcher", | ||
matcher, | ||
thrown | ||
); | ||
} | ||
throw new Error( | ||
`"toThrow" expects string, RegExp, function, Error instance or asymmetric matcher, got "${typeof expected}"` | ||
); | ||
} | ||
if (expected instanceof Error) { | ||
return this.assert( | ||
thrown && expected.message === thrown.message, | ||
`expected error to have message: ${expected.message}`, | ||
`expected error not to have message: ${expected.message}`, | ||
expected.message, | ||
thrown && thrown.message | ||
); | ||
} | ||
if (typeof expected === "object" && "asymmetricMatch" in expected && typeof expected.asymmetricMatch === "function") { | ||
const matcher = expected; | ||
return this.assert( | ||
thrown && matcher.asymmetricMatch(thrown), | ||
"expected error to match asymmetric matcher", | ||
"expected error not to match asymmetric matcher", | ||
matcher, | ||
thrown | ||
); | ||
} | ||
throw new Error(`"toThrow" expects string, RegExp, function, Error instance or asymmetric matcher, got "${typeof expected}"`); | ||
}); | ||
); | ||
[ | ||
@@ -1494,3 +1656,6 @@ { | ||
name: "toHaveResolvedTimes", | ||
condition: (spy, times) => spy.mock.settledResults.reduce((s, { type }) => type === "fulfilled" ? ++s : s, 0) === times, | ||
condition: (spy, times) => spy.mock.settledResults.reduce( | ||
(s, { type }) => type === "fulfilled" ? ++s : s, | ||
0 | ||
) === times, | ||
action: "resolved" | ||
@@ -1500,3 +1665,6 @@ }, | ||
name: ["toHaveReturnedTimes", "toReturnTimes"], | ||
condition: (spy, times) => spy.mock.results.reduce((s, { type }) => type === "throw" ? s : ++s, 0) === times, | ||
condition: (spy, times) => spy.mock.results.reduce( | ||
(s, { type }) => type === "throw" ? s : ++s, | ||
0 | ||
) === times, | ||
action: "called" | ||
@@ -1522,3 +1690,5 @@ } | ||
name: "toHaveResolvedWith", | ||
condition: (spy, value) => spy.mock.settledResults.some(({ type, value: result }) => type === "fulfilled" && equals(value, result)), | ||
condition: (spy, value) => spy.mock.settledResults.some( | ||
({ type, value: result }) => type === "fulfilled" && equals(value, result) | ||
), | ||
action: "resolve" | ||
@@ -1528,3 +1698,5 @@ }, | ||
name: ["toHaveReturnedWith", "toReturnWith"], | ||
condition: (spy, value) => spy.mock.results.some(({ type, value: result }) => type === "return" && equals(value, result)), | ||
condition: (spy, value) => spy.mock.results.some( | ||
({ type, value: result }) => type === "return" && equals(value, result) | ||
), | ||
action: "return" | ||
@@ -1539,11 +1711,8 @@ } | ||
const spyName = spy.getMockName(); | ||
const msg = utils.getMessage( | ||
this, | ||
[ | ||
pass, | ||
`expected "${spyName}" to ${action} with: #{exp} at least once`, | ||
`expected "${spyName}" to not ${action} with: #{exp}`, | ||
value | ||
] | ||
); | ||
const msg = utils.getMessage(this, [ | ||
pass, | ||
`expected "${spyName}" to ${action} with: #{exp} at least once`, | ||
`expected "${spyName}" to not ${action} with: #{exp}`, | ||
value | ||
]); | ||
const results = action === "return" ? spy.mock.results : spy.mock.settledResults; | ||
@@ -1623,80 +1792,117 @@ throw new AssertionError(formatReturns(spy, results, msg, value)); | ||
def("withContext", function(context) { | ||
for (const key in context) | ||
for (const key in context) { | ||
utils.flag(this, key, context[key]); | ||
} | ||
return this; | ||
}); | ||
utils.addProperty(chai.Assertion.prototype, "resolves", function __VITEST_RESOLVES__() { | ||
const error = new Error("resolves"); | ||
utils.flag(this, "promise", "resolves"); | ||
utils.flag(this, "error", error); | ||
const test = utils.flag(this, "vitest-test"); | ||
const obj = utils.flag(this, "object"); | ||
if (utils.flag(this, "poll")) | ||
throw new SyntaxError(`expect.poll() is not supported in combination with .resolves`); | ||
if (typeof (obj == null ? void 0 : obj.then) !== "function") | ||
throw new TypeError(`You must provide a Promise to expect() when using .resolves, not '${typeof obj}'.`); | ||
const proxy = new Proxy(this, { | ||
get: (target, key, receiver) => { | ||
const result = Reflect.get(target, key, receiver); | ||
if (typeof result !== "function") | ||
return result instanceof chai.Assertion ? proxy : result; | ||
return async (...args) => { | ||
const promise = obj.then( | ||
(value) => { | ||
utils.flag(this, "object", value); | ||
return result.call(this, ...args); | ||
}, | ||
(err) => { | ||
const _error = new AssertionError( | ||
`promise rejected "${utils.inspect(err)}" instead of resolving`, | ||
{ showDiff: false } | ||
); | ||
_error.cause = err; | ||
_error.stack = error.stack.replace(error.message, _error.message); | ||
throw _error; | ||
} | ||
); | ||
return recordAsyncExpect(test, promise); | ||
}; | ||
utils.addProperty( | ||
chai.Assertion.prototype, | ||
"resolves", | ||
function __VITEST_RESOLVES__() { | ||
const error = new Error("resolves"); | ||
utils.flag(this, "promise", "resolves"); | ||
utils.flag(this, "error", error); | ||
const test = utils.flag(this, "vitest-test"); | ||
const obj = utils.flag(this, "object"); | ||
if (utils.flag(this, "poll")) { | ||
throw new SyntaxError( | ||
`expect.poll() is not supported in combination with .resolves` | ||
); | ||
} | ||
}); | ||
return proxy; | ||
}); | ||
utils.addProperty(chai.Assertion.prototype, "rejects", function __VITEST_REJECTS__() { | ||
const error = new Error("rejects"); | ||
utils.flag(this, "promise", "rejects"); | ||
utils.flag(this, "error", error); | ||
const test = utils.flag(this, "vitest-test"); | ||
const obj = utils.flag(this, "object"); | ||
const wrapper = typeof obj === "function" ? obj() : obj; | ||
if (utils.flag(this, "poll")) | ||
throw new SyntaxError(`expect.poll() is not supported in combination with .rejects`); | ||
if (typeof (wrapper == null ? void 0 : wrapper.then) !== "function") | ||
throw new TypeError(`You must provide a Promise to expect() when using .rejects, not '${typeof wrapper}'.`); | ||
const proxy = new Proxy(this, { | ||
get: (target, key, receiver) => { | ||
const result = Reflect.get(target, key, receiver); | ||
if (typeof result !== "function") | ||
return result instanceof chai.Assertion ? proxy : result; | ||
return async (...args) => { | ||
const promise = wrapper.then( | ||
(value) => { | ||
const _error = new AssertionError( | ||
`promise resolved "${utils.inspect(value)}" instead of rejecting`, | ||
{ showDiff: true, expected: new Error("rejected promise"), actual: value } | ||
); | ||
_error.stack = error.stack.replace(error.message, _error.message); | ||
throw _error; | ||
}, | ||
(err) => { | ||
utils.flag(this, "object", err); | ||
return result.call(this, ...args); | ||
} | ||
); | ||
return recordAsyncExpect(test, promise); | ||
}; | ||
if (typeof (obj == null ? void 0 : obj.then) !== "function") { | ||
throw new TypeError( | ||
`You must provide a Promise to expect() when using .resolves, not '${typeof obj}'.` | ||
); | ||
} | ||
}); | ||
return proxy; | ||
}); | ||
const proxy = new Proxy(this, { | ||
get: (target, key, receiver) => { | ||
const result = Reflect.get(target, key, receiver); | ||
if (typeof result !== "function") { | ||
return result instanceof chai.Assertion ? proxy : result; | ||
} | ||
return async (...args) => { | ||
const promise = obj.then( | ||
(value) => { | ||
utils.flag(this, "object", value); | ||
return result.call(this, ...args); | ||
}, | ||
(err) => { | ||
const _error = new AssertionError( | ||
`promise rejected "${utils.inspect( | ||
err | ||
)}" instead of resolving`, | ||
{ showDiff: false } | ||
); | ||
_error.cause = err; | ||
_error.stack = error.stack.replace( | ||
error.message, | ||
_error.message | ||
); | ||
throw _error; | ||
} | ||
); | ||
return recordAsyncExpect(test, promise); | ||
}; | ||
} | ||
}); | ||
return proxy; | ||
} | ||
); | ||
utils.addProperty( | ||
chai.Assertion.prototype, | ||
"rejects", | ||
function __VITEST_REJECTS__() { | ||
const error = new Error("rejects"); | ||
utils.flag(this, "promise", "rejects"); | ||
utils.flag(this, "error", error); | ||
const test = utils.flag(this, "vitest-test"); | ||
const obj = utils.flag(this, "object"); | ||
const wrapper = typeof obj === "function" ? obj() : obj; | ||
if (utils.flag(this, "poll")) { | ||
throw new SyntaxError( | ||
`expect.poll() is not supported in combination with .rejects` | ||
); | ||
} | ||
if (typeof (wrapper == null ? void 0 : wrapper.then) !== "function") { | ||
throw new TypeError( | ||
`You must provide a Promise to expect() when using .rejects, not '${typeof wrapper}'.` | ||
); | ||
} | ||
const proxy = new Proxy(this, { | ||
get: (target, key, receiver) => { | ||
const result = Reflect.get(target, key, receiver); | ||
if (typeof result !== "function") { | ||
return result instanceof chai.Assertion ? proxy : result; | ||
} | ||
return async (...args) => { | ||
const promise = wrapper.then( | ||
(value) => { | ||
const _error = new AssertionError( | ||
`promise resolved "${utils.inspect( | ||
value | ||
)}" instead of rejecting`, | ||
{ | ||
showDiff: true, | ||
expected: new Error("rejected promise"), | ||
actual: value | ||
} | ||
); | ||
_error.stack = error.stack.replace( | ||
error.message, | ||
_error.message | ||
); | ||
throw _error; | ||
}, | ||
(err) => { | ||
utils.flag(this, "object", err); | ||
return result.call(this, ...args); | ||
} | ||
); | ||
return recordAsyncExpect(test, promise); | ||
}; | ||
} | ||
}); | ||
return proxy; | ||
} | ||
); | ||
}; | ||
@@ -1742,69 +1948,89 @@ | ||
return (_, utils) => { | ||
Object.entries(matchers).forEach(([expectAssertionName, expectAssertion]) => { | ||
function expectWrapper(...args) { | ||
const { state, isNot, obj } = getMatcherState(this, expect); | ||
const result = expectAssertion.call(state, obj, ...args); | ||
if (result && typeof result === "object" && result instanceof Promise) { | ||
return result.then(({ pass: pass2, message: message2, actual: actual2, expected: expected2 }) => { | ||
if (pass2 && isNot || !pass2 && !isNot) | ||
throw new JestExtendError(message2(), actual2, expected2); | ||
}); | ||
Object.entries(matchers).forEach( | ||
([expectAssertionName, expectAssertion]) => { | ||
function expectWrapper(...args) { | ||
const { state, isNot, obj } = getMatcherState(this, expect); | ||
const result = expectAssertion.call(state, obj, ...args); | ||
if (result && typeof result === "object" && result instanceof Promise) { | ||
return result.then(({ pass: pass2, message: message2, actual: actual2, expected: expected2 }) => { | ||
if (pass2 && isNot || !pass2 && !isNot) { | ||
throw new JestExtendError(message2(), actual2, expected2); | ||
} | ||
}); | ||
} | ||
const { pass, message, actual, expected } = result; | ||
if (pass && isNot || !pass && !isNot) { | ||
throw new JestExtendError(message(), actual, expected); | ||
} | ||
} | ||
const { pass, message, actual, expected } = result; | ||
if (pass && isNot || !pass && !isNot) | ||
throw new JestExtendError(message(), actual, expected); | ||
} | ||
const softWrapper = wrapSoft(utils, expectWrapper); | ||
utils.addMethod(globalThis[JEST_MATCHERS_OBJECT].matchers, expectAssertionName, softWrapper); | ||
utils.addMethod(c.Assertion.prototype, expectAssertionName, softWrapper); | ||
class CustomMatcher extends AsymmetricMatcher { | ||
constructor(inverse = false, ...sample) { | ||
super(sample, inverse); | ||
const softWrapper = wrapSoft(utils, expectWrapper); | ||
utils.addMethod( | ||
globalThis[JEST_MATCHERS_OBJECT].matchers, | ||
expectAssertionName, | ||
softWrapper | ||
); | ||
utils.addMethod( | ||
c.Assertion.prototype, | ||
expectAssertionName, | ||
softWrapper | ||
); | ||
class CustomMatcher extends AsymmetricMatcher { | ||
constructor(inverse = false, ...sample) { | ||
super(sample, inverse); | ||
} | ||
asymmetricMatch(other) { | ||
const { pass } = expectAssertion.call( | ||
this.getMatcherContext(expect), | ||
other, | ||
...this.sample | ||
); | ||
return this.inverse ? !pass : pass; | ||
} | ||
toString() { | ||
return `${this.inverse ? "not." : ""}${expectAssertionName}`; | ||
} | ||
getExpectedType() { | ||
return "any"; | ||
} | ||
toAsymmetricMatcher() { | ||
return `${this.toString()}<${this.sample.map(String).join(", ")}>`; | ||
} | ||
} | ||
asymmetricMatch(other) { | ||
const { pass } = expectAssertion.call( | ||
this.getMatcherContext(expect), | ||
other, | ||
...this.sample | ||
); | ||
return this.inverse ? !pass : pass; | ||
} | ||
toString() { | ||
return `${this.inverse ? "not." : ""}${expectAssertionName}`; | ||
} | ||
getExpectedType() { | ||
return "any"; | ||
} | ||
toAsymmetricMatcher() { | ||
return `${this.toString()}<${this.sample.map(String).join(", ")}>`; | ||
} | ||
const customMatcher = (...sample) => new CustomMatcher(false, ...sample); | ||
Object.defineProperty(expect, expectAssertionName, { | ||
configurable: true, | ||
enumerable: true, | ||
value: customMatcher, | ||
writable: true | ||
}); | ||
Object.defineProperty(expect.not, expectAssertionName, { | ||
configurable: true, | ||
enumerable: true, | ||
value: (...sample) => new CustomMatcher(true, ...sample), | ||
writable: true | ||
}); | ||
Object.defineProperty( | ||
globalThis[ASYMMETRIC_MATCHERS_OBJECT], | ||
expectAssertionName, | ||
{ | ||
configurable: true, | ||
enumerable: true, | ||
value: customMatcher, | ||
writable: true | ||
} | ||
); | ||
} | ||
const customMatcher = (...sample) => new CustomMatcher(false, ...sample); | ||
Object.defineProperty(expect, expectAssertionName, { | ||
configurable: true, | ||
enumerable: true, | ||
value: customMatcher, | ||
writable: true | ||
}); | ||
Object.defineProperty(expect.not, expectAssertionName, { | ||
configurable: true, | ||
enumerable: true, | ||
value: (...sample) => new CustomMatcher(true, ...sample), | ||
writable: true | ||
}); | ||
Object.defineProperty(globalThis[ASYMMETRIC_MATCHERS_OBJECT], expectAssertionName, { | ||
configurable: true, | ||
enumerable: true, | ||
value: customMatcher, | ||
writable: true | ||
}); | ||
}); | ||
); | ||
}; | ||
} | ||
const JestExtend = (chai, utils) => { | ||
utils.addMethod(chai.expect, "extend", (expect, expects) => { | ||
use(JestExtendPlugin(chai, expect, expects)); | ||
}); | ||
utils.addMethod( | ||
chai.expect, | ||
"extend", | ||
(expect, expects) => { | ||
use(JestExtendPlugin(chai, expect, expects)); | ||
} | ||
); | ||
}; | ||
export { ASYMMETRIC_MATCHERS_OBJECT, Any, Anything, ArrayContaining, AsymmetricMatcher, GLOBAL_EXPECT, JEST_MATCHERS_OBJECT, JestAsymmetricMatchers, JestChaiExpect, JestExtend, MATCHERS_OBJECT, ObjectContaining, StringContaining, StringMatching, addCustomEqualityTesters, arrayBufferEquality, equals, fnNameFor, generateToBeMessage, getObjectKeys, getObjectSubset, getState, hasAsymmetric, hasProperty, isA, isAsymmetric, isImmutableUnorderedKeyed, isImmutableUnorderedSet, iterableEquality, pluralize, setState, sparseArrayEquality, subsetEquality, typeEquality }; |
{ | ||
"name": "@vitest/expect", | ||
"type": "module", | ||
"version": "2.0.0-beta.10", | ||
"version": "2.0.0-beta.11", | ||
"description": "Jest's expect matchers as a Chai plugin", | ||
@@ -34,4 +34,4 @@ "license": "MIT", | ||
"chai": "^5.1.1", | ||
"@vitest/spy": "2.0.0-beta.10", | ||
"@vitest/utils": "2.0.0-beta.10" | ||
"@vitest/spy": "2.0.0-beta.11", | ||
"@vitest/utils": "2.0.0-beta.11" | ||
}, | ||
@@ -42,3 +42,3 @@ "devDependencies": { | ||
"rollup-plugin-copy": "^3.5.0", | ||
"@vitest/runner": "2.0.0-beta.10" | ||
"@vitest/runner": "2.0.0-beta.11" | ||
}, | ||
@@ -45,0 +45,0 @@ "scripts": { |
@@ -9,3 +9,7 @@ # @vitest/expect | ||
import * as chai from 'chai' | ||
import { JestAsymmetricMatchers, JestChaiExpect, JestExtend } from '@vitest/expect' | ||
import { | ||
JestAsymmetricMatchers, | ||
JestChaiExpect, | ||
JestExtend, | ||
} from '@vitest/expect' | ||
@@ -12,0 +16,0 @@ // allows using expect.extend instead of chai.use to extend plugins |
148911
2212
22
+ Added@vitest/spy@2.0.0-beta.11(transitive)
+ Added@vitest/utils@2.0.0-beta.11(transitive)
- Removed@vitest/spy@2.0.0-beta.10(transitive)
- Removed@vitest/utils@2.0.0-beta.10(transitive)
Updated@vitest/spy@2.0.0-beta.11
Updated@vitest/utils@2.0.0-beta.11