vitest-auto-spy 5.20.0 → 5.21.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 +33 -21
- package/README.md +45 -40
- package/dist/angular-diagnostics.d.ts +168 -0
- package/dist/angular-diagnostics.js +326 -0
- package/dist/angular-doubles.d.ts +254 -0
- package/dist/angular-doubles.js +203 -0
- package/dist/angular-http.js +3 -27
- package/dist/angular-matchers.d.ts +96 -0
- package/dist/angular-matchers.js +145 -0
- package/dist/angular.d.ts +8 -517
- package/dist/angular.js +127 -733
- package/dist/bun-angular.d.ts +3 -3
- package/dist/bun-angular.js +15 -51
- package/dist/bun.d.ts +2 -2
- package/dist/bun.js +6 -4
- package/dist/chunk-3EV45V6W.js +52 -0
- package/dist/{chunk-LE6IB3DK.js → chunk-BN2QV45R.js} +1 -1
- package/dist/chunk-GWEQZHMN.js +189 -0
- package/dist/{chunk-SXR7EAUS.js → chunk-I6JZIWIZ.js} +102 -79
- package/dist/{chunk-TCGO3VCY.js → chunk-ITOFGQTX.js} +2 -30
- package/dist/chunk-KOR5OK4H.js +134 -0
- package/dist/chunk-NS3Y6AQB.js +44 -0
- package/dist/{chunk-CEDA5PUY.js → chunk-QT5JIDOK.js} +1 -1
- package/dist/chunk-RPCKHTDN.js +41 -0
- package/dist/chunk-SA2QIER3.js +31 -0
- package/dist/{chunk-IIZTARIO.js → chunk-TQAYWU5J.js} +1 -1
- package/dist/{chunk-QB5VKPTI.js → chunk-ZZSFAQT4.js} +5 -153
- package/dist/cli.js +266 -266
- package/dist/console.js +3 -2
- package/dist/dom-stubs.js +61 -3
- package/dist/{expect-emission-CjwVMb2W.d.ts → expect-emission-S5asJaJP.d.ts} +9 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +60 -3
- package/dist/jasmine.js +4 -3
- package/dist/nestjs.js +6 -5
- package/dist/node.cjs +60 -3
- package/dist/node.d.ts +2 -2
- package/dist/node.js +60 -3
- package/dist/{prop-mock-DMEpvaAU.d.ts → prop-mock-C--a8rpU.d.ts} +2 -0
- package/dist/react.d.ts +2 -2
- package/dist/react.js +60 -3
- package/dist/rstest.d.ts +2 -2
- package/dist/rstest.js +5 -3
- package/dist/setup.d.ts +1 -1
- package/dist/setup.js +35 -2
- package/dist/svelte.d.ts +2 -2
- package/dist/svelte.js +60 -3
- package/dist/{track-signal-runs-CErdcgTA.d.ts → track-signal-runs-C3V6OIGM.d.ts} +2 -2
- package/dist/vue.d.ts +2 -2
- package/dist/vue.js +60 -3
- package/package.json +16 -1
- package/skills/vitest-auto-spy/SKILL.md +1 -1
- package/dist/chunk-X4BVCE4V.js +0 -78
package/AGENTS.md
CHANGED
|
@@ -53,19 +53,22 @@ adapter installed and spies fail at runtime.
|
|
|
53
53
|
|
|
54
54
|
Add-ons, orthogonal to the runner:
|
|
55
55
|
|
|
56
|
-
| Add-on
|
|
57
|
-
|
|
|
58
|
-
| Observable spies
|
|
59
|
-
| observer-spy shim
|
|
60
|
-
| Console spies
|
|
61
|
-
| DOM stubs
|
|
62
|
-
| Run diagnostics
|
|
63
|
-
| Angular HTTP
|
|
64
|
-
| Angular router
|
|
65
|
-
|
|
|
66
|
-
|
|
|
67
|
-
|
|
|
68
|
-
|
|
|
56
|
+
| Add-on | Import | Needed for |
|
|
57
|
+
| ------------------- | ------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
58
|
+
| Observable spies | `import 'vitest-auto-spy/rxjs'` | `nextWith` & friends. **Side-effect import, once**, and in a file the `tsconfig` includes (§4) |
|
|
59
|
+
| 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 |
|
|
60
|
+
| Console spies | `vitest-auto-spy/console` | silent typed spies over the global `console` — `installConsoleSpies()` per test, `restoreConsole()` after |
|
|
61
|
+
| DOM stubs | `vitest-auto-spy/dom-stubs` | `stubIntersectionObserver` / `stubResizeObserver` / `stubMutationObserver` / `stubObserver`, `stubMediaElement`, `stubAbortController`, `stubWebStorage` (§12), `intersectionEntry` / `resizeEntry` / `mutationRecord`. **Moved off the root in 4.0.0** |
|
|
62
|
+
| Run diagnostics | `vitest-auto-spy/diagnostics` | `compareTestRuns`, `summarizeTestRun`, `formatTestRunComparison`, `diffByField`. **Moved off the root in 4.0.0** |
|
|
63
|
+
| Angular HTTP | `vitest-auto-spy/angular-http` | `provideHttpTesting`, `expectRequest` — `httpResource()` / `HttpClient` (§13). Optional `@angular/common` peer, this entry only |
|
|
64
|
+
| Angular router | `vitest-auto-spy/angular-router` | `provideActivatedRoute`, `injectActivatedRoute` — an `ActivatedRoute` whose streams and snapshot share one record; `provideRouterDouble`, `injectRouterDouble` — a `Router` whose URL, `routerState` and `events` agree (§13). Optional `@angular/router` peer, this entry only |
|
|
65
|
+
| Angular diagnostics | `vitest-auto-spy/angular/diagnostics` | `enableAngularDiagnostics` and the whole TestBed timing family (§13). Companion to `/angular` like `/angular-http` — no core re-export; **moved off `/angular` in 6.0** so importing spies stops evaluating it |
|
|
66
|
+
| Angular doubles | `vitest-auto-spy/angular/doubles` | The Material dialog trio and the `Window`/`Document` platform doubles (§13). Companion to `/angular`; registers the Vitest adapter, so its doubles spy out of the box; **moved off `/angular` in 6.0** |
|
|
67
|
+
| Angular matchers | `vitest-auto-spy/angular/matchers` | `registerDirectiveMatchers`, `registerResourceMatchers`, `registerSignalMatchers` (§13). Companion to `/angular` — no core re-export; **moved off `/angular` in 6.0** |
|
|
68
|
+
| Signal forms | `vitest-auto-spy/signal-forms` | `createForm`, `registerFormMatchers` — a signal form built where `form()` can inject, and `toHaveFieldErrors` over what it produced (§13). Optional `@angular/forms` peer, this entry only; Angular 22+ |
|
|
69
|
+
| Setup helpers | `vitest-auto-spy/setup` | `setupAutoSpy()`, `setupFakeTimers()`, `blockNetwork()`, `stubResponse()`; the entry imports Vitest, so it is not for `bun test` |
|
|
70
|
+
| Zone patch | `import 'vitest-auto-spy/zone'` | `fakeAsync` / `waitForAsync` on Vitest (§14) |
|
|
71
|
+
| jasmine compat | `vitest-auto-spy/jasmine` | `.and` / `.calls` / `.withArgs`, the `jasmine` namespace (§20) |
|
|
69
72
|
|
|
70
73
|
`vitest-auto-spy/jasmine` is Vitest-only, because it registers the Vitest adapter. On `bun test` and
|
|
71
74
|
`node --test` call `enableJasmineCompat()` from `vitest-auto-spy/jasmine-compat` instead.
|
|
@@ -1991,7 +1994,7 @@ an unconfigured call returns — a semantic switch, not a grade; the name was ta
|
|
|
1991
1994
|
stray-timer counts (the sweep fails the file from `afterAll`, and a callback scheduled after the
|
|
1992
1995
|
previous file's sweep is charged to the next — opt in with
|
|
1993
1996
|
`onStrayTimers: ({ timers }) => expect(timers).toEqual([])`, whose diff names each one's file), and
|
|
1994
|
-
`enableAngularDiagnostics()`, which lives in `/angular` — call it in the same setup file as the Angular half of strict.
|
|
1997
|
+
`enableAngularDiagnostics()`, which lives in `/angular/diagnostics` — call it in the same setup file as the Angular half of strict.
|
|
1995
1998
|
|
|
1996
1999
|
`misconfiguration: 'throw'` on its own makes the library's misuse reports — an `onlyMethodsToSpyOn`
|
|
1997
2000
|
typo, `gettersToSpyOn` naming a method, a `returns` key no spy answers to, `injectSpy` handed a real
|
|
@@ -2282,6 +2285,13 @@ const myService = injectSpy(MyService); // Spy<MyService>
|
|
|
2282
2285
|
zone.js alike. The entry needs **Angular >= 20** (§1); on 16 or 17 it does not link at all, because
|
|
2283
2286
|
`ɵSIGNAL` is not there to import.
|
|
2284
2287
|
|
|
2288
|
+
**Niche Angular helpers ship in narrow companion subpaths, never in `/angular`** — the pattern of
|
|
2289
|
+
`/angular-http`, `/angular-router` and `/signal-forms`, and since 6.0 of `/angular/diagnostics`,
|
|
2290
|
+
`/angular/doubles` and `/angular/matchers`. An import of `/angular` evaluates its whole graph, so a
|
|
2291
|
+
helper a suite names once in a setup file must not ride along with every `provideAutoSpy` import;
|
|
2292
|
+
the diagnostics, doubles and matcher registrars left on that rule (`trackInjections` stays —
|
|
2293
|
+
`createWithAutoSpies` needs its module).
|
|
2294
|
+
|
|
2285
2295
|
### The same thing as fixtures — `extendWithAutoSpies` (Vitest 4.1+)
|
|
2286
2296
|
|
|
2287
2297
|
The block above, written once instead of once per dependency, with the types inferred rather than
|
|
@@ -2625,7 +2635,7 @@ replace a stack inside `@angular/core` with a line naming what is missing.
|
|
|
2625
2635
|
```ts
|
|
2626
2636
|
// vitest.setup.ts — AFTER getTestBed().initTestEnvironment(…), because Vitest runs
|
|
2627
2637
|
// afterEach hooks in reverse registration order and this one must run before the teardown.
|
|
2628
|
-
import { enableAngularDiagnostics } from 'vitest-auto-spy/angular';
|
|
2638
|
+
import { enableAngularDiagnostics } from 'vitest-auto-spy/angular/diagnostics';
|
|
2629
2639
|
|
|
2630
2640
|
enableAngularDiagnostics(); // { ngModuleScopes, deadSchemas, unspiedProviders, pendingRequests }
|
|
2631
2641
|
```
|
|
@@ -2886,7 +2896,7 @@ that owns them (assert through `component.form.tags()`), and custom controls —
|
|
|
2886
2896
|
### `window` and `document` over the real ones — `provideWindowDouble` / `provideDocumentDouble`
|
|
2887
2897
|
|
|
2888
2898
|
```ts
|
|
2889
|
-
import { provideDocumentDouble, provideWindowDouble } from 'vitest-auto-spy/angular';
|
|
2899
|
+
import { provideDocumentDouble, provideWindowDouble } from 'vitest-auto-spy/angular/doubles';
|
|
2890
2900
|
|
|
2891
2901
|
TestBed.configureTestingModule({
|
|
2892
2902
|
providers: [
|
|
@@ -2945,7 +2955,8 @@ Five things to know:
|
|
|
2945
2955
|
|
|
2946
2956
|
```ts
|
|
2947
2957
|
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
|
2948
|
-
import { expectEmission
|
|
2958
|
+
import { expectEmission } from 'vitest-auto-spy/angular';
|
|
2959
|
+
import { injectMatDialogRef, provideMatDialogData, provideMatDialogRef } from 'vitest-auto-spy/angular/doubles';
|
|
2949
2960
|
|
|
2950
2961
|
TestBed.configureTestingModule({
|
|
2951
2962
|
providers: [provideMatDialogData<EditUserData>(MAT_DIALOG_DATA, { id: 7, name: 'Ada' }), provideMatDialogRef(MatDialogRef)],
|
|
@@ -3170,11 +3181,11 @@ service.products.set([edited]); // the component's own optimistic write — stat
|
|
|
3170
3181
|
expect(products.reload).toHaveBeenCalled(); // reload is spied, answers true, and re-issues nothing
|
|
3171
3182
|
|
|
3172
3183
|
// signal assertions
|
|
3173
|
-
registerSignalMatchers(); // once, in the setup file
|
|
3184
|
+
registerSignalMatchers(); // once, in the setup file — /angular/matchers
|
|
3174
3185
|
expect(component.total).toHaveSignalValue(3);
|
|
3175
3186
|
|
|
3176
3187
|
// resource assertions — value AND status, which is the whole point
|
|
3177
|
-
registerResourceMatchers(); // once, in the setup file
|
|
3188
|
+
registerResourceMatchers(); // once, in the setup file — /angular/matchers
|
|
3178
3189
|
expect(component.products).toBeLoading();
|
|
3179
3190
|
expect(component.products).toHaveResourceValue([product]);
|
|
3180
3191
|
expect(component.products).toHaveResourceError(/503/);
|
|
@@ -3238,7 +3249,7 @@ now throws, naming what it received.
|
|
|
3238
3249
|
Per-file timing, to find which specs actually pay for `TestBed`:
|
|
3239
3250
|
|
|
3240
3251
|
```ts
|
|
3241
|
-
import { enableTestBedDiagnostics } from 'vitest-auto-spy/angular';
|
|
3252
|
+
import { enableTestBedDiagnostics } from 'vitest-auto-spy/angular/diagnostics';
|
|
3242
3253
|
|
|
3243
3254
|
if (process.env['SPEC_TIMING']) {
|
|
3244
3255
|
enableTestBedDiagnostics();
|
|
@@ -3268,7 +3279,8 @@ It exports the core, `provideAutoSpy` / `injectSpy`, `renderShallow`, `createWit
|
|
|
3268
3279
|
`expect.extend` and the TestBed diagnostics its suite-level hooks; the overrides, `extendWithAutoSpies`,
|
|
3269
3280
|
`provideAutoSpyForToken`, `trackInjections`, `setupAngularTestEnv`, the stub factories and the
|
|
3270
3281
|
`mock*Prop`, platform and dialog doubles are simply not routed to Bun. Import them from `/angular`
|
|
3271
|
-
under Vitest
|
|
3282
|
+
under Vitest, and the registrars, doubles and diagnostics from their companion entries
|
|
3283
|
+
(`/angular/matchers`, `/angular/doubles`, `/angular/diagnostics`).
|
|
3272
3284
|
|
|
3273
3285
|
---
|
|
3274
3286
|
|
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)
|
|
@@ -1442,31 +1442,34 @@ the conditional types that pick helpers from a return type — see
|
|
|
1442
1442
|
The library ships a framework-agnostic core plus runtime and framework layers, so a plain
|
|
1443
1443
|
Node / Bun / React / Vue project pulls **neither rxjs nor Angular into its runtime bundle**:
|
|
1444
1444
|
|
|
1445
|
-
| Import
|
|
1446
|
-
|
|
|
1447
|
-
| `vitest-auto-spy`
|
|
1448
|
-
| `vitest-auto-spy/rxjs`
|
|
1449
|
-
| `vitest-auto-spy/angular`
|
|
1450
|
-
| `vitest-auto-spy/angular
|
|
1451
|
-
| `vitest-auto-spy/angular
|
|
1452
|
-
| `vitest-auto-spy/
|
|
1453
|
-
| `vitest-auto-spy/
|
|
1454
|
-
| `vitest-auto-spy/
|
|
1455
|
-
| `vitest-auto-spy/
|
|
1456
|
-
| `vitest-auto-spy/
|
|
1457
|
-
| `vitest-auto-spy/
|
|
1458
|
-
| `vitest-auto-spy/
|
|
1459
|
-
| `vitest-auto-spy/
|
|
1460
|
-
| `vitest-auto-spy/
|
|
1461
|
-
| `vitest-auto-spy/
|
|
1462
|
-
| `vitest-auto-spy/
|
|
1463
|
-
| `vitest-auto-spy/
|
|
1464
|
-
| `vitest-auto-spy/
|
|
1465
|
-
| `vitest-auto-spy/
|
|
1466
|
-
| `vitest-auto-spy/
|
|
1467
|
-
| `vitest-auto-spy/
|
|
1468
|
-
| `vitest-auto-spy/
|
|
1469
|
-
| `vitest-auto-spy/
|
|
1445
|
+
| Import | Provides | Pulls in | Status |
|
|
1446
|
+
| ------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------- | :----: |
|
|
1447
|
+
| `vitest-auto-spy` | `createSpyFromClass`, `createAutoMock`, `createFunctionSpy`, sync + promise + accessor spies, `errorHandler`, types | `vitest` | ✅ |
|
|
1448
|
+
| `vitest-auto-spy/rxjs` | observable spies (`nextWith`, `nextWithValues`, `observablePropsToSpyOn`, …) and `createObservableWithValues` | `rxjs` | ✅ |
|
|
1449
|
+
| `vitest-auto-spy/angular` | `provideAutoSpy`, `injectSpy`, `renderShallow`, `setInputs`, `createWithAutoSpies`, `stable`/`flushEffects`, `settleResource`, `mockResourceProp`, the `mock*Prop` helpers, `trackRecomputations` / `trackEffectRuns` — the diagnostics, doubles and matchers below moved off this entry in 6.0 | `@angular/core` | ✅ |
|
|
1450
|
+
| `vitest-auto-spy/angular/diagnostics` | `enableAngularDiagnostics` and the whole TestBed timing family — a companion to `/angular`, no core re-export; moved off `/angular` in 6.0 | `@angular/core` | ✅ |
|
|
1451
|
+
| `vitest-auto-spy/angular/doubles` | the `window` / `document` and Material-dialog doubles; registers the Vitest adapter, so its doubles spy out of the box; moved off `/angular` in 6.0 | `@angular/core` | ✅ |
|
|
1452
|
+
| `vitest-auto-spy/angular/matchers` | `registerDirectiveMatchers`, `registerResourceMatchers`, `registerSignalMatchers` — a companion to `/angular`, no core re-export; moved off `/angular` in 6.0 | `@angular/core`, `@angular/platform-browser` | ✅ |
|
|
1453
|
+
| `vitest-auto-spy/angular-http` | `provideHttpTesting`, `expectRequest`, `expectNoRequest`, `verifyNoPendingRequests` — the `httpResource()` / `HttpClient` recipe in two lines, settling included | `@angular/common` | ✅ |
|
|
1454
|
+
| `vitest-auto-spy/angular-router` | `provideActivatedRoute`, `injectActivatedRoute`, `createActivatedRoute` — Angular's own `ActivatedRoute` over one record, streams and snapshot moved together; `provideRouterDouble` / `injectRouterDouble` for the `Router` beside it | `@angular/router` | ✅ |
|
|
1455
|
+
| `vitest-auto-spy/signal-forms` | `createForm` — Angular's own `form()` built where it can inject — and `registerFormMatchers()` for `toHaveFieldErrors` | `@angular/forms` (>=22) | ✅ |
|
|
1456
|
+
| `vitest-auto-spy/bun` | the same core, driven by Bun's `bun:test` mocks | `bun:test` | ✅ |
|
|
1457
|
+
| `vitest-auto-spy/bun-angular` | Angular's `TestBed` under `bun test` — DOM, JIT `templateUrl` resolution and a zoneless environment, from one preload | `bun:test`, `@angular/core`, `@angular/platform-browser` | ✅ |
|
|
1458
|
+
| `vitest-auto-spy/node` | the same core, driven by `node:test`'s `mock.fn()`, plus `trackNodeMocks()` — a private `MockTracker` so dropped spies are actually freed | `node:test` | ✅ |
|
|
1459
|
+
| `vitest-auto-spy/rstest` | the same core, driven by Rstest's `rstest.fn()` / `rstest.spyOn()` | `@rstest/core` | ✅ |
|
|
1460
|
+
| `vitest-auto-spy/nestjs` | `provideAutoSpy`, `injectSpy` for `Test.createTestingModule` | — (your `@nestjs/*`) | ✅ |
|
|
1461
|
+
| `vitest-auto-spy/react` | the core, with a natural import for React Testing Library suites | — (your `react`) | ✅ |
|
|
1462
|
+
| `vitest-auto-spy/vue` | `provideAutoSpy` for `global.provide` + Pinia store spying | — (your `vue`/`pinia`) | ✅ |
|
|
1463
|
+
| `vitest-auto-spy/svelte` | the core, with a natural import for Svelte suites | — (your `svelte`) | ✅ |
|
|
1464
|
+
| `vitest-auto-spy/console` | `consoleInfoSpy` & friends — silent typed spies over the global `console`, installed on import | `vitest` | ✅ |
|
|
1465
|
+
| `vitest-auto-spy/jasmine` | the drop-in surface for a `jasmine-auto-spies` suite — `.and` / `.calls` / `.withArgs` on every spy, `createSpyObj`, the `jasmine` namespace, `registerJasmineMatchers` | `vitest` | ✅ |
|
|
1466
|
+
| `vitest-auto-spy/jasmine-compat` | `enableJasmineCompat()` alone — the same `.and` / `.calls` layer, registering no adapter, for `bun test` and `node --test` | — (your runner) | ✅ |
|
|
1467
|
+
| `vitest-auto-spy/observer-spy` | `subscribeSpyTo` / `ObserverSpy` / `SubscriberSpy` — the `@hirez_io/observer-spy` surface, so `/rxjs` does not carry it | `rxjs` | ✅ |
|
|
1468
|
+
| `vitest-auto-spy/dom-stubs` | the globals a component builds for itself — `stubIntersectionObserver`, `stubResizeObserver`, `stubMutationObserver`, `stubObserver`, `stubMediaElement`, `stubAbortController`, the entry builders, and `stubWebStorage` with its `snapshot()`. On the root entry until 4.0.0 | — | ✅ |
|
|
1469
|
+
| `vitest-auto-spy/diagnostics` | `compareTestRuns` / `summarizeTestRun` / `formatTestRunComparison`, `diffByField` and `explainSpy` — the two reports a counter cannot give; pure functions, so a plain Node script can import this one too. On the root entry until 4.0.0 | — | ✅ |
|
|
1470
|
+
| `vitest-auto-spy/setup` | `setupAutoSpy()` — property restore, duplicate-copy detection and mock-registry hygiene in one call; `setupFakeTimers()` / `advanceTimers()` | `vitest` | ✅ |
|
|
1471
|
+
| `vitest-auto-spy/zone` | `fakeAsync` / `waitForAsync` on Vitest — the ProxyZone patch `zone.js/testing` does not ship. Reads the `zone.js` **you** loaded; imports none of it | — (your `zone.js`) | ✅ |
|
|
1472
|
+
| `vitest-auto-spy/eslint-plugin` | the lint rules that steer a suite onto these helpers | — (your `eslint`) | ✅ |
|
|
1470
1473
|
|
|
1471
1474
|
✅ all entry points published (see [Availability](#availability)).
|
|
1472
1475
|
|
|
@@ -1586,11 +1589,12 @@ describe('GreetingComponent', () => {
|
|
|
1586
1589
|
> a spec as well is harmless — the module is cached and every step is guarded.
|
|
1587
1590
|
|
|
1588
1591
|
`provideAutoSpy`, `injectSpy`, `renderShallow`, `createWithAutoSpies`, `stable` / `flushEffects` and
|
|
1589
|
-
the whole core behave identically to the Vitest entry. The rest of
|
|
1590
|
-
the matcher registrars (`registerSignalMatchers`, `registerDirectiveMatchers`,
|
|
1591
|
-
`registerResourceMatchers`) need the runner's `expect.extend`, the TestBed
|
|
1592
|
-
suite-level hooks, and the overrides, `extendWithAutoSpies`,
|
|
1593
|
-
factories and the prop, platform and dialog doubles
|
|
1592
|
+
the whole core behave identically to the Vitest entry. The rest of the Angular surface is not routed
|
|
1593
|
+
to Bun: the matcher registrars (`registerSignalMatchers`, `registerDirectiveMatchers`,
|
|
1594
|
+
`registerResourceMatchers`, on `/angular/matchers`) need the runner's `expect.extend`, the TestBed
|
|
1595
|
+
diagnostics (`/angular/diagnostics`) its suite-level hooks, and the overrides, `extendWithAutoSpies`,
|
|
1596
|
+
`provideAutoSpyForToken`, the stub factories and the prop, platform and dialog doubles (`/angular`,
|
|
1597
|
+
`/angular/doubles`) are simply not exported there.
|
|
1594
1598
|
|
|
1595
1599
|
Bun 1.4's runner flags need no configuration here — `--isolate` (a fresh global per file, matching
|
|
1596
1600
|
Vitest's default), `--parallel`, `--shard`, `--changed` and `--timings` all work. Without
|
|
@@ -2701,7 +2705,8 @@ awaited through it finishes the budget having issued zero requests.
|
|
|
2701
2705
|
#### Driving a resource with no HTTP at all
|
|
2702
2706
|
|
|
2703
2707
|
```ts
|
|
2704
|
-
import { mockResourceProp
|
|
2708
|
+
import { mockResourceProp } from 'vitest-auto-spy/angular';
|
|
2709
|
+
import { registerResourceMatchers } from 'vitest-auto-spy/angular/matchers';
|
|
2705
2710
|
|
|
2706
2711
|
const products = mockResourceProp(service, 'products', []);
|
|
2707
2712
|
|
|
@@ -2738,7 +2743,7 @@ wrong type instead of reporting a failure. A reported failure is a pass under `.
|
|
|
2738
2743
|
#### Asserting a signal's value
|
|
2739
2744
|
|
|
2740
2745
|
```ts
|
|
2741
|
-
import { registerSignalMatchers } from 'vitest-auto-spy/angular';
|
|
2746
|
+
import { registerSignalMatchers } from 'vitest-auto-spy/angular/matchers';
|
|
2742
2747
|
|
|
2743
2748
|
registerSignalMatchers(); // once, in your setup file
|
|
2744
2749
|
|
|
@@ -2754,7 +2759,7 @@ getter, so the missing-parentheses mistake fails instead of quietly passing.
|
|
|
2754
2759
|
|
|
2755
2760
|
```ts
|
|
2756
2761
|
// vitest.setup.ts
|
|
2757
|
-
import { enableTestBedDiagnostics } from 'vitest-auto-spy/angular';
|
|
2762
|
+
import { enableTestBedDiagnostics } from 'vitest-auto-spy/angular/diagnostics';
|
|
2758
2763
|
|
|
2759
2764
|
if (process.env['SPEC_TIMING']) {
|
|
2760
2765
|
enableTestBedDiagnostics();
|
|
@@ -2807,7 +2812,7 @@ the helper, and it stays silent when the component was not rendered. It covers t
|
|
|
2807
2812
|
|
|
2808
2813
|
```ts
|
|
2809
2814
|
// vitest.setup.ts — AFTER getTestBed().initTestEnvironment(…)
|
|
2810
|
-
import { enableAngularDiagnostics } from 'vitest-auto-spy/angular';
|
|
2815
|
+
import { enableAngularDiagnostics } from 'vitest-auto-spy/angular/diagnostics';
|
|
2811
2816
|
|
|
2812
2817
|
enableAngularDiagnostics(); // all five
|
|
2813
2818
|
enableAngularDiagnostics({ pendingRequests: false }); // or pick
|
|
@@ -3105,8 +3110,8 @@ single-purpose utility you can pick up independently — they all ride on the sa
|
|
|
3105
3110
|
| `createDirectiveHost({ template, scope, props })` | `/angular` | A standalone host for a directive under test, with its scope where the compiler reads it |
|
|
3106
3111
|
| `createComponentStub(Class, overrides?, opts?)` | `/angular` | A standalone stand-in for a child component, directive or pipe, selector and inputs read from the real one ([details](#how-to-mock-a-child-the-template-still-binds)) |
|
|
3107
3112
|
| `mockResourceProp(obj, prop, initial, opts?)` | `/angular` | Drive a resource with no HTTP — a whole `ResourceRef` double: writable `value`, `set` / `update` / `asReadonly` / `destroy` / `snapshot`, `set` / `fail` / `loading` / `idle` from the spec, plus a spied `reload` that answers `false` while idle or loading; `value()` after `fail()` throws, as a real resource does |
|
|
3108
|
-
| `registerResourceMatchers()` | `/angular`
|
|
3109
|
-
| `registerDirectiveMatchers()` | `/angular`
|
|
3113
|
+
| `registerResourceMatchers()` | `/angular/matchers` | Adds `toBeLoading` / `toHaveResourceValue` / `toHaveResourceError`; the value matcher fails an unresolved resource |
|
|
3114
|
+
| `registerDirectiveMatchers()` | `/angular/matchers` | Adds `expect(fixture).toHaveDirectiveApplied(Directive, selector?)` |
|
|
3110
3115
|
| `installProxyZonePatch(opts?)` | `/zone` | `fakeAsync` / `waitForAsync` on Vitest — the patch `zone.js/testing` does not ship; `scope: 'callback'` per callback |
|
|
3111
3116
|
| `autoMocked<T>(overrides?)` | core | `createAutoMock` typed as `T & Spy<T>`, for a collaborator passed as an argument rather than injected |
|
|
3112
3117
|
| `mockSystemTime(time)` / `withSystemTime(time, fn)` | `/setup` | Freeze the clock whether or not fake timers are already running |
|
|
@@ -3115,7 +3120,7 @@ single-purpose utility you can pick up independently — they all ride on the sa
|
|
|
3115
3120
|
| `overrideAutoSpy(Token, config?)` / `overrideComponentProvider(Cmp, Token, config?)` | `/angular` | Replace a dependency a component declares in its own `providers` |
|
|
3116
3121
|
| `assertNgModuleScopes(...modules)` | `/angular` | Fail early when an AOT test bundle left an NgModule with no runtime declarations |
|
|
3117
3122
|
| `assertComponentDefIntact(...components)` | `/angular` | Fail before rendering when a half-loaded barrel chunk left a hole in a component's own `providers` or scope |
|
|
3118
|
-
| `enableAngularDiagnostics(opts?)` / `assertNoPendingRequests()` | `/angular`
|
|
3123
|
+
| `enableAngularDiagnostics(opts?)` / `assertNoPendingRequests()` | `/angular/diagnostics` | Dead NgModule imports, dead `schemas`, an unspied provider, a provider the component's own `providers` shadows and unflushed HTTP requests, as failures ([details](#diagnostics--five-silent-failures-made-loud)) |
|
|
3119
3124
|
| `trackInjections(tokens, opts?)` | `/angular`, `/nestjs` | Which collaborators DI actually constructed, recorded through provider factories — with the doubles attached |
|
|
3120
3125
|
| `mockSignalProp(obj, prop, initial)` | `/angular` | Drive a signal-valued property with a real `WritableSignal`: a member that already is one — `signal()`, `model()`, `linkedSignal()`, or a `signal().asReadonly()` view — is written through rather than replaced, so the order against the first render stops mattering; a `computed()` a live consumer has read, and an `input()`, are refused by name |
|
|
3121
3126
|
| `runEffect(effectRef)` | `/angular` | Run one `effect()` body on demand, for an effect whose trigger a spec replaced with a static signal |
|
|
@@ -3123,8 +3128,8 @@ single-purpose utility you can pick up independently — they all ride on the sa
|
|
|
3123
3128
|
| `trackRecomputations(signal)` / `trackEffectRuns(ref)` | `/angular` | Count what the reactive graph actually did: `{ count, stop() }`, so "the filter change did not re-run the sync effect" is an assertion |
|
|
3124
3129
|
| `provideRouterDouble(init?)` / `injectRouterDouble()` | `/angular-router` | A `Router` derived from one URL — real `serializeUrl` / `createUrlTree`, `navigate` spies, `emitNavigation()` over a `BehaviorSubject`, `currentNavigation()` and `setCurrentNavigation()` |
|
|
3125
3130
|
| `createForm(model, schema?)` / `registerFormMatchers()` | `/signal-forms` | A signal form built where `form()` can inject, and `expect(field).toHaveFieldErrors(['required'])` over what it produced |
|
|
3126
|
-
| `provideWindowDouble(TOKEN, over?)` / `provideDocumentDouble(over?)` | `/angular`
|
|
3127
|
-
| `provideMatDialogData(TOKEN, data)` / `provideMatDialogRef(Ref, init?)` | `/angular`
|
|
3131
|
+
| `provideWindowDouble(TOKEN, over?)` / `provideDocumentDouble(over?)` | `/angular/doubles` | A `window` / `document` merged **over** the real jsdom one, so members the spec never named still answer; the globals stay untouched |
|
|
3132
|
+
| `provideMatDialogData(TOKEN, data)` / `provideMatDialogRef(Ref, init?)` | `/angular/doubles` | The Material dialog trio without `@angular/material` as a dependency — the token and the ref class are arguments, `afterClosed()` still answers after the close |
|
|
3128
3133
|
| `blockNetwork(options?)` | `/setup` | Close `fetch`, `XMLHttpRequest` and `sendBeacon`, naming what was requested; called twice, the last caller's mode wins; `fetch` is left to an applied MSW / nock interceptor ([details](#test-run-hygiene)) |
|
|
3129
3134
|
| `stubResponse(init?)` | `/setup` | A real `Response` for a stubbed `fetch` — `body` (plain data → JSON), `status`, `ok`, `statusText`, `headers`, `url`; no cast ([recipe](#how-to-mock-fetch-and-other-globals)) |
|
|
3130
3135
|
| `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)) |
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/** Which checks {@link enableAngularDiagnostics} installs. Every member defaults to `true`. */
|
|
2
|
+
interface AngularDiagnosticsOptions {
|
|
3
|
+
/**
|
|
4
|
+
* Fail when a testing module imports an NgModule that contributes nothing at runtime — the AOT
|
|
5
|
+
* bundle that dropped `ɵɵsetNgModuleScope`, checked automatically instead of by hand.
|
|
6
|
+
*/
|
|
7
|
+
ngModuleScopes?: boolean;
|
|
8
|
+
/** Fail when `schemas` are configured next to a standalone component, where they can never apply. */
|
|
9
|
+
deadSchemas?: boolean;
|
|
10
|
+
/** Fail — rather than warn — when `injectSpy` finds a real instance where a spy was expected. */
|
|
11
|
+
unspiedProviders?: boolean;
|
|
12
|
+
/** Fail a test that ends with unflushed `HttpTestingController` requests. */
|
|
13
|
+
pendingRequests?: boolean;
|
|
14
|
+
/**
|
|
15
|
+
* Fail when a double registered on the testing module loses to the component's own `providers`,
|
|
16
|
+
* so the component under test is running against the real service.
|
|
17
|
+
*/
|
|
18
|
+
shadowedProviders?: boolean;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Fail when the `HttpTestingController` this test configured is still holding requests.
|
|
22
|
+
*
|
|
23
|
+
* `pendingRequests` runs this after every test; it is exported because the same question is worth
|
|
24
|
+
* asking mid-test — after the arrange step, before the assertions that depend on it — and because
|
|
25
|
+
* reading it takes the requests, so calling it yourself is not paid for twice.
|
|
26
|
+
*
|
|
27
|
+
* A no-op when the group is off, or when the test never configured HTTP testing at all.
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* ```ts
|
|
31
|
+
* facade.load();
|
|
32
|
+
* controller.expectOne('/api/users').flush([]);
|
|
33
|
+
* assertNoPendingRequests(); // nothing else went out
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
declare function assertNoPendingRequests(): void;
|
|
37
|
+
/**
|
|
38
|
+
* Fail when a double this test registered on the testing module never reached `component`.
|
|
39
|
+
*
|
|
40
|
+
* `shadowedProviders` calls this on every fixture; it is exported for the same reason
|
|
41
|
+
* {@link assertNoPendingRequests} is — a spec that builds its component through a helper of its own
|
|
42
|
+
* can ask directly, and a check that can only be reached through one code path is a check that
|
|
43
|
+
* stops running the day that path changes.
|
|
44
|
+
*
|
|
45
|
+
* A no-op when the fixture never rendered `component`, and when everything it resolved is a double.
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* ```ts
|
|
49
|
+
* const fixture = renderThroughOurHelper(CartComponent);
|
|
50
|
+
* assertNoShadowedProviders(CartComponent, fixture); // the doubles really are the ones in play
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
declare function assertNoShadowedProviders(component: unknown, fixture: unknown): void;
|
|
54
|
+
/**
|
|
55
|
+
* Turn the group on. Every member defaults to `true`; pass `false` to leave one out.
|
|
56
|
+
*
|
|
57
|
+
* ```ts
|
|
58
|
+
* enableAngularDiagnostics({ unspiedProviders: false }); // the other three
|
|
59
|
+
* ```
|
|
60
|
+
*
|
|
61
|
+
* Calling it again replaces the previous selection rather than adding to it.
|
|
62
|
+
*/
|
|
63
|
+
declare function enableAngularDiagnostics(options?: AngularDiagnosticsOptions): void;
|
|
64
|
+
/**
|
|
65
|
+
* Turn the group off: no more configuration inspection, and `injectSpy` warns again instead of
|
|
66
|
+
* failing.
|
|
67
|
+
*
|
|
68
|
+
* The `TestBed` timing instrumentation is left in place — `enableTestBedDiagnostics` may be using
|
|
69
|
+
* it, it is idempotent, and `disableTestBedDiagnostics()` is what removes it.
|
|
70
|
+
*/
|
|
71
|
+
declare function disableAngularDiagnostics(): void;
|
|
72
|
+
|
|
73
|
+
/** What one spec file cost. */
|
|
74
|
+
interface SpecTiming {
|
|
75
|
+
/** Absolute path of the spec file, or `'unknown file'` when the runner did not report one. */
|
|
76
|
+
file: string;
|
|
77
|
+
/** Wall-clock time spent inside the instrumented `TestBed` calls. */
|
|
78
|
+
testBedMs: number;
|
|
79
|
+
/** Wall-clock time of the whole file. */
|
|
80
|
+
totalMs: number;
|
|
81
|
+
/** `totalMs - testBedMs` — the part that is plain TypeScript. */
|
|
82
|
+
otherMs: number;
|
|
83
|
+
/** How many components the file created. */
|
|
84
|
+
components: number;
|
|
85
|
+
/** How many testing modules it configured. */
|
|
86
|
+
configurations: number;
|
|
87
|
+
}
|
|
88
|
+
/** Options for {@link enableTestBedDiagnostics}. */
|
|
89
|
+
interface TestBedDiagnosticsOptions {
|
|
90
|
+
/** Receives each file's timing. Defaults to one `console.info` line per file. */
|
|
91
|
+
report?: (timing: SpecTiming) => void;
|
|
92
|
+
/** Stay quiet for files whose `testBedMs` is below this. Default `0` (report every file). */
|
|
93
|
+
minTestBedMs?: number;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Wrap the `TestBed` entry points — on the instance, which every static delegates to. Idempotent,
|
|
97
|
+
* and safe on Angular versions missing one of them.
|
|
98
|
+
*
|
|
99
|
+
* @example
|
|
100
|
+
* ```ts
|
|
101
|
+
* instrumentTestBed(); // start measuring; pair with getTestBedTiming() in an afterAll
|
|
102
|
+
* ```
|
|
103
|
+
*/
|
|
104
|
+
declare function instrumentTestBed(): void;
|
|
105
|
+
/**
|
|
106
|
+
* Undo the instrumentation, putting the original `TestBed` methods back.
|
|
107
|
+
*
|
|
108
|
+
* @example
|
|
109
|
+
* ```ts
|
|
110
|
+
* disableTestBedDiagnostics(); // put the untouched TestBed back
|
|
111
|
+
* ```
|
|
112
|
+
*/
|
|
113
|
+
declare function disableTestBedDiagnostics(): void;
|
|
114
|
+
/**
|
|
115
|
+
* The timing accumulated so far in the current file.
|
|
116
|
+
*
|
|
117
|
+
* @example
|
|
118
|
+
* ```ts
|
|
119
|
+
* afterAll(() => {
|
|
120
|
+
* const timing = getTestBedTiming();
|
|
121
|
+
*
|
|
122
|
+
* if (timing.testBedMs > 200) {
|
|
123
|
+
* reportSpecTiming(timing);
|
|
124
|
+
* }
|
|
125
|
+
* });
|
|
126
|
+
* ```
|
|
127
|
+
*/
|
|
128
|
+
declare function getTestBedTiming(): SpecTiming;
|
|
129
|
+
/**
|
|
130
|
+
* One human-readable line: what the file cost and how much of it was `TestBed`.
|
|
131
|
+
*
|
|
132
|
+
* @example
|
|
133
|
+
* ```ts
|
|
134
|
+
* process.stdout.write(`${formatSpecTiming(getTestBedTiming())}\n`);
|
|
135
|
+
* ```
|
|
136
|
+
*/
|
|
137
|
+
declare function formatSpecTiming(timing: SpecTiming): string;
|
|
138
|
+
/**
|
|
139
|
+
* Write the report where a test run can actually show it.
|
|
140
|
+
*
|
|
141
|
+
* Not `console.info`: a project that imports `vitest-auto-spy/console` (or spies the console for
|
|
142
|
+
* any other reason) has replaced that method with a silent mock, and the report would vanish —
|
|
143
|
+
* which is exactly what happened the first time these diagnostics were pointed at a real suite.
|
|
144
|
+
*
|
|
145
|
+
* Exported as the default `report`: a project that wants both its own bookkeeping and the printed
|
|
146
|
+
* line can call it from a custom reporter.
|
|
147
|
+
*
|
|
148
|
+
* @example
|
|
149
|
+
* ```ts
|
|
150
|
+
* reportSpecTiming(getTestBedTiming()); // one line to process.stdout, not console.info
|
|
151
|
+
* ```
|
|
152
|
+
*/
|
|
153
|
+
declare function reportSpecTiming(timing: SpecTiming): void;
|
|
154
|
+
/**
|
|
155
|
+
* Instrument `TestBed` and report each spec file's cost.
|
|
156
|
+
*
|
|
157
|
+
* ```ts
|
|
158
|
+
* // vitest.setup.ts
|
|
159
|
+
* import { enableTestBedDiagnostics } from 'vitest-auto-spy/angular';
|
|
160
|
+
*
|
|
161
|
+
* if (process.env['SPEC_TIMING']) {
|
|
162
|
+
* enableTestBedDiagnostics();
|
|
163
|
+
* }
|
|
164
|
+
* ```
|
|
165
|
+
*/
|
|
166
|
+
declare function enableTestBedDiagnostics(options?: TestBedDiagnosticsOptions): void;
|
|
167
|
+
|
|
168
|
+
export { type AngularDiagnosticsOptions, type SpecTiming, type TestBedDiagnosticsOptions, assertNoPendingRequests, assertNoShadowedProviders, disableAngularDiagnostics, disableTestBedDiagnostics, enableAngularDiagnostics, enableTestBedDiagnostics, formatSpecTiming, getTestBedTiming, instrumentTestBed, reportSpecTiming };
|