vitest-auto-spy 3.18.0 → 4.0.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 +70 -24
- package/README.md +82 -30
- package/dist/angular.d.ts +5 -6
- package/dist/bun-angular.d.ts +5 -6
- package/dist/bun-angular.js +1 -1
- package/dist/bun.d.ts +5 -368
- package/dist/bun.js +1 -1
- package/dist/{chunk-CJCRAL6X.js → chunk-M3XZL5NF.js} +2 -386
- package/dist/console.d.ts +1 -2
- package/dist/diagnostics.d.ts +93 -0
- package/dist/diagnostics.js +202 -0
- package/dist/dom-stubs.d.ts +274 -0
- package/dist/dom-stubs.js +401 -0
- package/dist/eslint-plugin.cjs +13 -10
- package/dist/eslint-plugin.js +13 -10
- package/dist/{expect-emission-C-IjL1J2.d.ts → expect-emission-BDuFuqTb.d.ts} +1 -1
- package/dist/index.d.ts +4 -5
- package/dist/index.js +1 -386
- package/dist/jasmine-compat.d.ts +4 -5
- package/dist/jasmine.d.ts +2 -3
- package/dist/nestjs.d.ts +2 -3
- package/dist/node.cjs +0 -402
- package/dist/node.d.cts +0 -13
- package/dist/node.d.ts +4 -5
- package/dist/node.js +1 -1
- package/dist/{prop-mock-DDtfdw9e.d.ts → prop-mock-Dp2ZMFqj.d.ts} +1 -1
- package/dist/react.d.ts +4 -5
- package/dist/react.js +1 -1
- package/dist/rxjs.d.ts +34 -3
- package/dist/setup.d.ts +2 -3
- package/dist/svelte.d.ts +4 -5
- package/dist/svelte.js +1 -1
- package/dist/{track-injections-CzzXQwTl.d.ts → track-injections-CAqxzlhV.d.ts} +1 -1
- package/dist/{types-BvbrSBUX.d.ts → types-CQLPjw8z.d.ts} +77 -7
- package/dist/vue.d.ts +5 -6
- package/dist/vue.js +1 -1
- package/dist/{zoneless-D_hHdObW.d.ts → zoneless-DGwkbWKm.d.ts} +3 -3
- package/package.json +11 -1
- package/skills/vitest-auto-spy/SKILL.md +5 -2
package/AGENTS.md
CHANGED
|
@@ -51,15 +51,17 @@ adapter installed and spies fail at runtime.
|
|
|
51
51
|
|
|
52
52
|
Add-ons, orthogonal to the runner:
|
|
53
53
|
|
|
54
|
-
| Add-on | Import | Needed for
|
|
55
|
-
| ----------------- | ------------------------------- |
|
|
56
|
-
| Observable spies | `import 'vitest-auto-spy/rxjs'` | `nextWith` & friends. **Side-effect import, once
|
|
57
|
-
| 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
|
|
58
|
-
| Console spies | `vitest-auto-spy/console` | silent typed spies over the global `console`
|
|
59
|
-
|
|
|
60
|
-
|
|
|
61
|
-
|
|
|
62
|
-
|
|
|
54
|
+
| Add-on | Import | Needed for |
|
|
55
|
+
| ----------------- | ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
56
|
+
| Observable spies | `import 'vitest-auto-spy/rxjs'` | `nextWith` & friends. **Side-effect import, once**, and in a file the `tsconfig` includes (§4) |
|
|
57
|
+
| 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 |
|
|
58
|
+
| Console spies | `vitest-auto-spy/console` | silent typed spies over the global `console` |
|
|
59
|
+
| DOM stubs | `vitest-auto-spy/dom-stubs` | `stubIntersectionObserver` / `stubResizeObserver` / `stubMutationObserver` / `stubObserver`, `stubMediaElement`, `stubAbortController`, `intersectionEntry` / `resizeEntry` / `mutationRecord`. **Moved off the root in 4.0.0** |
|
|
60
|
+
| Run diagnostics | `vitest-auto-spy/diagnostics` | `compareTestRuns`, `summarizeTestRun`, `formatTestRunComparison`, `diffByField`. **Moved off the root in 4.0.0** |
|
|
61
|
+
| Angular HTTP | `vitest-auto-spy/angular-http` | `provideHttpTesting`, `expectRequest` — `httpResource()` / `HttpClient` (§13). Optional `@angular/common` peer, this entry only |
|
|
62
|
+
| Setup helpers | `vitest-auto-spy/setup` | `setupAutoSpy()`, `setupFakeTimers()` |
|
|
63
|
+
| Zone patch | `import 'vitest-auto-spy/zone'` | `fakeAsync` / `waitForAsync` on Vitest (§14) |
|
|
64
|
+
| jasmine compat | `vitest-auto-spy/jasmine` | `.and` / `.calls` / `.withArgs`, the `jasmine` namespace (§20) |
|
|
63
65
|
|
|
64
66
|
`vitest-auto-spy/jasmine` is Vitest-only, because it registers the Vitest adapter. On `bun test` and
|
|
65
67
|
`node --test` call `enableJasmineCompat()` from `vitest-auto-spy/jasmine-compat` instead.
|
|
@@ -365,6 +367,26 @@ Every spied method is a real runner mock, so `mockReturnValue`, `mockImplementat
|
|
|
365
367
|
`Observable` **properties** (not just methods) get the same helpers — list them in
|
|
366
368
|
`observablePropsToSpyOn`.
|
|
367
369
|
|
|
370
|
+
**What counts as an `Observable` is structural, and no declaration names rxjs (4.0.0).** A member
|
|
371
|
+
earns the observable bundle when its type satisfies the exported `ObservableLike<T>` — `subscribe`
|
|
372
|
+
plus a promise-returning `forEach(next)`:
|
|
373
|
+
rxjs's `Observable`, every `Subject`, Angular's `EventEmitter` — and, new in 4.0.0, an `Observable`
|
|
374
|
+
from a _second copy_ of rxjs in the tree, which used to fall through to the plain-spy branch and
|
|
375
|
+
produce `nextWith is not a function` with nothing pointing at the duplicate. `Promise`, arrays,
|
|
376
|
+
`Signal` and Angular's `OutputEmitterRef` are not observables and do not earn it.
|
|
377
|
+
|
|
378
|
+
`returnSubject()` and `nextWithPerCall()` return `SubjectOf<T>` — rxjs's own `Subject<T>` wherever
|
|
379
|
+
`import 'vitest-auto-spy/rxjs'` is in the TypeScript program, the structural `SubjectLike<T>`
|
|
380
|
+
(`next` / `error` / `complete` / `asObservable` / `closed`) where it is not. The switch is one
|
|
381
|
+
augmentable interface, `AutoSpyRxjsTypes<T>`, which `/rxjs` fills in with `subject: Subject<T>`;
|
|
382
|
+
augment it yourself only to plug in a different subject type. If
|
|
383
|
+
`const s: Subject<T> = spy.m.returnSubject()` fails to compile, the import is missing from the
|
|
384
|
+
program the specs are checked in — usually a Vitest `setupFiles` entry that no `tsconfig`
|
|
385
|
+
`include` covers. That is the _only_ breaking change in 4.0.0; the reason for it is that
|
|
386
|
+
`dist/types-*.d.ts` used to open with `import { Observable, Subject } from 'rxjs'` and load 189 rxjs
|
|
387
|
+
`.d.ts` files into every consumer's program (303 files against 114 without it), `import type`
|
|
388
|
+
included — TypeScript resolves a type-only import the same way.
|
|
389
|
+
|
|
368
390
|
```ts
|
|
369
391
|
// argument dispatch — other arguments return undefined
|
|
370
392
|
users.getName.calledWith(1).mockReturnValue('Ada');
|
|
@@ -1544,7 +1566,7 @@ Do not assign `globalThis.IntersectionObserver` by hand: it stays assigned, and
|
|
|
1544
1566
|
`isolate: false` the next file inherits it.
|
|
1545
1567
|
|
|
1546
1568
|
```ts
|
|
1547
|
-
import { intersectionEntry, stubIntersectionObserver } from 'vitest-auto-spy';
|
|
1569
|
+
import { intersectionEntry, stubIntersectionObserver } from 'vitest-auto-spy/dom-stubs';
|
|
1548
1570
|
|
|
1549
1571
|
const observers = stubIntersectionObserver(); // also stubResizeObserver / stubMutationObserver
|
|
1550
1572
|
|
|
@@ -2144,32 +2166,55 @@ import autoSpy from 'vitest-auto-spy/eslint-plugin';
|
|
|
2144
2166
|
export default [{ files: ['**/*.spec.ts'], ...autoSpy.configs.recommended }];
|
|
2145
2167
|
```
|
|
2146
2168
|
|
|
2169
|
+
The `files` glob has no default — too narrow and the plugin is silently inert
|
|
2170
|
+
(`npx eslint --print-config a.spec.ts` settles it), too wide and `no-object-define-property` starts
|
|
2171
|
+
reporting on application code that is entitled to it. To override a severity, spread the rule map
|
|
2172
|
+
too: a bare `rules` key beside the spread config **replaces** it rather than merging, and nothing
|
|
2173
|
+
reports that.
|
|
2174
|
+
|
|
2175
|
+
```js
|
|
2176
|
+
export default [
|
|
2177
|
+
{
|
|
2178
|
+
files: ['**/*.spec.ts'],
|
|
2179
|
+
...autoSpy.configs.recommended,
|
|
2180
|
+
rules: { ...autoSpy.configs.recommended.rules, 'vitest-auto-spy/prefer-native-spy-api': 'off' },
|
|
2181
|
+
},
|
|
2182
|
+
];
|
|
2183
|
+
```
|
|
2184
|
+
|
|
2147
2185
|
| Rule | Level | Fix | Flags |
|
|
2148
2186
|
| --------------------------------- | ------- | ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
|
|
2149
2187
|
| `no-expect-in-subscribe` | `error` | suggest | `expect()` inside `subscribe()` → `expectEmission` / `firstValueFrom` |
|
|
2150
2188
|
| `no-object-define-property` | `error` | suggest | `Object.defineProperty` in a spec → `mockReadonlyProp` / `mockValueProp` |
|
|
2151
|
-
| `prefer-provide-auto-spy` | `
|
|
2152
|
-
| `prefer-create-spy-from-class` | `
|
|
2153
|
-
| `prefer-inject-spy` | `
|
|
2189
|
+
| `prefer-provide-auto-spy` | `error` | — | a hand-rolled `useValue` **or** `useFactory` → `provideAutoSpy(Class)` / `provideAutoSpyForToken(TOKEN)` |
|
|
2190
|
+
| `prefer-create-spy-from-class` | `error` | — | an object literal of 2+ `vi.fn()`s → `createSpyFromClass` (a factory's own seed is exempt) |
|
|
2191
|
+
| `prefer-inject-spy` | `error` | suggest | `vi.spyOn(TestBed.inject(X), 'm')`, inline or via a `const` → `injectSpy(X).m` |
|
|
2154
2192
|
| `no-shared-module-level-mock` | `error` | — | an **exported** value holding `vi.fn()`s → export a factory instead |
|
|
2155
|
-
| `no-mocked-for-spy` | `
|
|
2156
|
-
| `prefer-as-spy` | `
|
|
2193
|
+
| `no-mocked-for-spy` | `error` | `--fix` / suggest | `Mocked<T>` in any type position → `Spy<T>`, import and all — a suggestion where the assigned value is not from a factory of this library |
|
|
2194
|
+
| `prefer-as-spy` | `error` | `--fix` | `TestBed.inject(X) as Spy<X>` → `asSpy<X>(TestBed.inject(X))`, import and all |
|
|
2157
2195
|
| `no-done-callback` | `error` | — | `it('x', (done) => …)` → `async` + an awaited assertion, and `done.fail(…)` at the call site |
|
|
2158
2196
|
| `no-floating-assertion` | `error` | — | `expect()` in a `.then()` nobody awaits → `expect(await promise)` |
|
|
2159
2197
|
| `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 |
|
|
2160
2198
|
| `no-overridden-provider` | `error` | suggest | two providers for one token in one array → the earlier one never runs; the exact duplicate can be deleted |
|
|
2161
|
-
| `no-inject-before-override` | `
|
|
2199
|
+
| `no-inject-before-override` | `error` | — | `TestBed.inject()` in a hook, in a suite that still calls `override*` |
|
|
2162
2200
|
| `no-import-time-spread` | `error` | suggest | `export const x = [...Imported]` at module scope → a `TypeError` while the bundle loads |
|
|
2163
|
-
| `no-unregistered-inject-spy` | `
|
|
2164
|
-
| `jasmine-namespace-without-entry` | `
|
|
2201
|
+
| `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 |
|
|
2202
|
+
| `jasmine-namespace-without-entry` | `error` | — | `.and` / `.calls` / `.withArgs` on a library spy in a file that installs the compat layer nowhere — option: `{ setupModules: […] }` |
|
|
2165
2203
|
| `no-jasmine-globals` | `error` | — | `jasmine.*`, bare `spyOn(` / `spyOnProperty(` / `spyOnAllFunctions(` / `fail(` / `pending(`, `.withContext(` |
|
|
2166
2204
|
| `no-save-arguments-by-value` | `error` | — | `spy.calls.saveArgumentsByValue()` — a no-op here, so the spec silently asserts on post-mutation state |
|
|
2167
|
-
| `prefer-native-spy-api` | `
|
|
2168
|
-
|
|
2169
|
-
Nineteen rules
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2205
|
+
| `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 |
|
|
2206
|
+
|
|
2207
|
+
Nineteen rules, **every one an `error` since 4.0.0**; three fix on their own, seven offer
|
|
2208
|
+
suggestions. The config used to be a graded mix of `error` / `warn` / `off`, which decided for the
|
|
2209
|
+
consumer how much each finding mattered — a `warn` nothing reads is `off` with extra output. Three of
|
|
2210
|
+
them can report on a _correct_ project, and only one has an option:
|
|
2211
|
+
`jasmine-namespace-without-entry` takes `['error', { setupModules: ['./test-setup'] }]`, naming the
|
|
2212
|
+
file where `enableJasmineCompat()` is called; `prefer-native-spy-api` goes `'off'` for as long as a
|
|
2213
|
+
suite is still running on the jasmine bridge it reports; and `no-unregistered-inject-spy` has no
|
|
2214
|
+
option and needs none — it stays silent unless the file calls `provideAutoSpy` and its whole
|
|
2215
|
+
`providers` array is readable (a spread, an unknown factory, `createWithAutoSpies`, `renderShallow`
|
|
2216
|
+
or `TestBed.overrideProvider` all silence it), so the residue is a scoped `'off'`. The last four are
|
|
2217
|
+
for a suite mid-migration off `jasmine-auto-spies` (§20). `no-mocked-for-spy` only ever touches a
|
|
2173
2218
|
**type position**, where a wrong rewrite is a compile error rather than a test that quietly changed
|
|
2174
2219
|
meaning — so `--fix` renames the type, adds `import type { Spy } from 'vitest-auto-spy'` and drops
|
|
2175
2220
|
the orphaned `Mocked` import. Every type position, not only a `let`: a factory's return type, a
|
|
@@ -2288,6 +2333,7 @@ packages, which a subpath export can never be.
|
|
|
2288
2333
|
| -------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
|
|
2289
2334
|
| `No mock adapter registered` | no runtime entry was imported, or the wrong one | import `vitest-auto-spy` (Vitest) / `…/bun` / `…/node` once before creating spies |
|
|
2290
2335
|
| `Observable spies require rxjs` | the rxjs layer was never loaded | `import 'vitest-auto-spy/rxjs';` once, in the setup file |
|
|
2336
|
+
| `Type 'SubjectLike<T>' is not assignable to type 'Subject<T>'` | 4.0.0: `returnSubject()` is rxjs-typed only where `vitest-auto-spy/rxjs` is in the TypeScript program, and it is not in this one | put `import 'vitest-auto-spy/rxjs';` in a file the spec `tsconfig` includes — a `setupFiles` entry outside `include` is the usual cause |
|
|
2291
2337
|
| `Cannot read properties of undefined (reading 'returnValue')` on `spy.m.and.…` | the jasmine namespaces are not installed — the spy was built before them, or nothing installed them at all | import from `vitest-auto-spy/jasmine`; on Bun / `node:test`, `enableJasmineCompat()` in the setup file (§20) |
|
|
2292
2338
|
| `spy.withArgs is not a function` | the same, seen from the argument-matching side | the same — or write `spy.m.calledWith(a).mockReturnValue(v)`, which needs no layer (§20) |
|
|
2293
2339
|
| `jasmine is not defined` | jasmine's global is the runner's, and Vitest declares none | `import { jasmine } from 'vitest-auto-spy/jasmine'`, then `codemod --from jasmine` to rewrite the members (§20) |
|
package/README.md
CHANGED
|
@@ -17,9 +17,11 @@ identical API, with **RxJS** spies and **Angular / NestJS / React / Vue·Pinia /
|
|
|
17
17
|
`.withArgs` working while you land the suite green.
|
|
18
18
|
|
|
19
19
|
[](https://www.npmjs.com/package/vitest-auto-spy)
|
|
20
|
-
[](https://www.npmjs.com/package/vitest-auto-spy)
|
|
21
|
+
[](https://www.npmjs.com/package/vitest-auto-spy)
|
|
22
|
+
[](https://www.npmjs.com/package/vitest-auto-spy)
|
|
21
23
|
[](https://github.com/ASDAlexey/vitest-auto-spy/actions/workflows/ci.yml)
|
|
22
|
-
[](#install)
|
|
23
25
|
[](https://www.npmjs.com/package/vitest-auto-spy)
|
|
24
26
|
[](https://github.com/ASDAlexey/vitest-auto-spy/actions/workflows/ci.yml)
|
|
25
27
|
[](./LICENSE)
|
|
@@ -70,7 +72,7 @@ identical API, with **RxJS** spies and **Angular / NestJS / React / Vue·Pinia /
|
|
|
70
72
|
- 🌀 `fakeAsync` / `waitForAsync` on Vitest — one import of `vitest-auto-spy/zone`; zone.js stays out of every other entry
|
|
71
73
|
- 🧩 Module mocks that prove they applied — `assertMocked`, `moduleNamespace`, for a `vi.mock()` a bundler quietly ignored
|
|
72
74
|
- 🧾 Fixtures without casts — deep-partial `createMock`, `createFixture` / `createFixtureFactory`, `narrow()`, `withOverrides()`, `asInstances()`, `captureArg()`
|
|
73
|
-
- 🚚 A migration you can verify — `compareTestRuns` on the two JSON reports, `diffByField` for the assertion the reporter collapses
|
|
75
|
+
- 🚚 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
|
|
74
76
|
- 📏 Lint rules and one-line test-run hygiene — nineteen rules in `vitest-auto-spy/eslint-plugin` (three `--fix`, seven suggestions, four of them for a suite mid-migration off jasmine), `setupAutoSpy()`
|
|
75
77
|
- 🩺 [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
|
|
76
78
|
- 🔎 [`npx vitest-auto-spy doctor`](#the-cli--doctor-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
|
|
@@ -80,6 +82,23 @@ identical API, with **RxJS** spies and **Angular / NestJS / React / Vue·Pinia /
|
|
|
80
82
|
- 🤖 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
|
|
81
83
|
- 🟢 100% test coverage, **zero runtime dependencies** (in-tree arg serializer, no `javascript-stringify`)
|
|
82
84
|
|
|
85
|
+
## New in 4.0
|
|
86
|
+
|
|
87
|
+
A major with **one job**: take out of your project the weight this library was making it carry.
|
|
88
|
+
Nothing was removed or renamed and no runtime behaviour changed — the whole cost is two import
|
|
89
|
+
specifiers, listed with their fix in
|
|
90
|
+
[Upgrading to 4.0](https://asdalexey.github.io/vitest-auto-spy/upgrading-4).
|
|
91
|
+
|
|
92
|
+
| What you get | Measured |
|
|
93
|
+
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
94
|
+
| **rxjs is out of your TypeScript program.** The declarations named `Observable` and `Subject`, so every consumer loaded rxjs whether the project used it or not. `import type` does not fix that — TypeScript resolves it the same way — so the reference had to go | consumer program **303 → 114 files**, of which **189 → 0** are rxjs; `TS2307` inside a shipped `.d.ts` under `skipLibCheck: false` with no rxjs installed: gone |
|
|
95
|
+
| **Every spec file gets time back.** The DOM stubs and the run diagnostics moved to `vitest-auto-spy/dom-stubs` and `…/diagnostics`, so a spec that never touches a DOM global stops evaluating 27 kB to reach `createSpyFromClass` | **−0.159 ms per spec file** that does not import them, +0.155 ms in the files that do; root entry **15.5 → 12.9 kB** min+gzip |
|
|
96
|
+
| **A second copy of rxjs stops breaking detection.** Observable detection is structural now, so an `Observable` from a duplicated rxjs earns the observable helpers instead of failing with `nextWith is not a function` | — |
|
|
97
|
+
| **The lint rules stop deciding how much you care.** All nineteen ship as `error`; which findings block a merge is one line of config, documented per rule | — |
|
|
98
|
+
|
|
99
|
+
If you use the observable helpers, keep `import 'vitest-auto-spy/rxjs'` somewhere your `tsconfig`
|
|
100
|
+
includes — that one import is what keeps `returnSubject()` typed as rxjs's own `Subject<T>`.
|
|
101
|
+
|
|
83
102
|
## Table of contents
|
|
84
103
|
|
|
85
104
|
- [Install](#install)
|
|
@@ -213,11 +232,11 @@ the published package roughly in half.
|
|
|
213
232
|
All peers are **provided by your project**; `rxjs` and `@angular/core` are **optional** — install
|
|
214
233
|
them only for the matching entry point. The package itself has **zero runtime dependencies**.
|
|
215
234
|
|
|
216
|
-
| Peer | Needed for
|
|
217
|
-
| --------------- |
|
|
218
|
-
| `vitest` | the default runner
|
|
219
|
-
| `rxjs` | `vitest-auto-spy/rxjs` observable spies
|
|
220
|
-
| `@angular/core` | `vitest-auto-spy/angular` helpers
|
|
235
|
+
| Peer | Needed for | Optional? |
|
|
236
|
+
| --------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | --------- |
|
|
237
|
+
| `vitest` | the default runner | no |
|
|
238
|
+
| `rxjs` | `vitest-auto-spy/rxjs` observable spies — `>=7`, **no upper bound** (the rxjs 8 line included); not needed to type-check a spy since 4.0.0 | yes |
|
|
239
|
+
| `@angular/core` | `vitest-auto-spy/angular` helpers | yes |
|
|
221
240
|
|
|
222
241
|
## The CLI — `doctor`, `codemod` and `init`
|
|
223
242
|
|
|
@@ -995,9 +1014,11 @@ Angular's `TestBed` runs on Bun too — see [Angular on Bun](#angular-on-bun-bun
|
|
|
995
1014
|
> Using an observable spy (`observablePropsToSpyOn`, `nextWith`, …) without importing
|
|
996
1015
|
> `vitest-auto-spy/rxjs` throws a clear hint telling you to add that import.
|
|
997
1016
|
>
|
|
998
|
-
>
|
|
999
|
-
>
|
|
1000
|
-
>
|
|
1017
|
+
> Since **4.0.0** the decoupling is at the **type** level too: no declaration this package ships
|
|
1018
|
+
> names an rxjs type, so a project without rxjs never loads it — 189 rxjs `.d.ts` files left every
|
|
1019
|
+
> consumer's TypeScript program, 303 files down to 114 for the same fixture. `returnSubject()` is
|
|
1020
|
+
> still rxjs's own `Subject<T>` wherever `import 'vitest-auto-spy/rxjs'` is part of the program.
|
|
1021
|
+
> See [Upgrading to 4.0](https://asdalexey.github.io/vitest-auto-spy/upgrading-4).
|
|
1001
1022
|
>
|
|
1002
1023
|
> The same inversion-of-control applies to the **test runner**: the core no longer imports
|
|
1003
1024
|
> `vitest` directly — `vi.fn()` / `vi.spyOn()` sit behind a `MockAdapter` that the
|
|
@@ -1206,7 +1227,8 @@ specs: observer-spy is synchronous inspection and passes on silence, those fail
|
|
|
1206
1227
|
|
|
1207
1228
|
Four ESLint rules cover the window between landing green and finishing —
|
|
1208
1229
|
`jasmine-namespace-without-entry`, `no-jasmine-globals`, `no-save-arguments-by-value`, and
|
|
1209
|
-
`prefer-native-spy-api` (
|
|
1230
|
+
`prefer-native-spy-api` (the last-mile one — set it to `'off'` while the suite still runs on the
|
|
1231
|
+
bridge it reports).
|
|
1210
1232
|
|
|
1211
1233
|
The full mapping — the auto-spies API, jasmine's own globals, `withContext`, `DEFAULT_TIMEOUT_INTERVAL`,
|
|
1212
1234
|
`done` callbacks, and what upstream cannot do — is on the docs site:
|
|
@@ -1496,6 +1518,14 @@ const subject = myService.getProducts$.returnSubject();
|
|
|
1496
1518
|
subject.next([{ name: 'manual' }]);
|
|
1497
1519
|
```
|
|
1498
1520
|
|
|
1521
|
+
`returnSubject()` and `nextWithPerCall()` are typed `SubjectOf<T>`: rxjs's own `Subject<T>` wherever
|
|
1522
|
+
`import 'vitest-auto-spy/rxjs'` is in the TypeScript program, and the structural `SubjectLike<T>`
|
|
1523
|
+
(`next` / `error` / `complete` / `asObservable` / `closed`) where it is not. That is how the
|
|
1524
|
+
published declarations name no rxjs type while a suite that has rxjs keeps the exact type it had.
|
|
1525
|
+
The seam is one augmentable interface, `AutoSpyRxjsTypes<T>`, which the `/rxjs` entry fills in; what
|
|
1526
|
+
decides that a member _is_ an observable is the other new type, `ObservableLike<T>` — `subscribe`
|
|
1527
|
+
plus a promise-returning `forEach(next)`.
|
|
1528
|
+
|
|
1499
1529
|
`calledWith(...)` / `mustBeCalledWith(...)` also chain into the observable helpers:
|
|
1500
1530
|
|
|
1501
1531
|
```ts
|
|
@@ -2483,7 +2513,7 @@ naming the fix, rather than letting Vitest fail deeper in with "timers are not m
|
|
|
2483
2513
|
## Observer stubs
|
|
2484
2514
|
|
|
2485
2515
|
```ts
|
|
2486
|
-
import { intersectionEntry, stubIntersectionObserver } from 'vitest-auto-spy';
|
|
2516
|
+
import { intersectionEntry, stubIntersectionObserver } from 'vitest-auto-spy/dom-stubs';
|
|
2487
2517
|
|
|
2488
2518
|
it('reveals the card once it scrolls into view', async () => {
|
|
2489
2519
|
const observers = stubIntersectionObserver();
|
|
@@ -2542,6 +2572,26 @@ import autoSpy from 'vitest-auto-spy/eslint-plugin';
|
|
|
2542
2572
|
export default [{ files: ['**/*.spec.ts'], ...autoSpy.configs.recommended }];
|
|
2543
2573
|
```
|
|
2544
2574
|
|
|
2575
|
+
**Every rule is an `error` since 4.0.0.** The config used to grade them `error` / `warn` / `off`,
|
|
2576
|
+
which chose for you how much each finding mattered; a `warn` in a repository that does not read lint
|
|
2577
|
+
output is `off` with extra noise, and which findings block a merge is one line of config either way.
|
|
2578
|
+
To turn one down, spread the rule map as well — a bare `rules` key beside the spread config replaces
|
|
2579
|
+
it rather than merging, silently:
|
|
2580
|
+
|
|
2581
|
+
```js
|
|
2582
|
+
export default [
|
|
2583
|
+
{
|
|
2584
|
+
files: ['**/*.spec.ts'],
|
|
2585
|
+
...autoSpy.configs.recommended,
|
|
2586
|
+
rules: { ...autoSpy.configs.recommended.rules, 'vitest-auto-spy/prefer-provide-auto-spy': 'warn' },
|
|
2587
|
+
},
|
|
2588
|
+
];
|
|
2589
|
+
```
|
|
2590
|
+
|
|
2591
|
+
The full dial — landing it on a large existing suite without a red CI, the three rules that can
|
|
2592
|
+
report on correct code, and `setupModules` — is on the docs site:
|
|
2593
|
+
[ESLint plugin → Tuning it for your project](https://asdalexey.github.io/vitest-auto-spy/utilities/eslint-plugin#tuning-it-for-your-project).
|
|
2594
|
+
|
|
2545
2595
|
Scope it to spec files yourself: every rule is about test code, and `Object.defineProperty` or an
|
|
2546
2596
|
object of `vi.fn()`s is perfectly reasonable in application code. Flat config only — the legacy
|
|
2547
2597
|
`.eslintrc` `plugins: ['…']` form resolves names to `eslint-plugin-*` packages, which a subpath
|
|
@@ -2549,25 +2599,25 @@ export can never be.
|
|
|
2549
2599
|
|
|
2550
2600
|
| Rule | Recommended | Fix | Flags |
|
|
2551
2601
|
| --------------------------------- | :---------: | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
2552
|
-
| `prefer-provide-auto-spy` | `
|
|
2553
|
-
| `prefer-create-spy-from-class` | `
|
|
2554
|
-
| `prefer-inject-spy` | `
|
|
2602
|
+
| `prefer-provide-auto-spy` | `error` | — | a hand-rolled `useValue` **or** `useFactory` → `provideAutoSpy(Class)` / `provideAutoSpyForToken(TOKEN)` |
|
|
2603
|
+
| `prefer-create-spy-from-class` | `error` | — | an object literal of two or more `vi.fn()`s → `createSpyFromClass` / `createAutoMock`, unless it is a factory's own seed |
|
|
2604
|
+
| `prefer-inject-spy` | `error` | suggest | `vi.spyOn(TestBed.inject(X), 'm')`, in one step or two → `injectSpy(X).m` |
|
|
2555
2605
|
| `no-object-define-property` | `error` | suggest | `Object.defineProperty` in a spec → `mockReadonlyProp` / `mockValueProp` |
|
|
2556
2606
|
| `no-expect-in-subscribe` | `error` | suggest | `expect()` inside a `subscribe()` callback → `expectEmission` / `firstValueFrom` |
|
|
2557
2607
|
| `no-shared-module-level-mock` | `error` | — | an **exported** value holding `vi.fn()`s → export a factory that returns it |
|
|
2558
|
-
| `no-mocked-for-spy` | `
|
|
2559
|
-
| `prefer-as-spy` | `
|
|
2608
|
+
| `no-mocked-for-spy` | `error` | `--fix` / suggest | `Mocked<T>` in any type position → `Spy<T>`, import and all — a suggestion where the value assigned is not one of this library's factories |
|
|
2609
|
+
| `prefer-as-spy` | `error` | `--fix` | `TestBed.inject(X) as Spy<X>` → `asSpy<X>(TestBed.inject(X))`, import and all |
|
|
2560
2610
|
| `no-done-callback` | `error` | — | `it('x', (done) => …)` → `async` + an awaited assertion, and `done.fail(…)` at the call site |
|
|
2561
2611
|
| `no-floating-assertion` | `error` | — | `expect()` in a `.then()` nobody awaits → `expect(await promise)` |
|
|
2562
2612
|
| `no-bare-called-with` | `error` | — | `spy.m.calledWith(1);` as a statement of its own — a stub nobody continued, asserting nothing |
|
|
2563
2613
|
| `no-overridden-provider` | `error` | suggest | two providers for one token in one array → the earlier one never runs; the exact duplicate can be deleted |
|
|
2564
|
-
| `no-inject-before-override` | `
|
|
2614
|
+
| `no-inject-before-override` | `error` | — | `TestBed.inject()` in a hook, in a suite that still calls `override*` |
|
|
2565
2615
|
| `no-import-time-spread` | `error` | suggest | `export const x = [...Imported]` at module scope → a `TypeError` while the bundle loads |
|
|
2566
|
-
| `no-unregistered-inject-spy` | `
|
|
2567
|
-
| `jasmine-namespace-without-entry` | `
|
|
2616
|
+
| `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 |
|
|
2617
|
+
| `jasmine-namespace-without-entry` | `error` | — | `.and` / `.calls` / `.withArgs` on a library spy in a file that installs the compatibility layer nowhere |
|
|
2568
2618
|
| `no-jasmine-globals` | `error` | — | `jasmine.*`, `spyOn(` / `spyOnProperty(` / `spyOnAllFunctions(` / `fail(` / `pending(`, `.withContext(` — none of them exist under Vitest |
|
|
2569
2619
|
| `no-save-arguments-by-value` | `error` | — | `spy.calls.saveArgumentsByValue()`, which is a no-op here → take the copy at call time |
|
|
2570
|
-
| `prefer-native-spy-api` |
|
|
2620
|
+
| `prefer-native-spy-api` | `error` | `--fix` / suggest | `.and` / `.calls` where the spy's own API says the same thing — the last mile off the jasmine shim |
|
|
2571
2621
|
|
|
2572
2622
|
Every message ends with a link to the matching [recipe](#how-to-mock): a rule that only says
|
|
2573
2623
|
"don't" moves the problem rather than solving it. Rules travel with the API they recommend, so they
|
|
@@ -2649,17 +2699,19 @@ a spread, an unknown provider factory, `createWithAutoSpies`, `renderShallow` or
|
|
|
2649
2699
|
[`vitest-auto-spy/jasmine`](#migrating-from-jasmine-auto-spies), or one that thinks it is.
|
|
2650
2700
|
`no-jasmine-globals` is the one that pays for itself on the first run: jasmine's `spyOn` **stubs**
|
|
2651
2701
|
the method and `vi.spyOn` **calls through**, so the rename compiles, the spec passes, and the code
|
|
2652
|
-
under test starts really talking to its collaborator. `jasmine-namespace-without-entry`
|
|
2653
|
-
|
|
2654
|
-
is usually written in a setup file the linted spec never imports;
|
|
2655
|
-
is how a project says where it comes from
|
|
2702
|
+
under test starts really talking to its collaborator. `jasmine-namespace-without-entry` is one of the
|
|
2703
|
+
two rules that can report on a correct project, because the fact that settles it — does this project
|
|
2704
|
+
install the compatibility layer? — is usually written in a setup file the linted spec never imports;
|
|
2705
|
+
`['error', { setupModules: ['./test-setup'] }]` is how a project says where it comes from, and that
|
|
2706
|
+
option is the fix rather than a lower severity. `no-save-arguments-by-value` is the purest silent case in
|
|
2656
2707
|
the whole plugin: the call still runs, nothing fails, and the spec quietly stops asserting what it
|
|
2657
2708
|
was written to assert.
|
|
2658
2709
|
|
|
2659
|
-
`prefer-native-spy-api` is **off**
|
|
2660
|
-
code that works: the compatibility layer is what a suite runs on _while_
|
|
2661
|
-
|
|
2662
|
-
|
|
2710
|
+
`prefer-native-spy-api` is the one rule to switch **off** while a migration is in flight, and that
|
|
2711
|
+
is not timidity. It reports code that works: the compatibility layer is what a suite runs on _while_
|
|
2712
|
+
it is being migrated, so on day one it flags every line of the bridge. One line
|
|
2713
|
+
(`'vitest-auto-spy/prefer-native-spy-api': 'off'`) buys the whole migration; delete it for the last
|
|
2714
|
+
mile — `eslint --fix` then does the renames whose
|
|
2663
2715
|
receiver it can trace to one of this library's factories (`.and.returnValue(x)` →
|
|
2664
2716
|
`.mockReturnValue(x)`, `.and.nextWith(v)` → `.nextWith(v)`, `.withArgs(a).and.returnValue(v)` →
|
|
2665
2717
|
`.calledWith(a).mockReturnValue(v)`, `.calls.count()` → `.mock.calls.length`), offers the same edit
|
package/dist/angular.d.ts
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
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-DGwkbWKm.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,
|
|
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, r as restoreMockedProps } from './prop-mock-
|
|
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-CQLPjw8z.js';
|
|
5
|
+
export { I as InjectionLog, T as TrackInjectionsOptions, a as TrackedProvider, t as trackInjections } from './track-injections-CAqxzlhV.js';
|
|
6
|
+
export { A as AccessorImplementations, R as RestoreProp, c as countMockedProps, m as mockAccessorsProp, a as mockReadonlyProp, b as mockReadonlyPropGetter, d as mockValueProp, r as restoreMockedProps } from './prop-mock-Dp2ZMFqj.js';
|
|
7
|
+
export { C as CallbackSubscribable, E as EmissionObserver, a as EmissionOptions, b as EmissionSource, S as SubscribableLike, e as expectCompletion, f as expectEmission, g as expectEmissions, h as expectError, i as expectNoEmission, s as setEmissionTimeout } from './expect-emission-BDuFuqTb.js';
|
|
8
8
|
import '@angular/core/testing';
|
|
9
|
-
import 'rxjs';
|
|
10
9
|
|
|
11
10
|
/**
|
|
12
11
|
* `extendWithAutoSpies` — the `TestBed` half of a spec, moved into Vitest fixtures.
|
package/dist/bun-angular.d.ts
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
export { A as AccessorKeysOf, a as AddAccessorsSpies, b as AddCalledWithObservable, c as AddCalledWithPromise, d as AddCalledWithSpyMethods, e as AddObservableSpyMethods, f as AddPromiseSpyMethods, g as AddSpyMethodsByReturnTypes, h as AddThrowHelper, i as AddVoidReturnHelpers, j as ClassSpyConfiguration, C as ClassType,
|
|
2
|
-
export { ArgCaptor, AsInstances, AssertMockedOptions, ConstructorMock, ConstructorSpy, FixtureFactory, FlushUntilOptions,
|
|
3
|
-
export { A as AutoMockConfiguration, C as CallbackSubscribable, E as EmissionObserver, a as EmissionOptions, b as EmissionSource, S as SubscribableLike, c as autoMocked, d as createAutoMock, e as expectCompletion, f as expectEmission, g as expectEmissions, h as expectError, i as expectNoEmission, s as setEmissionTimeout } from './expect-emission-
|
|
4
|
-
export { A as AccessorImplementations, R as RestoreProp, c as countMockedProps, m as mockAccessorsProp, a as mockReadonlyProp, b as mockReadonlyPropGetter, d as mockValueProp, r as restoreMockedProps } from './prop-mock-
|
|
1
|
+
export { A as AccessorKeysOf, a as AddAccessorsSpies, b as AddCalledWithObservable, c as AddCalledWithPromise, d as AddCalledWithSpyMethods, e as AddObservableSpyMethods, f as AddPromiseSpyMethods, g as AddSpyMethodsByReturnTypes, h as AddThrowHelper, i as AddVoidReturnHelpers, j as AutoSpyRxjsTypes, k as ClassSpyConfiguration, C as ClassType, l as CompleteValueConfig, D as DeepMockProxy, m as DeepPartial, E as ErrorValueConfig, F as Func, M as MethodReturns, n as Mutable, N as NextValueConfig, O as ObservableLike, o as OnlyMethodKeysOf, p as OnlyObservablePropsOf, q as OnlyPropsOf, r as Overload, s as Overloads, P as PropStubValue, S as Spy, t as SpyDisposable, u as SpyOptions, v as StrictSpyConfiguration, w as SubjectLike, x as SubjectOf, U as UnstubbedCall, y as UnstubbedCallHandler, V as ValueConfig, z as ValueConfigPerCall, W as WithMockReturnValue } from './types-CQLPjw8z.js';
|
|
2
|
+
export { ArgCaptor, AsInstances, AssertMockedOptions, ConstructorMock, ConstructorSpy, FixtureFactory, FlushUntilOptions, MockDeepOptions, ModuleNamespace, ModuleNamespaceOptions, asInstance, asInstances, asSpy, assertMocked, captureArg, clearAutoSpy, createFixture, createFixtureFactory, createFunctionSpy, createMock, createSpyClass, createSpyFromClass, errorHandler, flushEventLoop, flushEventLoopUntil, mockConstructor, mockDeep, moduleNamespace, narrow, resetAutoSpy, settleDynamicImport, stubConstructor, withOverrides } from './bun.js';
|
|
3
|
+
export { A as AutoMockConfiguration, C as CallbackSubscribable, E as EmissionObserver, a as EmissionOptions, b as EmissionSource, S as SubscribableLike, c as autoMocked, d as createAutoMock, e as expectCompletion, f as expectEmission, g as expectEmissions, h as expectError, i as expectNoEmission, s as setEmissionTimeout } from './expect-emission-BDuFuqTb.js';
|
|
4
|
+
export { A as AccessorImplementations, R as RestoreProp, c as countMockedProps, m as mockAccessorsProp, a as mockReadonlyProp, b as mockReadonlyPropGetter, d as mockValueProp, r as restoreMockedProps } from './prop-mock-Dp2ZMFqj.js';
|
|
5
5
|
export { d as describeDuplicateCopies, g as getPackageCopies } from './package-identity-B1pqa-Sh.js';
|
|
6
|
-
export { 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, r as renderShallow, j as runEffect, s as settleResource, k as stable } from './zoneless-
|
|
7
|
-
import 'rxjs';
|
|
6
|
+
export { 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, r as renderShallow, j as runEffect, s as settleResource, k as stable } from './zoneless-DGwkbWKm.js';
|
|
8
7
|
import 'vitest';
|
|
9
8
|
import '@angular/core';
|
|
10
9
|
import '@angular/core/testing';
|
package/dist/bun-angular.js
CHANGED
|
@@ -2,7 +2,7 @@ import { flushEffects } from './chunk-5BPM3BLI.js';
|
|
|
2
2
|
export { flushEffects, stable } from './chunk-5BPM3BLI.js';
|
|
3
3
|
import './chunk-Y4YG7UOK.js';
|
|
4
4
|
import './chunk-NQXXUJPS.js';
|
|
5
|
-
export { asInstance, asInstances, asSpy, assertMocked, captureArg,
|
|
5
|
+
export { asInstance, asInstances, asSpy, assertMocked, captureArg, createFixture, createFixtureFactory, createMock, createSpyClass, describeDuplicateCopies, expectCompletion, expectEmission, expectEmissions, expectError, expectNoEmission, flushEventLoop, flushEventLoopUntil, getPackageCopies, mockConstructor, mockDeep, moduleNamespace, narrow, setEmissionTimeout, settleDynamicImport, stubConstructor, withOverrides } from './chunk-M3XZL5NF.js';
|
|
6
6
|
export { countMockedProps, mockAccessorsProp, mockReadonlyProp, mockReadonlyPropGetter, mockValueProp, restoreMockedProps } from './chunk-DSIYS6Y5.js';
|
|
7
7
|
import { createSpyForToken } from './chunk-6EGUFU73.js';
|
|
8
8
|
import { createSpyFromClass } from './chunk-N4PT3JYE.js';
|