vitest-auto-spy 5.0.1 → 5.2.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 (56) hide show
  1. package/AGENTS.md +119 -11
  2. package/README.md +46 -11
  3. package/dist/angular.d.ts +28 -6
  4. package/dist/angular.js +203 -15
  5. package/dist/bun-angular.d.ts +5 -5
  6. package/dist/bun-angular.js +7 -8
  7. package/dist/bun.d.ts +81 -5
  8. package/dist/bun.js +5 -6
  9. package/dist/{chunk-Q74KQJWG.js → chunk-5TUDM4DS.js} +39 -1
  10. package/dist/chunk-ED4EVQKA.js +74 -0
  11. package/dist/{chunk-CWHCYCCQ.js → chunk-GOG2CJOY.js} +1 -1
  12. package/dist/{chunk-VX5DDHAS.js → chunk-J33KX6XT.js} +48 -3
  13. package/dist/{chunk-Y4YG7UOK.js → chunk-JJ2JA2E3.js} +1 -1
  14. package/dist/{chunk-NTJDMRHP.js → chunk-KE3LEHPW.js} +11 -56
  15. package/dist/{chunk-5LLY42R7.js → chunk-NHSUUN3D.js} +1 -1
  16. package/dist/{chunk-YZOB2KRF.js → chunk-URZIJ5CL.js} +3 -3
  17. package/dist/{chunk-ZLF2YBXV.js → chunk-XAS66AWX.js} +50 -2
  18. package/dist/cli.js +316 -300
  19. package/dist/console.d.ts +1 -1
  20. package/dist/console.js +3 -2
  21. package/dist/dom-stubs.d.ts +1 -1
  22. package/dist/dom-stubs.js +56 -6
  23. package/dist/eslint-plugin.cjs +331 -76
  24. package/dist/eslint-plugin.d.cts +56 -0
  25. package/dist/eslint-plugin.d.ts +56 -0
  26. package/dist/{expect-emission-Dy5CvsF2.d.ts → expect-emission-DYtyyKM5.d.ts} +1 -1
  27. package/dist/index.d.ts +4 -4
  28. package/dist/index.js +145 -8
  29. package/dist/jasmine-compat.d.ts +1 -1
  30. package/dist/jasmine.d.ts +2 -2
  31. package/dist/jasmine.js +5 -4
  32. package/dist/nestjs.d.ts +2 -2
  33. package/dist/nestjs.js +6 -5
  34. package/dist/node.cjs +96 -3
  35. package/dist/node.d.cts +3 -0
  36. package/dist/node.d.ts +4 -4
  37. package/dist/node.js +5 -5
  38. package/dist/{prop-mock-Dp2ZMFqj.d.ts → prop-mock-DmM-rPcG.d.ts} +44 -2
  39. package/dist/react.d.ts +4 -4
  40. package/dist/react.js +6 -5
  41. package/dist/rstest.d.ts +6 -0
  42. package/dist/rstest.js +63 -0
  43. package/dist/rxjs.d.ts +2 -2
  44. package/dist/setup.d.ts +21 -9
  45. package/dist/setup.js +7 -4
  46. package/dist/shared-state.js +1 -1
  47. package/dist/svelte.d.ts +4 -4
  48. package/dist/svelte.js +6 -5
  49. package/dist/{track-injections-CAqxzlhV.d.ts → track-injections-D4TuS8yk.d.ts} +1 -1
  50. package/dist/{types-CQLPjw8z.d.ts → types-DTi4dIeN.d.ts} +91 -22
  51. package/dist/vue.d.ts +5 -5
  52. package/dist/vue.js +7 -6
  53. package/dist/{zoneless-BFq3ve6Q.d.ts → zoneless-BerZbxtu.d.ts} +36 -4
  54. package/package.json +14 -3
  55. package/skills/vitest-auto-spy/SKILL.md +38 -6
  56. package/dist/chunk-NQXXUJPS.js +0 -39
package/AGENTS.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # vitest-auto-spy — instructions for AI coding agents
2
2
 
3
3
  You are looking at the agent-facing reference for **`vitest-auto-spy`**: typed test spies generated
4
- from a class, a type, or nothing at all, on Vitest / `bun:test` / `node:test`.
4
+ from a class, a type, or nothing at all, on Vitest / `bun:test` / `node:test` / Rstest.
5
5
 
6
6
  This file is written for an agent **using** the library in someone's test suite. It is shipped
7
7
  inside the npm package, so it is readable with no network:
@@ -43,6 +43,7 @@ adapter installed and spies fail at runtime.
43
43
  | `bun test` | `vitest-auto-spy/bun` |
44
44
  | `bun test` + Angular | `vitest-auto-spy/bun-angular` |
45
45
  | `node --test` | `vitest-auto-spy/node` |
46
+ | `rstest run` | `vitest-auto-spy/rstest` |
46
47
  | Angular + Vitest | `vitest-auto-spy/angular` |
47
48
  | NestJS | `vitest-auto-spy/nestjs` |
48
49
  | React | `vitest-auto-spy/react` |
@@ -633,6 +634,31 @@ Also true, and worth not re-deriving:
633
634
  mapping = createSpyFromClass(MgwMappingService); // no second type argument here
