vitest-auto-spy 5.7.0 → 5.8.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 +202 -16
- package/README.md +179 -16
- package/dist/angular-http.js +2 -2
- package/dist/angular-router.d.ts +102 -0
- package/dist/angular-router.js +184 -0
- package/dist/angular.d.ts +119 -8
- package/dist/angular.js +289 -77
- package/dist/bun-angular.d.ts +6 -6
- package/dist/bun-angular.js +13 -13
- package/dist/bun.d.ts +6 -128
- package/dist/bun.js +8 -8
- package/dist/{chunk-G73VE5ED.js → chunk-56WFZULJ.js} +14 -11
- package/dist/{chunk-IW6ZHFTL.js → chunk-5PJOEE3N.js} +1 -1
- package/dist/{chunk-B45HLBA4.js → chunk-5ROGATP4.js} +2 -0
- package/dist/{chunk-2PBOTZ4S.js → chunk-A7U5NBQK.js} +2 -2
- package/dist/{chunk-B4Z3CFDJ.js → chunk-B273FCFO.js} +1 -1
- package/dist/{chunk-RYJDNCAY.js → chunk-ENI3G4RS.js} +2 -2
- package/dist/{chunk-374SMZYV.js → chunk-JIG2WCRH.js} +2 -2
- package/dist/{chunk-U53SDEP3.js → chunk-KXV23662.js} +2 -2
- package/dist/chunk-NRVAJJKN.js +113 -0
- package/dist/{chunk-ALFMIDWW.js → chunk-OG6UKRFZ.js} +1 -1
- package/dist/{chunk-4EJRJ3QP.js → chunk-PSUX3M3P.js} +1 -1
- package/dist/{chunk-6ER757ZM.js → chunk-PXYOVG56.js} +6 -3
- package/dist/{chunk-O5IKAXWI.js → chunk-UEG2I3VY.js} +51 -29
- package/dist/cli.js +207 -191
- package/dist/console.d.ts +1 -1
- package/dist/console.js +5 -5
- package/dist/dom-stubs.d.ts +2 -1
- package/dist/dom-stubs.js +41 -1
- package/dist/eslint-plugin.cjs +281 -22
- package/dist/eslint-plugin.d.cts +22 -1
- package/dist/eslint-plugin.d.ts +22 -1
- package/dist/{expect-emission-H8lAQz2f.d.ts → expect-emission-BUdHCyYJ.d.ts} +138 -3
- package/dist/index.d.ts +5 -5
- package/dist/index.js +127 -55
- package/dist/jasmine-compat.d.ts +1 -1
- package/dist/jasmine.d.ts +2 -2
- package/dist/jasmine.js +8 -8
- package/dist/nestjs.d.ts +2 -2
- package/dist/nestjs.js +9 -9
- package/dist/node.cjs +141 -67
- package/dist/node.d.ts +5 -5
- package/dist/node.js +8 -8
- package/dist/{package-identity-B3aSBJRI.d.ts → package-identity-DDylC3ri.d.ts} +1 -1
- package/dist/{prop-mock-DSWMTtRE.d.ts → prop-mock-sa1PC4xj.d.ts} +1 -1
- package/dist/react.d.ts +5 -5
- package/dist/react.js +9 -9
- package/dist/rstest.d.ts +5 -5
- package/dist/rstest.js +9 -9
- package/dist/rxjs.d.ts +2 -2
- package/dist/rxjs.js +11 -4
- package/dist/setup.d.ts +24 -60
- package/dist/setup.js +35 -30
- package/dist/svelte.d.ts +5 -5
- package/dist/svelte.js +9 -9
- package/dist/{track-injections-Bh7AZ2-0.d.ts → track-injections-D1KdfjXr.d.ts} +1 -1
- package/dist/{types-DYGoBQGg.d.ts → types-CrmNZKfd.d.ts} +51 -1
- package/dist/vue.d.ts +6 -6
- package/dist/vue.js +10 -10
- package/dist/web-storage-dwQ9E7Lm.d.ts +83 -0
- package/dist/{zoneless-CvgjkxKw.d.ts → zoneless-CBxpxspb.d.ts} +17 -4
- package/package.json +11 -1
- package/skills/vitest-auto-spy/SKILL.md +35 -9
- package/dist/chunk-RZRKEUE6.js +0 -15
package/AGENTS.md
CHANGED
|
@@ -57,9 +57,10 @@ Add-ons, orthogonal to the runner:
|
|
|
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
59
|
| Console spies | `vitest-auto-spy/console` | silent typed spies over the global `console` — `installConsoleSpies()` per test, `restoreConsole()` after |
|
|
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** |
|
|
60
|
+
| 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** |
|
|
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 |
|
|
63
|
+
| Angular router | `vitest-auto-spy/angular-router` | `provideActivatedRoute`, `injectActivatedRoute` — an `ActivatedRoute` whose streams and snapshot share one record (§13). Optional `@angular/router` peer, this entry only |
|
|
63
64
|
| Setup helpers | `vitest-auto-spy/setup` | `setupAutoSpy()`, `setupFakeTimers()` |
|
|
64
65
|
| Zone patch | `import 'vitest-auto-spy/zone'` | `fakeAsync` / `waitForAsync` on Vitest (§14) |
|
|
65
66
|
| jasmine compat | `vitest-auto-spy/jasmine` | `.and` / `.calls` / `.withArgs`, the `jasmine` namespace (§20) |
|
|
@@ -73,8 +74,8 @@ so the specifier moved to the root `rxjs` entry — which is where rxjs re-expor
|
|
|
73
74
|
the floor moved with it. Any Angular project already satisfies it: Angular 16 through 22 all peer on
|
|
74
75
|
`^6.5.3 || ^7.4.0`.
|
|
75
76
|
|
|
76
|
-
The Angular entries need **Angular >= 20** — `@angular/core`, `@angular/common
|
|
77
|
-
`@angular/platform-browser`, all optional peers on the same range. Below 20 the failure is a link
|
|
77
|
+
The Angular entries need **Angular >= 20** — `@angular/core`, `@angular/common`,
|
|
78
|
+
`@angular/platform-browser` and `@angular/router`, all optional peers on the same range. Below 20 the failure is a link
|
|
78
79
|
error, not a missing helper: `/angular` imports `ɵSIGNAL` (Angular 18+) as a value on the first line
|
|
79
80
|
of its bundle, so on 16 and 17 the whole entry fails to load, and `/bun-angular` imports
|
|
80
81
|
`provideZonelessChangeDetection` (Angular 20+; called `provideExperimentalZonelessChangeDetection`
|
|
@@ -173,8 +174,8 @@ out, and the one that costs an afternoon otherwise. `mock.repo.user.find()` chai
|
|
|
173
174
|
but the last is a _read_. A node that is **called** returns what it was configured to return, and by
|
|
174
175
|
default that is `undefined`, so `mockDeep<AppLogger>().channel('app').info('x')` is a `TypeError` at
|
|
175
176
|
the second call — while `DeepMockProxy<AppLogger>` types it perfectly, so nothing warns. Pass
|
|
176
|
-
`{ selfReturning: true }` for a fluent API, or use
|
|
177
|
-
`channel
|
|
177
|
+
`{ selfReturning: true }` for a fluent API, or use
|
|
178
|
+
`createAutoMock<T>(undefined, { selfReturning: ['channel'] })` when only one method chains:
|
|
178
179
|
|
|
179
180
|
```ts
|
|
180
181
|
const logger = mockDeep<AppLogger>({}, { selfReturning: true });
|
|
@@ -557,6 +558,7 @@ createSpyFromClass(MyService, {
|
|
|
557
558
|
autoSpyAccessors: true, // discover every accessor on the prototype chain
|
|
558
559
|
lazySpies: true, // build each method spy on first access ('proxy' for very wide classes)
|
|
559
560
|
returns: { getProducts: of([]) }, // what a spied METHOD answers
|
|
561
|
+
selfReturning: ['where'], // a method that answers the double itself, for a chained call
|
|
560
562
|
overrides: { products$: subject }, // a member that is not a method result
|
|
561
563
|
});
|
|
562
564
|
```
|
|
@@ -570,6 +572,7 @@ createSpyFromClass(MyService, {
|
|
|
570
572
|
| `lazySpies` | Behaviour-identical; only changes _when_ each spy is built. `'proxy'` also changes _what holds the name_. |
|
|
571
573
|
| `strict` | Throw on a method nobody configured, instead of answering `undefined` (below). |
|
|
572
574
|
| `onUnstubbedCall` | The general form of `strict`; its return value becomes the call's return value. |
|
|
575
|
+
| `selfReturning` | The named methods answer the double itself — a default like `returns`; a name in both answers `returns`. |
|
|
573
576
|
|
|
574
577
|
**`instanceMethodsToSpyOn` is not an edge case — it is a top-5 option** (103 of ~370 spec files in
|
|
575
578
|
the reference suite). Method discovery walks the _prototype chain_; a callable assigned to an
|
|
@@ -707,6 +710,33 @@ registerAutoSpyDefaults([
|
|
|
707
710
|
A later row for a class an earlier row already named replaces it, exactly as a second call would.
|
|
708
711
|
`AutoSpyDefaultEntry<T>` is the row type, for a row built outside the literal.
|
|
709
712
|
|
|
713
|
+
**An `InjectionToken` is a key too — import `registerAutoSpyDefaults` from `vitest-auto-spy/angular`
|
|
714
|
+
for it.** Same registry, same merge; the core export takes a class only, because the core entry may
|
|
715
|
+
not mention Angular's types. `provideAutoSpyForToken(TOKEN)` reads the registration exactly as
|
|
716
|
+
`provideAutoSpy(Class)` reads a class's — both of its arguments are merged over it, and a registered
|
|
717
|
+
`returns` stays a default a later `calledWith` / `resolveWith` wins over:
|
|
718
|
+
|
|
719
|
+
```ts
|
|
720
|
+
import { registerAutoSpyDefaults } from 'vitest-auto-spy/angular';
|
|
721
|
+
|
|
722
|
+
registerAutoSpyDefaults(LOGGER, { returns: { info: undefined, err: undefined }, selfReturning: ['channel'] });
|
|
723
|
+
registerAutoSpyDefaults(NAVIGATION, { overrides: { activeRow$: of({}) }, returns: { setFocus: undefined } });
|
|
724
|
+
|
|
725
|
+
providers: [provideAutoSpyForToken(LOGGER), provideAutoSpyForToken(NAVIGATION, { activeRow$: rows$ })];
|
|
726
|
+
```
|
|
727
|
+
|
|
728
|
+
A token row holds `AutoSpyTokenDefaults<T>`: what `createAutoMock` takes (`returns`, `selfReturning`,
|
|
729
|
+
`observablePropsToSpyOn`, `strict`, `name`) plus `overrides`, the seeds `provideAutoSpyForToken` takes
|
|
730
|
+
second. Keys are checked against the token's `T`; a table may mix class rows and token rows;
|
|
731
|
+
`clearAutoSpyDefaults(TOKEN)` from the same entry drops one. Handing a token to the **core**
|
|
732
|
+
`registerAutoSpyDefaults` fails with `TS2345 … 'InjectionToken<X>' is not assignable to parameter of
|
|
733
|
+
type 'ClassType<unknown>'` — the fix is the import, not a cast.
|
|
734
|
+
|
|
735
|
+
**`selfReturning` names the methods that answer the double itself.** `returns` cannot say it — the
|
|
736
|
+
double does not exist when the literal is written — and without it a chained call dies on
|
|
737
|
+
`undefined`. It works on every factory and in a registration; a registered chain takes a link back out
|
|
738
|
+
through the call site's `returns`, since lists only ever union.
|
|
739
|
+
|
|
710
740
|
### `strict` — a method nobody configured throws instead of answering `undefined`
|
|
711
741
|
|
|
712
742
|
```ts
|
|
@@ -733,6 +763,7 @@ Precedence, first one set wins: the double's `onUnstubbedCall` → the double's
|
|
|
733
763
|
- **`returns:` is a default in the spy's own container**: a `calledWith` / `resolveWith` / `failWith`
|
|
734
764
|
configured later wins for its arguments or supersedes it, every other call still gets it, and
|
|
735
765
|
`undefined` counts as configured. `returns: { save: undefined }` is how a `void` call is expected.
|
|
766
|
+
`selfReturning: ['channel']` is the same kind of default, and counts as configured too.
|
|
736
767
|
- **`mockReturnValue` / `mockImplementation` replace the dispatch**, so they never reach the guard —
|
|
737
768
|
and a `calledWith` configured after them is never consulted. After the last `mockReturnValueOnce`
|
|
738
769
|
the queue empties back onto the library dispatch and the next call **is** reported as unstubbed —
|
|
@@ -745,6 +776,17 @@ Precedence, first one set wins: the double's `onUnstubbedCall` → the double's
|
|
|
745
776
|
- **It does not reach** accessor spies, observable-property spies, `mockDeep` nodes, `console-spy`,
|
|
746
777
|
`mockResourceProp`'s `reload` or a standalone `createFunctionSpy`. A strict double still answers
|
|
747
778
|
`undefined` for an unconfigured getter or `items$`. `fillMissing` members **are** covered.
|
|
779
|
+
- **The getter and the stream are reported after the test instead** — a read cannot throw, because a
|
|
780
|
+
failure diff that prints the double reads it. `setupAutoSpy({ unconfiguredReads: 'warn' | 'throw' })`
|
|
781
|
+
(default `'off'`, not in the preset) reports a strict double's getter read that reached no
|
|
782
|
+
configuration and a subscription to a stream nothing fed **by the end of the test** — subscribe in
|
|
783
|
+
`beforeEach`, `nextWith` in the test is fine. Counted from setup's `beforeEach` to its `afterEach`,
|
|
784
|
+
so the spec's own hooks count. Configured means `accessorSpies.getters.x.mockReturnValue(…)` /
|
|
785
|
+
`mockImplementation`, `overrides: { x }` (call site or registration — a registered _list_ alone is
|
|
786
|
+
not), `mockReadonlyProp`; for a stream any of `nextWith` / `throwWith` / `complete` /
|
|
787
|
+
`returnSubject`. `undefined` meant: `mockReturnValue(undefined)`. `onUnstubbedRead({ className,
|
|
788
|
+
member, kind, count })` — suite-wide or per double, same precedence as `onUnstubbedCall` — takes the
|
|
789
|
+
findings instead of the report, from every double not `strict: false`: survey with it first.
|
|
748
790
|
- **Angular lifecycle hooks are exempt.** `ngOnDestroy`, `ngOnInit`, `ngOnChanges`, `ngDoCheck` and the
|
|
749
791
|
`ngAfter…` hooks answer `undefined` on a strict double: Angular calls `ngOnDestroy` itself on every
|
|
750
792
|
provided value at teardown (a `createAutoMock` / `provideAutoSpyForToken` double always has one), and
|
|
@@ -832,6 +874,14 @@ const config = asSpy<FeatureFlagService>(TestBed.inject(FeatureFlagService)); //
|
|
|
832
874
|
const config = injectSpy<FeatureFlagService>(FeatureFlagService); // ✅
|
|
833
875
|
```
|
|
834
876
|
|
|
877
|
+
It applies to `createSpyFromClass` with a configuration too, in one combination: an accessor list
|
|
878
|
+
(or `overrides`) **and** `returns` on a generic class. TypeScript checks a generic class argument
|
|
879
|
+
after the configuration, reads `T` back from `gettersToSpyOn: ['remoteConfig']` as
|
|
880
|
+
`{ remoteConfig: any }`, and fails with `'isKeyEnabled' does not exist in type
|
|
881
|
+
'MethodReturns<{ remoteConfig: any; }>'`. Spell it out — `createSpyFromClass<RemoteConfigService>(…)`.
|
|
882
|
+
`provideAutoSpy`, `overrideAutoSpy` and `overrideComponentProvider` (`/angular`) take `T` from the
|
|
883
|
+
class alone, so there the inferred form compiles.
|
|
884
|
+
|
|
835
885
|
**A declared default now reaches the double, and used not to.** `class RemoteConfigService<T =
|
|
836
886
|
RemoteConfigDefaults>` handed to `createSpyFromClass` / `injectSpy` inferred `T` as `unknown`, so
|
|
837
887
|
every member typed against it read as `unknown` on a class that had said exactly what it should be.
|
|
@@ -1610,7 +1660,8 @@ setupAutoSpy({ preset: 'strict' });
|
|
|
1610
1660
|
Sets `duplicateCopies`, `propsOutsideHooks`, `guardGlobals`, `prototypePollution`, `strayConsole` and
|
|
1611
1661
|
`misconfiguration` to `'throw'`, turns `strayTimers` on, and `strayRejections` on when zone.js is
|
|
1612
1662
|
loaded. An option passed alongside still wins. **Not** included: `strict` (strict doubles change what
|
|
1613
|
-
an unconfigured call returns — a semantic switch, not a grade; the name was taken, hence `preset`)
|
|
1663
|
+
an unconfigured call returns — a semantic switch, not a grade; the name was taken, hence `preset`) and
|
|
1664
|
+
`unconfiguredReads`, its read side (survey with `onUnstubbedRead` before turning it on),
|
|
1614
1665
|
`blockNetwork` (changes the code under test), `restoreMocks` (drops `beforeAll` spies), failing on
|
|
1615
1666
|
stray-timer counts (the sweep fails the file from `afterAll`, and a callback scheduled after the
|
|
1616
1667
|
previous file's sweep is charged to the next — opt in with
|
|
@@ -1750,6 +1801,34 @@ State is per element, so an ad and the content report different durations.
|
|
|
1750
1801
|
the record and fires `timeupdate` too — `media.set()` is not the only way in, and the component's own
|
|
1751
1802
|
handler runs where it used to stay unrun while the assertion read the new value.
|
|
1752
1803
|
|
|
1804
|
+
### `localStorage` and `sessionStorage`
|
|
1805
|
+
|
|
1806
|
+
Do not write a `TestingStorage` class per project. `stubWebStorage()` installs an in-memory `Storage`
|
|
1807
|
+
for this test — `getItem` / `setItem` / `removeItem` / `clear` / `key` / `length`, string coercion
|
|
1808
|
+
as the platform does it — and `snapshot()` is the plain record to assert on:
|
|
1809
|
+
|
|
1810
|
+
```ts
|
|
1811
|
+
import { stubWebStorage, type WebStorageStub } from 'vitest-auto-spy/dom-stubs';
|
|
1812
|
+
|
|
1813
|
+
let local: WebStorageStub;
|
|
1814
|
+
|
|
1815
|
+
beforeEach(() => {
|
|
1816
|
+
local = stubWebStorage('localStorage', { items: { token: 'abc' } }); // or 'sessionStorage'
|
|
1817
|
+
});
|
|
1818
|
+
|
|
1819
|
+
it('forgets the token on logout', () => {
|
|
1820
|
+
session.logout();
|
|
1821
|
+
expect(local.snapshot()).toEqual({});
|
|
1822
|
+
});
|
|
1823
|
+
```
|
|
1824
|
+
|
|
1825
|
+
It goes through `mockValueProp`, so `restoreMockedProps()` (and `setupAutoSpy()` between tests) puts
|
|
1826
|
+
the previous storage back; install it in `beforeEach`. It also lands on `document.defaultView` when
|
|
1827
|
+
that is a separate object. It is not `restoreWebStorage()`: that one repairs a broken environment
|
|
1828
|
+
once and leaves a working storage alone; this one replaces whatever is there, for one test, and
|
|
1829
|
+
installs even in a `node` environment because the spec asked for it. Named-property access
|
|
1830
|
+
(`localStorage.token`, `Object.keys(localStorage)`) does not see the items — read through the API.
|
|
1831
|
+
|
|
1753
1832
|
### A module mock that did nothing
|
|
1754
1833
|
|
|
1755
1834
|
`vi.mock()` is the one thing in a ported suite that fails **silently**. Under a bundler
|
|
@@ -2160,8 +2239,9 @@ and the next line reads the settled value.
|
|
|
2160
2239
|
|
|
2161
2240
|
Three things to know before reaching for it:
|
|
2162
2241
|
|
|
2163
|
-
- **It does not re-export the core
|
|
2164
|
-
`vitest-auto-spy/angular`, which stays the import for spies, `TestBed` helpers and
|
|
2242
|
+
- **It does not re-export the core** — nor does `/angular-router`; every other subpath does. It is a
|
|
2243
|
+
companion to `vitest-auto-spy/angular`, which stays the import for spies, `TestBed` helpers and
|
|
2244
|
+
`settleResource`.
|
|
2165
2245
|
Staying narrow is what keeps `@angular/common` — an **optional** peer — out of every other entry.
|
|
2166
2246
|
- **`settleResource` is unchanged and still the answer** whenever the wait is not tied to one
|
|
2167
2247
|
request: a `resource()` with an async loader, an `rxResource`, a reload, anything not HTTP.
|
|
@@ -2170,6 +2250,52 @@ Three things to know before reaching for it:
|
|
|
2170
2250
|
suite using `provideHttpTesting()` everywhere the diagnostic is redundant; keep it on while any
|
|
2171
2251
|
file still configures HTTP testing by hand.
|
|
2172
2252
|
|
|
2253
|
+
### `ActivatedRoute` from one record — `vitest-auto-spy/angular-router`
|
|
2254
|
+
|
|
2255
|
+
```ts
|
|
2256
|
+
import { injectActivatedRoute, provideActivatedRoute } from 'vitest-auto-spy/angular-router';
|
|
2257
|
+
|
|
2258
|
+
TestBed.configureTestingModule({
|
|
2259
|
+
providers: [provideActivatedRoute({ params: { id: '7' }, queryParams: { tab: 'reviews' } })],
|
|
2260
|
+
});
|
|
2261
|
+
|
|
2262
|
+
const fixture = TestBed.createComponent(ProductPage);
|
|
2263
|
+
|
|
2264
|
+
injectActivatedRoute().setParams({ id: '8' }); // params + paramMap emit; snapshot.params already agrees
|
|
2265
|
+
fixture.detectChanges();
|
|
2266
|
+
```
|
|
2267
|
+
|
|
2268
|
+
**Do not hand-write an `ActivatedRoute` double, and do not `provideAutoSpy(ActivatedRoute)`.** Its
|
|
2269
|
+
streams and its snapshot are instance fields: the spy has neither, and a `useValue` has whichever half
|
|
2270
|
+
its author thought of — `route.paramMap.pipe(…)` throws on the `{ snapshot }` one, `route.snapshot`
|
|
2271
|
+
is `undefined` on the `{ params: of(…) }` one, and a spec that updates one half by hand tests a route
|
|
2272
|
+
no navigation produces. `provideActivatedRoute(init)` provides **Angular's own `ActivatedRoute`**
|
|
2273
|
+
built over one record, so every stream, both `ParamMap`s and `snapshot` read the same values.
|
|
2274
|
+
|
|
2275
|
+
| Call | Does |
|
|
2276
|
+
| -------------------------------------- | ------------------------------------------------------------------------------------------------------ |
|
|
2277
|
+
| `provideActivatedRoute(init?)` | a `FactoryProvider` — a fresh route per injector; `init`: `params`, `queryParams`, `data`, `fragment`, `url`, `outlet`, `component`, `routeConfig` |
|
|
2278
|
+
| `injectActivatedRoute(injector?)` | the handle: `.route`, `setParams`, `setQueryParams`, `setData`, `setFragment`, `setUrl`, `set({ … })` |
|
|
2279
|
+
| `createActivatedRoute(init?)` | the same handle without a `TestBed` — for `new Page(route)`, or a guard given `route.snapshot` |
|
|
2280
|
+
|
|
2281
|
+
A setter behaves like the router after a navigation: it **replaces** (not merges) that part, builds
|
|
2282
|
+
a new snapshot first, then emits only the streams whose value changed (router equality: same keys,
|
|
2283
|
+
arrays as sorted sets), in the order `queryParams`, `fragment`, `params`, `url`, `data`. Setting
|
|
2284
|
+
an equal value emits nothing; a string `url` is split on `/`.
|
|
2285
|
+
|
|
2286
|
+
Four things to know:
|
|
2287
|
+
|
|
2288
|
+
- **List it after `provideRouter()`** — the later provider of `ActivatedRoute` wins, and
|
|
2289
|
+
`injectActivatedRoute()` throws naming the one that did. With the real router present, the double
|
|
2290
|
+
still works as `relativeTo` for `router.createUrlTree`.
|
|
2291
|
+
- **One node, no tree.** `root` is the route itself, `parent` / `firstChild` are `null`. For
|
|
2292
|
+
`route.parent.params` patch that member with `mockReadonlyProp`, or use `RouterTestingHarness`.
|
|
2293
|
+
- **No `title`, no navigation, no input binding** — `title` emits `undefined`, `Router.navigate`
|
|
2294
|
+
does not move it (the setters do), and `withComponentInputBinding()` is the outlet's job
|
|
2295
|
+
(`fixture.componentRef.setInput`).
|
|
2296
|
+
- **Its own entry, like `/angular-http`.** It does not re-export the core, imports no runner, and is
|
|
2297
|
+
the only file that reaches `@angular/router` — an optional peer.
|
|
2298
|
+
|
|
2173
2299
|
### Which collaborators the code asked for — `trackInjections`
|
|
2174
2300
|
|
|
2175
2301
|
Do **not** reach for `vi.mock('@app/services')` to answer "was this collaborator used". Register the
|
|
@@ -2368,12 +2494,16 @@ spec — after which `Spy<Mock>` and `Spy<Interface>` disagree and somebody cast
|
|
|
2368
2494
|
**The second argument is not optional as often as it looks.** A spy answers `undefined` until it is
|
|
2369
2495
|
told otherwise, and that is fatal the moment the code under test _chains_ off it — a constructor
|
|
2370
2496
|
doing `inject(LOGGER).channel('auth').debug('…')` dies on the `.debug` of `undefined` before the
|
|
2371
|
-
spec's first line runs, because nothing in production wrote `?.` there.
|
|
2497
|
+
spec's first line runs, because nothing in production wrote `?.` there. Name the link:
|
|
2372
2498
|
|
|
2373
2499
|
```ts
|
|
2374
|
-
provideAutoSpyForToken(LOGGER, {
|
|
2500
|
+
provideAutoSpyForToken(LOGGER, undefined, { selfReturning: ['channel'] });
|
|
2375
2501
|
```
|
|
2376
2502
|
|
|
2503
|
+
`channel` stays a spy (assertable, and configured under `strict`) and answers the double itself.
|
|
2504
|
+
When every file wants it, register it once — `registerAutoSpyDefaults(LOGGER, { selfReturning:
|
|
2505
|
+
['channel'] })` from `vitest-auto-spy/angular` (§5) — and the provider is `provideAutoSpyForToken(LOGGER)`.
|
|
2506
|
+
|
|
2377
2507
|
For a chain more than one link long, `mockDeep<T>()` is the double that answers every level (§2).
|
|
2378
2508
|
|
|
2379
2509
|
### A host for a directive under test
|
|
@@ -2401,6 +2531,33 @@ _is_ declared; `NG0304` calls a missing directive a missing component; a bare at
|
|
|
2401
2531
|
nothing at all). `schemas: [NO_ERRORS_SCHEMA]` next to a standalone component is a dead entry —
|
|
2402
2532
|
schemas apply to a testing module's `declarations` only.
|
|
2403
2533
|
|
|
2534
|
+
### A stand-in for a child — `createComponentStub`
|
|
2535
|
+
|
|
2536
|
+
Do not hand-write `class MockChartComponent` with a copied selector: nothing checks the copy, so a
|
|
2537
|
+
renamed input leaves the parent's binding pointing at nothing and the spec green. Build the stub
|
|
2538
|
+
from the real class's compiled definition instead:
|
|
2539
|
+
|
|
2540
|
+
```ts
|
|
2541
|
+
import { createComponentStub } from 'vitest-auto-spy/angular';
|
|
2542
|
+
|
|
2543
|
+
const ChartStub = createComponentStub(ChartComponent); // also a directive or a pipe
|
|
2544
|
+
|
|
2545
|
+
TestBed.configureTestingModule({ imports: [DashboardComponent] });
|
|
2546
|
+
TestBed.overrideComponent(DashboardComponent, { remove: { imports: [ChartComponent] }, add: { imports: [ChartStub] } });
|
|
2547
|
+
|
|
2548
|
+
const chart = fixture.debugElement.query(By.directive(ChartStub)).componentInstance;
|
|
2549
|
+
expect(chart.series()).toEqual([1, 2, 3]); // a signal input stays a signal input
|
|
2550
|
+
chart.pointSelected.emit(2); // outputs are EventEmitters; a model() stays a model
|
|
2551
|
+
```
|
|
2552
|
+
|
|
2553
|
+
Copied from `ɵcmp` / `ɵdir` / `ɵpipe`: the selector, every input under its public name (transforms
|
|
2554
|
+
included), every output, `exportAs`, a pipe's name and purity. Not copied: the template (the stub
|
|
2555
|
+
renders one `<ng-content>` per slot the real one projects — `{ template }` in the third argument
|
|
2556
|
+
replaces it), host bindings, providers, lifecycle hooks, queries. The second argument seeds members
|
|
2557
|
+
per instance — a method the parent calls through `viewChild`, a pipe's `transform` (identity by
|
|
2558
|
+
default). With `renderShallow`: `renderShallow(Parent, { keepTemplate: true, keepChildren: [ChartStub] })`
|
|
2559
|
+
— the real child is dropped as a component import and the stub is the one child left.
|
|
2560
|
+
|
|
2404
2561
|
### Patching a property of a spy
|
|
2405
2562
|
|
|
2406
2563
|
`mockReadonlyProp` / `mockValueProp` / `mockSignalProp` accept the `Spy<T>` that `injectSpy` returns,
|
|
@@ -2644,17 +2801,22 @@ blanket downgrade so those keep their severity; do not copy the two names into a
|
|
|
2644
2801
|
| `no-passthrough-console-spy` | `error` | suggest | `vi.spyOn(console, m)` nothing gives an implementation — it calls through and prints → `installConsoleSpies()` + `consoleXSpy`, or `.mockImplementation(() => undefined)` |
|
|
2645
2802
|
| `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` |
|
|
2646
2803
|
| `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` |
|
|
2647
|
-
| `no-mistyped-use-value` | `error` | — | `{ provide: TOKEN, useValue }` whose value is not assignable to the primitive `T` of `InjectionToken<T>` (string, number, boolean, bigint, enum, their literals, `null`, `undefined`) — `useValue` is `any`, so `{}` for a `boolean` token compiles and is truthy. **Type-aware**: silent without `parserOptions.project`; object-typed tokens are
|
|
2804
|
+
| `no-mistyped-use-value` | `error` | — | `{ provide: TOKEN, useValue }` whose value is not assignable to the primitive `T` of `InjectionToken<T>` (string, number, boolean, bigint, enum, their literals, `null`, `undefined`) — `useValue` is `any`, so `{}` for a `boolean` token compiles and is truthy. **Type-aware**: silent without `parserOptions.project`; object-typed tokens are `no-unknown-use-value-key`'s |
|
|
2805
|
+
| `no-unknown-use-value-key` | `error` | — | a key of an object `useValue` literal the provided type does not have — `T` of `InjectionToken<T>`, or the instance type of a `provide:` class; `{ provide: ActivatedRoute, useValue: { queryParams$: … } }` compiles, since `useValue` is `any`. Keys only, never assignability (a partial fixture is fine). **Type-aware**; silent on `any` / `unknown` / `object`, an index signature, a spread's keys, `multi: true` |
|
|
2648
2806
|
| `no-instance-lifecycle-spy` | `warn` | — | `vi.spyOn(instance, 'ngOnInit')` (and `ngOnDestroy`, `ngDoCheck`, `ngAfterContent*`, `ngAfterView*`) — a view calls the hook read off the prototype, so the instance spy is never called and its stub never runs → `vi.spyOn(Cls.prototype, …)` before `createComponent`, or assert the effect. `ngOnChanges` is exempt: Angular calls it through the instance |
|
|
2807
|
+
| `no-ts-expect-error-on-double` | `error` | — | `@ts-expect-error` / `@ts-ignore` above `nextWith`, `resolveWith`, `mockReturnValue`, `returnValue`, `calledWith(…)` and the other helpers that check a stub against the method's signature → an overloaded method takes `Spy<X, { overload: { m: 'first' } }>`; otherwise the fixture is the wrong shape, checked against `ReturnType<X['m']>`. A reason after the directive does not silence it; a deliberate out-of-type value keeps it under `eslint-disable-next-line … -- <why>` |
|
|
2808
|
+
| `no-constant-expect` | `error` | — | `expect(true).toBe(true)`, `expect({ … }).toBeDefined()` — a value the spec spelled out, under a matcher whose answer it already fixes (`toBe` / `toEqual` / `toStrictEqual` against a literal; `toBeTruthy`, `toBeDefined`, `toBeNull`… for any literal) → assert on what the code produced, or `expect.fail(…)` for an unreachable branch |
|
|
2809
|
+
| `no-compile-components` | `error` | suggest | `compileComponents()` under a builder that inlines `templateUrl` / `styleUrls` — a promise already settled → delete it, and the `async` of a hook that awaits nothing else. **Silent until** `['error', { builder: 'inline-resources' }]`: under a JIT setup that loads resources at run time the call is load-bearing |
|
|
2649
2810
|
| `jasmine-namespace-without-entry` | `error` | — | `.and` / `.calls` / `.withArgs` on a library spy in a file that installs the compat layer nowhere — option: `{ setupModules: […] }` |
|
|
2650
2811
|
| `no-jasmine-globals` | `error` | — | `jasmine.*`, bare `spyOn(` / `spyOnProperty(` / `spyOnAllFunctions(` / `fail(` / `pending(`, `.withContext(` |
|
|
2651
2812
|
| `no-save-arguments-by-value` | `error` | — | `spy.calls.saveArgumentsByValue()` — a no-op here, so the spec silently asserts on post-mutation state |
|
|
2652
2813
|
| `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 |
|
|
2653
2814
|
|
|
2654
|
-
Thirty rules, **every one an `error` since 4.0.0 except `prefer-render-shallow`,
|
|
2655
|
-
`no-stub-class-double`, `no-structural-double` and `no-instance-lifecycle-spy`**; three fix on their own,
|
|
2656
|
-
read types, and
|
|
2657
|
-
rather than guessing.
|
|
2815
|
+
Thirty-four rules, **every one an `error` since 4.0.0 except `prefer-render-shallow`,
|
|
2816
|
+
`no-stub-class-double`, `no-structural-double` and `no-instance-lifecycle-spy`**; three fix on their own, ten offer suggestions. Thirty-one are syntactic; `no-private-member-access`, `no-mistyped-use-value` and
|
|
2817
|
+
`no-unknown-use-value-key` read types, and all three report nothing at all without `parserOptions.project` / `projectService`
|
|
2818
|
+
rather than guessing. `no-compile-components` waits the same way for a fact no file holds — which
|
|
2819
|
+
builder the project has — and reports nothing until `{ builder: 'inline-resources' }` states it. The config used to be a graded mix of `error` / `warn` / `off`, which decided for the
|
|
2658
2820
|
consumer how much each finding mattered — a `warn` nothing reads is `off` with extra output. The
|
|
2659
2821
|
four `warn`s left are not judgements about how much a finding matters. `prefer-render-shallow` is
|
|
2660
2822
|
about the _kind_ of finding: every other rule names something wrong or dead, while this one names a
|
|
@@ -2787,6 +2949,18 @@ written above it inside a `createComponent` helper. Configure the double after t
|
|
|
2787
2949
|
(`injectSpy(X)` in the test), or keep the access lazy: `const api = () => injectSpy(Api)`. The check
|
|
2788
2950
|
is order-free by design, since a helper declared above the hook still runs after it.
|
|
2789
2951
|
|
|
2952
|
+
`no-ts-expect-error-on-double` reports the suppression, not the reason written after it. On one
|
|
2953
|
+
1759-file suite it reports 34 directives in 15 files and **every one carries a reason** — so an
|
|
2954
|
+
escape for "a directive with a reason" would have silenced all of them. Four sat on overloaded
|
|
2955
|
+
clients, which `overload` repairs (§5); seven blamed "the collapsed generic" for a fixture the real
|
|
2956
|
+
instantiation rejects as well; nineteen hid a fixture or a production type that disagrees with the
|
|
2957
|
+
declared one. Four were deliberate — a value outside the declared type, to reach a default branch —
|
|
2958
|
+
and that is the case a per-line disable is for:
|
|
2959
|
+
`// eslint-disable-next-line vitest-auto-spy/no-ts-expect-error-on-double -- <why>` above the
|
|
2960
|
+
directive. The report sits on the directive's line so that comment reaches it. `rejectWith`,
|
|
2961
|
+
`failWith` and `throwWith` are not read: their parameter is `unknown`, so a directive there
|
|
2962
|
+
suppresses something other than the stub.
|
|
2963
|
+
|
|
2790
2964
|
The legacy `.eslintrc` `plugins: []` form cannot work — it resolves names to `eslint-plugin-*`
|
|
2791
2965
|
packages, which a subpath export can never be.
|
|
2792
2966
|
|
|
@@ -2811,6 +2985,7 @@ packages, which a subpath export can never be.
|
|
|
2811
2985
|
| `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 |
|
|
2812
2986
|
| `advanceTimers() requires fake timers` | no fake timers installed | `setupFakeTimers()` or `vi.useFakeTimers()` first |
|
|
2813
2987
|
| `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) |
|
|
2988
|
+
| `X.member was read N times and nothing configured it, and strict mode is on` (or `was subscribed to N times and nothing fed it`), after the test | `setupAutoSpy({ unconfiguredReads })` — the code under test read a strict double's spied getter and got `undefined`, or subscribed to its observable property and nothing ever emitted | configure it: `accessorSpies.getters.member.mockReturnValue(…)`, `overrides: { member }`, `mockReadonlyProp`; `nextWith` / `returnSubject` / `complete` for a stream; `mockReturnValue(undefined)` when `undefined` is the answer meant; `{ strict: false }` on that double (§5) |
|
|
2814
2989
|
| `[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 |
|
|
2815
2990
|
| `… 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 |
|
|
2816
2991
|
| `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` |
|
|
@@ -2855,6 +3030,8 @@ packages, which a subpath export can never be.
|
|
|
2855
3030
|
| `TS2739` / `TS2740` / `TS2345` with `Spy<X>` on the left | a spy handed to an API typed against the class | `asInstance(spy)` (§6) |
|
|
2856
3031
|
| `is missing the following properties: _private, …` | declared as Vitest's `Mocked<T>` | declare `Spy<T>` (§6) |
|
|
2857
3032
|
| `AddPromiseSpyMethods<unknown>` vs `WithMockReturnValue<…>` | a generic class inferred as `Service<any>` | `asSpy<Service>(…)` / `injectSpy<Service>(…)` (§6) |
|
|
3033
|
+
| `createComponentStub(): X carries no ɵcmp, ɵdir or ɵpipe to copy` | the class is not an Angular declaration, or the import resolved to `undefined` (a barrel split across chunks) | pass the `@Component` / `@Directive` / `@Pipe` class, imported from its own file (§13) |
|
|
3034
|
+
| `'isKeyEnabled' does not exist in type 'MethodReturns<{ remoteConfig: any; }>'` | generic class: `createSpyFromClass(Config, { gettersToSpyOn: ['remoteConfig'], returns: { … } })` reads `T` back from the list, because the generic class argument is checked after the configuration | generic class: spell out the type argument — `createSpyFromClass<Config>(Config, …)`; `provideAutoSpy` from `/angular` infers it (§6) |
|
|
2858
3035
|
| `'addEventListener' called on an object that is not a valid instance of EventTarget` | Node's `AbortSignal` under jsdom + zone.js | `stubAbortController()` (§12) |
|
|
2859
3036
|
| `vi.requireMock is not a function` | a mechanical `jest.` → `vi.` rename | there is no equivalent — provide the double through the TestBed instead |
|
|
2860
3037
|
| a `vi.mock()` factory that never applies, only sometimes | under `isolate: false` the module was already in the worker's graph | do not mock it; inject the dependency, or `vi.hoisted()` + a real seam |
|
|
@@ -2871,7 +3048,7 @@ packages, which a subpath export can never be.
|
|
|
2871
3048
|
| a spec dying on the runner's 5 s file timeout right after `await stable(fixture)` | the fixture never stabilised — an unflushed request, a real `setInterval` | `stable` now fails at 2000 ms naming the cause; raise `{ timeout }` only once neither is true |
|
|
2872
3049
|
| `flushEventLoopUntil` timing out on the **first** such test only, the rest green | a cold dynamic `import()` outran the turn budget; later tests hit the module cache | `await settleDynamicImport(() => import('…'))` — await the module, do not count turns |
|
|
2873
3050
|
| `Expected to be running in 'ProxyZone', but it was not found` | `zone.js/testing` patches jasmine/mocha/jest, not Vitest | `import 'vitest-auto-spy/zone'` after zone.js, with `globals: true` (§14) |
|
|
2874
|
-
| `nextWith` demanding `HttpEvent<T>` on a generated API client | `Parameters`/`ReturnType` read the **last** overload | `asSpy<Client, { overload: 'first' }>(…)`, or `Overload<M, 0>`
|
|
3051
|
+
| `nextWith` demanding `HttpEvent<T>` on a generated API client | `Parameters`/`ReturnType` read the **last** overload | `asSpy<Client, { overload: 'first' }>(…)`, or `Overload<M, 0>` — never `@ts-expect-error`, which `no-ts-expect-error-on-double` reports (§16) |
|
|
2875
3052
|
| `NG0303` / `NG0304` / nothing at all, from a directive spec | the host is `standalone: false`, or the module is in the TestBed | `createDirectiveHost({ template, scope: [Module] })` (§13) |
|
|
2876
3053
|
| `TS2540: Cannot assign to 'X' because it is a read-only property` | a `readonly` field of an object under test | `mockValueProp(obj, 'X', value)` — on a class **getter**, `mockReadonlyProp` |
|
|
2877
3054
|
| `TypeError: Cannot read properties of undefined (reading 'mockReturnValue')` | the member is an **instance field**, not on the prototype — `Router.currentNavigation` since Angular 20 | `provideAutoSpy(Router, { instanceMethodsToSpyOn: ['currentNavigation'] })` (§5) |
|
|
@@ -2892,10 +3069,16 @@ packages, which a subpath export can never be.
|
|
|
2892
3069
|
| `createNestUnit(X).spies.get(Y): the unit never asked for that token` | same guard as `createWithAutoSpies` — the spy would not be the one the unit uses | stub the token the unit actually injects (the message lists the auto-spied ones) |
|
|
2893
3070
|
| `createNestUnit(X).spies.get(Y): Y is in \`expose\`, so the unit got a real instance` | an exposed class is real, not a spy | read the spies of its own collaborators, or drop it from `expose` |
|
|
2894
3071
|
| `expect(component.ngOnInit).toHaveBeenCalled()` fails after `fixture.detectChanges()`, or a hook's `.mockImplementation` never runs | the spy was put on the **instance**; a view calls the hook it read off the class prototype when the component was created | `vi.spyOn(Cls.prototype, 'ngOnInit')` before `TestBed.createComponent`, or assert what the hook does — `no-instance-lifecycle-spy` (§16) |
|
|
3072
|
+
| `TS2345: Argument of type 'InjectionToken<X>' is not assignable to parameter of type 'ClassType<unknown>'`, on `registerAutoSpyDefaults` | the core entry's `registerAutoSpyDefaults` takes a class only — it may not name Angular's token type | import `registerAutoSpyDefaults` (and `clearAutoSpyDefaults`) from `vitest-auto-spy/angular`, which also takes an `InjectionToken` (§5) |
|
|
3073
|
+
| `selfReturning names 'x', which is not a spied method` | the name is not a method of the double — a typo, or a restricting `onlyMethodsToSpyOn` left it out; on a type-driven double, `then` and `constructor` are never spies | fix the name or the whitelist; the value is silently never returned otherwise (§5) |
|
|
3074
|
+
| a spec green over a `useValue` fixture whose key the code never reads — `queryParams$` on an `ActivatedRoute` | `useValue` is `any`, so a renamed or misspelled key of an object literal compiles | the member's real name — `no-unknown-use-value-key` (§16), with type information; or `provideAutoSpy(X, { overrides })`, which the compiler checks |
|
|
2895
3075
|
| a spec runs down the "browser" (or any flag-on) branch whatever its `useValue` says | `{ provide: IS_BROWSER, useValue: {} }` for an `InjectionToken<boolean>` — `useValue` is `any`, and an object is truthy | a value of the token's type (`useValue: false`) — `no-mistyped-use-value` (§16), with type information |
|
|
2896
3076
|
| `no request matched …` (with the requests that were made listed) | the URL, verb or predicate does not describe anything the code under test sent, or nothing subscribed | read the list in the message — it is the requests that were actually made; then fix the URL, add `{ method }`, or subscribe (§13) |
|
|
2897
3077
|
| `this TestBed has no HttpTestingController` | `expectRequest` / `expectNoRequest` on a module that never got the testing backend | `TestBed.configureTestingModule({ providers: [...provideHttpTesting()] })` from `vitest-auto-spy/angular-http` (§13) |
|
|
2898
3078
|
| `the test ended with N unanswered request(s)` | `provideHttpTesting({ verifyOnTeardown })` found a request the spec never flushed | answer it (`await expectRequest(url).flush(body)`), assert its absence (`expectNoRequest(url)`), or `{ verifyOnTeardown: false }` (§13) |
|
|
3079
|
+
| `injectActivatedRoute(): nothing provides ActivatedRoute here` | no `ActivatedRoute` provider in the injector it read | `provideActivatedRoute({ … })` from `vitest-auto-spy/angular-router` in `providers`; `injectActivatedRoute(fixture.debugElement.injector)` for a component-level one (§13) |
|
|
3080
|
+
| `the ActivatedRoute here is … not one provideActivatedRoute() built` | a later provider of `ActivatedRoute` won — `provideRouter()`, `RouterModule`, a `useValue`, `provideAutoSpy(ActivatedRoute)` | list `provideActivatedRoute()` last, or drop the other provider (§13) |
|
|
3081
|
+
| `provideActivatedRoute: the installed @angular/router does not wire ActivatedRoute …` | a router major builds `ActivatedRoute` with different internal constructors | report it with the `@angular/router` version; provide the route by hand until then (§13) |
|
|
2899
3082
|
|
|
2900
3083
|
**Seven of these are library defects, not spec ones.** On anything below 3.8.0, upgrade and change
|
|
2901
3084
|
nothing in the spec: `of(double)` never emitting; `accessorSpies.setters.X` still `undefined` after
|
|
@@ -2927,6 +3110,7 @@ diagnostic and is free to change: print it, never assert on it.
|
|
|
2927
3110
|
| `let s: MyService = createSpyFromClass(MyService)` | `let s: Spy<MyService> = …` |
|
|
2928
3111
|
| `createSpyFromClass(X) as unknown as X` | `asInstance(createSpyFromClass(X))` |
|
|
2929
3112
|
| `{ provide: X, useValue: { a: vi.fn(), b: vi.fn() } }` | `provideAutoSpy(X)` |
|
|
3113
|
+
| `{ provide: ActivatedRoute, useValue: { snapshot: { params } } }` | `provideActivatedRoute({ params })` — streams and snapshot from one record |
|
|
2930
3114
|
| `vi.spyOn(TestBed.inject(X), 'method')` | `injectSpy(X).method` |
|
|
2931
3115
|
| `vi.spyOn(component, 'ngOnInit')` after `createComponent` | `vi.spyOn(Cls.prototype, 'ngOnInit')` before it, or assert the effect |
|
|
2932
3116
|
| `{ provide: IS_BROWSER, useValue: {} }` for an `InjectionToken<boolean>` | `useValue: false` — a value of the type the token declares |
|
|
@@ -2964,6 +3148,8 @@ diagnostic and is free to change: print it, never assert on it.
|
|
|
2964
3148
|
| `.overrideProvider(X, provideAutoSpy(X))` (works, but says the wrong thing) | `.overrideProvider(X, overrideAutoSpy(X))` |
|
|
2965
3149
|
| `TestBed.overrideComponent` to swap a provider | `overrideComponentProvider(Cmp, X)` |
|
|
2966
3150
|
| `{ target, isIntersecting } as unknown as IntersectionObserverEntry` | `intersectionEntry(target, true)` |
|
|
3151
|
+
| `@Component({ selector: 'app-chart' }) class MockChartComponent { @Input() … }` | `createComponentStub(ChartComponent)` — the selector cannot drift |
|
|
3152
|
+
| a `TestingStorage` class assigned to `globalThis.localStorage` | `stubWebStorage('localStorage')`, restored with the other patches |
|
|
2967
3153
|
| an assertion containing a date, with no clock set | `mockSystemTime(iso)` first |
|
|
2968
3154
|
| `configureTestingModule` inside every `it()` | one per `describe` |
|
|
2969
3155
|
| `vi.mock('@angular/core')` to neutralise `effect()` | set the signals, `await stable(fixture)`, assert the result |
|