vitest-auto-spy 5.6.0 → 5.7.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 (52) hide show
  1. package/AGENTS.md +30 -13
  2. package/README.md +84 -44
  3. package/dist/angular.d.ts +5 -5
  4. package/dist/angular.js +39 -5
  5. package/dist/bun-angular.d.ts +6 -6
  6. package/dist/bun-angular.js +8 -8
  7. package/dist/bun.d.ts +6 -43
  8. package/dist/bun.js +5 -5
  9. package/dist/{chunk-APSBHO3I.js → chunk-2PBOTZ4S.js} +1 -1
  10. package/dist/{chunk-SWXAPWFW.js → chunk-374SMZYV.js} +1 -1
  11. package/dist/{chunk-YL7EQKZU.js → chunk-4EJRJ3QP.js} +1 -1
  12. package/dist/{chunk-AVVUXEEI.js → chunk-6ER757ZM.js} +38 -5
  13. package/dist/{chunk-C5EPUC3A.js → chunk-G73VE5ED.js} +3 -3
  14. package/dist/{chunk-X57WQSOC.js → chunk-IW6ZHFTL.js} +1 -1
  15. package/dist/{chunk-GSWNTF5E.js → chunk-O5IKAXWI.js} +9 -5
  16. package/dist/{chunk-4XY4DEM5.js → chunk-RYJDNCAY.js} +2 -2
  17. package/dist/{chunk-PBLYL3Q7.js → chunk-U53SDEP3.js} +1 -1
  18. package/dist/cli.js +3 -1
  19. package/dist/console.d.ts +1 -1
  20. package/dist/console.js +3 -3
  21. package/dist/dom-stubs.d.ts +1 -1
  22. package/dist/eslint-plugin.cjs +131 -9
  23. package/dist/{expect-emission-C0xY_mYK.d.ts → expect-emission-H8lAQz2f.d.ts} +4 -3
  24. package/dist/index.d.ts +5 -5
  25. package/dist/index.js +41 -7
  26. package/dist/jasmine-compat.d.ts +1 -1
  27. package/dist/jasmine.d.ts +2 -2
  28. package/dist/jasmine.js +5 -5
  29. package/dist/nestjs.d.ts +2 -2
  30. package/dist/nestjs.js +6 -6
  31. package/dist/node.cjs +41 -7
  32. package/dist/node.d.ts +5 -5
  33. package/dist/node.js +5 -5
  34. package/dist/package-identity-B3aSBJRI.d.ts +88 -0
  35. package/dist/{prop-mock-DmM-rPcG.d.ts → prop-mock-DSWMTtRE.d.ts} +1 -1
  36. package/dist/react.d.ts +5 -5
  37. package/dist/react.js +6 -6
  38. package/dist/rstest.d.ts +5 -5
  39. package/dist/rstest.js +6 -6
  40. package/dist/rxjs.d.ts +2 -2
  41. package/dist/setup.d.ts +14 -5
  42. package/dist/setup.js +33 -7
  43. package/dist/svelte.d.ts +5 -5
  44. package/dist/svelte.js +6 -6
  45. package/dist/{track-injections-D4TuS8yk.d.ts → track-injections-Bh7AZ2-0.d.ts} +1 -1
  46. package/dist/{types-DTi4dIeN.d.ts → types-DYGoBQGg.d.ts} +13 -7
  47. package/dist/vue.d.ts +6 -6
  48. package/dist/vue.js +7 -7
  49. package/dist/{zoneless-DnjTTKlC.d.ts → zoneless-CvgjkxKw.d.ts} +3 -3
  50. package/package.json +1 -1
  51. package/skills/vitest-auto-spy/SKILL.md +91 -82
  52. package/dist/package-identity-B1pqa-Sh.d.ts +0 -33
package/AGENTS.md CHANGED
@@ -121,7 +121,8 @@ and is only ever asserted on — `createAutoMock`. Handed to the function under
121
121
  (`detectVpnClient(url, logger)`, `applyPreferredTracks(target, …)`, `setLocalConfigEnabled(storage, …)`),
122
122
  it has to satisfy `T` at the call site _and_ expose the spy helpers at the assertion, and
123
123
  `autoMocked<T>()` is that intersection — otherwise every call site needs an `asInstance()` and the
124
- noise scales with the number of them.
124
+ noise scales with the number of them. Both take the same second argument (`returns`, `name`, `strict`,
125
+ `observablePropsToSpyOn`).
125
126
 
126
127
  `createMock<T>()` is the one to reach for on data shapes — it returns a plain `T`, so it satisfies a
127
128
  `no-type-assertion` lint rule without an `eslint-disable` on every fixture.
@@ -723,16 +724,24 @@ Called as: Cart.checkout(1,'now')
723
724
  Also on `createAutoMock`, `provideAutoSpy`, and suite-wide as `setupAutoSpy({ strict: true })` — which
724
725
  reaches a double whichever bundle of the package built it: the default lives on `globalThis`. Before
725
726
  this release it lived in module scope, and a setup file's `strict: true` reached no double a spec built.
726
- Precedence, first one set wins: the double's `onUnstubbedCall` → the global `onUnstubbedCall` → the
727
- double's `strict` (**including `strict: false`**, the only way to exempt one double from a suite-wide
728
- default) → the global `strict`.
727
+ Precedence, first one set wins: the double's `onUnstubbedCall` → the double's explicit **`strict: false`**
728
+ (the only way to exempt one double from a suite-wide default, a global handler included) → the global
729
+ `onUnstubbedCall` → the double's `strict: true` → the global `strict`.
729
730
 
730
731
  - **It is not argument-level.** A `calledWith(1, 'now')` chain says the method is stubbed, so
731
732
  `checkout(9, 'later')` still answers `undefined`. Use `mustBeCalledWith` for that.
732
- - **`mockReturnValue` / `mockImplementation` / the `returns:` option replace the dispatch**, so they
733
- never reach the guard. Consequence: after the last `mockReturnValueOnce` the queue empties back
734
- onto the library dispatch and the next call **is** reported as unstubbed — seed
735
- `mockReturnValue(...)` too when a `Once` sequence is meant to run out.
733
+ - **`returns:` is a default in the spy's own container**: a `calledWith` / `resolveWith` / `failWith`
734
+ configured later wins for its arguments or supersedes it, every other call still gets it, and
735
+ `undefined` counts as configured. `returns: { save: undefined }` is how a `void` call is expected.
736
+ - **`mockReturnValue` / `mockImplementation` replace the dispatch**, so they never reach the guard —
737
+ and a `calledWith` configured after them is never consulted. After the last `mockReturnValueOnce`
738
+ the queue empties back onto the library dispatch and the next call **is** reported as unstubbed —
739
+ seed `mockReturnValue(...)` too when a `Once` sequence is meant to run out.
740
+ - **A throw something swallowed still fails the test.** Under `setupAutoSpy({ strict: true })` every
741
+ strict throw is recorded, and one that a `try`/`catch` in the code under test or an RxJS error with
742
+ no handler (its rethrow waits on a fake clock) kept from the test fails it after the test
743
+ (`swallowedStrictCalls`, `'throw'` by default with `strict: true`). Provoking one on purpose:
744
+ `expect(() => cart.total()).toThrow(…)` then `expect(takeStrictViolations()).toHaveLength(1)`.
736
745
  - **It does not reach** accessor spies, observable-property spies, `mockDeep` nodes, `console-spy`,