634
635
  ```
635
636
 
637
+ **The symptom that leads here says nothing about overloads**, which is why the option is hard to
638
+ find from the error. A stub of the real response shape is rejected on the spec's own line —
639
+ `TS2345: Argument of type 'Page' is not assignable to parameter of type 'HttpEvent<Page>'` — on
640
+ `nextWith(body)`, `resolveWith(body)`, `calledWith(…).returnValue(body)` or
641
+ `mockReturnValue(of(body))` against a generated `observe` client. Neither the double nor the stub
642
+ is wrong; both are being checked against the signature nobody calls. Reach for `overload`, never
643
+ for `@ts-expect-error`: one migration wrote sixty of those across twenty-five files before anyone
644
+ found this option, and each one stops checking the response shape that line exists to describe.
645
+
646
+ **Name the method rather than the whole double.** `'first'` on the type moves *every* overloaded
647
+ member, and on a wide type that breaks the ones nobody was fixing — `Spy<Response, { overload:
648
+ 'first' }>` for one method collected five `TS2769`s on `download`. `overload` also takes a map:
649
+
650
+ ```ts
651
+ let perf: Spy<Performance, { overload: { getEntriesByType: 'first' } }>;
652
+ ```
653
+
654
+ A name the type does not have never matches, so a rename leaves a dead entry rather than a red
655
+ build. The default stays `'last'` because "the useful signature" is not decidable from the type: on
656
+ a generated `observe` client it is the first, on a four-overload `api-mgw` client the last, and both
657
+ live in one suite. And **overload order is not always the author's** — `declare global` in a
658
+ third-party package appends to a global interface (`web-vitals` does exactly this to
659
+ `Performance.getEntriesByType`), so which signature is last depends on which packages are in the
660
+ program and can move on a dependency bump.
661
+
636
662
  **A getter that returns a `Signal<T>` goes in `instanceMethodsToSpyOn`**, not in `gettersToSpyOn`.
637
663
  `get isKidMode(): Signal<boolean> { return this._isKidMode.asReadonly(); }` is read as a property
638
664
  and called as a function, and the accessor route makes you write
@@ -642,6 +668,29 @@ class prototype, so nothing is being shadowed), and
642
668
  `service.isKidMode.mockReturnValue(false)` reads like every other member. `mockSignalProp` is the
643
669
  other answer when the value has to change during the test.
644
670
 
671
+ ### The composition belongs to the class — `registerAutoSpyDefaults`
672
+
673
+ A class is doubled the same way in every file that doubles it, so register that once instead of
674
+ repeating it:
675
+
676
+ ```ts
677
+ // vitest.setup.ts, once
678
+ registerAutoSpyDefaults(Router, { observablePropsToSpyOn: ['events'], gettersToSpyOn: ['url'] });
679
+
680
+ // every spec, from then on
681
+ provideAutoSpy(Router);
682
+ provideAutoSpy(Router, { instanceMethodsToSpyOn: ['currentNavigation'] }); // ADDS to the registration
683
+ ```
684
+
685
+ The call site is **merged** into the registration, not substituted for it: lists unioned
686
+ (registration first, no repeats), `returns` / `overrides` merged key by key with the call site
687
+ winning, scalars decided by the call site when it names the key. The bare-array form counts as
688
+ `{ methodsToSpyOn: [...] }`. Registration is by class identity, so a subclass inherits nothing —
689
+ walking the prototype chain would let one registration change doubles in files nobody was looking at.
690
+ A second registration for the same class replaces the first, because two of them in one suite is the
691
+ drift this removes rather than a merge to perform. `clearAutoSpyDefaults(Class)` drops one,
692
+ `clearAutoSpyDefaults()` the lot.
693
+
645
694
  ### `strict` — a method nobody configured throws instead of answering `undefined`
646
695
 
647
696
  ```ts
@@ -751,6 +800,23 @@ const config = asSpy<FeatureFlagService>(TestBed.inject(FeatureFlagService)); //
751
800
  const config = injectSpy<FeatureFlagService>(FeatureFlagService); // ✅
752
801
  ```
753
802
 
803
+ **A declared default now reaches the double, and used not to.** `class RemoteConfigService<T =
804
+ RemoteConfigDefaults>` handed to `createSpyFromClass` / `injectSpy` inferred `T` as `unknown`, so
805
+ every member typed against it read as `unknown` on a class that had said exactly what it should be.
806
+ Two shapes caused it, both fixed: the `& { [key: string]: any }` intersection `ClassType<T>` used to
807
+ carry (an index signature makes inference drop the default) and the union in `injectSpy`'s token
808
+ parameter (a union does too — a class now matches a bare construct-signature overload first).
809
+ Spelling the argument out still works and is still the answer when the class has **no** default.
810
+
811
+ **A generic *method* is a different thing, and it does collapse.** `show<T, U>(component: Type<T>,
812
+ data: U)` on a modal or factory service reaches `Spy<T>` through `Parameters` / `ReturnType`, which
813
+ instantiate the method's own type parameters — so the double types as `show(component: Type<unknown>,
814
+ data: unknown)`. No mapped type in TypeScript can preserve a generic signature, so this is a limit
815
+ rather than a defect, and it is usually harmless: `unknown` accepts every argument and every seeded
816
+ return, so `spy.show.calledWith(MyModal, data).mockReturnValue(ref)` compiles. It bites only where
817
+ the instantiated type appears in a **contravariant** position; there, name the shape on a declaration
818
+ of your own and assign the factory's result to it.
819
+
754
820
  ---
755
821
 
756
822
  ## 7. Resetting
@@ -1015,6 +1081,12 @@ and a template error about a null profile appeared in a different `describe`. Th
1015
1081
  properties back and warns with the count and the cause. `countMockedProps()` is exported if you
1016
1082
  would rather assert it: `afterEach(() => expect(countMockedProps()).toBe(0))`.
1017
1083
 
1084
+ **A `mock*Prop` patch written in a `describe` body or a `beforeAll` is reported**, because the sweep
1085
+ undoes it after the test it was applied during and nothing puts it back: the first test passes and
1086
+ every one after it reads the real member. `propsOutsideHooks` grades that — `'warn'` (default),
1087
+ `'throw'`, `'off'` — and `reportPropsOutsideHooks(reaction)` sets the same dial for a suite that
1088
+ wires its own hooks. Move the call into `beforeEach`.
1089
+
1018
1090
  **The one that only bites at scale:** with `isolate: false`, a `setTimeout` or
1019
1091
  `requestAnimationFrame` a component schedules and never clears keeps running after its file is done,
1020
1092
  and fires while the **next** file is mid-test. It is reported against that innocent file, as
@@ -1139,10 +1211,16 @@ Everything a spec can observe is the same, and the suite pins it by putting the
1139
1211
  Vitest applies through those two functions.
1140
1212
 
1141
1213
  **The one difference.** `mock.invocationCallOrder` counts on this library's own scale, so
1142
- `expect(a).toHaveBeenCalledBefore(b)` is exact between two auto-spies and meaningless between an
1143
- auto-spy and a hand-written `vi.fn()` — the two counters never met. Nothing else in the library or in
1144
- Vitest reads that field. If a suite needs the comparison, switch the whole run back to the runner's
1145
- factory:
1214
+ `expect(a).toHaveBeenCalledBefore(b)` is exact between two auto-spies and **wrong** between an
1215
+ auto-spy and a hand-written `vi.fn()` — not an error, an answer, computed from two counters that
1216
+ never met. `@vitest/spy` keeps its own in a module-private variable it neither exports nor lets
1217
+ anything advance, so the scales cannot be reconciled after the fact; they drift apart by however many
1218
+ calls of each kind the run has already made. Measured in a converted suite: a double reporting
1219
+ `[164, 165, 167, 168, 169]` beside a `vi.fn()` reporting `[28]` for its single call in the same test,
1220
+ under an assertion that had been passing — because `jest-auto-spies` built both halves with
1221
+ `jest.fn()`, which did share one counter. Three earlier calls of one family are enough to invert the
1222
+ verdict. Nothing else in the library or in Vitest reads that field. If a suite needs the comparison,
1223
+ compare two doubles of the same family, or switch the whole run back to the runner's factory:
1146
1224
 
1147
1225
  ```ts
