@vitest/expect 2.0.0-beta.1 → 2.0.0-beta.10

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
@@ -65,6 +65,7 @@ interface MatcherState {
65
65
  subsetEquality: Tester;
66
66
  };
67
67
  soft?: boolean;
68
+ poll?: boolean;
68
69
  }
69
70
  interface SyncExpectationResult {
70
71
  pass: boolean;
@@ -80,12 +81,7 @@ interface RawMatcherFn<T extends MatcherState = MatcherState> {
80
81
  type MatchersObject<T extends MatcherState = MatcherState> = Record<string, RawMatcherFn<T>>;
81
82
  interface ExpectStatic extends Chai.ExpectStatic, AsymmetricMatchersContaining {
82
83
  <T>(actual: T, message?: string): Assertion<T>;
83
- unreachable: (message?: string) => never;
84
- soft: <T>(actual: T, message?: string) => Assertion<T>;
85
84
  extend: (expects: MatchersObject) => void;
86
- addEqualityTesters: (testers: Array<Tester>) => void;
87
- assertions: (expected: number) => void;
88
- hasAssertions: () => void;
89
85
  anything: () => any;
90
86
  any: (constructor: unknown) => any;
91
87
  getState: () => MatcherState;
@@ -150,12 +146,18 @@ type VitestAssertion<A, T> = {
150
146
  type Promisify<O> = {
151
147
  [K in keyof O]: O[K] extends (...args: infer A) => infer R ? O extends R ? Promisify<O[K]> : (...args: A) => Promise<R> : O[K];
152
148
  };
149
+ type PromisifyAssertion<T> = Promisify<Assertion<T>>;
153
150
  interface Assertion<T = any> extends VitestAssertion<Chai.Assertion, T>, JestAssertion<T> {
154
151
  toBeTypeOf: (expected: 'bigint' | 'boolean' | 'function' | 'number' | 'object' | 'string' | 'symbol' | 'undefined') => void;
155
152
  toHaveBeenCalledOnce: () => void;
156
153
  toSatisfy: <E>(matcher: (value: E) => boolean, message?: string) => void;
157
- resolves: Promisify<Assertion<T>>;
158
- rejects: Promisify<Assertion<T>>;
154
+ toHaveResolved: () => void;
155
+ toHaveResolvedWith: <E>(value: E) => void;
156
+ toHaveResolvedTimes: (times: number) => void;
157
+ toHaveLastResolvedWith: <E>(value: E) => void;
158
+ toHaveNthResolvedWith: <E>(nthCall: number, value: E) => void;
159
+ resolves: PromisifyAssertion<T>;
160
+ rejects: PromisifyAssertion<T>;
159
161
  }
160
162
  declare global {
161
163
  namespace jest {
@@ -255,4 +257,4 @@ declare const JestChaiExpect: ChaiPlugin;
255
257
 
256
258
  declare const JestExtend: ChaiPlugin;
257
259
 
258
- 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 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 };
260
+ 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 };
package/dist/index.js CHANGED
@@ -254,7 +254,7 @@ function isDomNode(obj) {
254
254
  function fnNameFor(func) {
255
255
  if (func.name)
256
256
  return func.name;
257
- const matches = functionToString.call(func).match(/^(?:async)?\s*function\s*\*?\s*([\w$]+)\s*\(/);
257
+ const matches = functionToString.call(func).match(/^(?:async)?\s*function\s*(?:\*\s*)?([\w$]+)\s*\(/);
258
258
  return matches ? matches[1] : "<anonymous>";
259
259
  }
260
260
  function getPrototype(obj) {
@@ -306,8 +306,9 @@ function hasIterator(object) {
306
306
  return !!(object != null && object[IteratorSymbol]);
307
307
  }
308
308
  function iterableEquality(a, b, customTesters = [], aStack = [], bStack = []) {
309
- if (typeof a !== "object" || typeof b !== "object" || Array.isArray(a) || Array.isArray(b) || !hasIterator(a) || !hasIterator(b))
309
+ if (typeof a !== "object" || typeof b !== "object" || Array.isArray(a) || Array.isArray(b) || !hasIterator(a) || !hasIterator(b)) {
310
310
  return void 0;
311
+ }
311
312
  if (a.constructor !== b.constructor)
312
313
  return false;
313
314
  let length = aStack.length;
@@ -379,8 +380,9 @@ function iterableEquality(a, b, customTesters = [], aStack = [], bStack = []) {
379
380
  const bIterator = b[IteratorSymbol]();
380
381
  for (const aValue of a) {
381
382
  const nextB = bIterator.next();
382
- if (nextB.done || !equals(aValue, nextB.value, filteredCustomTesters))
383
+ if (nextB.done || !equals(aValue, nextB.value, filteredCustomTesters)) {
383
384
  return false;
385
+ }
384
386
  }
385
387
  if (!bIterator.next().done)
386
388
  return false;
@@ -661,7 +663,7 @@ class Any extends AsymmetricMatcher {
661
663
  if (func.name)
662
664
  return func.name;
663
665
  const functionToString = Function.prototype.toString;
664
- const matches = functionToString.call(func).match(/^(?:async)?\s*function\s*\*?\s*([\w$]+)\s*\(/);
666
+ const matches = functionToString.call(func).match(/^(?:async)?\s*function\s*(?:\*\s*)?([\w$]+)\s*\(/);
665
667
  return matches ? matches[1] : "<anonymous>";
666
668
  }
667
669
  asymmetricMatch(other) {
@@ -817,10 +819,9 @@ function recordAsyncExpect(test, promise) {
817
819
  function wrapSoft(utils, fn) {
818
820
  return function(...args) {
819
821
  var _a;
820
- const test = utils.flag(this, "vitest-test");
821
- const state = (test == null ? void 0 : test.context._local) ? test.context.expect.getState() : getState(globalThis[GLOBAL_EXPECT]);
822
- if (!state.soft)
822
+ if (!utils.flag(this, "soft"))
823
823
  return fn.apply(this, args);
824
+ const test = utils.flag(this, "vitest-test");
824
825
  if (!test)
825
826
  throw new Error("expect.soft() can only be used inside a test");
826
827
  try {
@@ -1205,7 +1206,7 @@ const JestChaiExpect = (chai, utils) => {
1205
1206
  return `${i}rd`;
1206
1207
  return `${i}th`;
1207
1208
  };
1208
- const formatCalls = (spy, msg, actualCall) => {
1209
+ const formatCalls = (spy, msg, showActualCall) => {
1209
1210
  if (spy.mock.calls) {
1210
1211
  msg += c().gray(`
1211
1212
 
@@ -1215,8 +1216,8 @@ ${spy.mock.calls.map((callArg, i) => {
1215
1216
  let methodCall = c().bold(` ${ordinalOf(i + 1)} ${spy.getMockName()} call:
1216
1217
 
1217
1218
  `);
1218
- if (actualCall)
1219
- methodCall += diff(actualCall, callArg, { omitAnnotationLines: true });
1219
+ if (showActualCall)
1220
+ methodCall += diff(showActualCall, callArg, { omitAnnotationLines: true });
1220
1221
  else
1221
1222
  methodCall += stringify(callArg).split("\n").map((line) => ` ${line}`).join("\n");
1222
1223
  methodCall += "\n";
@@ -1229,17 +1230,17 @@ Number of calls: ${c().bold(spy.mock.calls.length)}
1229
1230
  `);
1230
1231
  return msg;
1231
1232
  };
1232
- const formatReturns = (spy, msg, actualReturn) => {
1233
+ const formatReturns = (spy, results, msg, showActualReturn) => {
1233
1234
  msg += c().gray(`
1234
1235
 
1235
1236
  Received:
1236
1237
 
1237
- ${spy.mock.results.map((callReturn, i) => {
1238
+ ${results.map((callReturn, i) => {
1238
1239
  let methodCall = c().bold(` ${ordinalOf(i + 1)} ${spy.getMockName()} call return:
1239
1240
 
1240
1241
  `);
1241
- if (actualReturn)
1242
- methodCall += diff(actualReturn, callReturn.value, { omitAnnotationLines: true });
1242
+ if (showActualReturn)
1243
+ methodCall += diff(showActualReturn, callReturn.value, { omitAnnotationLines: true });
1243
1244
  else
1244
1245
  methodCall += stringify(callReturn).split("\n").map((line) => ` ${line}`).join("\n");
1245
1246
  methodCall += "\n";
@@ -1408,88 +1409,171 @@ Number of calls: ${c().bold(spy.mock.calls.length)}
1408
1409
  }
1409
1410
  throw new Error(`"toThrow" expects string, RegExp, function, Error instance or asymmetric matcher, got "${typeof expected}"`);
1410
1411
  });
1411
- def(["toHaveReturned", "toReturn"], function() {
1412
- const spy = getSpy(this);
1413
- const spyName = spy.getMockName();
1414
- const calledAndNotThrew = spy.mock.calls.length > 0 && spy.mock.results.some(({ type }) => type !== "throw");
1415
- this.assert(
1416
- calledAndNotThrew,
1417
- `expected "${spyName}" to be successfully called at least once`,
1418
- `expected "${spyName}" to not be successfully called`,
1419
- calledAndNotThrew,
1420
- !calledAndNotThrew,
1421
- false
1422
- );
1423
- });
1424
- def(["toHaveReturnedTimes", "toReturnTimes"], function(times) {
1425
- const spy = getSpy(this);
1426
- const spyName = spy.getMockName();
1427
- const successfulReturns = spy.mock.results.reduce((success, { type }) => type === "throw" ? success : ++success, 0);
1428
- this.assert(
1429
- successfulReturns === times,
1430
- `expected "${spyName}" to be successfully called ${times} times`,
1431
- `expected "${spyName}" to not be successfully called ${times} times`,
1432
- `expected number of returns: ${times}`,
1433
- `received number of returns: ${successfulReturns}`,
1434
- false
1435
- );
1412
+ [
1413
+ {
1414
+ name: "toHaveResolved",
1415
+ condition: (spy) => spy.mock.settledResults.length > 0 && spy.mock.settledResults.some(({ type }) => type === "fulfilled"),
1416
+ action: "resolved"
1417
+ },
1418
+ {
1419
+ name: ["toHaveReturned", "toReturn"],
1420
+ condition: (spy) => spy.mock.calls.length > 0 && spy.mock.results.some(({ type }) => type !== "throw"),
1421
+ action: "called"
1422
+ }
1423
+ ].forEach(({ name, condition, action }) => {
1424
+ def(name, function() {
1425
+ const spy = getSpy(this);
1426
+ const spyName = spy.getMockName();
1427
+ const pass = condition(spy);
1428
+ this.assert(
1429
+ pass,
1430
+ `expected "${spyName}" to be successfully ${action} at least once`,
1431
+ `expected "${spyName}" to not be successfully ${action}`,
1432
+ pass,
1433
+ !pass,
1434
+ false
1435
+ );
1436
+ });
1436
1437
  });
1437
- def(["toHaveReturnedWith", "toReturnWith"], function(value) {
1438
- const spy = getSpy(this);
1439
- const spyName = spy.getMockName();
1440
- const pass = spy.mock.results.some(({ type, value: result }) => type === "return" && equals(value, result));
1441
- const isNot = utils.flag(this, "negate");
1442
- const msg = utils.getMessage(
1443
- this,
1444
- [
1438
+ [
1439
+ {
1440
+ name: "toHaveResolvedTimes",
1441
+ condition: (spy, times) => spy.mock.settledResults.reduce((s, { type }) => type === "fulfilled" ? ++s : s, 0) === times,
1442
+ action: "resolved"
1443
+ },
1444
+ {
1445
+ name: ["toHaveReturnedTimes", "toReturnTimes"],
1446
+ condition: (spy, times) => spy.mock.results.reduce((s, { type }) => type === "throw" ? s : ++s, 0) === times,
1447
+ action: "called"
1448
+ }
1449
+ ].forEach(({ name, condition, action }) => {
1450
+ def(name, function(times) {
1451
+ const spy = getSpy(this);
1452
+ const spyName = spy.getMockName();
1453
+ const pass = condition(spy, times);
1454
+ this.assert(
1445
1455
  pass,
1446
- `expected "${spyName}" to return with: #{exp} at least once`,
1447
- `expected "${spyName}" to not return with: #{exp}`,
1448
- value
1449
- ]
1450
- );
1451
- if (pass && isNot || !pass && !isNot)
1452
- throw new AssertionError(formatReturns(spy, msg, value));
1456
+ `expected "${spyName}" to be successfully ${action} ${times} times`,
1457
+ `expected "${spyName}" to not be successfully ${action} ${times} times`,
1458
+ `expected resolved times: ${times}`,
1459
+ `received resolved times: ${pass}`,
1460
+ false
1461
+ );
1462
+ });
1453
1463
  });
1454
- def(["toHaveLastReturnedWith", "lastReturnedWith"], function(value) {
1455
- const spy = getSpy(this);
1456
- const spyName = spy.getMockName();
1457
- const { value: lastResult } = spy.mock.results[spy.mock.results.length - 1];
1458
- const pass = equals(lastResult, value);
1459
- this.assert(
1460
- pass,
1461
- `expected last "${spyName}" call to return #{exp}`,
1462
- `expected last "${spyName}" call to not return #{exp}`,
1463
- value,
1464
- lastResult
1465
- );
1464
+ [
1465
+ {
1466
+ name: "toHaveResolvedWith",
1467
+ condition: (spy, value) => spy.mock.settledResults.some(({ type, value: result }) => type === "fulfilled" && equals(value, result)),
1468
+ action: "resolve"
1469
+ },
1470
+ {
1471
+ name: ["toHaveReturnedWith", "toReturnWith"],
1472
+ condition: (spy, value) => spy.mock.results.some(({ type, value: result }) => type === "return" && equals(value, result)),
1473
+ action: "return"
1474
+ }
1475
+ ].forEach(({ name, condition, action }) => {
1476
+ def(name, function(value) {
1477
+ const spy = getSpy(this);
1478
+ const pass = condition(spy, value);
1479
+ const isNot = utils.flag(this, "negate");
1480
+ if (pass && isNot || !pass && !isNot) {
1481
+ const spyName = spy.getMockName();
1482
+ const msg = utils.getMessage(
1483
+ this,
1484
+ [
1485
+ pass,
1486
+ `expected "${spyName}" to ${action} with: #{exp} at least once`,
1487
+ `expected "${spyName}" to not ${action} with: #{exp}`,
1488
+ value
1489
+ ]
1490
+ );
1491
+ const results = action === "return" ? spy.mock.results : spy.mock.settledResults;
1492
+ throw new AssertionError(formatReturns(spy, results, msg, value));
1493
+ }
1494
+ });
1466
1495
  });
1467
- def(["toHaveNthReturnedWith", "nthReturnedWith"], function(nthCall, value) {
1468
- const spy = getSpy(this);
1469
- const spyName = spy.getMockName();
1470
- const isNot = utils.flag(this, "negate");
1471
- const { type: callType, value: callResult } = spy.mock.results[nthCall - 1];
1472
- const ordinalCall = `${ordinalOf(nthCall)} call`;
1473
- if (!isNot && callType === "throw")
1474
- chai.assert.fail(`expected ${ordinalCall} to return #{exp}, but instead it threw an error`);
1475
- const nthCallReturn = equals(callResult, value);
1476
- this.assert(
1477
- nthCallReturn,
1478
- `expected ${ordinalCall} "${spyName}" call to return #{exp}`,
1479
- `expected ${ordinalCall} "${spyName}" call to not return #{exp}`,
1480
- value,
1481
- callResult
1482
- );
1496
+ [
1497
+ {
1498
+ name: "toHaveLastResolvedWith",
1499
+ condition: (spy, value) => {
1500
+ const result = spy.mock.settledResults[spy.mock.settledResults.length - 1];
1501
+ return result && result.type === "fulfilled" && equals(result.value, value);
1502
+ },
1503
+ action: "resolve"
1504
+ },
1505
+ {
1506
+ name: ["toHaveLastReturnedWith", "lastReturnedWith"],
1507
+ condition: (spy, value) => {
1508
+ const result = spy.mock.results[spy.mock.results.length - 1];
1509
+ return result && result.type === "return" && equals(result.value, value);
1510
+ },
1511
+ action: "return"
1512
+ }
1513
+ ].forEach(({ name, condition, action }) => {
1514
+ def(name, function(value) {
1515
+ const spy = getSpy(this);
1516
+ const results = action === "return" ? spy.mock.results : spy.mock.settledResults;
1517
+ const result = results[results.length - 1];
1518
+ const spyName = spy.getMockName();
1519
+ this.assert(
1520
+ condition(spy, value),
1521
+ `expected last "${spyName}" call to ${action} #{exp}`,
1522
+ `expected last "${spyName}" call to not ${action} #{exp}`,
1523
+ value,
1524
+ result == null ? void 0 : result.value
1525
+ );
1526
+ });
1527
+ });
1528
+ [
1529
+ {
1530
+ name: "toHaveNthResolvedWith",
1531
+ condition: (spy, index, value) => {
1532
+ const result = spy.mock.settledResults[index - 1];
1533
+ return result && result.type === "fulfilled" && equals(result.value, value);
1534
+ },
1535
+ action: "resolve"
1536
+ },
1537
+ {
1538
+ name: ["toHaveNthReturnedWith", "nthReturnedWith"],
1539
+ condition: (spy, index, value) => {
1540
+ const result = spy.mock.results[index - 1];
1541
+ return result && result.type === "return" && equals(result.value, value);
1542
+ },
1543
+ action: "return"
1544
+ }
1545
+ ].forEach(({ name, condition, action }) => {
1546
+ def(name, function(nthCall, value) {
1547
+ const spy = getSpy(this);
1548
+ const spyName = spy.getMockName();
1549
+ const results = action === "return" ? spy.mock.results : spy.mock.settledResults;
1550
+ const result = results[nthCall - 1];
1551
+ const ordinalCall = `${ordinalOf(nthCall)} call`;
1552
+ this.assert(
1553
+ condition(spy, nthCall, value),
1554
+ `expected ${ordinalCall} "${spyName}" call to ${action} #{exp}`,
1555
+ `expected ${ordinalCall} "${spyName}" call to not ${action} #{exp}`,
1556
+ value,
1557
+ result == null ? void 0 : result.value
1558
+ );
1559
+ });
1483
1560
  });
1484
1561
  def("toSatisfy", function(matcher, message) {
1485
1562
  return this.be.satisfy(matcher, message);
1486
1563
  });
1564
+ def("withContext", function(context) {
1565
+ for (const key in context)
1566
+ utils.flag(this, key, context[key]);
1567
+ return this;
1568
+ });
1487
1569
  utils.addProperty(chai.Assertion.prototype, "resolves", function __VITEST_RESOLVES__() {
1488
1570
  const error = new Error("resolves");
1489
1571
  utils.flag(this, "promise", "resolves");
1490
1572
  utils.flag(this, "error", error);
1491
1573
  const test = utils.flag(this, "vitest-test");
1492
1574
  const obj = utils.flag(this, "object");
1575
+ if (utils.flag(this, "poll"))
1576
+ throw new SyntaxError(`expect.poll() is not supported in combination with .resolves`);
1493
1577
  if (typeof (obj == null ? void 0 : obj.then) !== "function")
1494
1578
  throw new TypeError(`You must provide a Promise to expect() when using .resolves, not '${typeof obj}'.`);
1495
1579
  const proxy = new Proxy(this, {
@@ -1526,6 +1610,8 @@ Number of calls: ${c().bold(spy.mock.calls.length)}
1526
1610
  const test = utils.flag(this, "vitest-test");
1527
1611
  const obj = utils.flag(this, "object");
1528
1612
  const wrapper = typeof obj === "function" ? obj() : obj;
1613
+ if (utils.flag(this, "poll"))
1614
+ throw new SyntaxError(`expect.poll() is not supported in combination with .rejects`);
1529
1615
  if (typeof (wrapper == null ? void 0 : wrapper.then) !== "function")
1530
1616
  throw new TypeError(`You must provide a Promise to expect() when using .rejects, not '${typeof wrapper}'.`);
1531
1617
  const proxy = new Proxy(this, {
@@ -1575,7 +1661,9 @@ function getMatcherState(assertion, expect) {
1575
1661
  promise,
1576
1662
  equals,
1577
1663
  // needed for built-in jest-snapshots, but we don't use it
1578
- suppressedErrors: []
1664
+ suppressedErrors: [],
1665
+ soft: util.flag(assertion, "soft"),
1666
+ poll: util.flag(assertion, "poll")
1579
1667
  };
1580
1668
  return {
1581
1669
  state: matcherState,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@vitest/expect",
3
3
  "type": "module",
4
- "version": "2.0.0-beta.1",
4
+ "version": "2.0.0-beta.10",
5
5
  "description": "Jest's expect matchers as a Chai plugin",
6
6
  "license": "MIT",
7
7
  "funding": "https://opencollective.com/vitest",
@@ -31,14 +31,14 @@
31
31
  ],
32
32
  "dependencies": {
33
33
  "chai": "^5.1.1",
34
- "@vitest/spy": "2.0.0-beta.1",
35
- "@vitest/utils": "2.0.0-beta.1"
34
+ "@vitest/spy": "2.0.0-beta.10",
35
+ "@vitest/utils": "2.0.0-beta.10"
36
36
  },
37
37
  "devDependencies": {
38
38
  "@types/chai": "4.3.6",
39
- "picocolors": "^1.0.0",
39
+ "picocolors": "^1.0.1",
40
40
  "rollup-plugin-copy": "^3.5.0",
41
- "@vitest/runner": "2.0.0-beta.1"
41
+ "@vitest/runner": "2.0.0-beta.10"
42
42
  },
43
43
  "scripts": {
44
44
  "build": "rimraf dist && rollup -c",