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/angular.js CHANGED
@@ -1,5 +1,5 @@
1
- import { guardAccessorSpies, registerMockAdapter, getMockAdapter, getObservableSupport, getJasmineSupport, requireObservableSupport } from './shared-state.js';
2
- export { expectCompletion, expectEmission, expectEmissions, expectError, expectNoEmission, setEmissionTimeout } from './shared-state.js';
1
+ import { guardAccessorSpies, registerMockAdapter, getMockAdapter, getObservableSupport, getJasmineSupport, emissionTimeout, requireObservableSupport } from './shared-state.js';
2
+ export { setEmissionTimeout } from './shared-state.js';
3
3
  import { vi, expect, beforeAll, afterAll, beforeEach, afterEach } from 'vitest';
4
4
  import { TestBed, getTestBed } from '@angular/core/testing';
5
5
  import { isStandalone, Component, Injector, runInInjectionContext, NO_ERRORS_SCHEMA, ApplicationRef, signal, computed, ɵSIGNAL as _SIGNAL, InjectionToken } from '@angular/core';
@@ -629,6 +629,28 @@ var ArgsMap = class {
629
629
  const asymmetric = this.#matcherConfigs.map((config) => `[${config.described.join(",")}]`);
630
630
  return [...Object.keys(this.#map), ...asymmetric];
631
631
  }
632
+ /**
633
+ * Every configured argument list as a {@link ConfiguredEntry} — the same lists {@link configured}
634
+ * returns, each carrying its own position and its own predicate.
635
+ *
636
+ * The order is the order a lookup consults them (exact configs first, then the asymmetric ones in
637
+ * registration order), so the first entry whose `matches` holds is the config `get` would have
638
+ * answered from. That is what lets a reader be told "call 3 hit config 2" instead of a list of
639
+ * configs and a list of calls with nothing joining them.
640
+ */
641
+ configuredEntries() {
642
+ const exact = Object.keys(this.#map).map((key, position) => ({
643
+ index: position + 1,
644
+ args: key,
645
+ matches: (actualArgs) => this.#serialize(actualArgs) === key
646
+ }));
647
+ const asymmetric = this.#matcherConfigs.map((config, position) => ({
648
+ index: exact.length + position + 1,
649
+ args: `[${config.described.join(",")}]`,
650
+ matches: (actualArgs) => this.#argsMatch(config, actualArgs)
651
+ }));
652
+ return [...exact, ...asymmetric];
653
+ }
632
654
  /** Render one asymmetric config once, at registration time. See {@link MatcherConfig}. */
633
655
  #buildMatcherConfig(key, value) {
634
656
  const serialized = [];
@@ -1506,7 +1528,12 @@ function resolveAccessors(prototype, config) {
1506
1528
  };
1507
1529
  }