737
746
  `mockResourceProp`'s `reload` or a standalone `createFunctionSpy`. A strict double still answers
738
747
  `undefined` for an unconfigured getter or `items$`. `fillMissing` members **are** covered.
@@ -2635,17 +2644,19 @@ blanket downgrade so those keep their severity; do not copy the two names into a
2635
2644
  | `no-passthrough-console-spy` | `error` | suggest | `vi.spyOn(console, m)` nothing gives an implementation — it calls through and prints → `installConsoleSpies()` + `consoleXSpy`, or `.mockImplementation(() => undefined)` |
2636
2645
  | `no-console-in-spec` | `error` | — | a spec calling `console.x(…)` itself, or `console.x = …`, which nothing restores → absorb the code's output through `vitest-auto-spy/console` |
2637
2646
  | `no-import-time-console-spies` | `error` | — | an import of `vitest-auto-spy/console` in a file that never calls `installConsoleSpies()` — the import installs once per worker and silences every later file → `installConsoleSpies()` in `beforeEach`, `restoreConsole()` in `afterEach` |
2647
+ | `no-mistyped-use-value` | `error` | — | `{ provide: TOKEN, useValue }` whose value is not assignable to the primitive `T` of `InjectionToken<T>` (string, number, boolean, bigint, enum, their literals, `null`, `undefined`) — `useValue` is `any`, so `{}` for a `boolean` token compiles and is truthy. **Type-aware**: silent without `parserOptions.project`; object-typed tokens are out of scope |
2648
+ | `no-instance-lifecycle-spy` | `warn` | — | `vi.spyOn(instance, 'ngOnInit')` (and `ngOnDestroy`, `ngDoCheck`, `ngAfterContent*`, `ngAfterView*`) — a view calls the hook read off the prototype, so the instance spy is never called and its stub never runs → `vi.spyOn(Cls.prototype, …)` before `createComponent`, or assert the effect. `ngOnChanges` is exempt: Angular calls it through the instance |
2638
2649
  | `jasmine-namespace-without-entry` | `error` | — | `.and` / `.calls` / `.withArgs` on a library spy in a file that installs the compat layer nowhere — option: `{ setupModules: […] }` |
2639
2650
  | `no-jasmine-globals` | `error` | — | `jasmine.*`, bare `spyOn(` / `spyOnProperty(` / `spyOnAllFunctions(` / `fail(` / `pending(`, `.withContext(` |
2640
2651
  | `no-save-arguments-by-value` | `error` | — | `spy.calls.saveArgumentsByValue()` — a no-op here, so the spec silently asserts on post-mutation state |
2641
2652
  | `prefer-native-spy-api` | `error` | `--fix` / suggest | `.and` / `.calls` where the spy's own API says the same thing — turn it on for the last mile off the jasmine shim |
2642
2653
 
2643
- Twenty-eight rules, **every one an `error` since 4.0.0 except `prefer-render-shallow`,
2644
- `no-stub-class-double` and `no-structural-double`**; three fix on their own, nine offer suggestions. Twenty-seven are syntactic; `no-private-member-access` is the one
2645
- that reads types, and it reports nothing at all without `parserOptions.project` / `projectService`
2654
+ Thirty rules, **every one an `error` since 4.0.0 except `prefer-render-shallow`,
2655
+ `no-stub-class-double`, `no-structural-double` and `no-instance-lifecycle-spy`**; three fix on their own, nine offer suggestions. Twenty-eight are syntactic; `no-private-member-access` and `no-mistyped-use-value`
2656
+ read types, and both report nothing at all without `parserOptions.project` / `projectService`
2646
2657
  rather than guessing. The config used to be a graded mix of `error` / `warn` / `off`, which decided for the
2647
2658
  consumer how much each finding mattered — a `warn` nothing reads is `off` with extra output. The
2648
- three `warn`s left are not judgements about how much a finding matters. `prefer-render-shallow` is
2659
+ four `warn`s left are not judgements about how much a finding matters. `prefer-render-shallow` is
2649
2660
  about the _kind_ of finding: every other rule names something wrong or dead, while this one names a
2650
2661
  file that could render more cheaply, and `renderShallow` is a migration a suite either takes or does
2651
2662
  not. At `error` the plugin would gate that migration — 491 findings across 398 of one consumer's
@@ -2658,7 +2669,9 @@ and moved a long way inside the release: measured on the same 1759 files, the tw
2658
2669
  reports in 8 files and 115 in 74, and are 10 in 7 and 5 in 4 once `prefer-provide-auto-spy` learnt to
2659
2670
  follow a name into a `useValue` — 112 of those doubles are handed to Angular DI one name away, where
2660
2671
  a `provide:` settles it. That rule reports **154 times across 87 files** on the same suite, all at
2661
- `error`. Set any of the three `warn`s to `'error'` once the batch is done. Three of
2672
+ `error`. `no-instance-lifecycle-spy` is on the evidence too: Angular never calls an instance spy on a
2673
+ hook it read off the prototype, but a spec that calls `component.ngOnInit()` itself does, and so does
2674
+ the injector for a service's `ngOnDestroy`. Set any of the four `warn`s to `'error'` once the batch is done. Three of
2662
2675
  them can report on a _correct_ project, and only one has an option:
2663
2676
  `jasmine-namespace-without-entry` takes `['error', { setupModules: ['./test-setup'] }]`, naming the
2664
2677
  file where `enableJasmineCompat()` is called; `prefer-native-spy-api` goes `'off'` for as long as a
@@ -2878,6 +2891,8 @@ packages, which a subpath export can never be.
2878
2891
  | `createNestUnit(X): A -> B -> A is a cycle among the classes built for real` | two exposed / `useClass` classes depend on each other; the helper does not resolve `forwardRef` cycles | expose one side less, or provide it |
2879
2892
  | `createNestUnit(X).spies.get(Y): the unit never asked for that token` | same guard as `createWithAutoSpies` — the spy would not be the one the unit uses | stub the token the unit actually injects (the message lists the auto-spied ones) |
2880
2893
  | `createNestUnit(X).spies.get(Y): Y is in \`expose\`, so the unit got a real instance` | an exposed class is real, not a spy | read the spies of its own collaborators, or drop it from `expose` |
2894
+ | `expect(component.ngOnInit).toHaveBeenCalled()` fails after `fixture.detectChanges()`, or a hook's `.mockImplementation` never runs | the spy was put on the **instance**; a view calls the hook it read off the class prototype when the component was created | `vi.spyOn(Cls.prototype, 'ngOnInit')` before `TestBed.createComponent`, or assert what the hook does — `no-instance-lifecycle-spy` (§16) |
2895
+ | a spec runs down the "browser" (or any flag-on) branch whatever its `useValue` says | `{ provide: IS_BROWSER, useValue: {} }` for an `InjectionToken<boolean>` — `useValue` is `any`, and an object is truthy | a value of the token's type (`useValue: false`) — `no-mistyped-use-value` (§16), with type information |
2881
2896
  | `no request matched …` (with the requests that were made listed) | the URL, verb or predicate does not describe anything the code under test sent, or nothing subscribed | read the list in the message — it is the requests that were actually made; then fix the URL, add `{ method }`, or subscribe (§13) |
