vitest-auto-spy 4.2.0 → 4.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/AGENTS.md +89 -2
  2. package/README.md +135 -6
  3. package/dist/angular.d.ts +2 -2
  4. package/dist/angular.js +276 -7
  5. package/dist/bun-angular.d.ts +3 -3
  6. package/dist/bun-angular.js +6 -6
  7. package/dist/bun.d.ts +72 -2
  8. package/dist/bun.js +4 -4
  9. package/dist/{chunk-DSIYS6Y5.js → chunk-D5LDT3W4.js} +1 -1
  10. package/dist/{chunk-2ZG27FVL.js → chunk-EMASXQDZ.js} +1 -1
  11. package/dist/{chunk-4KK3ES4P.js → chunk-FFKN6QKI.js} +22 -0
  12. package/dist/{chunk-4KCYFAJX.js → chunk-FYFF67EN.js} +23 -7
  13. package/dist/{chunk-5URFSYDT.js → chunk-GAY6LNAM.js} +300 -6
  14. package/dist/{chunk-WQZ2RKS7.js → chunk-KYAJCFUA.js} +1 -1
  15. package/dist/{chunk-KEKKEIRG.js → chunk-Z4VRMCW2.js} +1 -1
  16. package/dist/cli.js +606 -16
  17. package/dist/console.js +2 -2
  18. package/dist/diagnostics.d.ts +31 -1
  19. package/dist/diagnostics.js +350 -1
  20. package/dist/{expect-emission-BDuFuqTb.d.ts → expect-emission-Dy5CvsF2.d.ts} +27 -25
  21. package/dist/index.d.ts +2 -2
  22. package/dist/index.js +444 -111
  23. package/dist/jasmine.js +4 -4
  24. package/dist/nestjs.js +5 -5
  25. package/dist/node.cjs +272 -111
  26. package/dist/node.d.cts +2 -0
  27. package/dist/node.d.ts +2 -2
  28. package/dist/node.js +17 -5
  29. package/dist/react.d.ts +2 -2
  30. package/dist/react.js +5 -5
  31. package/dist/setup.js +4 -4
  32. package/dist/shared-state.js +4 -275
  33. package/dist/svelte.d.ts +2 -2
  34. package/dist/svelte.js +5 -5
  35. package/dist/vue.d.ts +2 -2
  36. package/dist/vue.js +6 -6
  37. package/dist/{zoneless-DGwkbWKm.d.ts → zoneless-LsBDcSNZ.d.ts} +1 -1
  38. package/package.json +6 -2
  39. package/skills/vitest-auto-spy/SKILL.md +4 -1
