vitest-auto-spy 5.5.0 → 5.6.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 +115 -19
- package/README.md +102 -14
- package/dist/angular-http.d.ts +4 -3
- package/dist/angular-http.js +46 -16
- package/dist/angular.d.ts +2 -2
- package/dist/angular.js +178 -73
- package/dist/bun-angular.d.ts +2 -2
- package/dist/bun-angular.js +25 -11
- package/dist/bun.d.ts +12 -2
- package/dist/bun.js +6 -5
- package/dist/{chunk-E2JLMCOR.js → chunk-4XY4DEM5.js} +2 -2
- package/dist/{chunk-7FUZBDFD.js → chunk-APSBHO3I.js} +13 -2
- package/dist/{chunk-XCIRU75U.js → chunk-AVVUXEEI.js} +33 -7
- package/dist/{chunk-M2ZI62ZY.js → chunk-C5EPUC3A.js} +3 -3
- package/dist/{chunk-SNRHI2QI.js → chunk-GSWNTF5E.js} +19 -9
- package/dist/{chunk-FPMWEHKF.js → chunk-PBLYL3Q7.js} +7 -4
- package/dist/chunk-RZRKEUE6.js +15 -0
- package/dist/{chunk-UF47X335.js → chunk-SWXAPWFW.js} +1 -1
- package/dist/{chunk-EPTJRSSO.js → chunk-X57WQSOC.js} +1 -1
- package/dist/{chunk-A7INJLDC.js → chunk-YL7EQKZU.js} +1 -1
- package/dist/cli.js +10 -1
- package/dist/console.d.ts +8 -9
- package/dist/console.js +44 -19
- package/dist/dom-stubs.js +17 -1
- package/dist/eslint-plugin.cjs +191 -7
- package/dist/{expect-emission-DYtyyKM5.d.ts → expect-emission-C0xY_mYK.d.ts} +2 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +89 -15
- package/dist/jasmine.js +9 -8
- package/dist/nestjs.js +7 -6
- package/dist/node.cjs +70 -14
- package/dist/node.d.ts +1 -1
- package/dist/node.js +6 -5
- package/dist/react.d.ts +1 -1
- package/dist/react.js +7 -6
- package/dist/rstest.d.ts +1 -1
- package/dist/rstest.js +7 -6
- package/dist/setup.d.ts +202 -105
- package/dist/setup.js +361 -46
- package/dist/svelte.d.ts +1 -1
- package/dist/svelte.js +7 -6
- package/dist/vue.d.ts +1 -1
- package/dist/vue.js +8 -7
- package/dist/{zoneless-DM1AWQV2.d.ts → zoneless-DnjTTKlC.d.ts} +7 -3
- package/package.json +1 -1
- package/skills/vitest-auto-spy/SKILL.md +10 -4
package/AGENTS.md
CHANGED
|
@@ -56,7 +56,7 @@ Add-ons, orthogonal to the runner:
|
|
|
56
56
|
| ----------------- | ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
57
57
|
| Observable spies | `import 'vitest-auto-spy/rxjs'` | `nextWith` & friends. **Side-effect import, once**, and in a file the `tsconfig` includes (§4) |
|
|
58
58
|
| observer-spy shim | `vitest-auto-spy/observer-spy` | `subscribeSpyTo` — the `@hirez_io/observer-spy` surface (§20). Its own entry so `/rxjs` does not carry it |
|
|
59
|
-
| Console spies | `vitest-auto-spy/console` | silent typed spies over the global `console` |
|
|
59
|
+
| Console spies | `vitest-auto-spy/console` | silent typed spies over the global `console` — `installConsoleSpies()` per test, `restoreConsole()` after |
|
|
60
60
|
| DOM stubs | `vitest-auto-spy/dom-stubs` | `stubIntersectionObserver` / `stubResizeObserver` / `stubMutationObserver` / `stubObserver`, `stubMediaElement`, `stubAbortController`, `intersectionEntry` / `resizeEntry` / `mutationRecord`. **Moved off the root in 4.0.0** |
|
|
61
61
|
| Run diagnostics | `vitest-auto-spy/diagnostics` | `compareTestRuns`, `summarizeTestRun`, `formatTestRunComparison`, `diffByField`. **Moved off the root in 4.0.0** |
|
|
62
62
|
| Angular HTTP | `vitest-auto-spy/angular-http` | `provideHttpTesting`, `expectRequest` — `httpResource()` / `HttpClient` (§13). Optional `@angular/common` peer, this entry only |
|
|
@@ -720,7 +720,9 @@ users.currentTenant(); // throws here, not four frames later inside the componen
|
|
|
720
720
|
Called as: Cart.checkout(1,'now')
|
|
721
721
|
```
|
|
722
722
|
|
|
723
|
-
Also on `createAutoMock`, `provideAutoSpy`, and suite-wide as `setupAutoSpy({ strict: true })
|
|
723
|
+
Also on `createAutoMock`, `provideAutoSpy`, and suite-wide as `setupAutoSpy({ strict: true })` — which
|
|
724
|
+
reaches a double whichever bundle of the package built it: the default lives on `globalThis`. Before
|
|
725
|
+
this release it lived in module scope, and a setup file's `strict: true` reached no double a spec built.
|
|
724
726
|
Precedence, first one set wins: the double's `onUnstubbedCall` → the global `onUnstubbedCall` → the
|
|
725
727
|
double's `strict` (**including `strict: false`**, the only way to exempt one double from a suite-wide
|
|
726
728
|
default) → the global `strict`.
|
|
@@ -734,6 +736,12 @@ default) → the global `strict`.
|
|
|
734
736
|
- **It does not reach** accessor spies, observable-property spies, `mockDeep` nodes, `console-spy`,
|
|
735
737
|
`mockResourceProp`'s `reload` or a standalone `createFunctionSpy`. A strict double still answers
|
|
736
738
|
`undefined` for an unconfigured getter or `items$`. `fillMissing` members **are** covered.
|
|
739
|
+
- **Angular lifecycle hooks are exempt.** `ngOnDestroy`, `ngOnInit`, `ngOnChanges`, `ngDoCheck` and the
|
|
740
|
+
`ngAfter…` hooks answer `undefined` on a strict double: Angular calls `ngOnDestroy` itself on every
|
|
741
|
+
provided value at teardown (a `createAutoMock` / `provideAutoSpyForToken` double always has one), and
|
|
742
|
+
a throw there broke the teardown for every test after it. The calls are still recorded.
|
|
743
|
+
- A class instance or DOM node in the `Called as:` line prints as `[ClassName]`, data up to 200
|
|
744
|
+
characters per argument; `provideAutoSpyForToken` names the token (`createAutoMock` takes `name`).
|
|
737
745
|
- Use `onUnstubbedCall` to survey a suite before turning the throw on — it is handed
|
|
738
746
|
`{ className, method, args }` and whatever it returns is what the call answers, so a handler that
|
|
739
747
|
pushes to an array records the gap without failing anything.
|
|
@@ -1137,23 +1145,28 @@ setupAutoSpy({ strayTimers: true }); // wrap the schedulers, sweep the survivors
|
|
|
1137
1145
|
```
|
|
1138
1146
|
|
|
1139
1147
|
The pieces are exported too — `trackStrayTimers()` (idempotent, returns the undo),
|
|
1140
|
-
`cancelStrayTimers()` (returns how many it cancelled)
|
|
1141
|
-
`
|
|
1142
|
-
|
|
1148
|
+
`cancelStrayTimers()` (returns how many it cancelled), `countStrayTimers()` and
|
|
1149
|
+
`describeStrayTimers()` (each pending timer's kind, the spec file that scheduled it and up to five
|
|
1150
|
+
frames), all from `vitest-auto-spy/setup`. Use `expect(countStrayTimers()).toBe(0)` in an `afterEach`
|
|
1151
|
+
to make a leak fail rather than be tidied away, or take the per-file report from the sweep itself —
|
|
1152
|
+
`timers` carries the same origins, so the failure diff names the file and the scheduling call:
|
|
1143
1153
|
|
|
1144
1154
|
```ts
|
|
1145
|
-
setupAutoSpy({ strayTimers: true, onStrayTimers: ({
|
|
1155
|
+
setupAutoSpy({ strayTimers: true, onStrayTimers: ({ timers }) => expect(timers).toEqual([]) });
|
|
1146
1156
|
```
|
|
1147
1157
|
|
|
1158
|
+
A stray whose `file` is not the file that failed was scheduled after the previous file's sweep —
|
|
1159
|
+
the previous file is the one to fix.
|
|
1160
|
+
|
|
1148
1161
|
**With Vitest 4.1's `--detect-async-leaks`, run one or the other — not both silently.** The two
|
|
1149
1162
|
arrive at the same timer from opposite ends and the quiet one wins: the sweep cancels in `afterAll`,
|
|
1150
1163
|
Vitest collects its leaks afterwards, and a cancelled timeout is no longer referenced, so the run
|
|
1151
1164
|
reports **no leaks** for a file that leaks. Cancelling is still the right default — a callback firing
|
|
1152
1165
|
during a later file is the more expensive failure — so when both are on and no `onStrayTimers` is
|
|
1153
|
-
given, the sweep prints one line to stderr saying how many it took away
|
|
1154
|
-
scheduled
|
|
1155
|
-
at the `setTimeout` in the spec, because the library's own scheduler wrappers go
|
|
1156
|
-
`vi.defineHelper` and are dropped from the stack.
|
|
1166
|
+
given, the sweep prints one line to stderr saying how many it took away and where the first three
|
|
1167
|
+
were scheduled; `onStrayTimers` gets all of them. Vitest's own report, with `strayTimers` off, points
|
|
1168
|
+
its code frame at the `setTimeout` in the spec, because the library's own scheduler wrappers go
|
|
1169
|
+
through `vi.defineHelper` and are dropped from the stack.
|
|
1157
1170
|
|
|
1158
1171
|
**The one that keeps a suite green while it is wrong:** zone.js replaces the global `Promise`, and a
|
|
1159
1172
|
rejection nobody handled is drained into `console.error` and no further — it never reaches
|
|
@@ -1546,6 +1559,65 @@ a `useValue` provider or a test double — patch the prototype of the class the
|
|
|
1546
1559
|
`guardPrototypePollution(reaction)` from `/setup` registers the same check on its own, for a suite
|
|
1547
1560
|
that does not call `setupAutoSpy()`.
|
|
1548
1561
|
|
|
1562
|
+
### Failing on console output nothing absorbed
|
|
1563
|
+
|
|
1564
|
+
```ts
|
|
1565
|
+
setupAutoSpy({ strayConsole: 'throw' }); // default 'off'; 'warn' prints the same report without failing
|
|
1566
|
+
```
|
|
1567
|
+
|
|
1568
|
+
Any call to a console method that writes (`log`, `info`, `warn`, `error`, `debug`, `trace`, `table`,
|
|
1569
|
+
`dir`, `dirxml`, `timeLog`, `timeEnd`, `count`; `group` with a label; a falsy `assert`) made during a
|
|
1570
|
+
test that nothing absorbed fails **that test**, quoting the method, the first three lines and the first
|
|
1571
|
+
frame outside `node_modules`:
|
|
1572
|
+
|
|
1573
|
+
```
|
|
1574
|
+
[vitest-auto-spy] "CartService > reports a failed load" wrote to the console 1 time(s) and nothing absorbed it:
|
|
1575
|
+
- console.error: Error: load failed
|
|
1576
|
+
at CartService.load (src/app/cart.service.ts:41:15)
|
|
1577
|
+
```
|
|
1578
|
+
|
|
1579
|
+
- **Absorbed** means the call never reached the console: a `/console` spy installed for the test or
|
|
1580
|
+
the file, `vi.spyOn(console, m).mockImplementation(…)`, any replacement that does not call
|
|
1581
|
+
through. A bare `vi.spyOn(console, m)` calls through and **counts**.
|
|
1582
|
+
- Output **outside any test** — collection/import, `beforeAll` / `afterAll`, a callback after its
|
|
1583
|
+
test ended — fails the **file** in `afterAll` (`… outside any test …`).
|
|
1584
|
+
- Every console method a test replaced is put back after it; one a file replaced, after the file.
|
|
1585
|
+
- Under the guard, **importing `vitest-auto-spy/console` installs nothing** — call
|
|
1586
|
+
`installConsoleSpies()` in a `beforeEach` (or at the top of the file). Spies an import installed
|
|
1587
|
+
before the guard armed are taken off.
|
|
1588
|
+
- The library's own warnings are console output: under the guard they fail the test that caused them.
|
|
1589
|
+
- `strayConsole: { allow: ['…', /…/] }` is the last resort, for environment noise no spec can reach.
|
|
1590
|
+
Output written to `process.stdout` / `process.stderr` directly, and jsdom's own virtual console, is
|
|
1591
|
+
not seen.
|
|
1592
|
+
- The wrapper forwards every call unchanged, so Vitest's attribution and `onConsoleLog` still work.
|
|
1593
|
+
`guardStrayConsole(reaction)` registers the same guard on its own.
|
|
1594
|
+
|
|
1595
|
+
### One grade for everything — `preset: 'strict'`
|
|
1596
|
+
|
|
1597
|
+
```ts
|
|
1598
|
+
setupAutoSpy({ preset: 'strict' });
|
|
1599
|
+
```
|
|
1600
|
+
|
|
1601
|
+
Sets `duplicateCopies`, `propsOutsideHooks`, `guardGlobals`, `prototypePollution`, `strayConsole` and
|
|
1602
|
+
`misconfiguration` to `'throw'`, turns `strayTimers` on, and `strayRejections` on when zone.js is
|
|
1603
|
+
loaded. An option passed alongside still wins. **Not** included: `strict` (strict doubles change what
|
|
1604
|
+
an unconfigured call returns — a semantic switch, not a grade; the name was taken, hence `preset`),
|
|
1605
|
+
`blockNetwork` (changes the code under test), `restoreMocks` (drops `beforeAll` spies), failing on
|
|
1606
|
+
stray-timer counts (the sweep fails the file from `afterAll`, and a callback scheduled after the
|
|
1607
|
+
previous file's sweep is charged to the next — opt in with
|
|
1608
|
+
`onStrayTimers: ({ timers }) => expect(timers).toEqual([])`, whose diff names each one's file), and
|
|
1609
|
+
`enableAngularDiagnostics()`, which lives in `/angular` — call it in the same setup file as the Angular half of strict.
|
|
1610
|
+
|
|
1611
|
+
`misconfiguration: 'throw'` on its own makes the library's misuse reports — an `onlyMethodsToSpyOn`
|
|
1612
|
+
typo, `gettersToSpyOn` naming a method, a `returns` key no spy answers to, `injectSpy` handed a real
|
|
1613
|
+
instance, a `jasmine.DEFAULT_TIMEOUT_INTERVAL` write, `providedMethodNames` — throw at the call site,
|
|
1614
|
+
every occurrence. The grade is process-wide and released after the file. The printed grade of the
|
|
1615
|
+
`injectSpy` warning is de-duplicated per token **per spec file**, no longer per worker.
|
|
1616
|
+
|
|
1617
|
+
`withoutStrayTimerTracking(work)` runs setup work whose timers `strayTimers` neither counts nor
|
|
1618
|
+
cancels — jsdom answers every Web Storage write with a real `setTimeout(…, 0)`, and the library's own
|
|
1619
|
+
storage probe runs under it.
|
|
1620
|
+
|
|
1549
1621
|
### Hook order differs from Jest
|
|
1550
1622
|
|
|
1551
1623
|
Vitest runs `afterEach` hooks as a stack (innermost / last-registered first); Jest ran them in
|
|
@@ -2481,15 +2553,29 @@ mount(Greeting, { global: { provide } });
|
|
|
2481
2553
|
import { createSpyFromClass } from 'vitest-auto-spy/react';
|
|
2482
2554
|
```
|
|
2483
2555
|
|
|
2484
|
-
Console spies —
|
|
2485
|
-
`timeEnd` / `trace` / `warn` with silent typed spies, named `console<Method>Spy
|
|
2556
|
+
Console spies — `installConsoleSpies()` replaces `console.debug` / `error` / `info` / `log` / `time` /
|
|
2557
|
+
`timeEnd` / `trace` / `warn` with silent typed spies, named `console<Method>Spy`. Install them per test
|
|
2558
|
+
and take them off after it:
|
|
2486
2559
|
|
|
2487
2560
|
```ts
|
|
2488
|
-
import {
|
|
2561
|
+
import { type ConsoleSpies, installConsoleSpies, restoreConsole } from 'vitest-auto-spy/console';
|
|
2489
2562
|
|
|
2490
|
-
|
|
2563
|
+
let consoleSpies: ConsoleSpies;
|
|
2564
|
+
|
|
2565
|
+
beforeEach(() => {
|
|
2566
|
+
consoleSpies = installConsoleSpies();
|
|
2567
|
+
});
|
|
2568
|
+
afterEach(() => restoreConsole());
|
|
2569
|
+
|
|
2570
|
+
expect(consoleSpies.consoleInfoSpy).toHaveBeenCalledWith('done'); // the output is silenced, not printed
|
|
2491
2571
|
```
|
|
2492
2572
|
|
|
2573
|
+
The exported `consoleInfoSpy` & co. are the same objects; `restoreConsole()` keeps them and clears
|
|
2574
|
+
their calls. **Do not rely on the import to install them**: it does so once per worker, so under
|
|
2575
|
+
`isolate: false` the spies go on in whichever file imported them first and silence every later file
|
|
2576
|
+
(`no-import-time-console-spies` reports it). Under `setupAutoSpy({ strayConsole })` the import installs
|
|
2577
|
+
nothing at all.
|
|
2578
|
+
|
|
2493
2579
|
Import your runtime entry (`…/bun`, `…/node`) **before** `…/console`, or it registers the Vitest
|
|
2494
2580
|
adapter. Prefer not to touch the real global? `createAutoMock<Console>()` gives a detached one.
|
|
2495
2581
|
|
|
@@ -2546,13 +2632,16 @@ blanket downgrade so those keep their severity; do not copy the two names into a
|
|
|
2546
2632
|
| `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 |
|
|
2547
2633
|
| `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 |
|
|
2548
2634
|
| `prefer-observer-stub` | `error` | — | a hand-rolled observer global → `stubIntersectionObserver()` / `stubResizeObserver()` / `stubMutationObserver()`; the manual save-and-restore goes too, `restoreMockedProps()` runs the undo |
|
|
2635
|
+
| `no-passthrough-console-spy` | `error` | suggest | `vi.spyOn(console, m)` nothing gives an implementation — it calls through and prints → `installConsoleSpies()` + `consoleXSpy`, or `.mockImplementation(() => undefined)` |
|
|
2636
|
+
| `no-console-in-spec` | `error` | — | a spec calling `console.x(…)` itself, or `console.x = …`, which nothing restores → absorb the code's output through `vitest-auto-spy/console` |
|
|
2637
|
+
| `no-import-time-console-spies` | `error` | — | an import of `vitest-auto-spy/console` in a file that never calls `installConsoleSpies()` — the import installs once per worker and silences every later file → `installConsoleSpies()` in `beforeEach`, `restoreConsole()` in `afterEach` |
|
|
2549
2638
|
| `jasmine-namespace-without-entry` | `error` | — | `.and` / `.calls` / `.withArgs` on a library spy in a file that installs the compat layer nowhere — option: `{ setupModules: […] }` |
|
|
2550
2639
|
| `no-jasmine-globals` | `error` | — | `jasmine.*`, bare `spyOn(` / `spyOnProperty(` / `spyOnAllFunctions(` / `fail(` / `pending(`, `.withContext(` |
|
|
2551
2640
|
| `no-save-arguments-by-value` | `error` | — | `spy.calls.saveArgumentsByValue()` — a no-op here, so the spec silently asserts on post-mutation state |
|
|
2552
2641
|
| `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 |
|
|
2553
2642
|
|
|
2554
|
-
Twenty-
|
|
2555
|
-
`no-stub-class-double` and `no-structural-double`**; three fix on their own,
|
|
2643
|
+
Twenty-eight rules, **every one an `error` since 4.0.0 except `prefer-render-shallow`,
|
|
2644
|
+
`no-stub-class-double` and `no-structural-double`**; three fix on their own, nine offer suggestions. Twenty-seven are syntactic; `no-private-member-access` is the one
|
|
2556
2645
|
that reads types, and it reports nothing at all without `parserOptions.project` / `projectService`
|
|
2557
2646
|
rather than guessing. The config used to be a graded mix of `error` / `warn` / `off`, which decided for the
|
|
2558
2647
|
consumer how much each finding mattered — a `warn` nothing reads is `off` with extra output. The
|
|
@@ -2709,6 +2798,11 @@ packages, which a subpath export can never be.
|
|
|
2709
2798
|
| `extendWithAutoSpies needs Vitest 4.1 or newer` | the `test` handed in has only the object-form `extend` (Vitest ≤ 4.0); the builder form the helper is written against arrived in 4.1 | upgrade Vitest, or keep `provideAutoSpy` + `injectSpy` in a `beforeEach` until then |
|
|
2710
2799
|
| `advanceTimers() requires fake timers` | no fake timers installed | `setupFakeTimers()` or `vi.useFakeTimers()` first |
|
|
2711
2800
|
| `Nothing configured X.method, and strict mode is on` | a strict double was asked for a method no line configured | configure it (`mockReturnValue` / `resolveWith` / `nextWith` / `calledWith`), or `{ strict: false }` on that double (§5) |
|
|
2801
|
+
| `[vitest-auto-spy] "…" wrote to the console N time(s) and nothing absorbed it` | `setupAutoSpy({ strayConsole })` — the test printed, and nothing that does not call through stood on `console`: no `/console` spy installed for it, no `mockImplementation`. A bare `vi.spyOn(console, m)` calls through, and a `console.warn` from this library counts too| absorb and assert: `installConsoleSpies()` in a `beforeEach`, then `expect(consoleErrorSpy)…`; or fix the code that printed. Importing a spy installs nothing under the guard. `strayConsole: { allow: [...] }` only for environment noise no spec can reach |
|
|
2802
|
+
| `… wrote to the console N time(s) outside any test` | output while the file was imported, in a `beforeAll` / `afterAll`, from a callback after its test ended, or from a test whose `afterEach` never ran — the frame names the line | move the work into a test and absorb it there; a callback firing late is a leak — clear it where it is scheduled |
|
|
2803
|
+
| `setupAutoSpy({ strict: true })` has no effect — no double throws, a suite with it on stays green | before this release the default lived in module scope, and `/setup` and the factory bundles each carried a copy; a 1759-file consumer ran 12 717 tests green with it on | upgrade — the default, `onUnstubbedCall` and `setSpyEngine()` now live on `globalThis` |
|
|
2804
|
+
| a seeded getter reads `undefined`: `provideAutoSpy(X, { overrides: { remoteConfig } })` next to `gettersToSpyOn` or a `registerAutoSpyDefaults` row| before this release the seed was assigned into the spied accessor's setter, and the getter kept answering `undefined` | upgrade — `overrides` now seeds the getter spy; a later `accessorSpies.getters.x.mockReturnValue(…)` still wins |
|
|
2805
|
+
| `Cannot read properties of undefined (reading 'name')` on `value.constructor.name`, with `createAutoMock<T>()`| `constructor` answered `undefined` on an auto-mock | upgrade — it answers `Object`, as every other double does; or seed it: `createAutoMock<T>({ constructor: … })` |
|
|
2712
2806
|
| `Cannot redefine property: …` from a library accessor spy | the target is an ES module namespace a bundler inlined | no spy library can win this — give the code a real seam (inject it, pass it in) and spy on that |
|
|
2713
2807
|
| `Cannot spy on this instance in place: it is not extensible` | `createSpyFromInstance` was handed a frozen or sealed object | spy it before it is frozen, or build a separate double with `createSpyFromClass` (§2) |
|
|
2714
2808
|
| `Cannot mock the property 'X': it is not configurable` | the member was defined with `configurable: false`, so it cannot be redefined | members replaced before it are still restorable — `restoreSpiedInstance(instance)` or `restoreMockedProps()` |
|
|
@@ -2721,7 +2815,7 @@ packages, which a subpath export can never be.
|
|
|
2721
2815
|
| `assertComponentDefIntact(): argument N is undefined, which carries no ɵcmp or ɵdir` | the class reference itself never arrived — the `Cannot read properties of undefined (reading 'ɵcmp')` case | the same split barrel; import the component from its own module, not the barrel (§13) |
|
|
2722
2816
|
| `TS1117: An object literal cannot have multiple properties with the same name`, in a fixture | a hundred-line model literal copied into eight specs and edited independently | one `createFixtureFactory<T>(defaults)`; note the runtime keeps the **second** key, so do not auto-fix by dropping one (§12) |
|
|
2723
2817
|
| `configureTestingModule was given N schema(s) that can never apply` | `enableAngularDiagnostics({ deadSchemas })` — `schemas` next to a standalone component, with `declarations` empty | drop the `schemas` entry and put the missing directive/pipe in the component's own `imports`, or use `createDirectiveHost` (§13) |
|
|
2724
|
-
| `injectSpy(X): the injector returned a plain instance, not an auto-spy` | the token is provided for real; a `console.warn`
|
|
2818
|
+
| `injectSpy(X): the injector returned a plain instance, not an auto-spy` | the token is provided for real; a `console.warn` once per token and spec file, a **throw** under `enableAngularDiagnostics({ unspiedProviders })` or `setupAutoSpy({ misconfiguration: 'throw' })` | `provideAutoSpy(X)` / `provideAutoSpyForToken(TOKEN)`, or `TestBed.inject(X)` if the real thing is what the spec wants |
|
|
2725
2819
|
| `trackInjections(...).get(X): that token is not tracked by this log` | `get` only answers for tokens whose providers the log created | add the token to the `trackInjections([...])` list, or read it from the injector directly (§13) |
|
|
2726
2820
|
| `Spread syntax requires ...iterable[Symbol.iterator] to be a function`, at load time | a module-scope spread of an imported binding, inside a bundle | build the array in a function or a getter — `no-import-time-spread` (§16) |
|
|
2727
2821
|
| `the timers APIs are not mocked` in a nested `describe`'s `beforeAll` | fakes armed in `beforeEach` only; Jest armed them for the whole file | `setupFakeTimers(cfg, { betweenTests: true })` / `setupAutoSpy({ globalFakeTimers: true })` |
|
|
@@ -2778,7 +2872,7 @@ packages, which a subpath export can never be.
|
|
|
2778
2872
|
| a 30 s timeout, in a different file each run | module-level `vi.fn()` in a fixture shared by files | make the fixture a factory (§10) |
|
|
2779
2873
|
| a component's `afterNextRender` state is empty | `detectChanges()` does not run the after-render phase | `await stable(fixture)` (§11) |
|
|
2780
2874
|
| a green test whose only line about a call is `spy.m.calledWith(1);` | that is a **stub**, not an assertion — chai's `expect(fn).to.have.been.calledWith(x)` is the one that checks | continue the chain, or `expect(spy.m).toHaveBeenCalledWith(1)` — `no-bare-called-with` (§16) |
|
|
2781
|
-
| `--detect-async-leaks` reporting no leaks in a suite that visibly leaks timers | `setupAutoSpy({ strayTimers: true })` cancelled them before Vitest collected |
|
|
2875
|
+
| `--detect-async-leaks` reporting no leaks in a suite that visibly leaks timers | `setupAutoSpy({ strayTimers: true })` cancelled them before Vitest collected | the warning on stderr says how many were taken and where the first three were scheduled; `onStrayTimers` gets every one in `timers` (§10) |
|
|
2782
2876
|
| `createNestUnit(X): parameter #n of Y is typed as \`Object\``(or`undefined — usually a circular import`) | the compiler emitted no runtime class for that parameter — interface, union, primitive, or a circular import | `@Inject(TOKEN)` plus a `providers` entry, or `@Optional()` to receive `undefined`; `@Inject(forwardRef(() => X))` for the import cycle |
|
|
2783
2877
|
| `createNestUnit(X): Y declares n constructor parameter(s) but carries no design:paramtypes metadata` | no `emitDecoratorMetadata` (esbuild / Vite), or `reflect-metadata` loaded after the class | tsc or SWC with `decoratorMetadata`, `@Injectable()`, `reflect-metadata` first; or `@Inject(X)` on every parameter |
|
|
2784
2878
|
| `createNestUnit(X): A -> B -> A is a cycle among the classes built for real` | two exposed / `useClass` classes depend on each other; the helper does not resolve `forwardRef` cycles | expose one side less, or provide it |
|
|
@@ -2856,7 +2950,9 @@ diagnostic and is free to change: print it, never assert on it.
|
|
|
2856
2950
|
| an assertion containing a date, with no clock set | `mockSystemTime(iso)` first |
|
|
2857
2951
|
| `configureTestingModule` inside every `it()` | one per `describe` |
|
|
2858
2952
|
| `vi.mock('@angular/core')` to neutralise `effect()` | set the signals, `await stable(fixture)`, assert the result |
|
|
2859
|
-
| a second `vi.spyOn(console, 'error')` | `consoleErrorSpy` from `vitest-auto-spy/console`
|
|
2953
|
+
| a second `vi.spyOn(console, 'error')` | `consoleErrorSpy` from `vitest-auto-spy/console`, installed with `installConsoleSpies()` |
|
|
2954
|
+
| `import { consoleErrorSpy } from 'vitest-auto-spy/console'` as the install | `installConsoleSpies()` in `beforeEach`, `restoreConsole()` in `afterEach` — the import installs once per worker |
|
|
2955
|
+
| a bare `vi.spyOn(console, 'error')` to keep a spec quiet | `.mockImplementation(() => undefined)` — without it the line still prints |
|
|
2860
2956
|
| `mockReadonlyProp(c, 'items', vi.fn(() => []))` | `mockReadonlyProp(c, 'items', signal([]))` — a real signal |
|
|
2861
2957
|
| `spy.m.mockReturnValue(subject$)` for a `vi.fn(() => subject$)` | `spy.m.mockImplementation(() => subject$)` — the variable is re-read |
|
|
2862
2958
|
|
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,12 +76,12 @@ 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-
|
|
79
|
+
- 📏 Lint rules and one-line test-run hygiene — twenty-eight rules in `vitest-auto-spy/eslint-plugin` (three `--fix`, nine suggestions, four of them for a suite mid-migration off jasmine), `setupAutoSpy()` — with `preset: 'strict'` for every guard at its strictest grade
|
|
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
|
|
83
83
|
- 🚚 [`npx vitest-auto-spy codemod`](#codemod--migrating-a-suite-off-jest-auto-spies) — thirteen transforms that move a suite off `jest-auto-spies` and Jest, or off `jasmine-auto-spies` and jasmine (`--from jasmine`), dry-run by default, with a `--verify` pass that also checks a file somebody edited by hand
|
|
84
|
-
- 🔇 Console spies — `import { consoleInfoSpy } from 'vitest-auto-spy/console'` silences `console` and asserts its calls
|
|
84
|
+
- 🔇 Console spies — `import { consoleInfoSpy } from 'vitest-auto-spy/console'` silences `console` and asserts its calls; [`setupAutoSpy({ strayConsole: 'throw' })`](#how-to-mock-the-console) fails any test whose console output nothing absorbed
|
|
85
85
|
- 🧭 [**Spec patterns**](https://asdalexey.github.io/vitest-auto-spy/recipes) — the shapes a ~370-file Angular suite converged on, and the traps that only surface at scale
|
|
86
86
|
- 🤖 Built for AI agents too — one `npx vitest-auto-spy init` writes the pointer into the files your agents actually read and specialises it for this repository, backed by an offline [`AGENTS.md`](#using-this-library-with-an-ai-agent) inside the package, a [per-agent map](#which-file-your-agent-reads) for **Claude Code**, **OpenAI Codex**, **GLM/z.ai**, **Cursor**, **Copilot**, **Gemini CLI** and the rest, `llms.txt` on the docs site, a Claude Code skill, and errors that name their own fix
|
|
87
87
|
- 🟢 100% test coverage, **zero runtime dependencies** (in-tree arg serializer, no `javascript-stringify`)
|
|
@@ -153,6 +153,7 @@ includes — that one import is what keeps `returnSubject()` typed as rxjs's own
|
|
|
153
153
|
- [A class the code under test builds with `new`](#how-to-mock-a-class-the-code-under-test-builds-with-new)
|
|
154
154
|
- [A double more than one spec uses](#how-to-mock-a-double-more-than-one-spec-uses)
|
|
155
155
|
- [A pipe](#how-to-mock-a-pipe)
|
|
156
|
+
- [The console](#how-to-mock-the-console)
|
|
156
157
|
- [A jasmine suite mid-migration](#how-to-mock-a-jasmine-suite-mid-migration)
|
|
157
158
|
- [Why](#why)
|
|
158
159
|
- [How it works (and what it won't spy)](#how-it-works-and-what-it-wont-spy)
|
|
@@ -1055,6 +1056,48 @@ currency.transform.calledWith(10, 'EUR').mockReturnValue('€10');
|
|
|
1055
1056
|
In a component spec, provide it (`provideAutoSpy(CurrencyPipe)`) or keep it out of the template
|
|
1056
1057
|
entirely — `renderShallow` drops it with the rest of the subtree.
|
|
1057
1058
|
|
|
1059
|
+
### How to mock: the console
|
|
1060
|
+
|
|
1061
|
+
A test that expects output absorbs it, and asserts on it:
|
|
1062
|
+
|
|
1063
|
+
```ts
|
|
1064
|
+
import { type ConsoleSpies, installConsoleSpies, restoreConsole } from 'vitest-auto-spy/console';
|
|
1065
|
+
|
|
1066
|
+
let consoleSpies: ConsoleSpies;
|
|
1067
|
+
|
|
1068
|
+
beforeEach(() => {
|
|
1069
|
+
consoleSpies = installConsoleSpies();
|
|
1070
|
+
});
|
|
1071
|
+
|
|
1072
|
+
afterEach(() => restoreConsole());
|
|
1073
|
+
|
|
1074
|
+
it('reports a failed load', () => {
|
|
1075
|
+
service.load();
|
|
1076
|
+
|
|
1077
|
+
expect(consoleSpies.consoleErrorSpy).toHaveBeenCalledWith('load failed', expect.any(Error));
|
|
1078
|
+
});
|
|
1079
|
+
```
|
|
1080
|
+
|
|
1081
|
+
The exported `consoleErrorSpy` is the same object, so `expect(consoleErrorSpy)` works too. Do not
|
|
1082
|
+
rely on the **import** to install them: it does so once per worker, so under `isolate: false` the
|
|
1083
|
+
spies go on in whichever file imported them first and silence every later file of the worker — on a
|
|
1084
|
+
1759-file Angular consumer, 32 of the 39 files importing the entry relied on that, and three files
|
|
1085
|
+
adding `restoreConsole()` was enough to fail 12 tests in 5 others and surface hidden output in 7.
|
|
1086
|
+
|
|
1087
|
+
The spies are silent and typed; `vi.spyOn(console, 'error').mockImplementation(() => undefined)` is
|
|
1088
|
+
the runner's own equivalent. A **bare** `vi.spyOn(console, 'error')` is not: with no implementation
|
|
1089
|
+
it records the call and then calls through, so the line still prints while the spec reads as though
|
|
1090
|
+
it silenced the console.
|
|
1091
|
+
|
|
1092
|
+
`setupAutoSpy({ strayConsole: 'throw' })` turns the rest into failures — any console output that
|
|
1093
|
+
reached the console fails its test, quoting the method, the first lines and the frame that wrote it,
|
|
1094
|
+
and output outside any test fails the file. Under the guard the `/console` import installs nothing,
|
|
1095
|
+
because under `isolate: false` it ran once per worker and silenced every later file; install the
|
|
1096
|
+
spies in a `beforeEach` (or at the top of the file) as above. The `no-passthrough-console-spy`,
|
|
1097
|
+
`no-console-in-spec` and `no-import-time-console-spies` lint rules report the three shapes that print
|
|
1098
|
+
or silence the wrong file. See
|
|
1099
|
+
[Test-run hygiene](#test-run-hygiene).
|
|
1100
|
+
|
|
1058
1101
|
### How to mock: a jasmine suite mid-migration
|
|
1059
1102
|
|
|
1060
1103
|
A suite arriving from `jasmine-auto-spies` writes every helper behind `.and`, because that is where
|
|
@@ -1752,7 +1795,9 @@ abstract-class fallback) never read a class, so it has no class name to print an
|
|
|
1752
1795
|
the method alone.
|
|
1753
1796
|
|
|
1754
1797
|
`strict` is available on `createSpyFromClass`, `createAutoMock` and `provideAutoSpy`, and suite-wide
|
|
1755
|
-
through `setupAutoSpy({ strict: true })` — one line rather than an edit per factory call.
|
|
1798
|
+
through `setupAutoSpy({ strict: true })` — one line rather than an edit per factory call. (Before
|
|
1799
|
+
this release the suite-wide switch reached no double a spec built: it lived in module scope, and
|
|
1800
|
+
`/setup` and the factories' bundles each carried their own copy. It now lives on `globalThis`.)
|
|
1756
1801
|
`onUnstubbedCall` is the general form, and whatever it returns becomes the call's return value: use
|
|
1757
1802
|
it to _record_ the gap before turning the throw on across a suite, or as a blanket fallback value.
|
|
1758
1803
|
|
|
@@ -2619,6 +2664,9 @@ single-purpose utility you can pick up independently — they all ride on the sa
|
|
|
2619
2664
|
| `blockNetwork(options?)` | `/setup` | Close `fetch`, `XMLHttpRequest` and `sendBeacon`, naming what was requested ([details](#test-run-hygiene)) |
|
|
2620
2665
|
| `trackStrayRejections()` / `flushStrayRejections()` / `countStrayRejections()` | `/setup` | Read back the promise rejections zone.js swallowed into `console.error`, so one can fail a test ([details](#test-run-hygiene)) |
|
|
2621
2666
|
| `guardGlobalPatches(reaction)` | `/setup` | Name the test that redefined a property of `document` / `navigator` / `globalThis` as non-configurable |
|
|
2667
|
+
| `guardStrayConsole(reaction)` | `/setup` | Fail a test that wrote to the console without absorbing it, and a file that wrote outside any test ([details](#test-run-hygiene)) |
|
|
2668
|
+
| `withoutStrayTimerTracking(work)` | `/setup` | Run setup work whose timers the stray-timer tracker neither counts nor cancels — jsdom schedules one per Web Storage write |
|
|
2669
|
+
| `describeStrayTimers()` | `/setup` | Every timer still pending, with its kind, the spec file that scheduled it and the scheduling frames — the list `onStrayTimers` gets |
|
|
2622
2670
|
| `guardPrototypePollution(reaction)` | `/setup` | Name the test that left a key on `Object.prototype` — it stops later files collecting ([details](#test-run-hygiene)) |
|
|
2623
2671
|
| `installPerTest(install)` | `/setup` | Re-install a stub before every test of the block — a `describe`-level stub is restored away after the first |
|
|
2624
2672
|
| `setupAngularTestEnv(opts)` | `/angular` | Zone and zoneless spec files in one worker, switching platforms per file |
|
|
@@ -2649,12 +2697,18 @@ onSave.calledWith(1).resolveWith();
|
|
|
2649
2697
|
|
|
2650
2698
|
### Console spies — `vitest-auto-spy/console`
|
|
2651
2699
|
|
|
2652
|
-
|
|
2653
|
-
`trace` / `warn` with **silent, fully-typed spies
|
|
2654
|
-
`vi.spyOn(console, 'info')` boilerplate in every suite, no log output polluting the test run
|
|
2700
|
+
`installConsoleSpies()` replaces `console.debug` / `error` / `info` / `log` / `time` / `timeEnd` /
|
|
2701
|
+
`trace` / `warn` with **silent, fully-typed spies**, and the entry exports each one ready to assert —
|
|
2702
|
+
no `vi.spyOn(console, 'info')` boilerplate in every suite, no log output polluting the test run.
|
|
2703
|
+
Install them in a `beforeEach` and `restoreConsole()` in the `afterEach` (see
|
|
2704
|
+
[How to mock: the console](#how-to-mock-the-console)); importing the entry installs them too, but only
|
|
2705
|
+
once per worker, which under `isolate: false` silences every later file:
|
|
2655
2706
|
|
|
2656
2707
|
```ts
|
|
2657
|
-
import { consoleInfoSpy, consoleWarnSpy } from 'vitest-auto-spy/console';
|
|
2708
|
+
import { consoleInfoSpy, consoleWarnSpy, installConsoleSpies, restoreConsole } from 'vitest-auto-spy/console';
|
|
2709
|
+
|
|
2710
|
+
beforeEach(() => installConsoleSpies());
|
|
2711
|
+
afterEach(() => restoreConsole());
|
|
2658
2712
|
|
|
2659
2713
|
service.doWork();
|
|
2660
2714
|
|
|
@@ -2664,7 +2718,14 @@ expect(consoleWarnSpy).not.toHaveBeenCalled();
|
|
|
2664
2718
|
|
|
2665
2719
|
Housekeeping: `resetConsoleSpies()` clears the recorded calls between tests (Vitest's
|
|
2666
2720
|
`clearMocks: true` already does that automatically), `restoreConsole()` puts the original
|
|
2667
|
-
methods back, and `installConsoleSpies()`
|
|
2721
|
+
methods back and keeps the spies, so the exports stay live, and `installConsoleSpies()` puts the same
|
|
2722
|
+
spies back on after a restore.
|
|
2723
|
+
|
|
2724
|
+
Under `setupAutoSpy({ strayConsole })` importing the entry **installs nothing**: under
|
|
2725
|
+
`isolate: false` the import ran once per worker and left the spies silencing every later file, which
|
|
2726
|
+
is exactly the output the guard exists to see. Call `installConsoleSpies()` in a `beforeEach` (for
|
|
2727
|
+
each test) or at the top of the spec file (for the file); the guard takes them off again when that
|
|
2728
|
+
scope ends. See [How to mock: the console](#how-to-mock-the-console).
|
|
2668
2729
|
|
|
2669
2730
|
> The spies use the registered `MockAdapter` — import your runtime entry
|
|
2670
2731
|
> (`vitest-auto-spy/bun`, `…/node`) **before** `vitest-auto-spy/console` and the console spies
|
|
@@ -2874,7 +2935,26 @@ not a function` on Node 25, `undefined` on Node 26, under jsdom and happy-dom al
|
|
|
2874
2935
|
filter runs before either. A suite stays green with this broken — only the specs that touch
|
|
2875
2936
|
storage fail — so it arrives as "CI moved to a new Node and eleven unrelated specs died". The
|
|
2876
2937
|
repair decides by using the storage rather than looking at it, replaces only one that cannot
|
|
2877
|
-
keep a value, and installs nothing at all in a `node` environment.
|
|
2938
|
+
keep a value, and installs nothing at all in a `node` environment. The probe runs under
|
|
2939
|
+
`withoutStrayTimerTracking`: jsdom answers every Web Storage write with a real
|
|
2940
|
+
`setTimeout(…, 0)`, and `strayTimers` used to charge those to the file.
|
|
2941
|
+
12. **Console output nothing absorbed.** Opt-in, `strayConsole: 'throw'`. Any console call a test
|
|
2942
|
+
made that nothing absorbed fails that test, quoting the method, the first lines written and the
|
|
2943
|
+
first frame outside `node_modules`; output outside any test — while the file was imported, in a
|
|
2944
|
+
`beforeAll` / `afterAll`, from a callback after its test ended — fails the file. Absorbed means it
|
|
2945
|
+
never reached the console: a `vitest-auto-spy/console` spy installed for the test or the file, or
|
|
2946
|
+
`vi.spyOn(console, m).mockImplementation(…)`; a bare `vi.spyOn` calls through and counts. Every
|
|
2947
|
+
console method a test replaced is put back after it, so one file's silence cannot reach the next,
|
|
2948
|
+
and the library's own warnings count like any other output. `allow: [...]` is the last resort,
|
|
2949
|
+
for environment noise no spec can reach.
|
|
2950
|
+
13. **Every guard at its strictest — `preset: 'strict'`.** `duplicateCopies`, `propsOutsideHooks`,
|
|
2951
|
+
`guardGlobals`, `prototypePollution`, `strayConsole` and `misconfiguration` to `'throw'`,
|
|
2952
|
+
`strayTimers` on, `strayRejections` on where zone.js is loaded; an option passed alongside still
|
|
2953
|
+
wins. Not in it: strict doubles (a semantic switch, not a grade), `blockNetwork`, `restoreMocks`,
|
|
2954
|
+
and failing on stray-timer counts, which land on a file from `afterAll` and can charge it a
|
|
2955
|
+
callback the previous file scheduled after its sweep. `misconfiguration: 'throw'` on its own
|
|
2956
|
+
makes the library's misuse reports — an `onlyMethodsToSpyOn` typo, a `returns` key no spy answers
|
|
2957
|
+
to, `injectSpy` handed a real instance — throw at the call site, every occurrence.
|
|
2878
2958
|
|
|
2879
2959
|
| Option | Default | Notes |
|
|
2880
2960
|
| --------------------- | --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
@@ -2883,11 +2963,14 @@ not a function` on Node 25, `undefined` on Node 26, under jsdom and happy-dom al
|
|
|
2883
2963
|
| `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 |
|
|
2884
2964
|
| `restoreMocks` | `false` | `vi.restoreAllMocks()` in a global `afterEach` — turn on for `isolate: false` |
|
|
2885
2965
|
| `strayTimers` | `false` | Cancel timeouts, intervals and frames that outlive their file |
|
|
2886
|
-
| `onStrayTimers` | — | Takes the per-file count
|
|
2966
|
+
| `onStrayTimers` | — | Takes the per-file count and each stray's origin (file, frames) — see `--detect-async-leaks` |
|
|
2887
2967
|
| `strayRejections` | `false` | Fail the test a rejection zone.js swallowed surfaced in — needs zone.js |
|
|
2888
2968
|
| `blockNetwork` | `false` | Close every network channel the environment has — `true`, or a narrowing object |
|
|
2889
2969
|
| `guardGlobals` | `'off'` | Report a test that redefines a global property as non-configurable |
|
|
2890
2970
|
| `prototypePollution` | `'throw'` | Sweep and report an enumerable key a test left on a built-in prototype |
|
|
2971
|
+
| `strayConsole` | `'off'` | Fail a test (or a file) whose console output nothing absorbed — `'warn'`, `'throw'`, or `{ reaction, allow }` |
|
|
2972
|
+
| `misconfiguration` | `'warn'` | `'throw'` fails the library's own misuse reports at the call site, every occurrence |
|
|
2973
|
+
| `preset` | — | `'strict'` starts every guard above at its strictest grade; explicit options still win |
|
|
2891
2974
|
| `globalFakeTimers` | `false` | Fake timers for every test **and between them** — Jest's `enableGlobally` |
|
|
2892
2975
|
| `restoreTimerGlobals` | `true` | Put back timer globals that uninstalling the fakes deleted |
|
|
2893
2976
|
| `restoreWebStorage` | `true` | Give the run a `localStorage` / `sessionStorage` that work |
|
|
@@ -3127,6 +3210,9 @@ export can never be.
|
|
|
3127
3210
|
| `no-unregistered-inject-spy` | `error` | — | `injectSpy(X)` for a token this file never registered → the real instance, whose spy helpers exist only for the compiler |
|
|
3128
3211
|
| `prefer-render-shallow` | `warn` | suggest | `TestBed.createComponent` in a file that never reads the template → `renderShallow(X)`; 0.24× the per-test cycle at 100 children |
|
|
3129
3212
|
| `prefer-observer-stub` | `error` | — | a hand-rolled `IntersectionObserver` / `ResizeObserver` / `MutationObserver` written into a global → `stubIntersectionObserver()` and friends, whose undo `restoreMockedProps()` already runs |
|
|
3213
|
+
| `no-passthrough-console-spy` | `error` | suggest | `vi.spyOn(console, m)` nothing gives an implementation — it calls through and still prints → `installConsoleSpies()` from `vitest-auto-spy/console`, or `.mockImplementation(() => undefined)` |
|
|
3214
|
+
| `no-console-in-spec` | `error` | — | a spec that calls `console.x(…)` itself, or replaces a method with `console.x = …`, which nothing puts back — under `isolate: false` every later file inherits it |
|
|
3215
|
+
| `no-import-time-console-spies` | `error` | — | an import of `vitest-auto-spy/console` in a file that never calls `installConsoleSpies()` — the import installs once per worker and, under `isolate: false`, silences every later file |
|
|
3130
3216
|
| `jasmine-namespace-without-entry` | `error` | — | `.and` / `.calls` / `.withArgs` on a library spy in a file that installs the compatibility layer nowhere |
|
|
3131
3217
|
| `no-jasmine-globals` | `error` | — | `jasmine.*`, `spyOn(` / `spyOnProperty(` / `spyOnAllFunctions(` / `fail(` / `pending(`, `.withContext(` — none of them exist under Vitest |
|
|
3132
3218
|
| `no-save-arguments-by-value` | `error` | — | `spy.calls.saveArgumentsByValue()`, which is a no-op here → take the copy at call time |
|
|
@@ -3136,7 +3222,7 @@ Every message ends with a link to the matching [recipe](#how-to-mock): a rule th
|
|
|
3136
3222
|
"don't" moves the problem rather than solving it. Rules travel with the API they recommend, so they
|
|
3137
3223
|
are versioned together and stop being re-written in every project that installs the package.
|
|
3138
3224
|
|
|
3139
|
-
**Three of the twenty-
|
|
3225
|
+
**Three of the twenty-eight fix on their own, nine offer suggestions**, and the split is not about how hard
|
|
3140
3226
|
the rewrite is. `no-mocked-for-spy` touches a _declaration_: get it wrong and the file stops
|
|
3141
3227
|
compiling, which is the loudest, cheapest failure there is — so `--fix` rewrites the type, adds
|
|
3142
3228
|
`import type { Spy } from 'vitest-auto-spy'` and drops the `Mocked` import once nothing else uses
|
|
@@ -3240,7 +3326,7 @@ package's own — it needs its ESLint integration switched on.
|
|
|
3240
3326
|
### WebStorm and the other JetBrains IDEs
|
|
3241
3327
|
|
|
3242
3328
|
No plugin to install: WebStorm, IntelliJ IDEA Ultimate, PhpStorm, PyCharm Professional and RubyMine
|
|
3243
|
-
all run ESLint natively, so the twenty-
|
|
3329
|
+
all run ESLint natively, so the twenty-eight rules appear inline, in the **Problems** tool window, and
|
|
3244
3330
|
under **Code → Inspect Code** for the whole project.
|
|
3245
3331
|
|
|
3246
3332
|
```js
|
|
@@ -3257,7 +3343,7 @@ has supported flat config since 2023.3); scope the block to spec files yourself;
|
|
|
3257
3343
|
the fixes and suggestions live.
|
|
3258
3344
|
|
|
3259
3345
|
A native JetBrains plugin is **not** planned — it would duplicate an integration the IDE already has
|
|
3260
|
-
and then keep a second copy of twenty-
|
|
3346
|
+
and then keep a second copy of twenty-eight rules, in Kotlin, in step with the TypeScript ones.
|
|
3261
3347
|
|
|
3262
3348
|
### VS Code, Cursor, Windsurf, VSCodium
|
|
3263
3349
|
|
|
@@ -3369,6 +3455,8 @@ another, and the next one is in that same file.
|
|
|
3369
3455
|
| `flushEventLoopUntil(isDone, opts?)` | Real event-loop turns until a condition holds, with a budget instead of a hang |
|
|
3370
3456
|
| `diffByField(actual, expected)` | Which field of an array of records moved, and in how many elements |
|
|
3371
3457
|
| `guardGlobalPatches(reaction)` / `installPerTest(install)` _(`/setup`)_ | Name the test that sealed a global property; re-install a stub before every test |
|
|
3458
|
+
| `guardStrayConsole(reaction)` / `withoutStrayTimerTracking(work)` _(`/setup`)_ | Fail a test on console output nothing absorbed; keep setup work's timers out of the stray-timer count |
|
|
3459
|
+
| `describeStrayTimers()` _(`/setup`)_ | Each pending timer with the spec file and frames that scheduled it — for a suite that sweeps by hand |
|
|
3372
3460
|
| `guardPrototypePollution(reaction)` _(`/setup`)_ | Name the test that left a key on `Object.prototype`, before the next file fails to collect |
|
|
3373
3461
|
| `consoleDebugSpy` … `consoleWarnSpy` _(`/console`)_ | Silent typed spies replacing the global `console` methods on import |
|
|
3374
3462
|
| `installConsoleSpies()` / `resetConsoleSpies()` / `restoreConsole()` | Install / clear / undo the console spies |
|
package/dist/angular-http.d.ts
CHANGED
|
@@ -77,9 +77,10 @@ interface RequestExpectation {
|
|
|
77
77
|
* TestBed.configureTestingModule({ providers: [...provideHttpTesting(), provideAutoSpy(Analytics)] });
|
|
78
78
|
* ```
|
|
79
79
|
*
|
|
80
|
-
* It is `provideHttpClient()` + `provideHttpClientTesting()
|
|
81
|
-
*
|
|
82
|
-
*
|
|
80
|
+
* It is `provideHttpClient()` + `provideHttpClientTesting()`, plus — unless `verifyOnTeardown` is
|
|
81
|
+
* `false` — the environment initializer that arms the end-of-test check: a suite whose interceptors
|
|
82
|
+
* are the thing under test keeps its own `provideHttpClient(withInterceptors([…]))` and adds
|
|
83
|
+
* `provideHttpClientTesting()` after it.
|
|
83
84
|
*/
|
|
84
85
|
declare function provideHttpTesting(options?: HttpTestingOptions): (EnvironmentProviders | Provider)[];
|
|
85
86
|
/**
|