1148
1226
  // vitest.setup.ts
@@ -1893,11 +1971,13 @@ enableAngularDiagnostics(); // { ngModuleScopes, deadSchemas, unspiedProviders,
1893
1971
  | `deadSchemas` | `schemas` sit next to a standalone component (`declarations` empty) — a no-op |
1894
1972
  | `unspiedProviders` | `injectSpy` got a real instance; a `console.warn` alone, a throw under the group |
1895
1973
  | `pendingRequests` | the test ended with unflushed `HttpTestingController` requests, named one by one |
1974
+ | `shadowedProviders` | a double on the testing module loses to the component's own `providers` |
1896
1975
 
1897
1976
  Every member defaults to `true`, takes `false` to opt out, and a second call **replaces** the
1898
1977
  selection (safe from anywhere, including inside a test). `disableAngularDiagnostics()` turns the
1899
- group off and leaves the TestBed timing instrumentation alone. `assertNoPendingRequests()` is the
1900
- HTTP check on its own, for use mid-test — reading takes the requests, so the group will not
1978
+ group off and leaves the TestBed timing instrumentation alone. `assertNoShadowedProviders(Component, fixture)`
1979
+ and `assertNoPendingRequests()` are the same checks on their own, for a spec that renders through a
1980
+ helper of its own or wants the HTTP one mid-test — reading takes the requests, so the group will not
1901
1981
  re-report what you inspected.
1902
1982
 
1903
1983
  Nothing here imports `@angular/common/http/testing`: the token is read out of the spec's own
@@ -2273,6 +2353,25 @@ and costs +206 B per mock against +65 B for naming at creation, measured over 20
2273
2353
  mock in `node:test`'s reporter output on its own. Full account:
2274
2354
  <https://asdalexey.github.io/vitest-auto-spy/runtimes/node#spy-names>.
2275
2355
 
2356
+ ### Rstest
2357
+
2358
+ `vitest-auto-spy/rstest` drives the same core through `rstest.fn()` / `rstest.spyOn()`. Rstest
2359
+ implements the Jest/Vitest mock surface, so **none of the `node:test` differences above apply**:
2360
+ `spy.method.mock.calls[0]` is a bare argument array, the `mockReturnValue` family is native, and
2361
+ `gettersToSpyOn` / `settersToSpyOn` go through `rstest.spyOn(obj, 'prop', 'get' | 'set')` rather than
2362
+ the redefinition fallback. `rstest.clearAllMocks()`, `rstest.resetAllMocks()` and the
2363
+ `clearMocks: true` / `resetMocks: true` config keys reach spies built by `createSpyFromClass` — the
2364
+ entry plants one sentinel mock for it, and there is nothing to enable.
2365
+
2366
+ Two things are not there. `vitest-auto-spy/setup` is wired to Vitest's hooks, so `setupAutoSpy()` and
2367
+ the fake-timer helpers are Vitest-only — on Rstest, import `vitest-auto-spy/rstest` once in the setup
2368
+ file, which is the part a setup file is for. `trackNodeMocks()` is `node:test`-only and is not needed:
2369
+ Rstest drops its mock registry between files, like Vitest and Bun.
2370
+
2371
+ Rstest is 0.x. Vitest stays the zero-config default; reach for this entry when the suite already runs
2372
+ on Rstest, which is typically an Rspack project reusing its bundler config for tests. Full account:
2373
+ <https://asdalexey.github.io/vitest-auto-spy/runtimes/rstest>.
2374
+
2276
2375
  ---
2277
2376
 
2278
2377
  ## 14. `fakeAsync` needs `vitest-auto-spy/zone`
@@ -2363,6 +2462,10 @@ export default [
2363
2462
  ];
