vitest-auto-spy 5.1.0 → 5.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +91 -7
- package/README.md +49 -13
- 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 +320 -73
- 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 +34 -4
- 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,18 +2480,29 @@ 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
|
-
| `prefer-render-shallow` | `
|
|
2488
|
+
| `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 |
|
|
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
|
-
|
|
2421
|
-
|
|
2495
|
+
Twenty-three rules, **every one an `error` since 4.0.0 except `prefer-render-shallow`**; three fix on
|
|
2496
|
+
their own, eight offer suggestions. Twenty-two are syntactic; `no-private-member-access` is the one
|
|
2497
|
+
that reads types, and it reports nothing at all without `parserOptions.project` / `projectService`
|
|
2498
|
+
rather than guessing. The config used to be a graded mix of `error` / `warn` / `off`, which decided for the
|
|
2499
|
+
consumer how much each finding mattered — a `warn` nothing reads is `off` with extra output. The one
|
|
2500
|
+
`warn` left is not a judgement about how much that finding matters but about what kind of finding it
|
|
2501
|
+
is: every other rule names something wrong or dead, while `prefer-render-shallow` names a file that
|
|
2502
|
+
could render more cheaply, and `renderShallow` is a migration a suite either takes or does not. At
|
|
2503
|
+
`error` the plugin would gate that migration — 491 findings across 398 of one consumer's 1759 spec
|
|
2504
|
+
files — so `recommended` would exist to be overridden. Set it to `'error'` once the project has decided
|
|
2505
|
+
to make the move. Three of
|
|
2422
2506
|
them can report on a _correct_ project, and only one has an option:
|
|
2423
2507
|
`jasmine-namespace-without-entry` takes `['error', { setupModules: ['./test-setup'] }]`, naming the
|
|
2424
2508
|
file where `enableJasmineCompat()` is called; `prefer-native-spy-api` goes `'off'` for as long as a
|
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` |
|
|
@@ -3004,11 +3026,11 @@ import autoSpy from 'vitest-auto-spy/eslint-plugin';
|
|
|
3004
3026
|
export default [{ files: ['**/*.spec.ts'], ...autoSpy.configs.recommended }];
|
|
3005
3027
|
```
|
|
3006
3028
|
|
|
3007
|
-
**Every rule is an `error` since 4.0.0.** The config used to grade them `error` / `warn` /
|
|
3008
|
-
which chose for you how much each finding mattered; a `warn` in a repository that does not read
|
|
3009
|
-
output is `off` with extra noise, and which findings block a merge is one line of config either
|
|
3010
|
-
To turn one down, spread the rule map as well — a bare `rules` key beside the spread config
|
|
3011
|
-
it rather than merging, silently:
|
|
3029
|
+
**Every rule is an `error` since 4.0.0, bar one.** The config used to grade them `error` / `warn` /
|
|
3030
|
+
`off`, which chose for you how much each finding mattered; a `warn` in a repository that does not read
|
|
3031
|
+
lint output is `off` with extra noise, and which findings block a merge is one line of config either
|
|
3032
|
+
way. To turn one down, spread the rule map as well — a bare `rules` key beside the spread config
|
|
3033
|
+
replaces it rather than merging, silently:
|
|
3012
3034
|
|
|
3013
3035
|
```js
|
|
3014
3036
|
export default [
|
|
@@ -3020,6 +3042,16 @@ export default [
|
|
|
3020
3042
|
];
|
|
3021
3043
|
```
|
|
3022
3044
|
|
|
3045
|
+
The one exception is `prefer-render-shallow`, which ships as a **`warn`**, and the reason is the kind
|
|
3046
|
+
of thing it says rather than how much it matters. Every other rule here names something wrong or dead
|
|
3047
|
+
— a double that drifts from its class, an assertion that never runs, a provider the container already
|
|
3048
|
+
dropped, a schema guarding nothing. That one names a file that could render more cheaply, which is a
|
|
3049
|
+
choice a suite makes and not a defect in the file: moving onto `renderShallow` is a migration a
|
|
3050
|
+
project either takes or does not, and at `error` the plugin would be gating it. On the 1759-file suite
|
|
3051
|
+
it was measured against the rule reports 491 times across 398 files — a first run every consumer would
|
|
3052
|
+
have to answer with a project-wide `warn` of its own. A project that has decided to make the move sets
|
|
3053
|
+
it to `'error'` in the same one line that turns the others down.
|
|
3054
|
+
|
|
3023
3055
|
A second config, `autoSpy.configs.typeErrors`, holds the subset whose findings are compile errors
|
|
3024
3056
|
(`prefer-as-spy` is `TS2352`, `no-mocked-for-spy` is `TS2322`). Spread it **after** a blanket
|
|
3025
3057
|
downgrade so those keep their severity — the recipe is a spread rather than rule names copied into
|
|
@@ -3048,10 +3080,13 @@ export can never be.
|
|
|
3048
3080
|
| `no-floating-assertion` | `error` | — | `expect()` in a `.then()` nobody awaits → `expect(await promise)` |
|
|
3049
3081
|
| `no-bare-called-with` | `error` | — | `spy.m.calledWith(1);` as a statement of its own — a stub nobody continued, asserting nothing |
|
|
3050
3082
|
| `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*` |
|
|
3083
|
+
| `no-inject-before-override` | `error` | — | `TestBed.inject()` / `injectSpy()` / `renderShallow()` in a hook, in a suite that still calls `override*` |
|
|
3084
|
+
| `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` |
|
|
3085
|
+
| `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
3086
|
| `no-import-time-spread` | `error` | suggest | `export const x = [...Imported]` at module scope → a `TypeError` while the bundle loads |
|
|
3053
3087
|
| `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
|
-
| `prefer-render-shallow` | `
|
|
3088
|
+
| `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 |
|
|
3089
|
+
| `prefer-observer-stub` | `error` | — | a hand-rolled `IntersectionObserver` / `ResizeObserver` / `MutationObserver` written into a global → `stubIntersectionObserver()` and friends, whose undo `restoreMockedProps()` already runs |
|
|
3055
3090
|
| `jasmine-namespace-without-entry` | `error` | — | `.and` / `.calls` / `.withArgs` on a library spy in a file that installs the compatibility layer nowhere |
|
|
3056
3091
|
| `no-jasmine-globals` | `error` | — | `jasmine.*`, `spyOn(` / `spyOnProperty(` / `spyOnAllFunctions(` / `fail(` / `pending(`, `.withContext(` — none of them exist under Vitest |
|
|
3057
3092
|
| `no-save-arguments-by-value` | `error` | — | `spy.calls.saveArgumentsByValue()`, which is a no-op here → take the copy at call time |
|
|
@@ -3061,7 +3096,7 @@ Every message ends with a link to the matching [recipe](#how-to-mock): a rule th
|
|
|
3061
3096
|
"don't" moves the problem rather than solving it. Rules travel with the API they recommend, so they
|
|
3062
3097
|
are versioned together and stop being re-written in every project that installs the package.
|
|
3063
3098
|
|
|
3064
|
-
**Three of the twenty fix on their own, eight offer suggestions**, and the split is not about how hard
|
|
3099
|
+
**Three of the twenty-three fix on their own, eight offer suggestions**, and the split is not about how hard
|
|
3065
3100
|
the rewrite is. `no-mocked-for-spy` touches a _declaration_: get it wrong and the file stops
|
|
3066
3101
|
compiling, which is the loudest, cheapest failure there is — so `--fix` rewrites the type, adds
|
|
3067
3102
|
`import type { Spy } from 'vitest-auto-spy'` and drops the `Mocked` import once nothing else uses
|
|
@@ -3165,7 +3200,7 @@ package's own — it needs its ESLint integration switched on.
|
|
|
3165
3200
|
### WebStorm and the other JetBrains IDEs
|
|
3166
3201
|
|
|
3167
3202
|
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
|
|
3203
|
+
all run ESLint natively, so the twenty-three rules appear inline, in the **Problems** tool window, and
|
|
3169
3204
|
under **Code → Inspect Code** for the whole project.
|
|
3170
3205
|
|
|
3171
3206
|
```js
|
|
@@ -3182,7 +3217,7 @@ has supported flat config since 2023.3); scope the block to spec files yourself;
|
|
|
3182
3217
|
the fixes and suggestions live.
|
|
3183
3218
|
|
|
3184
3219
|
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.
|
|
3220
|
+
and then keep a second copy of twenty-three rules, in Kotlin, in step with the TypeScript ones.
|
|
3186
3221
|
|
|
3187
3222
|
### VS Code, Cursor, Windsurf, VSCodium
|
|
3188
3223
|
|
|
@@ -3273,9 +3308,10 @@ another, and the next one is in that same file.
|
|
|
3273
3308
|
| `mockSystemTime` / `withSystemTime` / `mockNow` / `useCountingClock` _(`/setup`)_ | Clock control that survives fake timers being re-installed per test |
|
|
3274
3309
|
| `registerFocusMatchers()` _(`/setup`)_ | Adds `expect(el).toHaveFocus()` |
|
|
3275
3310
|
| `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)_
|
|
3311
|
+
| `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
3312
|
| `trackInjections(tokens, opts?)` _(Angular, NestJS)_ | Providers that record which collaborators DI constructed, with an auto-spy behind each token |
|
|
3278
3313
|
| `setupAutoSpy(opts?)` _(`/setup`)_ | Property restore + duplicate-copy detection + mock-registry hygiene, in one call |
|
|
3314
|
+
| `registerAutoSpyDefaults(Class, config)` / `clearAutoSpyDefaults(Class?)` | A class's spy composition registered once, merged into every `createSpyFromClass(X)` / `provideAutoSpy(X)` from then on |
|
|
3279
3315
|
| `setupFakeTimers(config?)` / `advanceTimers(ms?)` _(`/setup`)_ | Paired fake-timer install/restore, and an advance that also settles queued microtasks |
|
|
3280
3316
|
| `describeDuplicateCopies()` / `getPackageCopies()` | The duplicate-install report, and the copies behind it |
|
|
3281
3317
|
| `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 };
|