1508
1530
  function resolveMethodNames(ObjectClass, config) {
1509
- const base = config.onlyMethodsToSpyOn.length > 0 ? config.onlyMethodsToSpyOn : getAllMethodNames(ObjectClass.prototype);
1531
+ return mergeMethodNames(
1532
+ config.onlyMethodsToSpyOn.length > 0 ? config.onlyMethodsToSpyOn : getAllMethodNames(ObjectClass.prototype),
1533
+ config
1534
+ );
1535
+ }
1536
+ function mergeMethodNames(base, config) {
1510
1537
  if (config.methodsToSpyOn.length === 0 && config.instanceMethodsToSpyOn.length === 0) {
1511
1538
  return base;
1512
1539
  }
@@ -1545,7 +1572,7 @@ function warnOnAccessorNamingAMethod(ObjectClass, config) {
1545
1572
  function isCallable2(value) {
1546
1573
  return typeof value === "function";
1547
1574
  }
1548
- function applyReturns(autoSpy, ObjectClass, returns) {
1575
+ function applyReturns(autoSpy, factory, returns) {
1549
1576
  const entries = Object.entries(returns);
1550
1577
  if (entries.length === 0) {
1551
1578
  return;
@@ -1556,7 +1583,7 @@ function applyReturns(autoSpy, ObjectClass, returns) {
1556
1583
  if (!isCallable2(spy)) {
1557
1584
  console.warn(
1558
1585
  withDocs(
1559
- `[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.`,
1586
+ `[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.`,
1560
1587
  DOCS_LINKS.createSpyFromClass
1561
1588
  )
1562
1589
  );
@@ -1608,7 +1635,7 @@ function resolveConfiguration(methodsToSpyOnOrConfig) {
1608
1635
  function createSpyFromClass(ObjectClass, methodsToSpyOnOrConfig) {
1609
1636
  const config = resolveConfiguration(methodsToSpyOnOrConfig);
1610
1637
  const autoSpy = assembleSpy(ObjectClass, config);
1611
- applyReturns(autoSpy, ObjectClass, config.returns);
1638
+ applyReturns(autoSpy, `createSpyFromClass(${ObjectClass.name})`, config.returns);
1612
1639
  applyOverrides(autoSpy, config.overrides);
1613
1640
  return autoSpy;
1614
1641
  }
@@ -2737,6 +2764,248 @@ function registerSignalMatchers() {
2737
2764
  }
2738
2765
  });
2739
2766
  }
2767
+
2768
+ // src/lib/error-anchor.ts
2769
+ var OWN_FAILURE = /* @__PURE__ */ Symbol("vitest-auto-spy.own-failure");
2770
+ function ownFailure(message, options) {
2771
+ const error = new Error(message, options);
2772
+ Object.defineProperty(error, OWN_FAILURE, { value: true });
2773
+ return error;
2774
+ }
2775
+ function anchorOf(stack, boundaryName) {
2776
+ const frames = framesAfter(frameLines(stack), boundaryName);
2777
+ return (error) => anchor(frames, error);
2778
+ }
2779
+ function captureAnchor(boundary, host = Error) {
2780
+ const holder = new Error();
2781
+ const capture = host.captureStackTrace;
2782
+ if (capture) {
2783
+ capture(holder, boundary);
2784
+ return anchorOf(holder.stack, "");
2785
+ }
2786
+ return anchorOf(holder.stack, boundary.name);
2787
+ }
2788
+ function anchor(frames, error) {
2789
+ if (frames.length === 0 || Reflect.get(error, OWN_FAILURE) !== true) {
2790
+ return error;
2791
+ }
2792
+ error.stack = [`${error.name}: ${error.message}`, ...frames].join("\n");
2793
+ return error;
2794
+ }
2795
+ function frameLines(stack) {
2796
+ const lines = String(stack).split("\n");
2797
+ const first = lines.findIndex(isFrame);
2798
+ return first < 0 ? [] : lines.slice(first);
2799
+ }
2800
+ function isFrame(line) {
2801
+ return /^\s*at\s/.test(line) || /^\S*@/.test(line);
2802
+ }
2803
+ function framesAfter(frames, boundaryName) {
2804
+ const boundary = boundaryName === "" ? -1 : frames.findIndex((frame) => frame.includes(boundaryName));
2805
+ return boundary < 0 ? frames : frames.slice(boundary + 1);
2806
+ }
2807
+ function subscribeToSource(source$, observer) {
2808
+ if (isRxjsSource(source$)) {
2809
+ return source$.subscribe(observer);
2810
+ }
2811
+ return source$.subscribe(asHybridObserver(observer));
2812
+ }
2813
+ function isRxjsSource(source$) {
2814
+ return typeof Reflect.get(source$, "pipe") === "function";
2815
+ }
2816
+ function asHybridObserver(observer) {
2817
+ const listener = (value) => observer.next(value);
2818
+ return Object.assign(listener, observer);
2819
+ }
2820
+ var setTimer2 = globalThis.setTimeout.bind(globalThis);
2821
+ var clearTimer2 = globalThis.clearTimeout.bind(globalThis);
2822
+ function describeSource(options) {
2823
+ return options?.label ?? "the observable";
2824
+ }
2825
+ function accepted(values, options) {
2826
+ const past = options?.skip ? values.slice(options.skip) : values;
2827
+ const until = options?.until;
2828
+ return until ? past.filter((value) => until(value)) : past;
2829
+ }
2830
+ function describeExpectation(count, options) {
2831
+ return options?.until ? `${count} matching` : String(count);
2832
+ }
2833
+ function subscribeAndCollect(source$, options, settle, handlers) {
2834
+ const values = [];
2835
+ let subscription = void 0;
2836
+ let stopped = false;
2837
+ const stop = () => {
2838
+ stopped = true;
2839
+ clearTimer2(timer);
2840
+ subscription?.unsubscribe();
2841
+ };
2842
+ const timeout = options?.timeout ?? emissionTimeout();
2843
+ const timer = timeout > 0 ? setTimer2(() => {
2844
+ stop();
2845
+ settle.reject(handlers.onTimeout(values, options, timeout));
2846
+ }, timeout) : void 0;
2847
+ subscription = subscribeToSource(source$, {
2848
+ next: (value) => {
2849
+ values.push(value);
2850
+ if (handlers.isDone(values)) {
2851
+ stop();
2852
+ settle.resolve(values);
2853
+ }
2854
+ },
2855
+ error: (error) => {
2856
+ stop();
2857
+ handlers.onError(error, options, settle);
2858
+ },
2859
+ complete: () => {
2860
+ stop();
2861
+ handlers.onComplete(values);
2862
+ }
2863
+ });
2864
+ if (stopped) {
2865
+ subscription.unsubscribe();
2866
+ }
2867
+ if (!stopped) {
2868
+ options?.advance?.();
2869
+ }
2870
+ return { values, stop };
2871
+ }
2872
+ function anchoredRejecter(reject, anchor2) {
2873
+ return (error) => reject(anchor2(error));
2874
+ }
2875
+ function timeoutError(values, options, waited) {
2876
+ return ownFailure(
2877
+ `${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.`
2878
+ );
2879
+ }
2880
+ function rejectAsSourceError(error, options, settle) {
2881
+ settle.reject(ownFailure(`${describeSource(options)} errored instead of emitting: ${String(error)}`, { cause: error }));
2882
+ }
2883
+ function completedError(values, expected, options) {
2884
+ return ownFailure(
2885
+ `${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.`
2886
+ );
2887
+ }
2888
+ function expectEmission(source$, options) {
2889
+ return collectEmissions(source$, 1, options, captureAnchor(expectEmission)).then((values) => firstOf(values));
2890
+ }
2891
+ function firstOf(values) {
2892
+ return values[0];
2893
+ }
2894
+ function expectEmissions(source$, count, options) {
2895
+ return collectEmissions(source$, count, options, captureAnchor(expectEmissions));
2896
+ }
2897
+ function collectEmissions(source$, count, options, anchor2) {
2898
+ return new Promise((resolve, reject) => {
2899
+ const fail = anchoredRejecter(reject, anchor2);
2900
+ subscribeAndCollect(
2901
+ source$,
2902
+ options,
2903
+ // Resolved with everything that arrived, then narrowed to the accepted ones: the collector's
2904
+ // job is to record, and `skip` / `until` decide what the caller is handed.
2905
+ { resolve: (values) => resolve(accepted(values, options).slice(0, count)), reject: fail },
2906
+ {
2907
+ isDone: (values) => accepted(values, options).length >= count,
2908
+ onComplete: (values) => fail(completedError(values, count, options)),
2909
+ onTimeout: timeoutError,
2910
+ onError: rejectAsSourceError
2911
+ }
2912
+ );
2913
+ });
2914
+ }
2915
+ function expectNoEmission(source$, options) {
2916
+ const quietFor = options?.timeout ?? 0;
2917
+ const anchor2 = captureAnchor(expectNoEmission);
2918
+ let quietWindow = void 0;
2919
+ return new Promise((resolve, reject) => {
2920
+ const fail = anchoredRejecter(reject, anchor2);
2921
+ const collector = subscribeAndCollect(
2922
+ source$,
2923
+ { ...options, timeout: 0 },
2924
+ {
2925
+ resolve: (emitted) => fail(unexpectedEmissionError(accepted(emitted, options), options)),
2926
+ reject: fail
2927
+ },
2928
+ {
2929
+ isDone: (values) => accepted(values, options).length > 0,
2930
+ onComplete: () => void 0,
2931
+ // Never reached — the watchdog is off (`timeout: 0`) and the quiet window below is this
2932
+ // helper's own timer. Named rather than inlined so it is not an uncalled arrow.
2933
+ onTimeout: timeoutError,
2934
+ onError: rejectAsSourceError
2935
+ }
2936
+ );
2937
+ quietWindow = setTimer2(() => {
2938
+ collector.stop();
2939
+ resolve();
2940
+ }, quietFor);
2941
+ }).finally(() => {
2942
+ clearTimer2(quietWindow);
2943
+ });
2944
+ }
2945
+ function expectCompletion(source$, options) {
2946
+ const anchor2 = captureAnchor(expectCompletion);
2947
+ return new Promise((resolve, reject) => {
2948
+ subscribeAndCollect(
2949
+ source$,
2950
+ options,
2951
+ { resolve, reject: anchoredRejecter(reject, anchor2) },
2952
+ {
2953
+ // Emissions are collected for the failure message, but only completion settles this.
2954
+ isDone: staysOpen,
2955
+ onComplete: resolve,
2956
+ onTimeout: notCompletedError,
2957
+ onError: rejectAsNotCompleted
2958
+ }
2959
+ );
2960
+ }).then(() => void 0);
2961
+ }
2962
+ function staysOpen() {
2963
+ return false;
2964
+ }
2965
+ function notCompletedError(values, options, waited) {
2966
+ return ownFailure(
2967
+ `${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)\`.`
2968
+ );
2969
+ }
2970
+ function rejectAsNotCompleted(error, options, settle) {
2971
+ settle.reject(ownFailure(`${describeSource(options)} errored instead of completing: ${String(error)}`, { cause: error }));
2972
+ }
2973
+ function expectError(source$, options) {
2974
+ const anchor2 = captureAnchor(expectError);
2975
+ return new Promise((resolve, reject) => {
2976
+ const fail = anchoredRejecter(reject, anchor2);
2977
+ subscribeAndCollect(
2978
+ source$,
2979
+ options,
2980
+ { resolve, reject: fail },
2981
+ {
2982
+ // Only `error` settles this one: an emission is not the answer, and neither is completion.
2983
+ isDone: staysOpen,
2984
+ onComplete: (values) => fail(completedWithoutErrorError(values, options)),
2985
+ onTimeout: notErroredError,
2986
+ onError: resolveWithError
2987
+ }
2988
+ );
2989
+ }).then((values) => firstOf(values));
2990
+ }
2991
+ function completedWithoutErrorError(values, options) {
2992
+ return ownFailure(
2993
+ `${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.`
2994
+ );
2995
+ }
2996
+ function notErroredError(values, options, waited) {
2997
+ return ownFailure(
2998
+ `${describeSource(options)} did not error within ${waited} ms (${values.length} emission(s) received). The stream is still running: nothing failed, and nothing completed either.`
2999
+ );
3000
+ }
3001
+ function resolveWithError(error, _options, settle) {
3002
+ settle.resolve([error]);
3003
+ }
3004
+ function unexpectedEmissionError(values, options) {
3005
+ return ownFailure(`${describeSource(options)} emitted ${serializeValue(values[0])} but was expected to stay silent.`);
3006
+ }
3007
+
3008
+ // src/angular.ts
2740
3009
  useVitestAdapter();
2741
3010
 
2742
- export { assertComponentDefIntact, assertNgModuleScopes, assertNoPendingRequests, countMockedProps, createDirectiveHost, createWithAutoSpies, disableAngularDiagnostics, disableTestBedDiagnostics, enableAngularDiagnostics, enableTestBedDiagnostics, extendWithAutoSpies, flushEffects, formatSpecTiming, getTestBedTiming, injectSpy, instrumentTestBed, mockAccessorsProp, mockReadonlyProp, mockReadonlyPropGetter, mockResourceProp, mockSignalProp, mockValueProp, overrideAutoSpy, overrideComponentProvider, provideAutoSpy, provideAutoSpyForToken, registerDirectiveMatchers, registerResourceMatchers, registerSignalMatchers, renderShallow, reportSpecTiming, restoreMockedProps, runEffect, settleResource, setupAngularTestEnv, stable, trackInjections };
3011
+ export { assertComponentDefIntact, assertNgModuleScopes, assertNoPendingRequests, countMockedProps, createDirectiveHost, createWithAutoSpies, disableAngularDiagnostics, disableTestBedDiagnostics, enableAngularDiagnostics, enableTestBedDiagnostics, expectCompletion, expectEmission, expectEmissions, expectError, expectNoEmission, extendWithAutoSpies, flushEffects, formatSpecTiming, getTestBedTiming, injectSpy, instrumentTestBed, mockAccessorsProp, mockReadonlyProp, mockReadonlyPropGetter, mockResourceProp, mockSignalProp, mockValueProp, overrideAutoSpy, overrideComponentProvider, provideAutoSpy, provideAutoSpyForToken, registerDirectiveMatchers, registerResourceMatchers, registerSignalMatchers, renderShallow, reportSpecTiming, restoreMockedProps, runEffect, settleResource, setupAngularTestEnv, stable, trackInjections };
@@ -1,9 +1,9 @@
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
- export { A as AngularValueProvider, a as AutoSpiedInstance, C as ComponentInputs, b as CreateWithAutoSpiesOptions, R as RenderShallowOptions, c as ResourceStatusLike, S as SettleResourceOptions, d as ShallowRender, e as SpyRegistry, f as StableOptions, g as createWithAutoSpies, h as flushEffects, i as injectSpy, p as provideAutoSpy, r as renderShallow, j as runEffect, s as settleResource, k as stable } from './zoneless-DGwkbWKm.js';
6
+ export { A as AngularValueProvider, a as AutoSpiedInstance, C as ComponentInputs, b as CreateWithAutoSpiesOptions, R as RenderShallowOptions, c as ResourceStatusLike, S as SettleResourceOptions, d as ShallowRender, e as SpyRegistry, f as StableOptions, g as createWithAutoSpies, h as flushEffects, i as injectSpy, p as provideAutoSpy, r as renderShallow, j as runEffect, s as settleResource, k as stable } from './zoneless-LsBDcSNZ.js';
7
7
  import 'vitest';
8
8
  import '@angular/core';
9
9
  import '@angular/core/testing';
@@ -1,13 +1,13 @@
1
1
  import { flushEffects } from './chunk-5BPM3BLI.js';
2
2
  export { flushEffects, stable } from './chunk-5BPM3BLI.js';
3
3
  import './chunk-Y4YG7UOK.js';
4
- import { createSpyForToken } from './chunk-KEKKEIRG.js';
4
+ import { createSpyForToken } from './chunk-Z4VRMCW2.js';
5
5
  import './chunk-NQXXUJPS.js';
6
- 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';
7
- export { countMockedProps, mockAccessorsProp, mockReadonlyProp, mockReadonlyPropGetter, mockValueProp, restoreMockedProps } from './chunk-DSIYS6Y5.js';
8
- import { createSpyFromClass } from './chunk-4KCYFAJX.js';
9
- export { autoMocked, clearAutoSpy, createAutoMock, createSpyFromClass, resetAutoSpy } from './chunk-4KCYFAJX.js';
10
- export { createFunctionSpy, errorHandler } from './chunk-4KK3ES4P.js';
6
+ 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';
7
+ export { countMockedProps, mockAccessorsProp, mockReadonlyProp, mockReadonlyPropGetter, mockValueProp, restoreMockedProps } from './chunk-D5LDT3W4.js';
8
+ import { createSpyFromClass } from './chunk-FYFF67EN.js';
9
+ export { autoMocked, clearAutoSpy, createAutoMock, createSpyFromClass, resetAutoSpy } from './chunk-FYFF67EN.js';
10
+ export { createFunctionSpy, errorHandler } from './chunk-FFKN6QKI.js';
11
11
  import './chunk-PC3FAZT3.js';
12
12
  import { isAutoSpyLike } from './chunk-XRVVZXOM.js';
13
13
  import { withDocs, DOCS_LINKS } from './chunk-B45HLBA4.js';
package/dist/bun.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { u as SpyOptions, C as ClassType, k as ClassSpyConfiguration, o as OnlyMethodKeysOf, S as Spy, m as DeepPartial, D as DeepMockProxy, F as Func, y as UnstubbedCallHandler, g as AddSpyMethodsByReturnTypes } from './types-CQLPjw8z.js';
2
2
  export { A as AccessorKeysOf, a as AddAccessorsSpies, b as AddCalledWithObservable, c as AddCalledWithPromise, d as AddCalledWithSpyMethods, e as AddObservableSpyMethods, f as AddPromiseSpyMethods, h as AddThrowHelper, i as AddVoidReturnHelpers, j as AutoSpyRxjsTypes, l as CompleteValueConfig, E as ErrorValueConfig, M as MethodReturns, n as Mutable, N as NextValueConfig, O as ObservableLike, p as OnlyObservablePropsOf, q as OnlyPropsOf, r as Overload, s as Overloads, P as PropStubValue, t as SpyDisposable, v as StrictSpyConfiguration, w as SubjectLike, x as SubjectOf, U as UnstubbedCall, V as ValueConfig, z as ValueConfigPerCall, W as WithMockReturnValue } from './types-CQLPjw8z.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';
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 { Mock } from 'vitest';
@@ -30,6 +30,48 @@ import { Mock } from 'vitest';
30
30
  */
31
31
  declare function createSpyFromClass<T, Options extends SpyOptions = SpyOptions>(ObjectClass: ClassType<T>, methodsToSpyOnOrConfig?: ClassSpyConfiguration<T> | OnlyMethodKeysOf<T>[]): Spy<T, Options>;
32
32
 
33
+ /**
34
+ * Undo every {@link createSpyFromInstance} patch on `instance`, newest first. A no-op on an object
35
+ * that was never spied, or that has already been restored.
36
+ *
37
+ * `restoreMockedProps()` (and therefore `setupAutoSpy()`) undoes the same patches as part of its
38
+ * sweep — this is the targeted form, for an object that has to be real again inside the same test.
39
+ *
40
+ * @example
41
+ * ```ts
42
+ * restoreSpiedInstance(client); // client.send is the real method again
43
+ * ```
44
+ */
45
+ declare function restoreSpiedInstance(instance: object): void;
46
+ /**
47
+ * Replace an existing object's methods with this library's spies, in place, and hand it back typed
48
+ * as a double.
49
+ *
50
+ * @example
51
+ * ```ts
52
+ * const client = new PaymentsClient(config); // a real object the test already holds
53
+ * const spy = createSpyFromInstance(client);
54
+ *
55
+ * spy.charge.calledWith(100).resolveWith({ ok: true });
56
+ * await service.pay(); // the code under test still holds `client`, and sees the spy
57
+ *
58
+ * restoreSpiedInstance(client);
59
+ * ```
60
+ *
61
+ * @remarks
62
+ * Discovery takes the object's own function-valued fields *and* every prototype method up to but not
63
+ * including `Object.prototype`, so an arrow-function property needs no `instanceMethodsToSpyOn` here
64
+ * and `hasOwnProperty` is never replaced. The configuration is {@link createSpyFromClass}', minus
65
+ * the two options that describe a double being built rather than an object being patched:
66
+ * `lazySpies` (the members already exist, so there is nothing to defer) and `fillMissing` (an
67
+ * instance is not an erased `abstract` declaration).
68
+ *
69
+ * The returned value **is** the argument. `using spy = createSpyFromInstance(client)` restores the
70
+ * object at the end of the block rather than merely resetting it, which is the only sense `dispose`
71
+ * can have for an object the consumer owns.
72
+ */
73
+ declare function createSpyFromInstance<T extends object, Options extends SpyOptions = SpyOptions>(instance: T, methodsToSpyOnOrConfig?: ClassSpyConfiguration<T> | OnlyMethodKeysOf<T>[]): Spy<T, Options>;
74
+
33
75
  /**
34
76
  * `createMock` — a typed stand-in built from the fields a test actually touches.
35
77
  *
@@ -324,6 +366,24 @@ declare function clearAutoSpy(spy: object): void;
324
366
  */
325
367
  declare function resetAutoSpy(spy: object): void;
326
368
 
369
+ /**
370
+ * One registered config, addressable on its own.
371
+ *
372
+ * {@link ArgsMap.configured} renders every config as a string, which is all a failure message
373
+ * needs. Attributing an actual call to *which* config it hit needs a handle instead — re-rendering
374
+ * the text and comparing it is not one, because two chains can render the same list.
375
+ *
376
+ * Built on demand: nothing here is written by `set` or read by `get`, so the match path is
377
+ * untouched by the existence of this surface.
378
+ */
379
+ interface ConfiguredEntry {
380
+ /** 1-based position in {@link ArgsMap.configuredEntries}, in the order a lookup consults them. */
381
+ readonly index: number;
382
+ /** The config's argument list, rendered the way {@link ArgsMap.configured} renders it. */
383
+ readonly args: string;
384
+ /** Whether an actual call's arguments hit this config. */
385
+ matches(actualArgs: unknown[]): boolean;
386
+ }
327
387
  declare class ArgsMap {
328
388
  #private;
329
389
  set(key: unknown, value: unknown): void;
@@ -337,6 +397,16 @@ declare class ArgsMap {
337
397
  * assembling text it already has.
338
398
  */
339
399
  configured(): string[];
400
+ /**
401
+ * Every configured argument list as a {@link ConfiguredEntry} — the same lists {@link configured}
402
+ * returns, each carrying its own position and its own predicate.
403
+ *
404
+ * The order is the order a lookup consults them (exact configs first, then the asymmetric ones in
405
+ * registration order), so the first entry whose `matches` holds is the config `get` would have
406
+ * answered from. That is what lets a reader be told "call 3 hit config 2" instead of a list of
407
+ * configs and a list of calls with nothing joining them.
408
+ */
409
+ configuredEntries(): ConfiguredEntry[];
340
410
  }
341
411
 
342
412
  /**
@@ -829,4 +899,4 @@ type ModuleNamespace<T extends object> = T & {
829
899
  */
830
900
  declare function moduleNamespace<T extends object>(exports: T, options?: ModuleNamespaceOptions): ModuleNamespace<T>;
831
901
 
832
- export { AddSpyMethodsByReturnTypes, type ArgCaptor, type AsInstances, type AssertMockedOptions, ClassSpyConfiguration, ClassType, type ConstructorMock, type ConstructorSpy, DeepMockProxy, DeepPartial, type FixtureFactory, type FlushUntilOptions, Func, type MockDeepOptions, type ModuleNamespace, type ModuleNamespaceOptions, OnlyMethodKeysOf, Spy, SpyOptions, UnstubbedCallHandler, asInstance, asInstances, asSpy, assertMocked, captureArg, clearAutoSpy, createFixture, createFixtureFactory, createFunctionSpy, createMock, createSpyClass, createSpyFromClass, errorHandler, flushEventLoop, flushEventLoopUntil, mockConstructor, mockDeep, moduleNamespace, narrow, resetAutoSpy, settleDynamicImport, stubConstructor, withOverrides };
902
+ export { AddSpyMethodsByReturnTypes, type ArgCaptor, type AsInstances, type AssertMockedOptions, ClassSpyConfiguration, ClassType, type ConstructorMock, type ConstructorSpy, DeepMockProxy, DeepPartial, type FixtureFactory, type FlushUntilOptions, Func, type MockDeepOptions, type ModuleNamespace, type ModuleNamespaceOptions, OnlyMethodKeysOf, Spy, SpyOptions, UnstubbedCallHandler, 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 };
package/dist/bun.js CHANGED
@@ -1,9 +1,9 @@
1
1
  import './chunk-Y4YG7UOK.js';
2
2
  import './chunk-NQXXUJPS.js';
3
- 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';
4
- export { countMockedProps, mockAccessorsProp, mockReadonlyProp, mockReadonlyPropGetter, mockValueProp, restoreMockedProps } from './chunk-DSIYS6Y5.js';
5
- export { autoMocked, clearAutoSpy, createAutoMock, createSpyFromClass, resetAutoSpy } from './chunk-4KCYFAJX.js';
6
- export { createFunctionSpy, errorHandler } from './chunk-4KK3ES4P.js';
3
+ 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';
4
+ export { countMockedProps, mockAccessorsProp, mockReadonlyProp, mockReadonlyPropGetter, mockValueProp, restoreMockedProps } from './chunk-D5LDT3W4.js';
5
+ export { autoMocked, clearAutoSpy, createAutoMock, createSpyFromClass, resetAutoSpy } from './chunk-FYFF67EN.js';
6
+ export { createFunctionSpy, errorHandler } from './chunk-FFKN6QKI.js';
7
7
  import './chunk-PC3FAZT3.js';
8
8
  import './chunk-XRVVZXOM.js';
9
9
  import './chunk-B45HLBA4.js';
@@ -122,4 +122,4 @@ function mockAccessorsProp(object, property, accessors) {
122
122
  });
123
123
  }
124
124
 
125
- export { countMockedProps, mockAccessorsProp, mockReadonlyProp, mockReadonlyPropGetter, mockValueProp, restoreMockedProps };
125
+ export { countMockedProps, mockAccessorsProp, mockReadonlyProp, mockReadonlyPropGetter, mockValueProp, redefineFailure, restoreMockedProps };
@@ -1,4 +1,4 @@
1
- import { vitestMockAdapter } from './chunk-WQZ2RKS7.js';
1
+ import { vitestMockAdapter } from './chunk-KYAJCFUA.js';
2
2
  import { registerMockAdapter } from './shared-state.js';
3
3
 
4
4
  function useVitestAdapter() {
@@ -247,6 +247,28 @@ var ArgsMap = class {
247
247
  const asymmetric = this.#matcherConfigs.map((config) => `[${config.described.join(",")}]`);
248
248
  return [...Object.keys(this.#map), ...asymmetric];
249
249
  }
250
+ /**
251
+ * Every configured argument list as a {@link ConfiguredEntry} — the same lists {@link configured}
252
+ * returns, each carrying its own position and its own predicate.
253
+ *
254
+ * The order is the order a lookup consults them (exact configs first, then the asymmetric ones in
255
+ * registration order), so the first entry whose `matches` holds is the config `get` would have
256
+ * answered from. That is what lets a reader be told "call 3 hit config 2" instead of a list of
257
+ * configs and a list of calls with nothing joining them.
258
+ */
259
+ configuredEntries() {
260
+ const exact = Object.keys(this.#map).map((key, position) => ({
261
+ index: position + 1,
262
+ args: key,
263
+ matches: (actualArgs) => this.#serialize(actualArgs) === key
264
+ }));
265
+ const asymmetric = this.#matcherConfigs.map((config, position) => ({
266
+ index: exact.length + position + 1,
267
+ args: `[${config.described.join(",")}]`,
268
+ matches: (actualArgs) => this.#argsMatch(config, actualArgs)
269
+ }));
270
+ return [...exact, ...asymmetric];
271
+ }
250
272
  /** Render one asymmetric config once, at registration time. See {@link MatcherConfig}. */
251
273
  #buildMatcherConfig(key, value) {
252
274
  const serialized = [];
@@ -1,6 +1,6 @@
1
- import { resolveUnstubbedGuard, createFunctionSpy } from './chunk-4KK3ES4P.js';
1
+ import { resolveUnstubbedGuard, createFunctionSpy } from './chunk-FFKN6QKI.js';
2
2
  import { DISPOSE } from './chunk-PC3FAZT3.js';
3
- import { runClearHook, runConfigReset, AUTO_SPY_MARK, isMarkedMock, readDeepChildren, markAsMock } from './chunk-XRVVZXOM.js';
3
+ import { runClearHook, runConfigReset, AUTO_SPY_MARK, markAsMock, isMarkedMock, readDeepChildren } from './chunk-XRVVZXOM.js';
4
4
  import { withDocs, DOCS_LINKS } from './chunk-B45HLBA4.js';
5
5
  import { getMockAdapter, requireObservableSupport, getJasmineSupport } from './shared-state.js';
6
6
 
@@ -380,6 +380,17 @@ function walkOwnPrototypes(prototype, visit) {
380
380
  current = parent;
381
381
  }
382
382
  }
383
+ function getCallableMemberNames(target) {
384
+ const names = /* @__PURE__ */ new Set();
385
+ walkOwnPrototypes(target, (obj) => {
386
+ for (const [name, descriptor] of Object.entries(Object.getOwnPropertyDescriptors(obj))) {
387
+ if (name !== "constructor" && typeof descriptor.value === "function") {
388
+ names.add(name);
389
+ }
390
+ }
391
+ });
392
+ return [...names];
393
+ }
383
394
  var methodNamesCache = /* @__PURE__ */ new WeakMap();
384
395
  function getAllMethodNames(prototype) {
385
396
  const cached = methodNamesCache.get(prototype);
@@ -437,7 +448,12 @@ function resolveAccessors(prototype, config) {
437
448
  };
438
449
  }
439
450
  function resolveMethodNames(ObjectClass, config) {
440
- const base = config.onlyMethodsToSpyOn.length > 0 ? config.onlyMethodsToSpyOn : getAllMethodNames(ObjectClass.prototype);
451
+ return mergeMethodNames(
452
+ config.onlyMethodsToSpyOn.length > 0 ? config.onlyMethodsToSpyOn : getAllMethodNames(ObjectClass.prototype),
453
+ config
454
+ );
455
+ }
456
+ function mergeMethodNames(base, config) {
441
457
  if (config.methodsToSpyOn.length === 0 && config.instanceMethodsToSpyOn.length === 0) {
442
458
  return base;
443
459
  }
@@ -476,7 +492,7 @@ function warnOnAccessorNamingAMethod(ObjectClass, config) {
476
492
  function isCallable2(value) {
477
493
  return typeof value === "function";
478
494
  }
479
- function applyReturns(autoSpy, ObjectClass, returns) {
495
+ function applyReturns(autoSpy, factory, returns) {
480
496
  const entries = Object.entries(returns);
481
497
  if (entries.length === 0) {
482
498
  return;
@@ -487,7 +503,7 @@ function applyReturns(autoSpy, ObjectClass, returns) {
487
503
  if (!isCallable2(spy)) {
488
504
  console.warn(
489
505
  withDocs(
490
- `[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.`,
506
+ `[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.`,
491
507
  DOCS_LINKS.createSpyFromClass
492
508
  )
493
509
  );
@@ -539,7 +555,7 @@ function resolveConfiguration(methodsToSpyOnOrConfig) {
539
555
  function createSpyFromClass(ObjectClass, methodsToSpyOnOrConfig) {
540
556
  const config = resolveConfiguration(methodsToSpyOnOrConfig);
541
557
  const autoSpy = assembleSpy(ObjectClass, config);
542
- applyReturns(autoSpy, ObjectClass, config.returns);
558
+ applyReturns(autoSpy, `createSpyFromClass(${ObjectClass.name})`, config.returns);
543
559
  applyOverrides(autoSpy, config.overrides);
544
560
  return autoSpy;
545
561
  }
@@ -586,4 +602,4 @@ function hasNothingToRead(ObjectClass, accessors, config) {
586
602
  config.onlyMethodsToSpyOn.length === 0;
587
603
  }
588
604
 
589
- export { NOT_STORED, autoMocked, clearAutoSpy, createAutoMock, createProxyPropStore, createSpyFromClass, describeStoredProp, disposeAutoSpy, dropStoredProp, isDeletedProp, isProtocolKey, readStoredAccessor, resetAutoSpy, storeDefinedProp, writeStoredAccessor, writeStoredValue };
605
+ export { NOT_STORED, applyReturns, autoMocked, clearAutoSpy, createAccessorsSpies, createAutoMock, createProxyPropStore, createSpyFromClass, describeStoredProp, disposeAutoSpy, dropStoredProp, getCallableMemberNames, isDeletedProp, isProtocolKey, mergeMethodNames, readStoredAccessor, resetAutoSpy, resolveAccessors, resolveConfiguration, storeDefinedProp, writeStoredAccessor, writeStoredValue };