2364
2463
  ```
2365
2464
 
2465
+ `autoSpy.configs.typeErrors` is a second flat config holding the subset whose findings are compile
2466
+ errors — `prefer-as-spy` (`TS2352`) and `no-mocked-for-spy` (`TS2322`). Spread its `rules` after a
2467
+ blanket downgrade so those keep their severity; do not copy the two names into a consumer's config.
2468
+
2366
2469
  | Rule | Level | Fix | Flags |
2367
2470
  | --------------------------------- | ------- | ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
2368
2471
  | `no-expect-in-subscribe` | `error` | suggest | `expect()` inside `subscribe()` → `expectEmission` / `firstValueFrom` |
@@ -2377,17 +2480,21 @@ export default [
2377
2480
  | `no-floating-assertion` | `error` | — | `expect()` in a `.then()` nobody awaits → `expect(await promise)` |
2378
2481
  | `no-bare-called-with` | `error` | — | `spy.m.calledWith(1);` as a statement — a stub nobody continued, asserting nothing; chai's `expect(fn).to.have.been.calledWith()` exempt |
2379
2482
  | `no-overridden-provider` | `error` | suggest | two providers for one token in one array → the earlier one never runs; the exact duplicate can be deleted |
2380
- | `no-inject-before-override` | `error` | — | `TestBed.inject()` in a hook, in a suite that still calls `override*` |
2483
+ | `no-inject-before-override` | `error` | — | `TestBed.inject()` / `injectSpy()` / `renderShallow()` in a hook, in a suite that still calls `override*` |
2484
+ | `no-private-member-access` | `error` | — | `instance['privateMember']`, `(instance as any).privateMember` (and `as unknown as`, and a decoy type), `vi.spyOn(Object.getPrototypeOf(x), 'm')` → drive the member through the public API. **Type-aware**: silent without `parserOptions.project`, and silent on an index signature |
2485
+ | `no-dead-schemas` | `error` | — | `schemas` on a testing module with no `declarations` — the schema applies to nothing; the file decides, so a `declarations` in another `configureTestingModule` call silences it |
2381
2486
  | `no-import-time-spread` | `error` | suggest | `export const x = [...Imported]` at module scope → a `TypeError` while the bundle loads |
2382
2487
  | `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 |
2383
2488
  | `prefer-render-shallow` | `error` | suggest | `TestBed.createComponent` in a file that never reads the template → `renderShallow(X)`; 0.24× the per-test cycle at 100 children |
2489
+ | `prefer-observer-stub` | `error` | — | a hand-rolled observer global → `stubIntersectionObserver()` / `stubResizeObserver()` / `stubMutationObserver()`; the manual save-and-restore goes too, `restoreMockedProps()` runs the undo |
2384
2490
  | `jasmine-namespace-without-entry` | `error` | — | `.and` / `.calls` / `.withArgs` on a library spy in a file that installs the compat layer nowhere — option: `{ setupModules: […] }` |
2385
2491
  | `no-jasmine-globals` | `error` | — | `jasmine.*`, bare `spyOn(` / `spyOnProperty(` / `spyOnAllFunctions(` / `fail(` / `pending(`, `.withContext(` |
2386
2492
  | `no-save-arguments-by-value` | `error` | — | `spy.calls.saveArgumentsByValue()` — a no-op here, so the spec silently asserts on post-mutation state |