package/dist/node.cjs CHANGED
@@ -193,6 +193,18 @@ function spyOnAccessorByRedefine(createMockFn, target, property, type) {
193
193
  function asNodeMock(mockFn) {
194
194
  return mockFn;
195
195
  }
196
+ function nameImplementation(implementation, name) {
197
+ if (name === void 0) {
198
+ return implementation;
199
+ }
200
+ const named = {
201
+ // eslint-disable-next-line object-shorthand -- a concise method is not constructable, and `mockConstructor` has the code under test call `new` on this; a function expression is, and is still named by its computed key.
202
+ [name]: function(...args) {
203
+ return implementation.apply(this, args);
204
+ }
205
+ };
206
+ return named[name];
207
+ }
196
208
  function nameNodeMock(mockFn, name) {
197
209
  if (name !== void 0) {
198
210
  Object.defineProperty(mockFn, "displayName", { value: name, configurable: true });
@@ -202,7 +214,7 @@ function nameNodeMock(mockFn, name) {
202
214
  function createNodeMockAdapter(nodeTest) {
203
215
  return guardAccessorSpies(
204
216
  createRedefineMockAdapter({
205
- createMockFn: (implementation, name) => nameNodeMock(nodeTest.fn(implementation ?? (() => void 0)), name),
217
+ createMockFn: (implementation, name) => nameNodeMock(nodeTest.fn(nameImplementation(implementation ?? (() => void 0), name)), name),
206
218
  getCalls: (mockFn) => asNodeMock(mockFn).mock.calls.map((call) => call.arguments),
207
219
  reset: (mockFn) => asNodeMock(mockFn).mock.resetCalls(),
208
220
  // `node:test` mocks reset call history via `resetCalls()`; there is no
@@ -567,6 +579,28 @@ var ArgsMap = class {
567
579
  const asymmetric = this.#matcherConfigs.map((config) => `[${config.described.join(",")}]`);
568
580
  return [...Object.keys(this.#map), ...asymmetric];
569
581
  }
582
+ /**
583
+ * Every configured argument list as a {@link ConfiguredEntry} — the same lists {@link configured}
584
+ * returns, each carrying its own position and its own predicate.
585
+ *
586
+ * The order is the order a lookup consults them (exact configs first, then the asymmetric ones in
587
+ * registration order), so the first entry whose `matches` holds is the config `get` would have
588
+ * answered from. That is what lets a reader be told "call 3 hit config 2" instead of a list of
589
+ * configs and a list of calls with nothing joining them.
590
+ */
591
+ configuredEntries() {
592
+ const exact = Object.keys(this.#map).map((key, position) => ({
593
+ index: position + 1,
594
+ args: key,
595
+ matches: (actualArgs) => this.#serialize(actualArgs) === key
596
+ }));
597
+ const asymmetric = this.#matcherConfigs.map((config, position) => ({
598
+ index: exact.length + position + 1,
599
+ args: `[${config.described.join(",")}]`,
600
+ matches: (actualArgs) => this.#argsMatch(config, actualArgs)
601
+ }));
602
+ return [...exact, ...asymmetric];
603
+ }
570
604
  /** Render one asymmetric config once, at registration time. See {@link MatcherConfig}. */
571
605
  #buildMatcherConfig(key, value) {
572
606
  const serialized = [];
@@ -1351,6 +1385,17 @@ function walkOwnPrototypes(prototype, visit) {
1351
1385
  current = parent;
1352
1386
  }
1353
1387
  }
1388
+ function getCallableMemberNames(target) {
1389
+ const names = /* @__PURE__ */ new Set();
1390
+ walkOwnPrototypes(target, (obj) => {
1391
+ for (const [name, descriptor] of Object.entries(Object.getOwnPropertyDescriptors(obj))) {
1392
+ if (name !== "constructor" && typeof descriptor.value === "function") {
1393
+ names.add(name);
1394
+ }
1395
+ }
1396
+ });
1397
+ return [...names];
1398
+ }
1354
1399
  var methodNamesCache = /* @__PURE__ */ new WeakMap();
1355
1400
  function getAllMethodNames(prototype) {
1356
1401
  const cached = methodNamesCache.get(prototype);
@@ -1408,7 +1453,12 @@ function resolveAccessors(prototype, config) {
1408
1453
  };
1409
1454
  }
1410
1455
  function resolveMethodNames(ObjectClass, config) {
1411
- const base = config.onlyMethodsToSpyOn.length > 0 ? config.onlyMethodsToSpyOn : getAllMethodNames(ObjectClass.prototype);
1456
+ return mergeMethodNames(
1457
+ config.onlyMethodsToSpyOn.length > 0 ? config.onlyMethodsToSpyOn : getAllMethodNames(ObjectClass.prototype),
1458
+ config
1459
+ );
1460
+ }
1461
+ function mergeMethodNames(base, config) {
1412
1462
  if (config.methodsToSpyOn.length === 0 && config.instanceMethodsToSpyOn.length === 0) {
1413
1463
  return base;
1414
1464
  }
@@ -1447,7 +1497,7 @@ function warnOnAccessorNamingAMethod(ObjectClass, config) {
1447
1497
  function isCallable2(value) {
1448
1498
  return typeof value === "function";
1449
1499
  }
1450
- function applyReturns(autoSpy, ObjectClass, returns) {
1500
+ function applyReturns(autoSpy, factory, returns) {
1451
1501
  const entries = Object.entries(returns);
1452
1502
  if (entries.length === 0) {
1453
1503
  return;
@@ -1458,7 +1508,7 @@ function applyReturns(autoSpy, ObjectClass, returns) {
1458
1508
  if (!isCallable2(spy)) {
1459
1509
  console.warn(
1460
1510
  withDocs(
1461
- `[vitest-auto-spy] createSpyFromClass(${ObjectClass.name}): returns names '${name}', which is not a spied method of the spy. Check the spelling, and check that a restricting onlyMethodsToSpyOn list did not leave it out \u2014 a value configured for a method that is not there is silently never returned.`,
1511
+ `[vitest-auto-spy] ${factory}: returns names '${name}', which is not a spied method of the spy. Check the spelling, and check that a restricting onlyMethodsToSpyOn list did not leave it out \u2014 a value configured for a method that is not there is silently never returned.`,
1462
1512
  DOCS_LINKS.createSpyFromClass
1463
1513
  )
1464
1514
  );
@@ -1510,7 +1560,7 @@ function resolveConfiguration(methodsToSpyOnOrConfig) {
1510
1560
  function createSpyFromClass(ObjectClass, methodsToSpyOnOrConfig) {
1511
1561
  const config = resolveConfiguration(methodsToSpyOnOrConfig);
1512
1562
  const autoSpy = assembleSpy(ObjectClass, config);
1513
- applyReturns(autoSpy, ObjectClass, config.returns);
1563
+ applyReturns(autoSpy, `createSpyFromClass(${ObjectClass.name})`, config.returns);
1514
1564
  applyOverrides(autoSpy, config.overrides);
1515
1565
  return autoSpy;
1516
1566
  }
@@ -1557,6 +1607,155 @@ function hasNothingToRead(ObjectClass, accessors, config) {
1557
1607
  config.onlyMethodsToSpyOn.length === 0;
1558
1608
  }
1559
1609
 
1610
+ // src/lib/prop-mock.ts
1611
+ function getPatchedProps() {
1612
+ return globalThis.__vitestAutoSpyPatchedProps__ ??= [];
1613
+ }
1614
+ function rememberProp(object, property, descriptor) {
1615
+ const patch = {
1616
+ // The helpers only ever patch objects; the cast bridges the generic `T` of the public API.
1617
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- `T` is unconstrained on the public signatures, but every caller passes an object (a service instance, a class prototype or a global).
1618
+ object,
1619
+ property,
1620
+ descriptor,
1621
+ undone: false
1622
+ };
1623
+ getPatchedProps().push(patch);
1624
+ return () => {
1625
+ if (patch.undone) {
1626
+ return;
1627
+ }
1628
+ patch.undone = true;
1629
+ restorePatch(patch);
1630
+ };
1631
+ }
1632
+ function applyPatch(object, property, descriptor) {
1633
+ const previous = Object.getOwnPropertyDescriptor(object, property);
1634
+ try {
1635
+ Object.defineProperty(object, property, descriptor);
1636
+ } catch (error) {
1637
+ if (isCannotRedefine(error)) {
1638
+ throw redefineFailure(
1639
+ `Cannot mock the property '${String(property)}': it is not configurable, so it cannot be redefined.`,
1640
+ Object(object),
1641
+ error
1642
+ );
1643
+ }
1644
+ throw error;
1645
+ }
1646
+ return rememberProp(object, property, previous);
1647
+ }
1648
+ function restorePatch({ object, property, descriptor }) {
1649
+ if (descriptor) {
1650
+ Object.defineProperty(object, property, descriptor);
1651
+ return;
1652
+ }
1653
+ Reflect.deleteProperty(object, property);
1654
+ }
1655
+ function describeRestoreFailures(failures) {
1656
+ return withDocs(
1657
+ `[vitest-auto-spy] restoreMockedProps() could not put ${failures.length} of the patched properties back:
1658
+ ${failures.join("\n")}
1659
+ A property that was redefined as non-configurable can never be restored \u2014 \`Object.defineProperty\` defaults \`configurable\` to \`false\`, so a plain redefinition of an already-mocked property seals it for the rest of the worker. \`setupAutoSpy({ guardGlobals: 'throw' })\` names the test that does it. Every other patch of this sweep was restored, and the journal is empty either way \u2014 nothing here is replayed against a descriptor that has since moved on.`,
1660
+ DOCS_LINKS.setup
1661
+ );
1662
+ }
1663
+ function countMockedProps() {
1664
+ return getPatchedProps().filter((patch) => !patch.undone).length;
1665
+ }
1666
+ function restoreMockedProps() {
1667
+ const patchedProps = getPatchedProps();
1668
+ const pending = [...patchedProps].reverse();
1669
+ const failures = [];
1670
+ patchedProps.length = 0;
1671
+ for (const patch of pending) {
1672
+ if (patch.undone) {
1673
+ continue;
1674
+ }
1675
+ patch.undone = true;
1676
+ try {
1677
+ restorePatch(patch);
1678
+ } catch (error) {
1679
+ failures.push(` - ${String(patch.property)}: ${String(error)}`);
1680
+ }
1681
+ }
1682
+ if (failures.length > 0) {
1683
+ throw new Error(describeRestoreFailures(failures));
1684
+ }
1685
+ }
1686
+ function mockReadonlyProp(object, property, value) {
1687
+ return applyPatch(object, property, { get: () => value, configurable: true });
1688
+ }
1689
+ function mockReadonlyPropGetter(object, property, getter) {
1690
+ return applyPatch(object, property, { get: getter, configurable: true });
1691
+ }
1692
+ function mockValueProp(object, property, value) {
1693
+ return applyPatch(object, property, { value, writable: true, configurable: true });
1694
+ }
1695
+ function mockAccessorsProp(object, property, accessors) {
1696
+ const adapter = getMockAdapter();
1697
+ return applyPatch(object, property, {
1698
+ get: adapter.createMockFn(accessors?.get),
1699
+ set: adapter.createMockFn(accessors?.set),
1700
+ configurable: true
1701
+ });
1702
+ }
1703
+
1704
+ // src/lib/create-spy-from-instance.ts
1705
+ var installedSpies = /* @__PURE__ */ new WeakMap();
1706
+ function constructorName(instance) {
1707
+ const constructor = Reflect.get(instance, "constructor");
1708
+ return typeof constructor === "function" ? constructor.name : void 0;
1709
+ }
1710
+ function installMember(instance, name, value, restores) {
1711
+ restores.push(mockValueProp(instance, name, value));
1712
+ Object.defineProperty(instance, name, { enumerable: true });
1713
+ }
1714
+ function installAccessorSpies(instance, config, restores) {
1715
+ const { getters, setters } = resolveAccessors(instance, config);
1716
+ [.../* @__PURE__ */ new Set([...getters, ...setters])].forEach((name) => restores.push(mockAccessorsProp(instance, name)));
1717
+ restores.push(mockValueProp(instance, "accessorSpies", void 0));
1718
+ createAccessorsSpies(instance, getters, setters);
1719
+ }
1720
+ function disposeSpiedInstance() {
1721
+ restoreSpiedInstance(this);
1722
+ }
1723
+ function restoreSpiedInstance(instance) {
1724
+ const restores = installedSpies.get(instance);
1725
+ if (!restores) {
1726
+ return;
1727
+ }
1728
+ installedSpies.delete(instance);
1729
+ [...restores].reverse().forEach((restore) => restore());
1730
+ }
1731
+ function createSpyFromInstance(instance, methodsToSpyOnOrConfig) {
1732
+ if (!Object.isExtensible(instance)) {
1733
+ throw redefineFailure(
1734
+ "Cannot spy on this instance in place: it is not extensible, so its members cannot be replaced.",
1735
+ instance,
1736
+ void 0
1737
+ );
1738
+ }
1739
+ const config = resolveConfiguration(methodsToSpyOnOrConfig);
1740
+ const className = constructorName(instance);
1741
+ const unstubbed = resolveUnstubbedGuard(className, config);
1742
+ const restores = installedSpies.get(instance) ?? [];
1743
+ installedSpies.set(instance, restores);
1744
+ const methodNames = mergeMethodNames(
1745
+ config.onlyMethodsToSpyOn.length > 0 ? config.onlyMethodsToSpyOn : getCallableMemberNames(instance),
1746
+ config
1747
+ );
1748
+ methodNames.forEach((name) => installMember(instance, name, createFunctionSpy(name, unstubbed), restores));
1749
+ config.observablePropsToSpyOn.forEach((name) => installMember(instance, name, requireObservableSupport().createPropSpy(), restores));
1750
+ installAccessorSpies(instance, config, restores);
1751
+ applyReturns(instance, `createSpyFromInstance(${className ?? "object"})`, config.returns);
1752
+ for (const key of Reflect.ownKeys(config.overrides)) {
1753
+ installMember(instance, key, Reflect.get(config.overrides, key), restores);
1754
+ }
1755
+ restores.push(mockValueProp(instance, DISPOSE, disposeSpiedInstance));
1756
+ return instance;
1757
+ }
1758
+
1560
1759
  // src/lib/create-mock.ts
1561
1760
  function createMock(partial) {
1562
1761
  return partial ?? {};
@@ -1752,98 +1951,53 @@ function captureArg() {
1752
1951
  return new Captor();
1753
1952
  }
1754
1953
 
1755
- // src/lib/prop-mock.ts
1756
- function getPatchedProps() {
1757
- return globalThis.__vitestAutoSpyPatchedProps__ ??= [];
1758
- }
1759
- function rememberProp(object, property, descriptor) {
1760
- const patch = {
1761
- // The helpers only ever patch objects; the cast bridges the generic `T` of the public API.
1762
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- `T` is unconstrained on the public signatures, but every caller passes an object (a service instance, a class prototype or a global).
1763
- object,
1764
- property,
1765
- descriptor,
1766
- undone: false
1767
- };
1768
- getPatchedProps().push(patch);
1769
- return () => {
1770
- if (patch.undone) {
1771
- return;
1772
- }
1773
- patch.undone = true;
1774
- restorePatch(patch);
1775
- };
1954
+ // src/lib/emission-timeout.ts
1955
+ var defaultTimeoutMs = 1e3;
1956
+ function setEmissionTimeout(milliseconds) {
1957
+ defaultTimeoutMs = milliseconds;
1776
1958
  }
1777
- function applyPatch(object, property, descriptor) {
1778
- const previous = Object.getOwnPropertyDescriptor(object, property);
1779
- try {
1780
- Object.defineProperty(object, property, descriptor);
1781
- } catch (error) {
1782
- if (isCannotRedefine(error)) {
1783
- throw redefineFailure(
1784
- `Cannot mock the property '${String(property)}': it is not configurable, so it cannot be redefined.`,
1785
- Object(object),
1786
- error
1787
- );
1788
- }
1789
- throw error;
1790
- }
1791
- return rememberProp(object, property, previous);
1959
+ function emissionTimeout() {
1960
+ return defaultTimeoutMs;
1792
1961
  }
1793
- function restorePatch({ object, property, descriptor }) {
1794
- if (descriptor) {
1795
- Object.defineProperty(object, property, descriptor);
1796
- return;
1797
- }
1798
- Reflect.deleteProperty(object, property);
1799
- }
1800
- function describeRestoreFailures(failures) {
1801
- return withDocs(
1802
- `[vitest-auto-spy] restoreMockedProps() could not put ${failures.length} of the patched properties back:
1803
- ${failures.join("\n")}
1804
- A property that was redefined as non-configurable can never be restored \u2014 \`Object.defineProperty\` defaults \`configurable\` to \`false\`, so a plain redefinition of an already-mocked property seals it for the rest of the worker. \`setupAutoSpy({ guardGlobals: 'throw' })\` names the test that does it. Every other patch of this sweep was restored, and the journal is empty either way \u2014 nothing here is replayed against a descriptor that has since moved on.`,
1805
- DOCS_LINKS.setup
1806
- );
1962
+
1963
+ // src/lib/error-anchor.ts
1964
+ var OWN_FAILURE = /* @__PURE__ */ Symbol("vitest-auto-spy.own-failure");
1965
+ function ownFailure(message, options) {
1966
+ const error = new Error(message, options);
1967
+ Object.defineProperty(error, OWN_FAILURE, { value: true });
1968
+ return error;
1807
1969
  }
1808
- function countMockedProps() {
1809
- return getPatchedProps().filter((patch) => !patch.undone).length;
1970
+ function anchorOf(stack, boundaryName) {
1971
+ const frames = framesAfter(frameLines(stack), boundaryName);
1972
+ return (error) => anchor(frames, error);
1810
1973
  }
1811
- function restoreMockedProps() {
1812
- const patchedProps = getPatchedProps();
1813
- const pending = [...patchedProps].reverse();
1814
- const failures = [];
1815
- patchedProps.length = 0;
1816
- for (const patch of pending) {
1817
- if (patch.undone) {
1818
- continue;
1819
- }
1820
- patch.undone = true;
1821
- try {
1822
- restorePatch(patch);
1823
- } catch (error) {
1824
- failures.push(` - ${String(patch.property)}: ${String(error)}`);
1825
- }
1974
+ function captureAnchor(boundary, host2 = Error) {
1975
+ const holder = new Error();
1976
+ const capture = host2.captureStackTrace;
1977
+ if (capture) {
1978
+ capture(holder, boundary);
1979
+ return anchorOf(holder.stack, "");
1826
1980
  }
1827
- if (failures.length > 0) {
1828
- throw new Error(describeRestoreFailures(failures));
1981
+ return anchorOf(holder.stack, boundary.name);
1982
+ }
1983
+ function anchor(frames, error) {
1984
+ if (frames.length === 0 || Reflect.get(error, OWN_FAILURE) !== true) {
1985
+ return error;
1829
1986
  }
1987
+ error.stack = [`${error.name}: ${error.message}`, ...frames].join("\n");
1988
+ return error;
1830
1989
  }
1831
- function mockReadonlyProp(object, property, value) {
1832
- return applyPatch(object, property, { get: () => value, configurable: true });
1990
+ function frameLines(stack) {
1991
+ const lines = String(stack).split("\n");
1992
+ const first = lines.findIndex(isFrame);
1993
+ return first < 0 ? [] : lines.slice(first);
1833
1994
  }
1834
- function mockReadonlyPropGetter(object, property, getter) {
1835
- return applyPatch(object, property, { get: getter, configurable: true });
1995
+ function isFrame(line) {
1996
+ return /^\s*at\s/.test(line) || /^\S*@/.test(line);
1836
1997
  }
1837
- function mockValueProp(object, property, value) {
1838
- return applyPatch(object, property, { value, writable: true, configurable: true });
1839
- }
1840
- function mockAccessorsProp(object, property, accessors) {
1841
- const adapter = getMockAdapter();
1842
- return applyPatch(object, property, {
1843
- get: adapter.createMockFn(accessors?.get),
1844
- set: adapter.createMockFn(accessors?.set),
1845
- configurable: true
1846
- });
1998
+ function framesAfter(frames, boundaryName) {
1999
+ const boundary = boundaryName === "" ? -1 : frames.findIndex((frame) => frame.includes(boundaryName));
2000
+ return boundary < 0 ? frames : frames.slice(boundary + 1);
1847
2001
  }
1848
2002
 
1849
2003
  // src/lib/expect-emission.ts
@@ -1860,10 +2014,6 @@ function asHybridObserver(observer) {
1860
2014
  const listener = (value) => observer.next(value);
1861
2015
  return Object.assign(listener, observer);
1862
2016
  }
1863
- var defaultTimeoutMs = 1e3;
1864
- function setEmissionTimeout(milliseconds) {
1865
- defaultTimeoutMs = milliseconds;
1866
- }
1867
2017
  var setTimer = globalThis.setTimeout.bind(globalThis);
1868
2018
  var clearTimer = globalThis.clearTimeout.bind(globalThis);
1869
2019
  function describeSource(options) {
@@ -1886,7 +2036,7 @@ function subscribeAndCollect(source$, options, settle, handlers) {
1886
2036
  clearTimer(timer);
1887
2037
  subscription?.unsubscribe();
1888
2038
  };
1889
- const timeout = options?.timeout ?? defaultTimeoutMs;
2039
+ const timeout = options?.timeout ?? emissionTimeout();
1890
2040
  const timer = timeout > 0 ? setTimer(() => {
1891
2041
  stop();
1892
2042
  settle.reject(handlers.onTimeout(values, options, timeout));
@@ -1916,39 +2066,43 @@ function subscribeAndCollect(source$, options, settle, handlers) {
1916
2066
  }
1917
2067
  return { values, stop };
1918
2068
  }
2069
+ function anchoredRejecter(reject, anchor2) {
2070
+ return (error) => reject(anchor2(error));
2071
+ }
1919
2072
  function timeoutError(values, options, waited) {
1920
- return new Error(
2073
+ return ownFailure(
1921
2074
  `${describeSource(options)} did not emit within ${waited} ms (${values.length} emission(s) received). Either the stream never fired \u2014 check the trigger and any provider spy feeding it \u2014 or it is slower than the timeout; raise it with \`{ timeout: \u2026 }\`. This wait is real time even under fake timers, on purpose: a virtual watchdog would race the timers your spec advances. Lower it with \`setEmissionTimeout(100)\` in the setup file rather than disabling it with \`{ timeout: 0 }\`, which leaves the next silent stream with no message at all.`
1922
2075
  );
1923
2076
  }
1924
2077
  function rejectAsSourceError(error, options, settle) {
1925
- settle.reject(new Error(`${describeSource(options)} errored instead of emitting: ${String(error)}`, { cause: error }));
2078
+ settle.reject(ownFailure(`${describeSource(options)} errored instead of emitting: ${String(error)}`, { cause: error }));
1926
2079
  }
1927
2080
  function completedError(values, expected, options) {
1928
- return new Error(
2081
+ return ownFailure(
1929
2082
  `${describeSource(options)} completed after ${values.length} emission(s), expected ${describeExpectation(expected, options)}. A completed-but-empty stream is the usual sign that the value was produced before the subscription.`
1930
2083
  );
1931
2084
  }
1932
2085
  function expectEmission(source$, options) {
1933
- return collectEmissions(source$, 1, options).then((values) => firstOf(values));
2086
+ return collectEmissions(source$, 1, options, captureAnchor(expectEmission)).then((values) => firstOf(values));
1934
2087
  }
1935
2088
  function firstOf(values) {
1936
2089
  return values[0];
1937
2090
  }
1938
2091
  function expectEmissions(source$, count, options) {
1939
- return collectEmissions(source$, count, options);
2092
+ return collectEmissions(source$, count, options, captureAnchor(expectEmissions));
1940
2093
  }
1941
- function collectEmissions(source$, count, options) {
2094
+ function collectEmissions(source$, count, options, anchor2) {
1942
2095
  return new Promise((resolve, reject) => {
2096
+ const fail = anchoredRejecter(reject, anchor2);
1943
2097
  subscribeAndCollect(
1944
2098
  source$,
1945
2099
  options,
1946
2100
  // Resolved with everything that arrived, then narrowed to the accepted ones: the collector's
1947
2101
  // job is to record, and `skip` / `until` decide what the caller is handed.
1948
- { resolve: (values) => resolve(accepted(values, options).slice(0, count)), reject },
2102
+ { resolve: (values) => resolve(accepted(values, options).slice(0, count)), reject: fail },
1949
2103
  {
1950
2104
  isDone: (values) => accepted(values, options).length >= count,
1951
- onComplete: (values) => reject(completedError(values, count, options)),
2105
+ onComplete: (values) => fail(completedError(values, count, options)),
1952
2106
  onTimeout: timeoutError,
1953
2107
  onError: rejectAsSourceError
1954
2108
  }
@@ -1957,14 +2111,16 @@ function collectEmissions(source$, count, options) {
1957
2111
  }
1958
2112
  function expectNoEmission(source$, options) {
1959
2113
  const quietFor = options?.timeout ?? 0;
2114
+ const anchor2 = captureAnchor(expectNoEmission);
1960
2115
  let quietWindow = void 0;
1961
2116
  return new Promise((resolve, reject) => {
2117
+ const fail = anchoredRejecter(reject, anchor2);
1962
2118
  const collector = subscribeAndCollect(
1963
2119
  source$,
1964
2120
  { ...options, timeout: 0 },
1965
2121
  {
1966
- resolve: (emitted) => reject(unexpectedEmissionError(accepted(emitted, options), options)),
1967
- reject
2122
+ resolve: (emitted) => fail(unexpectedEmissionError(accepted(emitted, options), options)),
2123
+ reject: fail
1968
2124
  },
1969
2125
  {
1970
2126
  isDone: (values) => accepted(values, options).length > 0,
@@ -1984,11 +2140,12 @@ function expectNoEmission(source$, options) {
1984
2140
  });
1985
2141
  }
1986
2142
  function expectCompletion(source$, options) {
2143
+ const anchor2 = captureAnchor(expectCompletion);
1987
2144
  return new Promise((resolve, reject) => {
1988
2145
  subscribeAndCollect(
1989
2146
  source$,
1990
2147
  options,
1991
- { resolve, reject },
2148
+ { resolve, reject: anchoredRejecter(reject, anchor2) },
1992
2149
  {
1993
2150
  // Emissions are collected for the failure message, but only completion settles this.
1994
2151
  isDone: staysOpen,
@@ -2003,23 +2160,25 @@ function staysOpen() {
2003
2160
  return false;
2004
2161
  }
2005
2162
  function notCompletedError(values, options, waited) {
2006
- return new Error(
2163
+ return ownFailure(
2007
2164
  `${describeSource(options)} did not complete within ${waited} ms (${values.length} emission(s) received). A stream that keeps running usually means the completing operator never ran \u2014 check \`take\`, \`first\`, \`takeUntil\`, or a Subject nobody calls \`complete()\` on. The wait is real time even under fake timers; raise it with \`{ timeout: \u2026 }\`, or lower the suite default with \`setEmissionTimeout(\u2026)\`.`
2008
2165
  );
2009
2166
  }
2010
2167
  function rejectAsNotCompleted(error, options, settle) {
2011
- settle.reject(new Error(`${describeSource(options)} errored instead of completing: ${String(error)}`, { cause: error }));
2168
+ settle.reject(ownFailure(`${describeSource(options)} errored instead of completing: ${String(error)}`, { cause: error }));
2012
2169
  }
2013
2170
  function expectError(source$, options) {
2171
+ const anchor2 = captureAnchor(expectError);
2014
2172
  return new Promise((resolve, reject) => {
2173
+ const fail = anchoredRejecter(reject, anchor2);
2015
2174
  subscribeAndCollect(
2016
2175
  source$,
2017
2176
  options,
2018
- { resolve, reject },
2177
+ { resolve, reject: fail },
2019
2178
  {
2020
2179
  // Only `error` settles this one: an emission is not the answer, and neither is completion.
2021
2180
  isDone: staysOpen,
2022
- onComplete: (values) => reject(completedWithoutErrorError(values, options)),
2181
+ onComplete: (values) => fail(completedWithoutErrorError(values, options)),
2023
2182
  onTimeout: notErroredError,
2024
2183
  onError: resolveWithError
2025
2184
  }
@@ -2027,12 +2186,12 @@ function expectError(source$, options) {
2027
2186
  }).then((values) => firstOf(values));
2028
2187
  }
2029
2188
  function completedWithoutErrorError(values, options) {
2030
- return new Error(
2189
+ return ownFailure(
2031
2190
  `${describeSource(options)} completed after ${values.length} emission(s) without erroring, but an error was expected. Check that the failure path is the one the spec set up \u2014 a spy configured with \`resolveWith\`/\`nextWith\` rather than \`rejectWith\`/\`throwWith\` produces exactly this.`
2032
2191
  );
2033
2192
  }
2034
2193
  function notErroredError(values, options, waited) {
2035
- return new Error(
2194
+ return ownFailure(
2036
2195
  `${describeSource(options)} did not error within ${waited} ms (${values.length} emission(s) received). The stream is still running: nothing failed, and nothing completed either.`
2037
2196
  );
2038
2197
  }
@@ -2040,7 +2199,7 @@ function resolveWithError(error, _options, settle) {
2040
2199
  settle.resolve([error]);
2041
2200
  }
2042
2201
  function unexpectedEmissionError(values, options) {
2043
- return new Error(`${describeSource(options)} emitted ${serializeValue(values[0])} but was expected to stay silent.`);
2202
+ return ownFailure(`${describeSource(options)} emitted ${serializeValue(values[0])} but was expected to stay silent.`);
2044
2203
  }
2045
2204
 
2046
2205
  // src/lib/spy-typing.ts
@@ -2290,6 +2449,7 @@ exports.createFunctionSpy = createFunctionSpy;
2290
2449
  exports.createMock = createMock;
2291
2450
  exports.createSpyClass = createSpyClass;
2292
2451
  exports.createSpyFromClass = createSpyFromClass;
2452
+ exports.createSpyFromInstance = createSpyFromInstance;
2293
2453
  exports.describeDuplicateCopies = describeDuplicateCopies;
2294
2454
  exports.errorHandler = errorHandler;
2295
2455
  exports.expectCompletion = expectCompletion;
@@ -2311,6 +2471,7 @@ exports.narrow = narrow;
2311
2471
  exports.pruneNodeMocks = pruneNodeMocks;
2312
2472
  exports.resetAutoSpy = resetAutoSpy;
2313
2473
  exports.restoreMockedProps = restoreMockedProps;
2474
+ exports.restoreSpiedInstance = restoreSpiedInstance;
2314
2475
  exports.setEmissionTimeout = setEmissionTimeout;
2315
2476
  exports.settleDynamicImport = settleDynamicImport;
2316
2477
  exports.stubConstructor = stubConstructor;
package/dist/node.d.cts CHANGED
@@ -24,6 +24,7 @@ export declare const createFunctionSpy: typeof Entry.createFunctionSpy;
24
24
  export declare const createMock: typeof Entry.createMock;
25
25
  export declare const createSpyClass: typeof Entry.createSpyClass;
26
26
  export declare const createSpyFromClass: typeof Entry.createSpyFromClass;
27
+ export declare const createSpyFromInstance: typeof Entry.createSpyFromInstance;
27
28
  export declare const describeDuplicateCopies: typeof Entry.describeDuplicateCopies;
28
29
  export declare const errorHandler: typeof Entry.errorHandler;
29
30
  export declare const expectCompletion: typeof Entry.expectCompletion;
@@ -45,6 +46,7 @@ export declare const narrow: typeof Entry.narrow;
45
46
  export declare const pruneNodeMocks: typeof Entry.pruneNodeMocks;
46
47
  export declare const resetAutoSpy: typeof Entry.resetAutoSpy;
47
48
  export declare const restoreMockedProps: typeof Entry.restoreMockedProps;
49
+ export declare const restoreSpiedInstance: typeof Entry.restoreSpiedInstance;
48
50
  export declare const setEmissionTimeout: typeof Entry.setEmissionTimeout;
49
51
  export declare const settleDynamicImport: typeof Entry.settleDynamicImport;
50
52
  export declare const stubConstructor: typeof Entry.stubConstructor;
package/dist/node.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  export { A as AccessorKeysOf, a as AddAccessorsSpies, b as AddCalledWithObservable, c as AddCalledWithPromise, d as AddCalledWithSpyMethods, e as AddObservableSpyMethods, f as AddPromiseSpyMethods, g as AddSpyMethodsByReturnTypes, h as AddThrowHelper, i as AddVoidReturnHelpers, j as AutoSpyRxjsTypes, k as ClassSpyConfiguration, C as ClassType, l as CompleteValueConfig, D as DeepMockProxy, m as DeepPartial, E as ErrorValueConfig, F as Func, M as MethodReturns, n as Mutable, N as NextValueConfig, O as ObservableLike, o as OnlyMethodKeysOf, p as OnlyObservablePropsOf, q as OnlyPropsOf, r as Overload, s as Overloads, P as PropStubValue, S as Spy, t as SpyDisposable, u as SpyOptions, v as StrictSpyConfiguration, w as SubjectLike, x as SubjectOf, U as UnstubbedCall, y as UnstubbedCallHandler, V as ValueConfig, z as ValueConfigPerCall, W as WithMockReturnValue } from './types-CQLPjw8z.js';
2
- export { ArgCaptor, AsInstances, AssertMockedOptions, ConstructorMock, ConstructorSpy, FixtureFactory, FlushUntilOptions, MockDeepOptions, ModuleNamespace, ModuleNamespaceOptions, asInstance, asInstances, asSpy, assertMocked, captureArg, clearAutoSpy, createFixture, createFixtureFactory, createFunctionSpy, createMock, createSpyClass, createSpyFromClass, errorHandler, flushEventLoop, flushEventLoopUntil, mockConstructor, mockDeep, moduleNamespace, narrow, resetAutoSpy, settleDynamicImport, stubConstructor, withOverrides } from './bun.js';
3
- export { A as AutoMockConfiguration, C as CallbackSubscribable, E as EmissionObserver, a as EmissionOptions, b as EmissionSource, S as SubscribableLike, c as autoMocked, d as createAutoMock, e as expectCompletion, f as expectEmission, g as expectEmissions, h as expectError, i as expectNoEmission, s as setEmissionTimeout } from './expect-emission-BDuFuqTb.js';
2
+ export { ArgCaptor, AsInstances, AssertMockedOptions, ConstructorMock, ConstructorSpy, FixtureFactory, FlushUntilOptions, MockDeepOptions, ModuleNamespace, ModuleNamespaceOptions, asInstance, asInstances, asSpy, assertMocked, captureArg, clearAutoSpy, createFixture, createFixtureFactory, createFunctionSpy, createMock, createSpyClass, createSpyFromClass, createSpyFromInstance, errorHandler, flushEventLoop, flushEventLoopUntil, mockConstructor, mockDeep, moduleNamespace, narrow, resetAutoSpy, restoreSpiedInstance, settleDynamicImport, stubConstructor, withOverrides } from './bun.js';
3
+ export { A as AutoMockConfiguration, C as CallbackSubscribable, E as EmissionObserver, a as EmissionOptions, b as EmissionSource, S as SubscribableLike, c as autoMocked, d as createAutoMock, e as expectCompletion, f as expectEmission, g as expectEmissions, h as expectError, i as expectNoEmission, s as setEmissionTimeout } from './expect-emission-Dy5CvsF2.js';
4
4
  export { A as AccessorImplementations, R as RestoreProp, c as countMockedProps, m as mockAccessorsProp, a as mockReadonlyProp, b as mockReadonlyPropGetter, d as mockValueProp, r as restoreMockedProps } from './prop-mock-Dp2ZMFqj.js';
5
5
  export { d as describeDuplicateCopies, g as getPackageCopies } from './package-identity-B1pqa-Sh.js';
6
6
  import 'vitest';
package/dist/node.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import { createRedefineMockAdapter } from './chunk-NQXXUJPS.js';
2
- export { asInstance, asInstances, asSpy, assertMocked, captureArg, createFixture, createFixtureFactory, createMock, createSpyClass, describeDuplicateCopies, expectCompletion, expectEmission, expectEmissions, expectError, expectNoEmission, flushEventLoop, flushEventLoopUntil, getPackageCopies, mockConstructor, mockDeep, moduleNamespace, narrow, setEmissionTimeout, settleDynamicImport, stubConstructor, withOverrides } from './chunk-5URFSYDT.js';
3
- export { countMockedProps, mockAccessorsProp, mockReadonlyProp, mockReadonlyPropGetter, mockValueProp, restoreMockedProps } from './chunk-DSIYS6Y5.js';
4
- export { autoMocked, clearAutoSpy, createAutoMock, createSpyFromClass, resetAutoSpy } from './chunk-4KCYFAJX.js';
5
- export { createFunctionSpy, errorHandler } from './chunk-4KK3ES4P.js';
2
+ export { asInstance, asInstances, asSpy, assertMocked, captureArg, createFixture, createFixtureFactory, createMock, createSpyClass, createSpyFromInstance, describeDuplicateCopies, expectCompletion, expectEmission, expectEmissions, expectError, expectNoEmission, flushEventLoop, flushEventLoopUntil, getPackageCopies, mockConstructor, mockDeep, moduleNamespace, narrow, restoreSpiedInstance, setEmissionTimeout, settleDynamicImport, stubConstructor, withOverrides } from './chunk-GAY6LNAM.js';
3
+ export { countMockedProps, mockAccessorsProp, mockReadonlyProp, mockReadonlyPropGetter, mockValueProp, restoreMockedProps } from './chunk-D5LDT3W4.js';
4
+ export { autoMocked, clearAutoSpy, createAutoMock, createSpyFromClass, resetAutoSpy } from './chunk-FYFF67EN.js';
5
+ export { createFunctionSpy, errorHandler } from './chunk-FFKN6QKI.js';
6
6
  import './chunk-PC3FAZT3.js';
7
7
  import './chunk-XRVVZXOM.js';
8
8
  import './chunk-B45HLBA4.js';
@@ -12,6 +12,18 @@ import { registerMockAdapter, guardAccessorSpies } from './shared-state.js';
12
12
  function asNodeMock(mockFn) {
13
13
  return mockFn;
14
14
  }
15
+ function nameImplementation(implementation, name) {
16
+ if (name === void 0) {
17
+ return implementation;
18
+ }
19
+ const named = {
20
+ // eslint-disable-next-line object-shorthand -- a concise method is not constructable, and `mockConstructor` has the code under test call `new` on this; a function expression is, and is still named by its computed key.
21
+ [name]: function(...args) {
22
+ return implementation.apply(this, args);
23
+ }
24
+ };
25
+ return named[name];
26
+ }
15
27
  function nameNodeMock(mockFn, name) {
16
28
  if (name !== void 0) {
17
29
  Object.defineProperty(mockFn, "displayName", { value: name, configurable: true });
@@ -21,7 +33,7 @@ function nameNodeMock(mockFn, name) {
21
33
  function createNodeMockAdapter(nodeTest) {
22
34
  return guardAccessorSpies(
23
35
  createRedefineMockAdapter({
24
- createMockFn: (implementation, name) => nameNodeMock(nodeTest.fn(implementation ?? (() => void 0)), name),
36
+ createMockFn: (implementation, name) => nameNodeMock(nodeTest.fn(nameImplementation(implementation ?? (() => void 0), name)), name),
25
37
  getCalls: (mockFn) => asNodeMock(mockFn).mock.calls.map((call) => call.arguments),
26
38
  reset: (mockFn) => asNodeMock(mockFn).mock.resetCalls(),
27
39
  // `node:test` mocks reset call history via `resetCalls()`; there is no
package/dist/react.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  export { A as AccessorKeysOf, a as AddAccessorsSpies, b as AddCalledWithObservable, c as AddCalledWithPromise, d as AddCalledWithSpyMethods, e as AddObservableSpyMethods, f as AddPromiseSpyMethods, g as AddSpyMethodsByReturnTypes, h as AddThrowHelper, i as AddVoidReturnHelpers, j as AutoSpyRxjsTypes, k as ClassSpyConfiguration, C as ClassType, l as CompleteValueConfig, D as DeepMockProxy, m as DeepPartial, E as ErrorValueConfig, F as Func, M as MethodReturns, n as Mutable, N as NextValueConfig, O as ObservableLike, o as OnlyMethodKeysOf, p as OnlyObservablePropsOf, q as OnlyPropsOf, r as Overload, s as Overloads, P as PropStubValue, S as Spy, t as SpyDisposable, u as SpyOptions, v as StrictSpyConfiguration, w as SubjectLike, x as SubjectOf, U as UnstubbedCall, y as UnstubbedCallHandler, V as ValueConfig, z as ValueConfigPerCall, W as WithMockReturnValue } from './types-CQLPjw8z.js';
2
- export { ArgCaptor, AsInstances, AssertMockedOptions, ConstructorMock, ConstructorSpy, FixtureFactory, FlushUntilOptions, MockDeepOptions, ModuleNamespace, ModuleNamespaceOptions, asInstance, asInstances, asSpy, assertMocked, captureArg, clearAutoSpy, createFixture, createFixtureFactory, createFunctionSpy, createMock, createSpyClass, createSpyFromClass, errorHandler, flushEventLoop, flushEventLoopUntil, mockConstructor, mockDeep, moduleNamespace, narrow, resetAutoSpy, settleDynamicImport, stubConstructor, withOverrides } from './bun.js';
3
- export { A as AutoMockConfiguration, C as CallbackSubscribable, E as EmissionObserver, a as EmissionOptions, b as EmissionSource, S as SubscribableLike, c as autoMocked, d as createAutoMock, e as expectCompletion, f as expectEmission, g as expectEmissions, h as expectError, i as expectNoEmission, s as setEmissionTimeout } from './expect-emission-BDuFuqTb.js';
2
+ export { ArgCaptor, AsInstances, AssertMockedOptions, ConstructorMock, ConstructorSpy, FixtureFactory, FlushUntilOptions, MockDeepOptions, ModuleNamespace, ModuleNamespaceOptions, asInstance, asInstances, asSpy, assertMocked, captureArg, clearAutoSpy, createFixture, createFixtureFactory, createFunctionSpy, createMock, createSpyClass, createSpyFromClass, createSpyFromInstance, errorHandler, flushEventLoop, flushEventLoopUntil, mockConstructor, mockDeep, moduleNamespace, narrow, resetAutoSpy, restoreSpiedInstance, settleDynamicImport, stubConstructor, withOverrides } from './bun.js';
3
+ export { A as AutoMockConfiguration, C as CallbackSubscribable, E as EmissionObserver, a as EmissionOptions, b as EmissionSource, S as SubscribableLike, c as autoMocked, d as createAutoMock, e as expectCompletion, f as expectEmission, g as expectEmissions, h as expectError, i as expectNoEmission, s as setEmissionTimeout } from './expect-emission-Dy5CvsF2.js';
4
4
  export { A as AccessorImplementations, R as RestoreProp, c as countMockedProps, m as mockAccessorsProp, a as mockReadonlyProp, b as mockReadonlyPropGetter, d as mockValueProp, r as restoreMockedProps } from './prop-mock-Dp2ZMFqj.js';
5
5
  export { d as describeDuplicateCopies, g as getPackageCopies } from './package-identity-B1pqa-Sh.js';
6
6
  import 'vitest';