@vitest/expect 2.0.0-beta.2 → 2.0.0-beta.4

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) {
@@ -661,7 +661,7 @@ class Any extends AsymmetricMatcher {
661
661
  if (func.name)
662
662
  return func.name;
663
663
  const functionToString = Function.prototype.toString;
664
- const matches = functionToString.call(func).match(/^(?:async)?\s*function\s*\*?\s*([\w$]+)\s*\(/);
664
+ const matches = functionToString.call(func).match(/^(?:async)?\s*function\s*(?:\*\s*)?([\w$]+)\s*\(/);
665
665
  return matches ? matches[1] : "<anonymous>";
666
666
  }
667
667
  asymmetricMatch(other) {
@@ -817,10 +817,9 @@ function recordAsyncExpect(test, promise) {
817
817
  function wrapSoft(utils, fn) {
818
818
  return function(...args) {
819
819
  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)
820
+ if (!utils.flag(this, "soft"))
823
821
  return fn.apply(this, args);
822
+ const test = utils.flag(this, "vitest-test");
824
823
  if (!test)
825
824
  throw new Error("expect.soft() can only be used inside a test");
826
825
  try {
@@ -1205,7 +1204,7 @@ const JestChaiExpect = (chai, utils) => {
1205
1204
  return `${i}rd`;
1206
1205
  return `${i}th`;
1207
1206
  };
1208
- const formatCalls = (spy, msg, actualCall) => {
1207
+ const formatCalls = (spy, msg, showActualCall) => {
1209
1208
  if (spy.mock.calls) {
1210
1209
  msg += c().gray(`
1211
1210
 
@@ -1215,8 +1214,8 @@ ${spy.mock.calls.map((callArg, i) => {
1215
1214
  let methodCall = c().bold(` ${ordinalOf(i + 1)} ${spy.getMockName()} call:
1216
1215
 
1217
1216
  `);
1218
- if (actualCall)
1219
- methodCall += diff(actualCall, callArg, { omitAnnotationLines: true });
1217
+ if (showActualCall)
1218
+ methodCall += diff(showActualCall, callArg, { omitAnnotationLines: true });
1220
1219
  else
1221
1220
  methodCall += stringify(callArg).split("\n").map((line) => ` ${line}`).join("\n");
1222
1221
  methodCall += "\n";
@@ -1229,17 +1228,17 @@ Number of calls: ${c().bold(spy.mock.calls.length)}
1229
1228
  `);
1230
1229
  return msg;
1231
1230
  };
1232
- const formatReturns = (spy, msg, actualReturn) => {
1231
+ const formatReturns = (spy, results, msg, showActualReturn) => {
1233
1232
  msg += c().gray(`
1234
1233
 
1235
1234
  Received:
1236
1235
 
1237
- ${spy.mock.results.map((callReturn, i) => {
1236
+ ${results.map((callReturn, i) => {
1238
1237
  let methodCall = c().bold(` ${ordinalOf(i + 1)} ${spy.getMockName()} call return:
1239
1238
 
1240
1239
  `);
1241
- if (actualReturn)
1242
- methodCall += diff(actualReturn, callReturn.value, { omitAnnotationLines: true });
1240
+ if (showActualReturn)
1241
+ methodCall += diff(showActualReturn, callReturn.value, { omitAnnotationLines: true });
1243
1242
  else
1244
1243
  methodCall += stringify(callReturn).split("\n").map((line) => ` ${line}`).join("\n");
1245
1244
  methodCall += "\n";
@@ -1408,88 +1407,171 @@ Number of calls: ${c().bold(spy.mock.calls.length)}
1408
1407
  }
1409
1408
  throw new Error(`"toThrow" expects string, RegExp, function, Error instance or asymmetric matcher, got "${typeof expected}"`);
1410
1409
  });
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
- );
1410
+ [
1411
+ {
1412
+ name: "toHaveResolved",
1413
+ condition: (spy) => spy.mock.settledResults.length > 0 && spy.mock.settledResults.some(({ type }) => type === "fulfilled"),
1414
+ action: "resolved"
1415
+ },
1416
+ {
1417
+ name: ["toHaveReturned", "toReturn"],
1418
+ condition: (spy) => spy.mock.calls.length > 0 && spy.mock.results.some(({ type }) => type !== "throw"),
1419
+ action: "called"
1420
+ }
1421
+ ].forEach(({ name, condition, action }) => {
1422
+ def(name, function() {
1423
+ const spy = getSpy(this);
1424
+ const spyName = spy.getMockName();
1425
+ const pass = condition(spy);
1426
+ this.assert(
1427
+ pass,
1428
+ `expected "${spyName}" to be successfully ${action} at least once`,
1429
+ `expected "${spyName}" to not be successfully ${action}`,
1430
+ pass,
1431
+ !pass,
1432
+ false
1433
+ );
1434
+ });
1436
1435
  });
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
- [
1436
+ [
1437
+ {
1438
+ name: "toHaveResolvedTimes",
1439
+ condition: (spy, times) => spy.mock.settledResults.reduce((s, { type }) => type === "fulfilled" ? ++s : s, 0) === times,
1440
+ action: "resolved"
1441
+ },
1442
+ {
1443
+ name: ["toHaveReturnedTimes", "toReturnTimes"],
1444
+ condition: (spy, times) => spy.mock.results.reduce((s, { type }) => type === "throw" ? s : ++s, 0) === times,
1445
+ action: "called"
1446
+ }
1447
+ ].forEach(({ name, condition, action }) => {
1448
+ def(name, function(times) {
1449
+ const spy = getSpy(this);
1450
+ const spyName = spy.getMockName();
1451
+ const pass = condition(spy, times);
1452
+ this.assert(
1445
1453
  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));
1454
+ `expected "${spyName}" to be successfully ${action} ${times} times`,
1455
+ `expected "${spyName}" to not be successfully ${action} ${times} times`,
1456
+ `expected resolved times: ${times}`,
1457
+ `received resolved times: ${pass}`,
1458
+ false
1459
+ );
1460
+ });
1453
1461
  });
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
- );
1462
+ [
1463
+ {
1464
+ name: "toHaveResolvedWith",
1465
+ condition: (spy, value) => spy.mock.settledResults.some(({ type, value: result }) => type === "fulfilled" && equals(value, result)),
1466
+ action: "resolve"
1467
+ },
1468
+ {
1469
+ name: ["toHaveReturnedWith", "toReturnWith"],
1470
+ condition: (spy, value) => spy.mock.results.some(({ type, value: result }) => type === "return" && equals(value, result)),
1471
+ action: "return"
1472
+ }
1473
+ ].forEach(({ name, condition, action }) => {
1474
+ def(name, function(value) {
1475
+ const spy = getSpy(this);
1476
+ const pass = condition(spy, value);
1477
+ const isNot = utils.flag(this, "negate");
1478
+ if (pass && isNot || !pass && !isNot) {
1479
+ const spyName = spy.getMockName();
1480
+ const msg = utils.getMessage(
1481
+ this,
1482
+ [
1483
+ pass,
1484
+ `expected "${spyName}" to ${action} with: #{exp} at least once`,
1485
+ `expected "${spyName}" to not ${action} with: #{exp}`,
1486
+ value
1487
+ ]
1488
+ );
1489
+ const results = action === "return" ? spy.mock.results : spy.mock.settledResults;
1490
+ throw new AssertionError(formatReturns(spy, results, msg, value));
1491
+ }
1492
+ });
1466
1493
  });
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
- );
1494
+ [
1495
+ {
1496
+ name: "toHaveLastResolvedWith",
1497
+ condition: (spy, value) => {
1498
+ const result = spy.mock.settledResults[spy.mock.settledResults.length - 1];
1499
+ return result && result.type === "fulfilled" && equals(result.value, value);
1500
+ },
1501
+ action: "resolve"
1502
+ },
1503
+ {
1504
+ name: ["toHaveLastReturnedWith", "lastReturnedWith"],
1505
+ condition: (spy, value) => {
1506
+ const result = spy.mock.results[spy.mock.results.length - 1];
1507
+ return result && result.type === "return" && equals(result.value, value);
1508
+ },
1509
+ action: "return"
1510
+ }
1511
+ ].forEach(({ name, condition, action }) => {
1512
+ def(name, function(value) {
1513
+ const spy = getSpy(this);
1514
+ const results = action === "return" ? spy.mock.results : spy.mock.settledResults;
1515
+ const result = results[results.length - 1];
1516
+ const spyName = spy.getMockName();
1517
+ this.assert(
1518
+ condition(spy, value),
1519
+ `expected last "${spyName}" call to ${action} #{exp}`,
1520
+ `expected last "${spyName}" call to not ${action} #{exp}`,
1521
+ value,
1522
+ result == null ? void 0 : result.value
1523
+ );
1524
+ });
1525
+ });
1526
+ [
1527
+ {
1528
+ name: "toHaveNthResolvedWith",
1529
+ condition: (spy, index, value) => {
1530
+ const result = spy.mock.settledResults[index - 1];
1531
+ return result && result.type === "fulfilled" && equals(result.value, value);
1532
+ },
1533
+ action: "resolve"
1534
+ },
1535
+ {
1536
+ name: ["toHaveNthReturnedWith", "nthReturnedWith"],
1537
+ condition: (spy, index, value) => {
1538
+ const result = spy.mock.results[index - 1];
1539
+ return result && result.type === "return" && equals(result.value, value);
1540
+ },
1541
+ action: "return"
1542
+ }
1543
+ ].forEach(({ name, condition, action }) => {
1544
+ def(name, function(nthCall, value) {
1545
+ const spy = getSpy(this);
1546
+ const spyName = spy.getMockName();
1547
+ const results = action === "return" ? spy.mock.results : spy.mock.settledResults;
1548
+ const result = results[nthCall - 1];
1549
+ const ordinalCall = `${ordinalOf(nthCall)} call`;
1550
+ this.assert(
1551
+ condition(spy, nthCall, value),
1552
+ `expected ${ordinalCall} "${spyName}" call to ${action} #{exp}`,
1553
+ `expected ${ordinalCall} "${spyName}" call to not ${action} #{exp}`,
1554
+ value,
1555
+ result == null ? void 0 : result.value
1556
+ );
1557
+ });
1483
1558
  });
1484
1559
  def("toSatisfy", function(matcher, message) {
1485
1560
  return this.be.satisfy(matcher, message);
1486
1561
  });
1562
+ def("withContext", function(context) {
1563
+ for (const key in context)
1564
+ utils.flag(this, key, context[key]);
1565
+ return this;
1566
+ });
1487
1567
  utils.addProperty(chai.Assertion.prototype, "resolves", function __VITEST_RESOLVES__() {
1488
1568
  const error = new Error("resolves");
1489
1569
  utils.flag(this, "promise", "resolves");
1490
1570
  utils.flag(this, "error", error);
1491
1571
  const test = utils.flag(this, "vitest-test");
1492
1572
  const obj = utils.flag(this, "object");
1573
+ if (utils.flag(this, "poll"))
1574
+ throw new SyntaxError(`expect.poll() is not supported in combination with .resolves`);
1493
1575
  if (typeof (obj == null ? void 0 : obj.then) !== "function")
1494
1576
  throw new TypeError(`You must provide a Promise to expect() when using .resolves, not '${typeof obj}'.`);
1495
1577
  const proxy = new Proxy(this, {
@@ -1526,6 +1608,8 @@ Number of calls: ${c().bold(spy.mock.calls.length)}
1526
1608
  const test = utils.flag(this, "vitest-test");
1527
1609
  const obj = utils.flag(this, "object");
1528
1610
  const wrapper = typeof obj === "function" ? obj() : obj;
1611
+ if (utils.flag(this, "poll"))
1612
+ throw new SyntaxError(`expect.poll() is not supported in combination with .rejects`);
1529
1613
  if (typeof (wrapper == null ? void 0 : wrapper.then) !== "function")
1530
1614
  throw new TypeError(`You must provide a Promise to expect() when using .rejects, not '${typeof wrapper}'.`);
1531
1615
  const proxy = new Proxy(this, {
@@ -1575,7 +1659,9 @@ function getMatcherState(assertion, expect) {
1575
1659
  promise,
1576
1660
  equals,
1577
1661
  // needed for built-in jest-snapshots, but we don't use it
1578
- suppressedErrors: []
1662
+ suppressedErrors: [],
1663
+ soft: util.flag(assertion, "soft"),
1664
+ poll: util.flag(assertion, "poll")
1579
1665
  };
1580
1666
  return {
1581
1667
  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.2",
4
+ "version": "2.0.0-beta.4",
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/utils": "2.0.0-beta.2",
35
- "@vitest/spy": "2.0.0-beta.2"
34
+ "@vitest/spy": "2.0.0-beta.4",
35
+ "@vitest/utils": "2.0.0-beta.4"
36
36
  },
37
37
  "devDependencies": {
38
38
  "@types/chai": "4.3.6",
39
39
  "picocolors": "^1.0.0",
40
40
  "rollup-plugin-copy": "^3.5.0",
41
- "@vitest/runner": "2.0.0-beta.2"
41
+ "@vitest/runner": "2.0.0-beta.4"
42
42
  },
43
43
  "scripts": {
44
44
  "build": "rimraf dist && rollup -c",