2387
2493
  | `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 |
2388
2494
 
2389
- Nineteen rules, **every one an `error` since 4.0.0**; three fix on their own, seven offer
2390
- suggestions. The config used to be a graded mix of `error` / `warn` / `off`, which decided for the
2495
+ Twenty-three rules, **every one an `error` since 4.0.0**; three fix on their own, eight offer
2496
+ suggestions. Twenty-two are syntactic; `no-private-member-access` is the one that reads types, and it
2497
+ reports nothing at all without `parserOptions.project` / `projectService` rather than guessing. The config used to be a graded mix of `error` / `warn` / `off`, which decided for the
2391
2498
  consumer how much each finding mattered — a `warn` nothing reads is `off` with extra output. Three of
2392
2499
  them can report on a _correct_ project, and only one has an option:
2393
2500
  `jasmine-namespace-without-entry` takes `['error', { setupModules: ['./test-setup'] }]`, naming the
@@ -2585,7 +2692,8 @@ packages, which a subpath export can never be.
2585
2692
  | `TS2540: Cannot assign to 'X' because it is a read-only property` | a `readonly` field of an object under test | `mockValueProp(obj, 'X', value)` — on a class **getter**, `mockReadonlyProp` |
2586
2693
  | `TypeError: Cannot read properties of undefined (reading 'mockReturnValue')` | the member is an **instance field**, not on the prototype — `Router.currentNavigation` since Angular 20 | `provideAutoSpy(Router, { instanceMethodsToSpyOn: ['currentNavigation'] })` (§5) |
2587
2694
  | `throwWith` reaching the success branch first, with the previous test's value | the spy's `ReplaySubject(1)` outlived the test that filled it | upgrade; and `resetAutoSpy(spy)` in `beforeEach` when the TestBed is built in `beforeAll` |
2588
- | `TS2540` on a `Spy<T>` / `createAutoMock` member, which the runtime writes fine | `Spy<T>` is homomorphic, so it keeps the `readonly` of an abstract getter | `Mutable<Spy<T>>` for direct assignment, or `mockValueProp` for a patch that is undone |
2695
+ | `TS2540` on a `Spy<T>` / `createAutoMock` member, which the runtime writes fine | `Spy<T>` is homomorphic and keeps the `readonly` of the source member — deliberately: on a member replaced by a **spied accessor** the assignment reaches the setter spy and the getter still answers `undefined`, so the type error is the loud version of a silent no-op | `mockValueProp(spy, 'X', value)` — it defines rather than assigns, so it works in both cases; `Mutable<Spy<T>>` is the opt-in for plain data members, and `Reflect.set` is **not** a way round it |
2696
+ | `TS2345` from `mockReturnValue` / `mockImplementation` / `mockResolvedValue` on a double that used to compile | the mock surface is `MockInstance<Method>`, so the stub is now checked against the method's own return type | stub with what the method returns; if the double is meant to answer something else, the method's type or the spec's expectation is wrong |
2589
2697
  | `delete mock.optionalMethod` leaving the member present and truthy | the Proxy had no `deleteProperty` trap before 3.5.0 | upgrade; before that, `mock.optionalMethod = undefined` |
2590
2698
  | half a double's methods reaching the real implementation after `Object.assign` | `ownKeys` on a type-driven Proxy lists only the keys already read | `createSpyFromClass(X)` — a real object, with enumerable method keys |
2591
2699
  | `let s: MockInstance<() => unknown>` not matching anything | `MockInstance<F>` is invariant in `F`; Jest's `SpyInstance` was not | `MockInstance<T['method']>`, or better `injectSpy(X).method` |
package/README.md CHANGED
@@ -8,7 +8,7 @@ The only auto-spy library that reads a **class** and gives a **fully-typed** spy
8
8
  with **return-type-aware** helpers — `resolveWith` / `rejectWith` for `Promise`s, `nextWith` /
9
9
  `throwWith` for RxJS `Observable`s, and `calledWith` / `mustBeCalledWith` for argument matching. Or
10
10
  skip the class entirely and mock straight from a **type or interface** with `createAutoMock<T>()` and
11
- recursive `mockDeep<T>()`. Runs on **Vitest**, **Bun** (`bun:test`) and **`node:test`** behind one
11
+ recursive `mockDeep<T>()`. Runs on **Vitest**, **Bun** (`bun:test`), **`node:test`** and **Rstest** behind one
12
12
  identical API, with **RxJS** spies and **Angular / NestJS / React / Vue·Pinia / Svelte** recipes
13
13
  ([availability](#availability)). A drop-in replacement for
14
14
  [`jest-auto-spies`](https://www.npmjs.com/package/jest-auto-spies) — same API, and roughly 1.5x
@@ -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-15.1%20kB-brightgreen)](#install)
26
+ [![minzipped size](https://img.shields.io/badge/minzip-15.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 rules in `vitest-auto-spy/eslint-plugin` (three `--fix`, eight suggestions, four of them for a suite mid-migration off jasmine), `setupAutoSpy()`
79
+ - 📏 Lint rules and one-line test-run hygiene — twenty-three rules in `vitest-auto-spy/eslint-plugin` (three `--fix`, eight suggestions, four of them for a suite mid-migration off jasmine), `setupAutoSpy()`
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
@@ -719,7 +719,7 @@ the time. It works in any client that _is_ Claude Code — the z.ai and Kimi set
719
719
 
720
720
  ## Availability
721
721
 
722
- > **All entry points are published.** The **Vitest / Bun / `node:test`** runtimes, the **RxJS** layer,
722
+ > **All entry points are published.** The **Vitest / Bun / `node:test` / Rstest** runtimes, the **RxJS** layer,
723
723
  > and the **Angular / NestJS / React / Vue·Pinia / Svelte** recipes all ship as importable entry points —
724
724
  > one identical API across every runner and framework.
725
725
 
@@ -727,6 +727,7 @@ the time. It works in any client that _is_ Claude Code — the z.ai and Kimi set
727
727
  | ---------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
728
728
  | `vitest-auto-spy` · `vitest-auto-spy/rxjs` · `vitest-auto-spy/angular` | ✅ **Published** |
729
729
  | `vitest-auto-spy/bun` · `vitest-auto-spy/bun-angular` · `vitest-auto-spy/node` | ✅ **Published** |
730
+ | `vitest-auto-spy/rstest` | ✅ **Published** |
730
731
  | `vitest-auto-spy/nestjs` · `/react` · `/vue` · `/svelte` · `/console` | `provideAutoSpy`, `injectSpy` for `Test.createTestingModule`; `createNestUnit` builds the unit from its DI metadata with every unprovided token auto-spied, `expose` for sociable collaborators |
731
732
  | `vitest-auto-spy/setup` · `vitest-auto-spy/zone` · `vitest-auto-spy/eslint-plugin` | ✅ **Published** |
732
733
  | `vitest-auto-spy/jasmine` · `/jasmine-compat` · `/observer-spy` | ✅ **Published** |
@@ -1154,6 +1155,7 @@ Node / Bun / React / Vue project pulls **neither rxjs nor Angular into its runti
1154
1155
  | `vitest-auto-spy/bun` | the same core, driven by Bun's `bun:test` mocks | `bun:test` | ✅ |
1155
1156
  | `vitest-auto-spy/bun-angular` | Angular's `TestBed` under `bun test` — DOM, JIT `templateUrl` resolution and a zoneless environment, from one preload | `bun:test`, `@angular/core`, `@angular/platform-browser` | ✅ |
1156
1157
  | `vitest-auto-spy/node` | the same core, driven by `node:test`'s `mock.fn()`, plus `trackNodeMocks()` — a private `MockTracker` so dropped spies are actually freed | `node:test` | ✅ |
1158
+ | `vitest-auto-spy/rstest` | the same core, driven by Rstest's `rstest.fn()` / `rstest.spyOn()` | `@rstest/core` | ✅ |
1157
1159
  | `vitest-auto-spy/nestjs` | `provideAutoSpy`, `injectSpy` for `Test.createTestingModule` | — (your `@nestjs/*`) | ✅ |
1158
1160
  | `vitest-auto-spy/react` | the core, with a natural import for React Testing Library suites | — (your `react`) | ✅ |
1159
1161
  | `vitest-auto-spy/vue` | `provideAutoSpy` for `global.provide` + Pinia store spying | — (your `vue`/`pinia`) | ✅ |
@@ -1202,18 +1204,19 @@ import 'vitest-auto-spy/rxjs';
1202
1204
 
1203
1205
  The core is runner-agnostic behind a `MockAdapter`: pick the entry that matches your test
1204
1206
  runner — the public API (`createSpyFromClass`, `calledWith`, `resolveWith`, `nextWith`, …) is
1205
- identical across all three.
1207
+ identical across all four.
1206
1208
 
1207
1209
  ```ts
1208
1210
  import { createSpyFromClass } from 'vitest-auto-spy'; // Vitest (default, zero-config)
1209
1211
  import { createSpyFromClass } from 'vitest-auto-spy/bun'; // Bun — bun:test
1210
1212
  import { createSpyFromClass } from 'vitest-auto-spy/node'; // node:test
1213
+ import { createSpyFromClass } from 'vitest-auto-spy/rstest'; // Rstest
1211
1214
  ```
1212
1215
 
1213
1216
  Angular's `TestBed` runs on Bun too — see [Angular on Bun](#angular-on-bun-buntest) below.
1214
1217
 
1215
1218
  > Only the auto-spy helpers are normalised across runtimes; **native** mock methods stay the
1216
- > runner's own — `mockReturnValue` on Vitest/Bun, `spy.method.mock.mockImplementation` on
1219
+ > runner's own — `mockReturnValue` on Vitest/Bun/Rstest, `spy.method.mock.mockImplementation` on
1217
1220
  > `node:test`. Each entry registers its adapter on import, so import the one matching your runner.
1218
1221
 
1219
1222
  > Using an observable spy (`observablePropsToSpyOn`, `nextWith`, …) without importing
@@ -1659,6 +1662,27 @@ createSpyFromClass(MyService, {
1659
1662
  });
1660
1663
  ```
1661
1664
 
1665
+ ### The composition belongs to the class — `registerAutoSpyDefaults`
1666
+
1667
+ A spy's composition is a fact about the class rather than about the spec: `Router` needs `events`
1668
+ spied as an Observable property and `url` as a getter wherever it is doubled. Register it once and
1669
+ every call site starts from it.
1670
+
1671
+ ```ts
1672
+ // vitest-setup.ts, once
1673
+ registerAutoSpyDefaults(Router, { observablePropsToSpyOn: ['events'], gettersToSpyOn: ['url'] });
1674
+
1675
+ // every spec, from then on
1676
+ provideAutoSpy(Router);
1677
+ provideAutoSpy(Router, { instanceMethodsToSpyOn: ['currentNavigation'] }); // adds, does not replace
1678
+ ```
1679
+
1680
+ The call site is **merged** into the registration rather than replacing it: lists unioned,
1681
+ `returns` / `overrides` merged key by key, scalars decided by the call site when it names the key.
1682
+ Registration is by class identity — a subclass inherits nothing — and a second registration for one
1683
+ class replaces the first, because two of them in one suite is the drift this removes.
1684
+ `clearAutoSpyDefaults(Class)` drops one, `clearAutoSpyDefaults()` the lot.
1685
+
1662
1686
  ### Spying instance-assigned callables (`signal()`, arrow props, `signalStore()`)
1663
1687
 
1664
1688
  `createSpyFromClass` discovers methods by walking the **prototype**, so a callable assigned on the
@@ -2831,6 +2855,7 @@ not a function` on Node 25, `undefined` on Node 26, under jsdom and happy-dom al
2831
2855
  | --------------------- | --------- | ------------------------------------------------------------------------------------- |
2832
2856
  | `duplicateCopies` | `'throw'` | `'warn'` to report without failing, `'off'` to skip the check |
2833
2857
  | `restoreProps` | `true` | `restoreMockedProps()` in a global `afterEach` |
2858
+ | `propsOutsideHooks` | `'warn'` | Name a `mock*Prop` patch made in a `describe` body or `beforeAll` — it survives one test; `'throw'`, `'off'`. `reportPropsOutsideHooks(reaction)` sets the same dial without the setup helper |
2834
2859
  | `restoreMocks` | `false` | `vi.restoreAllMocks()` in a global `afterEach` — turn on for `isolate: false` |
2835
2860
  | `strayTimers` | `false` | Cancel timeouts, intervals and frames that outlive their file |
2836
2861
  | `onStrayTimers` | — | Takes the per-file count the sweep cancelled — see the note on `--detect-async-leaks` |
@@ -3017,6 +3042,11 @@ export default [
3017
3042
  ];
3018
3043
  ```
3019
3044
 
3045
+ A second config, `autoSpy.configs.typeErrors`, holds the subset whose findings are compile errors
3046
+ (`prefer-as-spy` is `TS2352`, `no-mocked-for-spy` is `TS2322`). Spread it **after** a blanket
3047
+ downgrade so those keep their severity — the recipe is a spread rather than rule names copied into
3048
+ your config, where they would go stale silently.
3049
+
3020
3050
  The full dial — landing it on a large existing suite without a red CI, the three rules that can
3021
3051
  report on correct code, and `setupModules` — is on the docs site:
3022
3052
  [ESLint plugin → Tuning it for your project](https://asdalexey.github.io/vitest-auto-spy/utilities/eslint-plugin#tuning-it-for-your-project).
@@ -3040,10 +3070,13 @@ export can never be.
3040
3070
  | `no-floating-assertion` | `error` | — | `expect()` in a `.then()` nobody awaits → `expect(await promise)` |
3041
3071
  | `no-bare-called-with` | `error` | — | `spy.m.calledWith(1);` as a statement of its own — a stub nobody continued, asserting nothing |
3042
3072
  | `no-overridden-provider` | `error` | suggest | two providers for one token in one array → the earlier one never runs; the exact duplicate can be deleted |
3043
- | `no-inject-before-override` | `error` | — | `TestBed.inject()` in a hook, in a suite that still calls `override*` |
3073
+ | `no-inject-before-override` | `error` | — | `TestBed.inject()` / `injectSpy()` / `renderShallow()` in a hook, in a suite that still calls `override*` |
3074
+ | `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` |
3075
+ | `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 |
3044
3076
  | `no-import-time-spread` | `error` | suggest | `export const x = [...Imported]` at module scope → a `TypeError` while the bundle loads |
3045
3077
  | `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 |
3046
3078
  | `prefer-render-shallow` | `error` | suggest | `TestBed.createComponent` in a file that never reads the template → `renderShallow(X)`; 0.24× the per-test cycle at 100 children |
3079
+ | `prefer-observer-stub` | `error` | — | a hand-rolled `IntersectionObserver` / `ResizeObserver` / `MutationObserver` written into a global → `stubIntersectionObserver()` and friends, whose undo `restoreMockedProps()` already runs |
3047
3080
  | `jasmine-namespace-without-entry` | `error` | — | `.and` / `.calls` / `.withArgs` on a library spy in a file that installs the compatibility layer nowhere |
3048
3081
  | `no-jasmine-globals` | `error` | — | `jasmine.*`, `spyOn(` / `spyOnProperty(` / `spyOnAllFunctions(` / `fail(` / `pending(`, `.withContext(` — none of them exist under Vitest |
3049
3082
  | `no-save-arguments-by-value` | `error` | — | `spy.calls.saveArgumentsByValue()`, which is a no-op here → take the copy at call time |
@@ -3053,7 +3086,7 @@ Every message ends with a link to the matching [recipe](#how-to-mock): a rule th
3053
3086
  "don't" moves the problem rather than solving it. Rules travel with the API they recommend, so they
3054
3087
  are versioned together and stop being re-written in every project that installs the package.
3055
3088
 
3056
- **Three of the twenty fix on their own, eight offer suggestions**, and the split is not about how hard
3089
+ **Three of the twenty-three fix on their own, eight offer suggestions**, and the split is not about how hard
3057
3090
  the rewrite is. `no-mocked-for-spy` touches a _declaration_: get it wrong and the file stops
3058
3091
  compiling, which is the loudest, cheapest failure there is — so `--fix` rewrites the type, adds
3059
3092
  `import type { Spy } from 'vitest-auto-spy'` and drops the `Mocked` import once nothing else uses
@@ -3157,7 +3190,7 @@ package's own — it needs its ESLint integration switched on.
3157
3190
  ### WebStorm and the other JetBrains IDEs
3158
3191
 
3159
3192
  No plugin to install: WebStorm, IntelliJ IDEA Ultimate, PhpStorm, PyCharm Professional and RubyMine
3160
- all run ESLint natively, so the twenty rules appear inline, in the **Problems** tool window, and
3193
+ all run ESLint natively, so the twenty-three rules appear inline, in the **Problems** tool window, and
3161
3194
  under **Code → Inspect Code** for the whole project.
3162
3195
 
3163
3196
  ```js
@@ -3174,7 +3207,7 @@ has supported flat config since 2023.3); scope the block to spec files yourself;
3174
3207
  the fixes and suggestions live.
3175
3208
 
3176
3209
  A native JetBrains plugin is **not** planned — it would duplicate an integration the IDE already has
3177
- and then keep a second copy of twenty rules, in Kotlin, in step with the TypeScript ones.
3210
+ and then keep a second copy of twenty-three rules, in Kotlin, in step with the TypeScript ones.
3178
3211
 
3179
3212
  ### VS Code, Cursor, Windsurf, VSCodium
3180
3213
 
@@ -3218,6 +3251,7 @@ names are unfindable from these messages otherwise — no `TS2739` text contains
3218
3251
  | `TS2322: Type 'Spy<X>' is not assignable to type 'Mocked<X>' …` | the variable was declared `Mocked<T>` | declare it `Spy<T>` |
3219
3252
  | `'AddPromiseSpyMethods<unknown>' is missing … from type 'WithMockReturnValue<…>'` | a generic class inferred as `Service<any>` | `asSpy<Service>(…)` / `injectSpy<Service>(…)` |
3220
3253
  | `TS2345` / `TS2554` **on a call to a spied method** (wrong arguments) | the arguments the real method rejects — the double's call signature is the method's own; it used to accept anything | fix the call; don't re-widen the spy |
3254
+ | `TS2345` **inside `mockReturnValue` / `mockImplementation` / `mockResolvedValue`** | the stub is not what the method returns — the mock surface is `MockInstance<Method>`, so it is checked | fix the stub; a deliberate mismatch is a type bug |
3221
3255
  | `TS2739 … 'Spy<X>' is missing …` **on a line with `injectSpy`** | the provider handed back the real object | `provideAutoSpy(X)`, or an honest `TestBed.inject` |
3222
3256
 
3223
3257
  Two notes that cost real time when they are missing.
@@ -3264,9 +3298,10 @@ another, and the next one is in that same file.
3264
3298
  | `mockSystemTime` / `withSystemTime` / `mockNow` / `useCountingClock` _(`/setup`)_ | Clock control that survives fake timers being re-installed per test |
3265
3299
  | `registerFocusMatchers()` _(`/setup`)_ | Adds `expect(el).toHaveFocus()` |
3266
3300
  | `overrideAutoSpy` / `overrideComponentProvider` / `assertNgModuleScopes` / `assertComponentDefIntact` _(Angular)_ | Override a component-level provider — and verify on the next fixture that the override applied; diagnose an NgModule with an empty runtime scope |
3267
- | `enableAngularDiagnostics(opts?)` / `disableAngularDiagnostics()` / `assertNoPendingRequests()` _(Angular)_ | Dead NgModule imports, dead `schemas`, an unspied provider and unflushed HTTP requests, as failures |
3301
+ | `enableAngularDiagnostics(opts?)` / `disableAngularDiagnostics()` / `assertNoPendingRequests()` / `assertNoShadowedProviders()` _(Angular)_ | `ngModuleScopes`, `deadSchemas`, `unspiedProviders`, `shadowedProviders` and `pendingRequests`: a dead NgModule import, `schemas` that can never apply, an unspied provider, a double the component's own `providers` shadow, and unflushed HTTP requests — each as a failure |
3268
3302
  | `trackInjections(tokens, opts?)` _(Angular, NestJS)_ | Providers that record which collaborators DI constructed, with an auto-spy behind each token |
3269
3303
  | `setupAutoSpy(opts?)` _(`/setup`)_ | Property restore + duplicate-copy detection + mock-registry hygiene, in one call |
3304
+ | `registerAutoSpyDefaults(Class, config)` / `clearAutoSpyDefaults(Class?)` | A class's spy composition registered once, merged into every `createSpyFromClass(X)` / `provideAutoSpy(X)` from then on |
3270
3305
  | `setupFakeTimers(config?)` / `advanceTimers(ms?)` _(`/setup`)_ | Paired fake-timer install/restore, and an advance that also settles queued microtasks |
3271
3306
  | `describeDuplicateCopies()` / `getPackageCopies()` | The duplicate-install report, and the copies behind it |
3272
3307
  | `renderShallow(Component, opts?)` _(Angular)_ | `TestBed` component, minus its children and (by default) its template |
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-BFq3ve6Q.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-BerZbxtu.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-CQLPjw8z.js';
5
- export { I as InjectionLog, T as TrackInjectionsOptions, a as TrackedProvider, t as trackInjections } from './track-injections-CAqxzlhV.js';
6
- 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';
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-Dy5CvsF2.js';
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-DYtyyKM5.js';
8
8
  import '@angular/core/testing';
9
9
 
10
10
  /**
@@ -245,6 +245,11 @@ interface AngularDiagnosticsOptions {
245
245
  unspiedProviders?: boolean;
246
246
  /** Fail a test that ends with unflushed `HttpTestingController` requests. */