2882
2897
  | `this TestBed has no HttpTestingController` | `expectRequest` / `expectNoRequest` on a module that never got the testing backend | `TestBed.configureTestingModule({ providers: [...provideHttpTesting()] })` from `vitest-auto-spy/angular-http` (§13) |
2883
2898
  | `the test ended with N unanswered request(s)` | `provideHttpTesting({ verifyOnTeardown })` found a request the spec never flushed | answer it (`await expectRequest(url).flush(body)`), assert its absence (`expectNoRequest(url)`), or `{ verifyOnTeardown: false }` (§13) |
@@ -2913,6 +2928,8 @@ diagnostic and is free to change: print it, never assert on it.
2913
2928
  | `createSpyFromClass(X) as unknown as X` | `asInstance(createSpyFromClass(X))` |
2914
2929
  | `{ provide: X, useValue: { a: vi.fn(), b: vi.fn() } }` | `provideAutoSpy(X)` |
2915
2930
  | `vi.spyOn(TestBed.inject(X), 'method')` | `injectSpy(X).method` |
2931
+ | `vi.spyOn(component, 'ngOnInit')` after `createComponent` | `vi.spyOn(Cls.prototype, 'ngOnInit')` before it, or assert the effect |
2932
+ | `{ provide: IS_BROWSER, useValue: {} }` for an `InjectionToken<boolean>` | `useValue: false` — a value of the type the token declares |
2916
2933
  | `Object.defineProperty(service, 'ready', { value: true })` | `mockReadonlyProp(service, 'ready', true)` |
2917
2934
  | `source$.subscribe(v => expect(v).toBe(1))` | `await expect(expectEmission(source$)).resolves.toBe(1)` |
2918
2935
  | `await lastValueFrom(done$, { defaultValue: undefined })` | `await expectCompletion(done$)` |
