vitest-auto-spy 5.1.0 → 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.
- package/AGENTS.md +82 -5
- package/README.md +33 -7
- package/dist/angular.d.ts +28 -6
- package/dist/angular.js +201 -15
- package/dist/bun-angular.d.ts +5 -5
- package/dist/bun-angular.js +7 -8
- package/dist/bun.d.ts +81 -5
- package/dist/bun.js +5 -6
- package/dist/{chunk-Q74KQJWG.js → chunk-5TUDM4DS.js} +39 -1
- package/dist/chunk-ED4EVQKA.js +74 -0
- package/dist/{chunk-CWHCYCCQ.js → chunk-GOG2CJOY.js} +1 -1
- package/dist/{chunk-VX5DDHAS.js → chunk-J33KX6XT.js} +48 -3
- package/dist/{chunk-Y4YG7UOK.js → chunk-JJ2JA2E3.js} +1 -1
- package/dist/{chunk-CUVQUISO.js → chunk-KE3LEHPW.js} +1 -1
- package/dist/{chunk-5WEFPNUG.js → chunk-NHSUUN3D.js} +1 -1
- package/dist/{chunk-YZOB2KRF.js → chunk-URZIJ5CL.js} +3 -3
- package/dist/{chunk-ZLF2YBXV.js → chunk-XAS66AWX.js} +50 -2
- package/dist/cli.js +7 -1
- package/dist/console.d.ts +1 -1
- package/dist/console.js +3 -3
- package/dist/dom-stubs.d.ts +1 -1
- package/dist/dom-stubs.js +54 -6
- package/dist/eslint-plugin.cjs +319 -72
- package/dist/eslint-plugin.d.cts +52 -0
- package/dist/eslint-plugin.d.ts +52 -0
- package/dist/{expect-emission-BfCKHEzi.d.ts → expect-emission-DYtyyKM5.d.ts} +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.js +143 -8
- package/dist/jasmine-compat.d.ts +1 -1
- package/dist/jasmine.d.ts +2 -2
- package/dist/jasmine.js +5 -5
- package/dist/nestjs.d.ts +2 -2
- package/dist/nestjs.js +6 -6
- package/dist/node.cjs +95 -2
- package/dist/node.d.cts +3 -0
- package/dist/node.d.ts +4 -4
- package/dist/node.js +5 -5
- package/dist/{prop-mock-DkSaUWiQ.d.ts → prop-mock-DmM-rPcG.d.ts} +44 -2
- package/dist/react.d.ts +4 -4
- package/dist/react.js +6 -6
- package/dist/rstest.d.ts +4 -4
- package/dist/rstest.js +16 -19
- package/dist/rxjs.d.ts +2 -2
- package/dist/setup.d.ts +13 -2
- package/dist/setup.js +7 -5
- package/dist/svelte.d.ts +4 -4
- package/dist/svelte.js +6 -6
- package/dist/{track-injections-CiYfR36E.d.ts → track-injections-D4TuS8yk.d.ts} +1 -1
- package/dist/{types-CroX5c53.d.ts → types-DTi4dIeN.d.ts} +31 -9
- package/dist/vue.d.ts +5 -5
- package/dist/vue.js +7 -7
- package/dist/{zoneless-C9ruGqto.d.ts → zoneless-BerZbxtu.d.ts} +16 -4
- package/package.json +3 -2
- package/skills/vitest-auto-spy/SKILL.md +26 -2
- package/dist/chunk-NQXXUJPS.js +0 -39
- package/dist/chunk-RQGMLVRO.js +0 -51
package/AGENTS.md
CHANGED
|
@@ -634,6 +634,31 @@ Also true, and worth not re-deriving:
|
|
|
634
634
|
mapping = createSpyFromClass(MgwMappingService); // no second type argument here
|
|
635
635
|
```
|
|
636
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
|
+
|
|
637
662
|
**A getter that returns a `Signal<T>` goes in `instanceMethodsToSpyOn`**, not in `gettersToSpyOn`.
|
|
638
663
|
`get isKidMode(): Signal<boolean> { return this._isKidMode.asReadonly(); }` is read as a property
|
|
639
664
|
and called as a function, and the accessor route makes you write
|
|
@@ -643,6 +668,29 @@ class prototype, so nothing is being shadowed), and
|
|
|
643
668
|
`service.isKidMode.mockReturnValue(false)` reads like every other member. `mockSignalProp` is the
|
|
644
669
|
other answer when the value has to change during the test.
|
|
645
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
|
+
|
|
646
694
|
### `strict` — a method nobody configured throws instead of answering `undefined`
|
|
647
695
|
|
|
648
696
|
```ts
|
|
@@ -752,6 +800,23 @@ const config = asSpy<FeatureFlagService>(TestBed.inject(FeatureFlagService)); //
|
|
|
752
800
|
const config = injectSpy<FeatureFlagService>(FeatureFlagService); // ✅
|
|
753
801
|
```
|
|
754
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
|
+
|
|
755
820
|
---
|
|
756
821
|
|
|
757
822
|
## 7. Resetting
|
|
@@ -1016,6 +1081,12 @@ and a template error about a null profile appeared in a different `describe`. Th
|
|
|
1016
1081
|
properties back and warns with the count and the cause. `countMockedProps()` is exported if you
|
|
1017
1082
|
would rather assert it: `afterEach(() => expect(countMockedProps()).toBe(0))`.
|
|
1018
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
|
+
|
|
1019
1090
|
**The one that only bites at scale:** with `isolate: false`, a `setTimeout` or
|
|
1020
1091
|
`requestAnimationFrame` a component schedules and never clears keeps running after its file is done,
|
|
1021
1092
|
and fires while the **next** file is mid-test. It is reported against that innocent file, as
|
|
@@ -1900,11 +1971,13 @@ enableAngularDiagnostics(); // { ngModuleScopes, deadSchemas, unspiedProviders,
|
|
|
1900
1971
|
| `deadSchemas` | `schemas` sit next to a standalone component (`declarations` empty) — a no-op |
|
|
1901
1972
|
| `unspiedProviders` | `injectSpy` got a real instance; a `console.warn` alone, a throw under the group |
|
|
1902
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` |
|
|
1903
1975
|
|
|
1904
1976
|
Every member defaults to `true`, takes `false` to opt out, and a second call **replaces** the
|
|
1905
1977
|
selection (safe from anywhere, including inside a test). `disableAngularDiagnostics()` turns the
|
|
1906
|
-
group off and leaves the TestBed timing instrumentation alone. `
|
|
1907
|
-
|
|
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
|
|
1908
1981
|
re-report what you inspected.
|
|
1909
1982
|
|
|
1910
1983
|
Nothing here imports `@angular/common/http/testing`: the token is read out of the spec's own
|
|
@@ -2407,17 +2480,21 @@ blanket downgrade so those keep their severity; do not copy the two names into a
|
|
|
2407
2480
|
| `no-floating-assertion` | `error` | — | `expect()` in a `.then()` nobody awaits → `expect(await promise)` |
|
|
2408
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 |
|
|
2409
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 |
|
|
2410
|
-
| `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 |
|
|
2411
2486
|
| `no-import-time-spread` | `error` | suggest | `export const x = [...Imported]` at module scope → a `TypeError` while the bundle loads |
|
|
2412
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 |
|
|
2413
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 |
|
|
2414
2490
|
| `jasmine-namespace-without-entry` | `error` | — | `.and` / `.calls` / `.withArgs` on a library spy in a file that installs the compat layer nowhere — option: `{ setupModules: […] }` |
|
|
2415
2491
|
| `no-jasmine-globals` | `error` | — | `jasmine.*`, bare `spyOn(` / `spyOnProperty(` / `spyOnAllFunctions(` / `fail(` / `pending(`, `.withContext(` |
|
|
2416
2492
|
| `no-save-arguments-by-value` | `error` | — | `spy.calls.saveArgumentsByValue()` — a no-op here, so the spec silently asserts on post-mutation state |
|
|
2417
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 |
|
|
2418
2494
|
|
|
2419
|
-
|
|
2420
|
-
suggestions.
|
|
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
|
|
2421
2498
|
consumer how much each finding mattered — a `warn` nothing reads is `off` with extra output. Three of
|
|
2422
2499
|
them can report on a _correct_ project, and only one has an option:
|
|
2423
2500
|
`jasmine-namespace-without-entry` takes `['error', { setupModules: ['./test-setup'] }]`, naming the
|
package/README.md
CHANGED
|
@@ -23,7 +23,7 @@ faster at suite scale ([benchmarks](#benchmarks)) — and for
|
|
|
23
23
|
[](https://www.npmjs.com/package/vitest-auto-spy)
|
|
24
24
|
[](https://www.npmjs.com/package/vitest-auto-spy)
|
|
25
25
|
[](https://github.com/ASDAlexey/vitest-auto-spy/actions/workflows/ci.yml)
|
|
26
|
-
[](#install)
|
|
27
27
|
[](https://www.npmjs.com/package/vitest-auto-spy)
|
|
28
28
|
[](https://github.com/ASDAlexey/vitest-auto-spy/actions/workflows/ci.yml)
|
|
29
29
|
[](./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
|
|
@@ -1662,6 +1662,27 @@ createSpyFromClass(MyService, {
|
|
|
1662
1662
|
});
|
|
1663
1663
|
```
|
|
1664
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
|
+
|
|
1665
1686
|
### Spying instance-assigned callables (`signal()`, arrow props, `signalStore()`)
|
|
1666
1687
|
|
|
1667
1688
|
`createSpyFromClass` discovers methods by walking the **prototype**, so a callable assigned on the
|
|
@@ -2834,6 +2855,7 @@ not a function` on Node 25, `undefined` on Node 26, under jsdom and happy-dom al
|
|
|
2834
2855
|
| --------------------- | --------- | ------------------------------------------------------------------------------------- |
|
|
2835
2856
|
| `duplicateCopies` | `'throw'` | `'warn'` to report without failing, `'off'` to skip the check |
|
|
2836
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 |
|
|
2837
2859
|
| `restoreMocks` | `false` | `vi.restoreAllMocks()` in a global `afterEach` — turn on for `isolate: false` |
|
|
2838
2860
|
| `strayTimers` | `false` | Cancel timeouts, intervals and frames that outlive their file |
|
|
2839
2861
|
| `onStrayTimers` | — | Takes the per-file count the sweep cancelled — see the note on `--detect-async-leaks` |
|
|
@@ -3048,10 +3070,13 @@ export can never be.
|
|
|
3048
3070
|
| `no-floating-assertion` | `error` | — | `expect()` in a `.then()` nobody awaits → `expect(await promise)` |
|
|
3049
3071
|
| `no-bare-called-with` | `error` | — | `spy.m.calledWith(1);` as a statement of its own — a stub nobody continued, asserting nothing |
|
|
3050
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 |
|
|
3051
|
-
| `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 |
|
|
3052
3076
|
| `no-import-time-spread` | `error` | suggest | `export const x = [...Imported]` at module scope → a `TypeError` while the bundle loads |
|
|
3053
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 |
|
|
3054
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 |
|
|
3055
3080
|
| `jasmine-namespace-without-entry` | `error` | — | `.and` / `.calls` / `.withArgs` on a library spy in a file that installs the compatibility layer nowhere |
|
|
3056
3081
|
| `no-jasmine-globals` | `error` | — | `jasmine.*`, `spyOn(` / `spyOnProperty(` / `spyOnAllFunctions(` / `fail(` / `pending(`, `.withContext(` — none of them exist under Vitest |
|
|
3057
3082
|
| `no-save-arguments-by-value` | `error` | — | `spy.calls.saveArgumentsByValue()`, which is a no-op here → take the copy at call time |
|
|
@@ -3061,7 +3086,7 @@ Every message ends with a link to the matching [recipe](#how-to-mock): a rule th
|
|
|
3061
3086
|
"don't" moves the problem rather than solving it. Rules travel with the API they recommend, so they
|
|
3062
3087
|
are versioned together and stop being re-written in every project that installs the package.
|
|
3063
3088
|
|
|
3064
|
-
**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
|
|
3065
3090
|
the rewrite is. `no-mocked-for-spy` touches a _declaration_: get it wrong and the file stops
|
|
3066
3091
|
compiling, which is the loudest, cheapest failure there is — so `--fix` rewrites the type, adds
|
|
3067
3092
|
`import type { Spy } from 'vitest-auto-spy'` and drops the `Mocked` import once nothing else uses
|
|
@@ -3165,7 +3190,7 @@ package's own — it needs its ESLint integration switched on.
|
|
|
3165
3190
|
### WebStorm and the other JetBrains IDEs
|
|
3166
3191
|
|
|
3167
3192
|
No plugin to install: WebStorm, IntelliJ IDEA Ultimate, PhpStorm, PyCharm Professional and RubyMine
|
|
3168
|
-
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
|
|
3169
3194
|
under **Code → Inspect Code** for the whole project.
|
|
3170
3195
|
|
|
3171
3196
|
```js
|
|
@@ -3182,7 +3207,7 @@ has supported flat config since 2023.3); scope the block to spec files yourself;
|
|
|
3182
3207
|
the fixes and suggestions live.
|
|
3183
3208
|
|
|
3184
3209
|
A native JetBrains plugin is **not** planned — it would duplicate an integration the IDE already has
|
|
3185
|
-
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.
|
|
3186
3211
|
|
|
3187
3212
|
### VS Code, Cursor, Windsurf, VSCodium
|
|
3188
3213
|
|
|
@@ -3273,9 +3298,10 @@ another, and the next one is in that same file.
|
|
|
3273
3298
|
| `mockSystemTime` / `withSystemTime` / `mockNow` / `useCountingClock` _(`/setup`)_ | Clock control that survives fake timers being re-installed per test |
|
|
3274
3299
|
| `registerFocusMatchers()` _(`/setup`)_ | Adds `expect(el).toHaveFocus()` |
|
|
3275
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 |
|
|
3276
|
-
| `enableAngularDiagnostics(opts?)` / `disableAngularDiagnostics()` / `assertNoPendingRequests()` _(Angular)_
|
|
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 |
|
|
3277
3302
|
| `trackInjections(tokens, opts?)` _(Angular, NestJS)_ | Providers that record which collaborators DI constructed, with an auto-spy behind each token |
|
|
3278
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 |
|
|
3279
3305
|
| `setupFakeTimers(config?)` / `advanceTimers(ms?)` _(`/setup`)_ | Paired fake-timer install/restore, and an advance that also settles queued microtasks |
|
|
3280
3306
|
| `describeDuplicateCopies()` / `getPackageCopies()` | The duplicate-install report, and the copies behind it |
|
|
3281
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-
|
|
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-
|
|
5
|
-
export { I as InjectionLog, T as TrackInjectionsOptions, a as TrackedProvider, t as trackInjections } from './track-injections-
|
|
6
|
-
export { A as AccessorImplementations, R as RestoreProp, c as countMockedProps, m as mockAccessorsProp, a as mockReadonlyProp, b as mockReadonlyPropGetter, d as mockValueProp,
|
|
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-
|
|
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 };
|