247
247
  pendingRequests?: boolean;
248
+ /**
249
+ * Fail when a double registered on the testing module loses to the component's own `providers`,
250
+ * so the component under test is running against the real service.
251
+ */
252
+ shadowedProviders?: boolean;
248
253
  }
249
254
  /**
250
255
  * Fail when the `HttpTestingController` this test configured is still holding requests.
@@ -263,6 +268,23 @@ interface AngularDiagnosticsOptions {
263
268
  * ```
264
269
  */
265
270
  declare function assertNoPendingRequests(): void;
271
+ /**
272
+ * Fail when a double this test registered on the testing module never reached `component`.
273
+ *
274
+ * `shadowedProviders` calls this on every fixture; it is exported for the same reason
275
+ * {@link assertNoPendingRequests} is — a spec that builds its component through a helper of its own
276
+ * can ask directly, and a check that can only be reached through one code path is a check that
277
+ * stops running the day that path changes.
278
+ *
279
+ * A no-op when the fixture never rendered `component`, and when everything it resolved is a double.
280
+ *
281
+ * @example
282
+ * ```ts
283
+ * const fixture = renderThroughOurHelper(CartComponent);
284
+ * assertNoShadowedProviders(CartComponent, fixture); // the doubles really are the ones in play
285
+ * ```
286
+ */
287
+ declare function assertNoShadowedProviders(component: unknown, fixture: unknown): void;
266
288
  /**
267
289
  * Turn the group on. Every member defaults to `true`; pass `false` to leave one out.
268
290
  *
@@ -709,4 +731,4 @@ declare function reportSpecTiming(timing: SpecTiming): void;
709
731
  */