package/README.md CHANGED
@@ -23,7 +23,7 @@ faster at suite scale ([benchmarks](#benchmarks)) — and for
23
23
  [![downloads per month](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fapi.npmjs.org%2Fdownloads%2Fpoint%2Flast-month%2Fvitest-auto-spy&query=%24.downloads&color=brightgreen&logo=npm&label=downloads%2Fmonth)](https://www.npmjs.com/package/vitest-auto-spy)
24
24
  [![downloads over 18 months](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fapi.npmjs.org%2Fdownloads%2Fpoint%2F2026-06-21%3A2030-01-01%2Fvitest-auto-spy&query=%24.downloads&color=brightgreen&logo=npm&label=downloads%2F18mo)](https://www.npmjs.com/package/vitest-auto-spy)
25
25
  [![CI](https://github.com/ASDAlexey/vitest-auto-spy/actions/workflows/ci.yml/badge.svg)](https://github.com/ASDAlexey/vitest-auto-spy/actions/workflows/ci.yml)
26
- [![minzipped size](https://img.shields.io/badge/minzip-16.6%20kB-brightgreen)](#install)
26
+ [![minzipped size](https://img.shields.io/badge/minzip-16.8%20kB-brightgreen)](#install)
27
27
  [![types](https://img.shields.io/npm/types/vitest-auto-spy?logo=typescript&logoColor=white)](https://www.npmjs.com/package/vitest-auto-spy)
28
28
  [![coverage](https://img.shields.io/badge/coverage-100%25-brightgreen)](https://github.com/ASDAlexey/vitest-auto-spy/actions/workflows/ci.yml)
29
29
  [![license](https://img.shields.io/npm/l/vitest-auto-spy?color=blue)](./LICENSE)
@@ -76,7 +76,7 @@ faster at suite scale ([benchmarks](#benchmarks)) — and for
76
76
  - 🧩 Module mocks that prove they applied — `assertMocked`, `moduleNamespace`, for a `vi.mock()` a bundler quietly ignored
77
77
  - 🧾 Fixtures without casts — deep-partial `createMock`, `createFixture` / `createFixtureFactory`, `narrow()`, `withOverrides()`, `asInstances()`, `captureArg()`
78
78
  - 🚚 A migration you can verify — `vitest-auto-spy/diagnostics`: `compareTestRuns` on the two JSON reports, `summarizeTestRun` / `formatTestRunComparison` to read the answer, `diffByField` for the assertion the reporter collapses, `explainSpy` for a double that answered something you did not configure
79
- - 📏 Lint rules and one-line test-run hygiene — twenty-eight rules in `vitest-auto-spy/eslint-plugin` (three `--fix`, nine suggestions, four of them for a suite mid-migration off jasmine), `setupAutoSpy()` — with `preset: 'strict'` for every guard at its strictest grade
79
+ - 📏 Lint rules and one-line test-run hygiene — thirty rules in `vitest-auto-spy/eslint-plugin` (three `--fix`, nine suggestions, four of them for a suite mid-migration off jasmine), `setupAutoSpy()` — with `preset: 'strict'` for every guard at its strictest grade
80
80
  - 🩺 [Editor diagnostics](#editor-diagnostics--webstorm--vs-code) — the same anti-patterns underlined while you type: native ESLint inspections in **WebStorm** and the other JetBrains IDEs, the ESLint extension in **VS Code**, no extra plugin either way
81
81
  - 🔎 [`npx vitest-auto-spy doctor`](#the-cli--doctor-perf-codemod-and-init) — suite-level defects **that never fail a run**: a `tsconfig` `include` matching no file, a production module importing a spec, a `@jest-environment` pragma the runner never reads, config left behind for a runner that is gone. Read-only, no config, exits 1 in CI
82
82
  - ⏱️ [`npx vitest-auto-spy perf`](#perf--where-the-cpu-time-actually-goes) — where a suite's CPU time actually goes, phase by phase, and which spec files to act on: the ones that reach no DOM and could run under `node`, the ones that import a barrel. Runs Vitest once with a reporter this package ships, reads `TestModule.diagnostic()`, names files, states the rule behind each finding
@@ -799,6 +799,11 @@ cart.total.mockReturnValue(42);
799
799
  `CartService` and the spy has it. A hand-written `{ provide: CartService, useValue: { total:
800
800
  vi.fn() } }` silently keeps mocking yesterday's class.
801
801
 
802
+ A token with a primitive value is the one place a `useValue` stays right: `{ provide: IS_BROWSER,
803
+ useValue: false }`. Angular types `useValue` as `any`, though, so nothing compares it with the
804
+ `InjectionToken<boolean>` it is for — `useValue: {}` compiles and is truthy. `no-mistyped-use-value`
805
+ reports that, given type information.
806
+
802
807
  On **Vitest 4.1+**, the same thing as fixtures — one statement, types inferred, and a test that
803
808
  never names a dependency never builds it:
804
809
 
@@ -984,6 +989,22 @@ One call for `configureTestingModule` + `NO_ERRORS_SCHEMA` + `overrideComponent`
984
989
  `@angular/core/testing` still applies. See [shallow rendering](#shallow-component-rendering) for
985
990
  `keepTemplate`, `keepChildren` and what it actually saves.
986
991
 
992
+ ### How to mock: a lifecycle hook
993
+
994
+ ```ts
995
+ const init = vi.spyOn(CardComponent.prototype, 'ngOnInit').mockImplementation(() => undefined);
996
+ const fixture = TestBed.createComponent(CardComponent);
997
+
998
+ fixture.detectChanges();
999
+ expect(init).toHaveBeenCalledTimes(1);
1000
+ ```
1001
+
1002
+ On the prototype, and before the component is created. A view calls the hook it read off the class
1003
+ when the component was created, so `vi.spyOn(component, 'ngOnInit')` on the instance is never
1004
+ called by Angular and its `.mockImplementation` never runs — the real hook does, under a spec that
1005
+ reads as though it stubbed it. Better still, assert what the hook does rather than that it ran.
1006
+ `no-instance-lifecycle-spy` reports the instance form.
1007
+
987
1008
  ### How to mock: a class the code under test builds with `new`
988
1009
 
989
1010
  ```ts
@@ -1808,6 +1829,19 @@ createSpyFromClass(Cart).total(); // throws
1808
1829
  createSpyFromClass(Cart, { strict: false }).total(); // undefined — the way to exempt one double
1809
1830
  ```
1810
1831
 
1832
+ A throw the production code caught used to end the test green — a `try`/`catch` took its error
1833
+ branch, or an RxJS operator with no error handler parked the rethrow on a `setTimeout` a fake clock
1834
+ never runs. Under `setupAutoSpy({ strict: true })` the test fails anyway, after it
1835
+ (`swallowedStrictCalls`: `'throw'` by default, `'warn'` to print, `'off'` to skip), naming each
1836
+ swallowed call with its first production frames. A test that provokes one on purpose takes it:
1837
+
1838
+ ```ts
1839
+ import { takeStrictViolations } from 'vitest-auto-spy/setup';
1840
+
1841
+ expect(() => cart.total()).toThrow('Nothing configured Cart.total');
1842
+ expect(takeStrictViolations()).toHaveLength(1);
1843
+ ```
1844
+
1811
1845
  Two limits worth knowing before turning it on. It answers _"nobody configured this method"_, never
1812
1846
  _"nobody configured this call"_ — a `calledWith` chain for other arguments does not trip it, because
1813
1847
  argument-level strictness is what `mustBeCalledWith` already does, with a better message. And it
@@ -2664,9 +2698,9 @@ single-purpose utility you can pick up independently — they all ride on the sa
2664
2698
  | `blockNetwork(options?)` | `/setup` | Close `fetch`, `XMLHttpRequest` and `sendBeacon`, naming what was requested ([details](#test-run-hygiene)) |
2665
2699
  | `trackStrayRejections()` / `flushStrayRejections()` / `countStrayRejections()` | `/setup` | Read back the promise rejections zone.js swallowed into `console.error`, so one can fail a test ([details](#test-run-hygiene)) |
2666
2700
  | `guardGlobalPatches(reaction)` | `/setup` | Name the test that redefined a property of `document` / `navigator` / `globalThis` as non-configurable |
2667
- | `guardStrayConsole(reaction)` | `/setup` | Fail a test that wrote to the console without absorbing it, and a file that wrote outside any test ([details](#test-run-hygiene)) |
2668
- | `withoutStrayTimerTracking(work)` | `/setup` | Run setup work whose timers the stray-timer tracker neither counts nor cancels — jsdom schedules one per Web Storage write |
2669
- | `describeStrayTimers()` | `/setup` | Every timer still pending, with its kind, the spec file that scheduled it and the scheduling frames — the list `onStrayTimers` gets |
2701
+ | `guardStrayConsole(reaction)` | `/setup` | Fail a test that wrote to the console without absorbing it, and a file that wrote outside any test ([details](#test-run-hygiene)) |
2702
+ | `withoutStrayTimerTracking(work)` | `/setup` | Run setup work whose timers the stray-timer tracker neither counts nor cancels — jsdom schedules one per Web Storage write |
2703
+ | `describeStrayTimers()` | `/setup` | Every timer still pending, with its kind, the spec file that scheduled it and the scheduling frames — the list `onStrayTimers` gets |
2670
2704
  | `guardPrototypePollution(reaction)` | `/setup` | Name the test that left a key on `Object.prototype` — it stops later files collecting ([details](#test-run-hygiene)) |
2671
2705
  | `installPerTest(install)` | `/setup` | Re-install a stub before every test of the block — a `describe`-level stub is restored away after the first |
2672
2706
  | `setupAngularTestEnv(opts)` | `/angular` | Zone and zoneless spec files in one worker, switching platforms per file |
@@ -2968,8 +3002,8 @@ not a function` on Node 25, `undefined` on Node 26, under jsdom and happy-dom al
2968
3002
  | `blockNetwork` | `false` | Close every network channel the environment has — `true`, or a narrowing object |
2969
3003
  | `guardGlobals` | `'off'` | Report a test that redefines a global property as non-configurable |
2970
3004
  | `prototypePollution` | `'throw'` | Sweep and report an enumerable key a test left on a built-in prototype |
2971
- | `strayConsole` | `'off'` | Fail a test (or a file) whose console output nothing absorbed — `'warn'`, `'throw'`, or `{ reaction, allow }` |
2972
- | `misconfiguration` | `'warn'` | `'throw'` fails the library's own misuse reports at the call site, every occurrence |
3005
+ | `strayConsole` | `'off'` | Fail a test (or a file) whose console output nothing absorbed — `'warn'`, `'throw'`, or `{ reaction, allow }` |
3006
+ | `misconfiguration` | `'warn'` | `'throw'` fails the library's own misuse reports at the call site, every occurrence |
2973
3007
  | `preset` | — | `'strict'` starts every guard above at its strictest grade; explicit options still win |
2974
3008
  | `globalFakeTimers` | `false` | Fake timers for every test **and between them** — Jest's `enableGlobally` |
2975
3009
  | `restoreTimerGlobals` | `true` | Put back timer globals that uninstalling the fakes deleted |
@@ -3135,7 +3169,7 @@ import autoSpy from 'vitest-auto-spy/eslint-plugin';
3135
3169
  export default [{ files: ['**/*.spec.ts'], ...autoSpy.configs.recommended }];
3136
3170
  ```
3137
3171
 
3138
- **Every rule is an `error` since 4.0.0, bar one.** The config used to grade them `error` / `warn` /
3172
+ **Every rule is an `error` since 4.0.0, bar four.** The config used to grade them `error` / `warn` /
3139
3173
  `off`, which chose for you how much each finding mattered; a `warn` in a repository that does not read
3140
3174
  lint output is `off` with extra noise, and which findings block a merge is one line of config either
3141
3175
  way. To turn one down, spread the rule map as well — a bare `rules` key beside the spread config
@@ -3151,7 +3185,7 @@ export default [
3151
3185
  ];
3152
3186
  ```
3153
3187
 
3154
- One of the three exceptions is `prefer-render-shallow`, which ships as a **`warn`**, and its reason is the kind
3188
+ One of the four exceptions is `prefer-render-shallow`, which ships as a **`warn`**, and its reason is the kind
3155
3189
  of thing it says rather than how much it matters. Every other rule here names something wrong or dead
3156
3190
  — a double that drifts from its class, an assertion that never runs, a provider the container already
3157
3191
  dropped, a schema guarding nothing. That one names a file that could render more cheaply, which is a
@@ -3173,6 +3207,10 @@ a heuristic — and it is also why they are rules of their own rather than arms
3173
3207
  a project that disagrees with either reading switches it off without losing the rule that reads a
3174
3208
  count.
3175
3209
 
3210
+ The fourth, `no-instance-lifecycle-spy`, is graded on the evidence too. A spy on an instance hook is
3211
+ never called by Angular, but it is by a spec that calls `component.ngOnInit()` itself, and by the
3212
+ injector for a service's `ngOnDestroy` — and one file's syntax cannot tell those apart.
3213
+
3176
3214
  A second config, `autoSpy.configs.typeErrors`, holds the subset whose findings are compile errors
3177
3215
  (`prefer-as-spy` is `TS2352`, `no-mocked-for-spy` is `TS2322`). Spread it **after** a blanket
3178
3216
  downgrade so those keep their severity — the recipe is a spread rather than rule names copied into
@@ -3187,42 +3225,44 @@ object of `vi.fn()`s is perfectly reasonable in application code. Flat config on
3187
3225
  `.eslintrc` `plugins: ['…']` form resolves names to `eslint-plugin-*` packages, which a subpath
3188
3226
  export can never be.
3189
3227
 
3190
- | Rule | Recommended | Fix | Flags |
3191
- | --------------------------------- | :---------: | ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
3192
- | `prefer-provide-auto-spy` | `error` | — | a hand-rolled `useValue`, `useFactory` **or** `useClass` → `provideAutoSpy(Class)` / `provideAutoSpyForToken(TOKEN)` |
3193
- | `prefer-create-spy-from-class` | `error` | — | an object literal of two or more `vi.fn()`s → `createSpyFromClass` / `createAutoMock`, unless it is a factory's own seed |
3194
- | `no-stub-class-double` | `warn` | — | a class whose fields are `vi.fn()`s → `createSpyFromClass` / `provideAutoSpy`, the stub class deleted |
3195
- | `no-structural-double` | `warn` | — | an object of `vi.fn()`s bound to a name declared `{ load: Mock }` → `createAutoMock<T>()` |
3196
- | `prefer-inject-spy` | `error` | suggest | `vi.spyOn(TestBed.inject(X), 'm')`, in one step or two → `injectSpy(X).m` |
3197
- | `no-object-define-property` | `error` | suggest | `Object.defineProperty` in a spec → `mockReadonlyProp` / `mockValueProp` |
3198
- | `no-expect-in-subscribe` | `error` | suggest | `expect()` inside a `subscribe()` callback → `expectEmission` / `firstValueFrom` |
3199
- | `no-shared-module-level-mock` | `error` | — | an **exported** value holding `vi.fn()`s → export a factory that returns it |
3200
- | `no-mocked-for-spy` | `error` | `--fix` / suggest | `Mocked<T>` in any type position → `Spy<T>`, import and all — a suggestion where the value assigned is not one of this library's factories |
3201
- | `prefer-as-spy` | `error` | `--fix` | `TestBed.inject(X) as Spy<X>` → `asSpy<X>(TestBed.inject(X))`, import and all |
3202
- | `no-done-callback` | `error` | — | `it('x', (done) => …)` → `async` + an awaited assertion, and `done.fail(…)` at the call site |
3203
- | `no-floating-assertion` | `error` | — | `expect()` in a `.then()` nobody awaits → `expect(await promise)` |
3204
- | `no-bare-called-with` | `error` | — | `spy.m.calledWith(1);` as a statement of its own — a stub nobody continued, asserting nothing |
3205
- | `no-overridden-provider` | `error` | suggest | two providers for one token in one array → the earlier one never runs; the exact duplicate can be deleted |
3206
- | `no-inject-before-override` | `error` | — | `TestBed.inject()` / `injectSpy()` / `renderShallow()` in a hook, in a suite that still calls `override*` |
3207
- | `no-private-member-access` | `error` | — | `instance['privateMember']`, `(instance as any).privateMember` and `vi.spyOn(Object.getPrototypeOf(x), 'm')` — the three spellings that get past `private`; **type-aware**, and silent without `parserOptions.project` |
3208
- | `no-dead-schemas` | `error` | — | `schemas` on a testing module that declares nothing — a charm protecting nobody, and one that starts working the day `declarations` arrive |
3209
- | `no-import-time-spread` | `error` | suggest | `export const x = [...Imported]` at module scope → a `TypeError` while the bundle loads |
3210
- | `no-unregistered-inject-spy` | `error` | — | `injectSpy(X)` for a token this file never registered → the real instance, whose spy helpers exist only for the compiler |
3211
- | `prefer-render-shallow` | `warn` | suggest | `TestBed.createComponent` in a file that never reads the template → `renderShallow(X)`; 0.24× the per-test cycle at 100 children |
3212
- | `prefer-observer-stub` | `error` | — | a hand-rolled `IntersectionObserver` / `ResizeObserver` / `MutationObserver` written into a global → `stubIntersectionObserver()` and friends, whose undo `restoreMockedProps()` already runs |
3213
- | `no-passthrough-console-spy` | `error` | suggest | `vi.spyOn(console, m)` nothing gives an implementation — it calls through and still prints → `installConsoleSpies()` from `vitest-auto-spy/console`, or `.mockImplementation(() => undefined)` |
3214
- | `no-console-in-spec` | `error` | — | a spec that calls `console.x(…)` itself, or replaces a method with `console.x = …`, which nothing puts back — under `isolate: false` every later file inherits it |
3215
- | `no-import-time-console-spies` | `error` | — | an import of `vitest-auto-spy/console` in a file that never calls `installConsoleSpies()` — the import installs once per worker and, under `isolate: false`, silences every later file |
3216
- | `jasmine-namespace-without-entry` | `error` | — | `.and` / `.calls` / `.withArgs` on a library spy in a file that installs the compatibility layer nowhere |
3217
- | `no-jasmine-globals` | `error` | — | `jasmine.*`, `spyOn(` / `spyOnProperty(` / `spyOnAllFunctions(` / `fail(` / `pending(`, `.withContext(` — none of them exist under Vitest |
3218
- | `no-save-arguments-by-value` | `error` | — | `spy.calls.saveArgumentsByValue()`, which is a no-op here → take the copy at call time |
3219
- | `prefer-native-spy-api` | `error` | `--fix` / suggest | `.and` / `.calls` where the spy's own API says the same thing — the last mile off the jasmine shim |
3228
+ | Rule | Recommended | Fix | Flags |
3229
+ | --------------------------------- | :---------: | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
3230
+ | `prefer-provide-auto-spy` | `error` | — | a hand-rolled `useValue`, `useFactory` **or** `useClass` → `provideAutoSpy(Class)` / `provideAutoSpyForToken(TOKEN)` |
3231
+ | `prefer-create-spy-from-class` | `error` | — | an object literal of two or more `vi.fn()`s → `createSpyFromClass` / `createAutoMock`, unless it is a factory's own seed |
3232
+ | `no-stub-class-double` | `warn` | — | a class whose fields are `vi.fn()`s → `createSpyFromClass` / `provideAutoSpy`, the stub class deleted |
3233
+ | `no-structural-double` | `warn` | — | an object of `vi.fn()`s bound to a name declared `{ load: Mock }` → `createAutoMock<T>()` |
3234
+ | `prefer-inject-spy` | `error` | suggest | `vi.spyOn(TestBed.inject(X), 'm')`, in one step or two → `injectSpy(X).m` |
3235
+ | `no-object-define-property` | `error` | suggest | `Object.defineProperty` in a spec → `mockReadonlyProp` / `mockValueProp` |
3236
+ | `no-expect-in-subscribe` | `error` | suggest | `expect()` inside a `subscribe()` callback → `expectEmission` / `firstValueFrom` |
3237
+ | `no-shared-module-level-mock` | `error` | — | an **exported** value holding `vi.fn()`s → export a factory that returns it |
3238
+ | `no-mocked-for-spy` | `error` | `--fix` / suggest | `Mocked<T>` in any type position → `Spy<T>`, import and all — a suggestion where the value assigned is not one of this library's factories |
3239
+ | `prefer-as-spy` | `error` | `--fix` | `TestBed.inject(X) as Spy<X>` → `asSpy<X>(TestBed.inject(X))`, import and all |
3240
+ | `no-done-callback` | `error` | — | `it('x', (done) => …)` → `async` + an awaited assertion, and `done.fail(…)` at the call site |
3241
+ | `no-floating-assertion` | `error` | — | `expect()` in a `.then()` nobody awaits → `expect(await promise)` |
3242
+ | `no-bare-called-with` | `error` | — | `spy.m.calledWith(1);` as a statement of its own — a stub nobody continued, asserting nothing |
3243
+ | `no-overridden-provider` | `error` | suggest | two providers for one token in one array → the earlier one never runs; the exact duplicate can be deleted |
3244
+ | `no-inject-before-override` | `error` | — | `TestBed.inject()` / `injectSpy()` / `renderShallow()` in a hook, in a suite that still calls `override*` |
3245
+ | `no-private-member-access` | `error` | — | `instance['privateMember']`, `(instance as any).privateMember` and `vi.spyOn(Object.getPrototypeOf(x), 'm')` — the three spellings that get past `private`; **type-aware**, and silent without `parserOptions.project` |
3246
+ | `no-dead-schemas` | `error` | — | `schemas` on a testing module that declares nothing — a charm protecting nobody, and one that starts working the day `declarations` arrive |
3247
+ | `no-import-time-spread` | `error` | suggest | `export const x = [...Imported]` at module scope → a `TypeError` while the bundle loads |
3248
+ | `no-unregistered-inject-spy` | `error` | — | `injectSpy(X)` for a token this file never registered → the real instance, whose spy helpers exist only for the compiler |
3249
+ | `prefer-render-shallow` | `warn` | suggest | `TestBed.createComponent` in a file that never reads the template → `renderShallow(X)`; 0.24× the per-test cycle at 100 children |
3250
+ | `prefer-observer-stub` | `error` | — | a hand-rolled `IntersectionObserver` / `ResizeObserver` / `MutationObserver` written into a global → `stubIntersectionObserver()` and friends, whose undo `restoreMockedProps()` already runs |
3251
+ | `no-passthrough-console-spy` | `error` | suggest | `vi.spyOn(console, m)` nothing gives an implementation — it calls through and still prints → `installConsoleSpies()` from `vitest-auto-spy/console`, or `.mockImplementation(() => undefined)` |
3252
+ | `no-console-in-spec` | `error` | — | a spec that calls `console.x(…)` itself, or replaces a method with `console.x = …`, which nothing puts back — under `isolate: false` every later file inherits it |
3253
+ | `no-import-time-console-spies` | `error` | — | an import of `vitest-auto-spy/console` in a file that never calls `installConsoleSpies()` — the import installs once per worker and, under `isolate: false`, silences every later file |
3254
+ | `no-mistyped-use-value` | `error` | — | `{ provide: TOKEN, useValue }` whose value is not assignable to the primitive `TOKEN` declares — `useValue` is `any`, so `{}` for an `InjectionToken<boolean>` compiles and is truthy; **type-aware**, and silent without `parserOptions.project` |
3255
+ | `no-instance-lifecycle-spy` | `warn` | — | `vi.spyOn(component, 'ngOnInit')` and the other hooks a view reads off the prototype — Angular never calls the instance spy → spy on `Cls.prototype` before the component is created, or assert the hook's effect |
3256
+ | `jasmine-namespace-without-entry` | `error` | — | `.and` / `.calls` / `.withArgs` on a library spy in a file that installs the compatibility layer nowhere |
3257
+ | `no-jasmine-globals` | `error` | — | `jasmine.*`, `spyOn(` / `spyOnProperty(` / `spyOnAllFunctions(` / `fail(` / `pending(`, `.withContext(` — none of them exist under Vitest |
3258
+ | `no-save-arguments-by-value` | `error` | — | `spy.calls.saveArgumentsByValue()`, which is a no-op here → take the copy at call time |
3259
+ | `prefer-native-spy-api` | `error` | `--fix` / suggest | `.and` / `.calls` where the spy's own API says the same thing — the last mile off the jasmine shim |
3220
3260
 
3221
3261
  Every message ends with a link to the matching [recipe](#how-to-mock): a rule that only says
3222
3262
  "don't" moves the problem rather than solving it. Rules travel with the API they recommend, so they
3223
3263
  are versioned together and stop being re-written in every project that installs the package.
3224
3264
 
3225
- **Three of the twenty-eight fix on their own, nine offer suggestions**, and the split is not about how hard
3265
+ **Three of the thirty fix on their own, nine offer suggestions**, and the split is not about how hard
3226
3266
  the rewrite is. `no-mocked-for-spy` touches a _declaration_: get it wrong and the file stops
3227
3267
  compiling, which is the loudest, cheapest failure there is — so `--fix` rewrites the type, adds
3228
3268
  `import type { Spy } from 'vitest-auto-spy'` and drops the `Mocked` import once nothing else uses
@@ -3326,7 +3366,7 @@ package's own — it needs its ESLint integration switched on.
3326
3366
  ### WebStorm and the other JetBrains IDEs
3327
3367
 
3328
3368
  No plugin to install: WebStorm, IntelliJ IDEA Ultimate, PhpStorm, PyCharm Professional and RubyMine
3329
- all run ESLint natively, so the twenty-eight rules appear inline, in the **Problems** tool window, and
3369
+ all run ESLint natively, so the thirty rules appear inline, in the **Problems** tool window, and
3330
3370
  under **Code → Inspect Code** for the whole project.
3331
3371
 
3332
3372
  ```js
@@ -3343,7 +3383,7 @@ has supported flat config since 2023.3); scope the block to spec files yourself;
3343
3383
  the fixes and suggestions live.
3344
3384
 
3345
3385
  A native JetBrains plugin is **not** planned — it would duplicate an integration the IDE already has
3346
- and then keep a second copy of twenty-eight rules, in Kotlin, in step with the TypeScript ones.
3386
+ and then keep a second copy of thirty rules, in Kotlin, in step with the TypeScript ones.
3347
3387
 
3348
3388
  ### VS Code, Cursor, Windsurf, VSCodium
3349
3389
 
@@ -3455,8 +3495,8 @@ another, and the next one is in that same file.
3455
3495
  | `flushEventLoopUntil(isDone, opts?)` | Real event-loop turns until a condition holds, with a budget instead of a hang |
3456
3496
  | `diffByField(actual, expected)` | Which field of an array of records moved, and in how many elements |
3457
3497
  | `guardGlobalPatches(reaction)` / `installPerTest(install)` _(`/setup`)_ | Name the test that sealed a global property; re-install a stub before every test |
3458
- | `guardStrayConsole(reaction)` / `withoutStrayTimerTracking(work)` _(`/setup`)_ | Fail a test on console output nothing absorbed; keep setup work's timers out of the stray-timer count |
3459
- | `describeStrayTimers()` _(`/setup`)_ | Each pending timer with the spec file and frames that scheduled it — for a suite that sweeps by hand |
3498
+ | `guardStrayConsole(reaction)` / `withoutStrayTimerTracking(work)` _(`/setup`)_ | Fail a test on console output nothing absorbed; keep setup work's timers out of the stray-timer count |
3499
+ | `describeStrayTimers()` _(`/setup`)_ | Each pending timer with the spec file and frames that scheduled it — for a suite that sweeps by hand |
3460
3500
  | `guardPrototypePollution(reaction)` _(`/setup`)_ | Name the test that left a key on `Object.prototype`, before the next file fails to collect |
3461
3501
  | `consoleDebugSpy` … `consoleWarnSpy` _(`/console`)_ | Silent typed spies replacing the global `console` methods on import |
3462
3502
  | `installConsoleSpies()` / `resetConsoleSpies()` / `restoreConsole()` | Install / clear / undo the console spies |
package/dist/angular.d.ts CHANGED
@@ -1,10 +1,10 @@
1
- export { l as AngularTokenProvider, 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, m as provideAutoSpyForToken, r as renderShallow, j as runEffect, s as settleResource, k as stable } from './zoneless-DnjTTKlC.js';
1
+ export { l as AngularTokenProvider, 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, m as provideAutoSpyForToken, r as renderShallow, j as runEffect, s as settleResource, k as stable } from './zoneless-CvgjkxKw.js';
2
2
  import { InjectionToken, Provider, Type, Signal, WritableSignal } from '@angular/core';
3
3
  import { TestAPI } from 'vitest';
4
- import { C as ClassType, S as Spy, k as ClassSpyConfiguration, o as OnlyMethodKeysOf, g as AddSpyMethodsByReturnTypes } from './types-DTi4dIeN.js';
5
- export { I as InjectionLog, T as TrackInjectionsOptions, a as TrackedProvider, t as trackInjections } from './track-injections-D4TuS8yk.js';
6
- export { A as AccessorImplementations, R as RestoreProp, c as countMockedProps, m as mockAccessorsProp, a as mockReadonlyProp, b as mockReadonlyPropGetter, d as mockValueProp, e as restoreMockedProps } from './prop-mock-DmM-rPcG.js';
7
- export { C as CallbackSubscribable, E as EmissionObserver, a as EmissionOptions, b as EmissionSource, S as SubscribableLike, e as expectCompletion, f as expectEmission, g as expectEmissions, h as expectError, i as expectNoEmission, s as setEmissionTimeout } from './expect-emission-C0xY_mYK.js';
4
+ import { C as ClassType, S as Spy, k as ClassSpyConfiguration, o as OnlyMethodKeysOf, g as AddSpyMethodsByReturnTypes } from './types-DYGoBQGg.js';
5
+ export { I as InjectionLog, T as TrackInjectionsOptions, a as TrackedProvider, t as trackInjections } from './track-injections-Bh7AZ2-0.js';
6
+ export { A as AccessorImplementations, R as RestoreProp, c as countMockedProps, m as mockAccessorsProp, a as mockReadonlyProp, b as mockReadonlyPropGetter, d as mockValueProp, e as restoreMockedProps } from './prop-mock-DSWMTtRE.js';
7
+ export { C as CallbackSubscribable, E as EmissionObserver, a as EmissionOptions, b as EmissionSource, S as SubscribableLike, e as expectCompletion, f as expectEmission, g as expectEmissions, h as expectError, i as expectNoEmission, s as setEmissionTimeout } from './expect-emission-H8lAQz2f.js';
8
8
  import '@angular/core/testing';
9
9
 
10
10
  /**
package/dist/angular.js CHANGED
@@ -1154,7 +1154,23 @@ function throwUnstubbedCall(call) {
1154
1154
  const message = `[vitest-auto-spy] Nothing configured ${target}, and strict mode is on.
1155
1155
  Called as: ${target}(${call.args.map(renderArgument).join(",")})
1156
1156
  Configure it \u2014 .mockReturnValue(\u2026), .mockImplementation(\u2026), .resolveWith(\u2026), .nextWith(\u2026) or .calledWith(\u2026), or seed it through the 'returns' option \u2014 or drop 'strict' from this double.`;
1157
- throw new Error(withDocs(message, DOCS_LINKS.strictMode));
1157
+ const limit = Error.stackTraceLimit;
1158
+ Error.stackTraceLimit = 30;
1159
+ const error = new Error(withDocs(message, DOCS_LINKS.strictMode));
1160
+ Error.stackTraceLimit = limit;
1161
+ recordStrictViolation(error);
1162
+ throw error;
1163
+ }
1164
+ var MAX_RECORDED_VIOLATIONS = 50;
1165
+ var sharedViolations;
1166
+ function strictViolations() {
1167
+ return sharedViolations ??= globalThis.__vitestAutoSpyStrictViolations__ ??= { errors: [] };
1168
+ }
1169
+ function recordStrictViolation(error) {
1170
+ const { errors } = strictViolations();
1171
+ if (errors.length < MAX_RECORDED_VIOLATIONS) {
1172
+ errors.push(error);
1173
+ }
1158
1174
  }
1159
1175
  var sharedStrictDefault;
1160
1176
  function strictDefault() {
@@ -1162,7 +1178,7 @@ function strictDefault() {
1162
1178
  }
1163
1179
  function resolveUnstubbedGuard(className2, config) {
1164
1180
  const defaults = strictDefault().config;
1165
- const handler = config.onUnstubbedCall ?? defaults?.onUnstubbedCall;
1181
+ const handler = config.onUnstubbedCall ?? (config.strict === false ? void 0 : defaults?.onUnstubbedCall);
1166
1182
  if (handler) {
1167
1183
  return { className: className2, handle: handler };
1168
1184
  }
@@ -1173,7 +1189,7 @@ function resolveUnstubbedGuard(className2, config) {
1173
1189
  }
1174
1190
  function isUnconfigured(state) {
1175
1191
  const container = state.valueContainer;
1176
- return state.calledWith === void 0 && state.mustBeCalledWith === void 0 && container.value === void 0 && !container._isRejectedPromise && !container._isThrown && !container.valuesPerCalls?.length;
1192
+ return state.calledWith === void 0 && state.mustBeCalledWith === void 0 && container.value === void 0 && !container._isRejectedPromise && !container._isThrown && !container._isSeeded && !container.valuesPerCalls?.length;
1177
1193
  }
1178
1194
  function lookupConfigured(calledWithObject, actualArgs) {
1179
1195
  const configured = calledWithObject.argsToValuesMap.get(actualArgs);
@@ -1266,6 +1282,7 @@ var FunctionSpyInternals = class {
1266
1282
  valueContainer.value = void 0;
1267
1283
  delete valueContainer._isRejectedPromise;
1268
1284
  delete valueContainer._isThrown;
1285
+ delete valueContainer._isSeeded;
1269
1286
  delete valueContainer.valuesPerCalls;
1270
1287
  this.#observable?.reset();
1271
1288
  getMockAdapter().restoreImplementation(this.host, this.dispatch);
@@ -1275,6 +1292,19 @@ var FunctionSpyInternals = class {
1275
1292
  this.recorder.clear();
1276
1293
  }
1277
1294
  };
1295
+ function seedReturnValue(spy, value) {
1296
+ const mark = Reflect.get(spy, AUTO_SPY_MARK);
1297
+ if (!(mark instanceof FunctionSpyInternals)) {
1298
+ return false;
1299
+ }
1300
+ const { valueContainer } = mark;
1301
+ valueContainer.value = value;
1302
+ valueContainer._isSeeded = true;
1303
+ delete valueContainer._isRejectedPromise;
1304
+ delete valueContainer._isThrown;
1305
+ delete valueContainer.valuesPerCalls;
1306
+ return true;
1307
+ }
1278
1308
  function internalsOf(self2, helper) {
1279
1309
  const mark = typeof self2 === "function" || typeof self2 === "object" && self2 !== null ? Reflect.get(self2, AUTO_SPY_MARK) : void 0;
1280
1310
  if (mark instanceof FunctionSpyInternals) {
@@ -1506,7 +1536,9 @@ function applyMockReturns(mock, returns) {
1506
1536
  );
1507
1537
  continue;
1508
1538
  }
1509
- adapter.restoreImplementation(spy, () => value);
1539
+ if (!seedReturnValue(spy, value)) {
1540
+ adapter.restoreImplementation(spy, () => value);
1541
+ }
1510
1542
  }
1511
1543
  }
1512
1544
  var INTERNALS = /* @__PURE__ */ Symbol("vitest-auto-spy.autoMock.internals");
@@ -1874,7 +1906,9 @@ function applyReturns(autoSpy, factory, returns) {
1874
1906
  );
1875
1907
  return;
1876
1908
  }
1877
- adapter.restoreImplementation(spy, () => value);
1909
+ if (!seedReturnValue(spy, value)) {
1910
+ adapter.restoreImplementation(spy, () => value);
1911
+ }
1878
1912
  });
1879
1913
  }
1880
1914
  function materializeMethodSpy(autoSpy, methodName, value) {
@@ -1,9 +1,9 @@
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 OverloadChoice, t as Overloads, P as PropStubValue, S as Spy, u as SpyDisposable, v as SpyOptions, w as StrictSpyConfiguration, x as SubjectLike, y as SubjectOf, U as UnstubbedCall, z as UnstubbedCallHandler, V as ValueConfig, B as ValueConfigPerCall, W as WithMockReturnValue } from './types-DTi4dIeN.js';
2
- export { ArgCaptor, AsInstances, AssertMockedOptions, AutoSpyDefaultEntry, ConstructorMock, ConstructorSpy, FixtureFactory, FlushUntilOptions, MockDeepOptions, ModuleNamespace, ModuleNamespaceOptions, asInstance, asInstances, asSpy, assertMocked, captureArg, clearAutoSpy, clearAutoSpyDefaults, createFixture, createFixtureFactory, createFunctionSpy, createMock, createSpyClass, createSpyFromClass, createSpyFromInstance, errorHandler, flushEventLoop, flushEventLoopUntil, mockConstructor, mockDeep, moduleNamespace, narrow, registerAutoSpyDefaults, 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-C0xY_mYK.js';
4
- export { A as AccessorImplementations, O as OutsideHookReaction, R as RestoreProp, c as countMockedProps, m as mockAccessorsProp, a as mockReadonlyProp, b as mockReadonlyPropGetter, d as mockValueProp, r as reportPropsOutsideHooks, e as restoreMockedProps } from './prop-mock-DmM-rPcG.js';
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-DnjTTKlC.js';
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 OverloadChoice, t as Overloads, P as PropStubValue, S as Spy, u as SpyDisposable, v as SpyOptions, w as StrictSpyConfiguration, x as SubjectLike, y as SubjectOf, U as UnstubbedCall, z as UnstubbedCallHandler, V as ValueConfig, B as ValueConfigPerCall, W as WithMockReturnValue } from './types-DYGoBQGg.js';
2
+ export { ArgCaptor, AsInstances, AssertMockedOptions, AutoSpyDefaultEntry, ConstructorMock, ConstructorSpy, FixtureFactory, FlushUntilOptions, MockDeepOptions, ModuleNamespace, ModuleNamespaceOptions, asInstance, asInstances, asSpy, assertMocked, captureArg, clearAutoSpy, clearAutoSpyDefaults, createFixture, createFixtureFactory, createMock, createSpyClass, createSpyFromClass, createSpyFromInstance, errorHandler, flushEventLoop, flushEventLoopUntil, mockConstructor, mockDeep, moduleNamespace, narrow, registerAutoSpyDefaults, 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-H8lAQz2f.js';
4
+ export { c as createFunctionSpy, d as describeDuplicateCopies, g as getPackageCopies } from './package-identity-B3aSBJRI.js';
5
+ export { A as AccessorImplementations, O as OutsideHookReaction, R as RestoreProp, c as countMockedProps, m as mockAccessorsProp, a as mockReadonlyProp, b as mockReadonlyPropGetter, d as mockValueProp, r as reportPropsOutsideHooks, e as restoreMockedProps } from './prop-mock-DSWMTtRE.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-CvgjkxKw.js';
7
7
  import 'vitest';
8
8
  import '@angular/core';
9
9
  import '@angular/core/testing';
@@ -1,14 +1,14 @@
1
1
  import { flushEffects } from './chunk-ALFMIDWW.js';
2
2
  export { flushEffects, stable } from './chunk-ALFMIDWW.js';
3
- import './chunk-X57WQSOC.js';
4
- import { createSpyForToken } from './chunk-SWXAPWFW.js';
5
- 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-C5EPUC3A.js';
6
- import { currentSpecFile } from './chunk-APSBHO3I.js';
7
- export { countMockedProps, mockAccessorsProp, mockReadonlyProp, mockReadonlyPropGetter, mockValueProp, reportPropsOutsideHooks, restoreMockedProps } from './chunk-APSBHO3I.js';
8
- import { createSpyFromClass } from './chunk-GSWNTF5E.js';
9
- export { autoMocked, clearAutoSpy, clearAutoSpyDefaults, createAutoMock, createSpyFromClass, registerAutoSpyDefaults, resetAutoSpy } from './chunk-GSWNTF5E.js';
3
+ import './chunk-IW6ZHFTL.js';
4
+ import { createSpyForToken } from './chunk-374SMZYV.js';
5
+ 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-G73VE5ED.js';
6
+ import { currentSpecFile } from './chunk-2PBOTZ4S.js';
7
+ export { countMockedProps, mockAccessorsProp, mockReadonlyProp, mockReadonlyPropGetter, mockValueProp, reportPropsOutsideHooks, restoreMockedProps } from './chunk-2PBOTZ4S.js';
8
+ import { createSpyFromClass } from './chunk-O5IKAXWI.js';
9
+ export { autoMocked, clearAutoSpy, clearAutoSpyDefaults, createAutoMock, createSpyFromClass, registerAutoSpyDefaults, resetAutoSpy } from './chunk-O5IKAXWI.js';
10
10
  import { misconfigurationThrows, reportMisconfiguration } from './chunk-RZRKEUE6.js';
11
- export { createFunctionSpy, errorHandler } from './chunk-AVVUXEEI.js';
11
+ export { createFunctionSpy, errorHandler } from './chunk-6ER757ZM.js';
12
12
  import './chunk-PC3FAZT3.js';
13
13
  import { isAutoSpyLike } from './chunk-B4Z3CFDJ.js';
14
14
  import { withDocs, DOCS_LINKS } from './chunk-B45HLBA4.js';