@vitest/expect 3.0.0-beta.1 → 3.0.0-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -97,7 +97,29 @@ interface ExpectStatic extends Chai.ExpectStatic, AsymmetricMatchersContaining {
97
97
  setState: (state: Partial<MatcherState>) => void;
98
98
  not: AsymmetricMatchersContaining;
99
99
  }
100
- interface AsymmetricMatchersContaining {
100
+ interface CustomMatcher {
101
+ /**
102
+ * Checks that a value satisfies a custom matcher function.
103
+ *
104
+ * @param matcher - A function returning a boolean based on the custom condition
105
+ * @param message - Optional custom error message on failure
106
+ *
107
+ * @example
108
+ * expect(age).toSatisfy(val => val >= 18, 'Age must be at least 18');
109
+ * expect(age).toEqual(expect.toSatisfy(val => val >= 18, 'Age must be at least 18'));
110
+ */
111
+ toSatisfy: (matcher: (value: any) => boolean, message?: string) => any;
112
+ /**
113
+ * Matches if the received value is one of the values in the expected array.
114
+ *
115
+ * @example
116
+ * expect(1).toBeOneOf([1, 2, 3])
117
+ * expect('foo').toBeOneOf([expect.any(String)])
118
+ * expect({ a: 1 }).toEqual({ a: expect.toBeOneOf(['1', '2', '3']) })
119
+ */
120
+ toBeOneOf: <T>(sample: Array<T>) => any;
121
+ }
122
+ interface AsymmetricMatchersContaining extends CustomMatcher {
101
123
  /**
102
124
  * Matches if the received string contains the expected substring.
103
125
  *
@@ -139,7 +161,7 @@ interface AsymmetricMatchersContaining {
139
161
  */
140
162
  closeTo: (expected: number, precision?: number) => any;
141
163
  }
142
- interface JestAssertion<T = any> extends jest.Matchers<void, T> {
164
+ interface JestAssertion<T = any> extends jest.Matchers<void, T>, CustomMatcher {
143
165
  /**
144
166
  * Used when you want to check that two objects have the same value.
145
167
  * This matcher recursively checks the equality of all fields, rather than checking for object identity.
@@ -558,16 +580,6 @@ interface Assertion<T = any> extends VitestAssertion<Chai.Assertion, T>, JestAss
558
580
  * expect(mockFunc).toHaveBeenCalledExactlyOnceWith('arg1', 42);
559
581
  */
560
582
  toHaveBeenCalledExactlyOnceWith: <E extends any[]>(...args: E) => void;
561
- /**
562
- * Checks that a value satisfies a custom matcher function.
563
- *
564
- * @param matcher - A function returning a boolean based on the custom condition
565
- * @param message - Optional custom error message on failure
566
- *
567
- * @example
568
- * expect(age).toSatisfy(val => val >= 18, 'Age must be at least 18');
569
- */
570
- toSatisfy: <E>(matcher: (value: E) => boolean, message?: string) => void;
571
583
  /**
572
584
  * This assertion checks if a `Mock` was called before another `Mock`.
573
585
  * @param mock - A mock function created by `vi.spyOn` or `vi.fn`
@@ -655,6 +667,8 @@ declare global {
655
667
  }
656
668
  }
657
669
 
670
+ declare const customMatchers: MatchersObject;
671
+
658
672
  interface AsymmetricMatcherInterface {
659
673
  asymmetricMatch: (other: unknown) => boolean;
660
674
  toString: () => string;
@@ -741,4 +755,4 @@ declare function getObjectSubset(object: any, subset: any, customTesters: Array<
741
755
  declare function getState<State extends MatcherState = MatcherState>(expect: ExpectStatic): State;
742
756
  declare function setState<State extends MatcherState = MatcherState>(state: Partial<State>, expect: ExpectStatic): void;
743
757
 
744
- export { ASYMMETRIC_MATCHERS_OBJECT, Any, Anything, ArrayContaining, type Assertion, AsymmetricMatcher, type AsymmetricMatcherInterface, type AsymmetricMatchersContaining, type AsyncExpectationResult, type ChaiPlugin, type ExpectStatic, type ExpectationResult, GLOBAL_EXPECT, JEST_MATCHERS_OBJECT, type JestAssertion, JestAsymmetricMatchers, JestChaiExpect, JestExtend, MATCHERS_OBJECT, type MatcherHintOptions, type MatcherState, type MatchersObject, ObjectContaining, type PromisifyAssertion, type RawMatcherFn, StringContaining, StringMatching, type SyncExpectationResult, type Tester, type TesterContext, addCustomEqualityTesters, arrayBufferEquality, equals, fnNameFor, generateToBeMessage, getObjectKeys, getObjectSubset, getState, hasAsymmetric, hasProperty, isA, isAsymmetric, isImmutableUnorderedKeyed, isImmutableUnorderedSet, iterableEquality, pluralize, setState, sparseArrayEquality, subsetEquality, typeEquality };
758
+ export { ASYMMETRIC_MATCHERS_OBJECT, Any, Anything, ArrayContaining, type Assertion, AsymmetricMatcher, type AsymmetricMatcherInterface, type AsymmetricMatchersContaining, type AsyncExpectationResult, type ChaiPlugin, type ExpectStatic, type ExpectationResult, GLOBAL_EXPECT, JEST_MATCHERS_OBJECT, type JestAssertion, JestAsymmetricMatchers, JestChaiExpect, JestExtend, MATCHERS_OBJECT, type MatcherHintOptions, type MatcherState, type MatchersObject, ObjectContaining, type PromisifyAssertion, type RawMatcherFn, StringContaining, StringMatching, type SyncExpectationResult, type Tester, type TesterContext, addCustomEqualityTesters, arrayBufferEquality, customMatchers, equals, fnNameFor, generateToBeMessage, getObjectKeys, getObjectSubset, getState, hasAsymmetric, hasProperty, isA, isAsymmetric, isImmutableUnorderedKeyed, isImmutableUnorderedSet, iterableEquality, pluralize, setState, sparseArrayEquality, subsetEquality, typeEquality };
package/dist/index.js CHANGED
@@ -12,6 +12,55 @@ const ASYMMETRIC_MATCHERS_OBJECT = Symbol.for(
12
12
  "asymmetric-matchers-object"
13
13
  );
14
14
 
15
+ const customMatchers = {
16
+ toSatisfy(actual, expected, message) {
17
+ const { printReceived, printExpected, matcherHint } = this.utils;
18
+ const pass = expected(actual);
19
+ return {
20
+ pass,
21
+ message: () => pass ? `${matcherHint(".not.toSatisfy", "received", "")}
22
+
23
+ Expected value to not satisfy:
24
+ ${message || printExpected(expected)}
25
+ Received:
26
+ ${printReceived(actual)}` : `${matcherHint(".toSatisfy", "received", "")}
27
+
28
+ Expected value to satisfy:
29
+ ${message || printExpected(expected)}
30
+
31
+ Received:
32
+ ${printReceived(actual)}`
33
+ };
34
+ },
35
+ toBeOneOf(actual, expected) {
36
+ const { equals, customTesters } = this;
37
+ const { printReceived, printExpected, matcherHint } = this.utils;
38
+ if (!Array.isArray(expected)) {
39
+ throw new TypeError(
40
+ `You must provide an array to ${matcherHint(".toBeOneOf")}, not '${typeof expected}'.`
41
+ );
42
+ }
43
+ const pass = expected.length === 0 || expected.some(
44
+ (item) => equals(item, actual, customTesters)
45
+ );
46
+ return {
47
+ pass,
48
+ message: () => pass ? `${matcherHint(".not.toBeOneOf", "received", "")}
49
+
50
+ Expected value to not be one of:
51
+ ${printExpected(expected)}
52
+ Received:
53
+ ${printReceived(actual)}` : `${matcherHint(".toBeOneOf", "received", "")}
54
+
55
+ Expected value to be one of:
56
+ ${printExpected(expected)}
57
+
58
+ Received:
59
+ ${printReceived(actual)}`
60
+ };
61
+ }
62
+ };
63
+
15
64
  const EXPECTED_COLOR = c.green;
16
65
  const RECEIVED_COLOR = c.red;
17
66
  const INVERTED_COLOR = c.inverse;
@@ -164,9 +213,6 @@ function eq(a, b, aStack, bStack, customTesters, hasKey2) {
164
213
  return customTesterResult;
165
214
  }
166
215
  }
167
- if (a instanceof Error && b instanceof Error) {
168
- return a.message === b.message;
169
- }
170
216
  if (typeof URL === "function" && a instanceof URL && b instanceof URL) {
171
217
  return a.href === b.href;
172
218
  }
@@ -219,6 +265,14 @@ function eq(a, b, aStack, bStack, customTesters, hasKey2) {
219
265
  if (className === "[object Array]" && a.length !== b.length) {
220
266
  return false;
221
267
  }
268
+ if (a instanceof Error && b instanceof Error) {
269
+ try {
270
+ return isErrorEqual(a, b, aStack, bStack, customTesters, hasKey2);
271
+ } finally {
272
+ aStack.pop();
273
+ bStack.pop();
274
+ }
275
+ }
222
276
  const aKeys = keys(a, hasKey2);
223
277
  let key;
224
278
  let size = aKeys.length;
@@ -236,6 +290,17 @@ function eq(a, b, aStack, bStack, customTesters, hasKey2) {
236
290
  bStack.pop();
237
291
  return result;
238
292
  }
293
+ function isErrorEqual(a, b, aStack, bStack, customTesters, hasKey2) {
294
+ let result = Object.getPrototypeOf(a) === Object.getPrototypeOf(b) && a.name === b.name && a.message === b.message;
295
+ if (typeof b.cause !== "undefined") {
296
+ result && (result = eq(a.cause, b.cause, aStack, bStack, customTesters, hasKey2));
297
+ }
298
+ if (a instanceof AggregateError && b instanceof AggregateError) {
299
+ result && (result = eq(a.errors, b.errors, aStack, bStack, customTesters, hasKey2));
300
+ }
301
+ result && (result = eq({ ...a }, { ...b }, aStack, bStack, customTesters, hasKey2));
302
+ return result;
303
+ }
239
304
  function keys(obj, hasKey2) {
240
305
  const keys2 = [];
241
306
  for (const key in obj) {
@@ -1625,12 +1690,16 @@ const JestChaiExpect = (chai, utils) => {
1625
1690
  );
1626
1691
  }
1627
1692
  if (expected instanceof Error) {
1693
+ const equal = equals(thrown, expected, [
1694
+ ...customTesters,
1695
+ iterableEquality
1696
+ ]);
1628
1697
  return this.assert(
1629
- thrown && expected.message === thrown.message,
1630
- `expected error to have message: ${expected.message}`,
1631
- `expected error not to have message: ${expected.message}`,
1632
- expected.message,
1633
- thrown && thrown.message
1698
+ equal,
1699
+ "expected a thrown error to be #{exp}",
1700
+ "expected a thrown error not to be #{exp}",
1701
+ expected,
1702
+ thrown
1634
1703
  );
1635
1704
  }
1636
1705
  if (typeof expected === "object" && "asymmetricMatch" in expected && typeof expected.asymmetricMatch === "function") {
@@ -1804,9 +1873,6 @@ const JestChaiExpect = (chai, utils) => {
1804
1873
  );
1805
1874
  });
1806
1875
  });
1807
- def("toSatisfy", function(matcher, message) {
1808
- return this.be.satisfy(matcher, message);
1809
- });
1810
1876
  def("withContext", function(context) {
1811
1877
  for (const key in context) {
1812
1878
  utils.flag(this, key, context[key]);
@@ -2100,7 +2166,7 @@ function JestExtendPlugin(c, expect, matchers) {
2100
2166
  return "any";
2101
2167
  }
2102
2168
  toAsymmetricMatcher() {
2103
- return `${this.toString()}<${this.sample.map(String).join(", ")}>`;
2169
+ return `${this.toString()}<${this.sample.map((item) => stringify(item)).join(", ")}>`;
2104
2170
  }
2105
2171
  }
2106
2172
  const customMatcher = (...sample) => new CustomMatcher(false, ...sample);
@@ -2140,4 +2206,4 @@ const JestExtend = (chai, utils) => {
2140
2206
  );
2141
2207
  };
2142
2208
 
2143
- 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 };
2209
+ export { ASYMMETRIC_MATCHERS_OBJECT, Any, Anything, ArrayContaining, AsymmetricMatcher, GLOBAL_EXPECT, JEST_MATCHERS_OBJECT, JestAsymmetricMatchers, JestChaiExpect, JestExtend, MATCHERS_OBJECT, ObjectContaining, StringContaining, StringMatching, addCustomEqualityTesters, arrayBufferEquality, customMatchers, equals, fnNameFor, generateToBeMessage, getObjectKeys, getObjectSubset, getState, hasAsymmetric, hasProperty, isA, isAsymmetric, isImmutableUnorderedKeyed, isImmutableUnorderedSet, iterableEquality, pluralize, setState, sparseArrayEquality, subsetEquality, typeEquality };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@vitest/expect",
3
3
  "type": "module",
4
- "version": "3.0.0-beta.1",
4
+ "version": "3.0.0-beta.3",
5
5
  "description": "Jest's expect matchers as a Chai plugin",
6
6
  "license": "MIT",
7
7
  "funding": "https://opencollective.com/vitest",
@@ -32,13 +32,13 @@
32
32
  "dependencies": {
33
33
  "chai": "^5.1.2",
34
34
  "tinyrainbow": "^1.2.0",
35
- "@vitest/utils": "3.0.0-beta.1",
36
- "@vitest/spy": "3.0.0-beta.1"
35
+ "@vitest/spy": "3.0.0-beta.3",
36
+ "@vitest/utils": "3.0.0-beta.3"
37
37
  },
38
38
  "devDependencies": {
39
39
  "@types/chai": "4.3.6",
40
40
  "rollup-plugin-copy": "^3.5.0",
41
- "@vitest/runner": "3.0.0-beta.1"
41
+ "@vitest/runner": "3.0.0-beta.3"
42
42
  },
43
43
  "scripts": {
44
44
  "build": "rimraf dist && rollup -c",