710
732
  declare function enableTestBedDiagnostics(options?: TestBedDiagnosticsOptions): void;
711
733
 
712
- export { type AngularDiagnosticsOptions, type AngularTestEnvMode, type AngularTestEnvOptions, type AutoSpyFixture, type AutoSpyOverride, type DirectiveHostOptions, type ExtendWithAutoSpiesOptions, type MockedResource, type ResourceDouble, type ResourceDoubleStatus, type ResourceLike, type SignalLike, type SpecTiming, type SpiedFixtures, type TestBedDiagnosticsOptions, assertComponentDefIntact, assertNgModuleScopes, assertNoPendingRequests, createDirectiveHost, disableAngularDiagnostics, disableTestBedDiagnostics, enableAngularDiagnostics, enableTestBedDiagnostics, extendWithAutoSpies, formatSpecTiming, getTestBedTiming, instrumentTestBed, mockResourceProp, mockSignalProp, overrideAutoSpy, overrideComponentProvider, registerDirectiveMatchers, registerResourceMatchers, registerSignalMatchers, reportSpecTiming, setupAngularTestEnv };
734
+ export { type AngularDiagnosticsOptions, type AngularTestEnvMode, type AngularTestEnvOptions, type AutoSpyFixture, type AutoSpyOverride, type DirectiveHostOptions, type ExtendWithAutoSpiesOptions, type MockedResource, type ResourceDouble, type ResourceDoubleStatus, type ResourceLike, type SignalLike, type SpecTiming, type SpiedFixtures, type TestBedDiagnosticsOptions, assertComponentDefIntact, assertNgModuleScopes, assertNoPendingRequests, assertNoShadowedProviders, createDirectiveHost, disableAngularDiagnostics, disableTestBedDiagnostics, enableAngularDiagnostics, enableTestBedDiagnostics, extendWithAutoSpies, formatSpecTiming, getTestBedTiming, instrumentTestBed, mockResourceProp, mockSignalProp, overrideAutoSpy, overrideComponentProvider, registerDirectiveMatchers, registerResourceMatchers, registerSignalMatchers, reportSpecTiming, setupAngularTestEnv };