vitest-auto-spy 5.15.1 → 5.16.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 CHANGED
@@ -3189,7 +3189,7 @@ blanket downgrade so those keep their severity; do not copy the two names into a
3189
3189
  | `no-inject-before-override` | `error` | — | `TestBed.inject()` / `injectSpy()` / `renderShallow()` in a hook, in a suite that still calls `override*` |
3190
3190
  | `no-private-member-access` | `error` | — | `instance['privateMember']`, `(instance as any).privateMember` (and `as unknown as`, and a decoy type), `vi.spyOn(Object.getPrototypeOf(x), 'm')` → drive the member through the public API. **Type-aware**: silent without `parserOptions.project`, and silent on an index signature |
3191
3191
  | `no-dead-schemas` | `error` | — | `schemas` on a testing module with no `declarations` — the schema applies to nothing; the file decides, so a `declarations` in another `configureTestingModule` call silences it |
3192
- | `no-import-time-spread` | `error` | suggest | `export const x = [...Imported]` at module scope → a `TypeError` while the bundle loads |
3192
+ | `no-import-time-spread` | `error` | suggest | `export const x = [...Imported]` at module scope → a `TypeError`, or a silently empty object, while the bundle loads |
3193
3193
  | `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 |
3194
3194
  | `prefer-render-shallow` | `warn` | suggest | `TestBed.createComponent` in a file that never reads the template → `renderShallow(X)`; 0.24× the per-test cycle at 100 children |
3195
3195
  | `prefer-set-inputs` | `warn` | suggest | a run of `fixture.componentRef.setInput('title', v)` on one fixture → `await setInputs(fixture, { title: v })` — the name is resolved against the compiled definition before the first write (an undeclared one is an `NG0303` and no change) and the value is typed. The run collapses into one call and a `detectChanges()` under it goes; offered, not applied, because `stable()` ticks and a zone.js suite answers that with `NG0101` |
@@ -3281,7 +3281,12 @@ passes: `export const events = [...BaseEvents]` at module scope raises
3281
3281
  `Spread syntax requires ...iterable[Symbol.iterator] to be a function` when a shared chunk is
3282
3282
  evaluated while the binding it re-exports is still `undefined`. Safe under `tsc` and under a
3283
3283
  browser's ESM loader, which is why nothing else catches it. A function body and an instance field are
3284
- not reported (they run later); a `static` field is.
3284
+ not reported (they run later); a `static` field is. An **object** spread is reported through a second
3285
+ message, `noImportTimeSpreadObject`, because that half fails without failing: `[...undefined]` and
3286
+ `f(...undefined)` throw, `{ ...undefined }` is `{}`, so `{ ...ShelfItemTypeEnum, ...Local }` loads
3287
+ clean and every key it meant to copy reads `undefined` for the rest of the run. One message cannot
3288
+ carry both: a reader sent looking for `Spread syntax requires …` in an object spread finds no such
3289
+ error in the log and takes the report for a false positive.
3285
3290
 
3286
3291
  `prefer-as-spy` is the one a migration meets in bulk: a `jest-auto-spies` suite writes
3287
3292
  `TestBed.inject(X) as Spy<X>` once per injected double, and that cast fails here with `TS2352`.
@@ -3467,6 +3472,7 @@ packages, which a subpath export can never be.
3467
3472
  | `setInputs: … declares no input named …` | the key is not an input of that component — a typo, an input renamed since, or one of its own fields | set the names the message lists; a signal the component owns is moved through the signal (§13) |
3468
3473
  | `setInputs: X carries no ɵcmp, so there are no inputs to set` | the fixture's `componentType` is not a compiled `@Component` — a `@Directive` or `@Pipe` class, or one Angular never compiled (a barrel import that resolved to `undefined`) | pass a component fixture; a directive takes its inputs through the host element that applies it (`createDirectiveHost`), and for `undefined` import the class from its own file (§13) |
3469
3474
  | `TypeError: dependencies is not a function`, from `renderShallow({ keepTemplate: true })` | the compiled `ɵcmp.dependencies` is the flat array AOT emits when no cycle forced a factory, and the trim called it | upgrade — all three shapes (array, factory, `null`) are read; nothing to change in the spec |
3475
+ | `renderShallow(X, { keepTemplate: true }): … declared by an NgModule rather than standalone` | an AOT dependency list: ngtsc flattened an imported `NgModule`'s exported declarations into it, and Angular takes only standalone declarations and `NgModule`s in `imports` | drop `keepTemplate` when the spec reads TypeScript state only, or build the component with `TestBed` directly and keep its compiled scope — the declarations named are not the thing to change |
3470
3476
  | `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` |
3471
3477
  | `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) |
3472
3478
  | `throwWith` reaching the success branch first, with the previous test's value | the spy's `ReplaySubject(1)` outlived the test that filled it | upgrade; and `resetAutoSpy(spy)` in `beforeEach` when the TestBed is built in `beforeAll` |
@@ -3558,6 +3564,7 @@ diagnostic and is free to change: print it, never assert on it.
3558
3564
  | `it('x', (done) => …)` / `beforeEach((done) => …)` | `async` + `await` — Vitest passes a `TestContext`, not `done` |
3559
3565
  | `{ ...modelInstance, flag: true }` (drops every getter) | `withOverrides(modelInstance, { flag: true })` |
3560
3566
  | `if ('params' in link) … else throw` in every spec | `narrow.byKey(link, 'params')` |
3567
+ | `const x = a?.b; assert.exists(x); … x …` on every optional read | `narrow.defined(a?.b)` — it returns the value, so the narrowing sits in the expression |
3561
3568
  | five `asInstance(...)` in one call | `...asInstances(a, b, c, d, e)` |
3562
3569
  | `vi.stubGlobal('Image', vi.fn(() => ({ src: '' })))` | `stubConstructor(globalThis, 'Image', () => ({ src: '' }))` |
3563
3570
  | `Object.defineProperty(document, 'cookie', { value })` | `mockValueProp(document, 'cookie', value)` |
package/README.md CHANGED
@@ -2408,7 +2408,14 @@ it, or `null` for a component that imports nothing — and which one is not deci
2408
2408
  AOT: JIT always emits the factory, AOT emits the array unless a cycle between two components forces
2409
2409
  it to defer the read. All three are handled; calling the array unconditionally was a
2410
2410
  `TypeError: dependencies is not a function` on an AOT-compiled component with no cycle in its
2411
- imports. A child re-exported by an imported `NgModule` still renders — the module is kept whole.
2411
+ imports. A child re-exported by an imported `NgModule` still renders **under JIT**, where the module
2412
+ is in that list and is kept whole. Under AOT it is not: ngtsc resolves the module at compile time and
2413
+ flattens its exported declarations into the list, so a `standalone: false` pipe or directive is what
2414
+ arrives, and Angular takes only standalone declarations and `NgModule`s in `imports`. That scope
2415
+ cannot be rebuilt from what the definition holds, so `renderShallow` refuses it by name instead of
2416
+ letting Angular answer `The "X" pipe … is not standalone` — a message aimed at the pipe's author,
2417
+ about a pipe that is fine and that works under JIT. Drop `keepTemplate` when the spec reads
2418
+ TypeScript state only, or build the component with `TestBed` directly and keep its compiled scope.
2412
2419
  Reach for it when the spec reads
2413
2420
  something the template creates, and keep `keepChildren` for the handful of children it genuinely
2414
2421
  needs resolvable. Full write-up:
@@ -2882,7 +2889,7 @@ single-purpose utility you can pick up independently — they all ride on the sa
2882
2889
  | `captureArg<T>()` | core | Take hold of a callback or config the code under test built, instead of describing its shape — assertions only, never `calledWith` |
2883
2890
  | `explainSpy(spy, method?)` | `/diagnostics` | Every configured argument list next to every recorded call, each attributed to the config it hit — before anything failed |
2884
2891
  | `asInstances(...spies)` | core | `asInstance` for a whole argument list — one edit against one compiler error, not five |
2885
- | `narrow(value, guard)` / `narrow.byKey` / `narrow.observable` | core | The branch of a union a test knows it got, failing with the shape the value actually had |
2892
+ | `narrow(value, guard)` / `narrow.byKey` / `narrow.defined` / `narrow.observable` | core | The branch of a union a test knows it got, failing with the shape the value actually had |
2886
2893
  | `withOverrides(model, overrides?)` | core | A fixture from a model instance: its getters read once, as data — a spread drops them |
2887
2894
  | `compareTestRuns(a, b, root?)` | core | Whether a migration lost a test — the set of `file::name`, which matching counters cannot answer |
2888
2895
  | `provideAutoSpyForToken(TOKEN, overrides?)` | `/angular` | The provider for a dependency behind an `InjectionToken` — no stand-in class to write |
@@ -3460,7 +3467,7 @@ export can never be.
3460
3467
  | `no-inject-before-override` | `error` | — | `TestBed.inject()` / `injectSpy()` / `renderShallow()` in a hook, in a suite that still calls `override*` |
3461
3468
  | `no-private-member-access` | `error` | — | `instance['privateMember']`, `(instance as any).privateMember` and `vi.spyOn(Object.getPrototypeOf(x), 'm')` — the three spellings that get past `private`; **type-aware**, and silent without `parserOptions.project` |
3462
3469
  | `no-dead-schemas` | `error` | — | `schemas` on a testing module that declares nothing — a charm protecting nobody, and one that starts working the day `declarations` arrive |
3463
- | `no-import-time-spread` | `error` | suggest | `export const x = [...Imported]` at module scope → a `TypeError` while the bundle loads |
3470
+ | `no-import-time-spread` | `error` | suggest | `export const x = [...Imported]` at module scope → a `TypeError`, or a silently empty object, while the bundle loads |
3464
3471
  | `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 |
3465
3472
  | `prefer-render-shallow` | `warn` | suggest | `TestBed.createComponent` in a file that never reads the template → `renderShallow(X)`; 0.24× the per-test cycle at 100 children |
3466
3473
  | `prefer-set-inputs` | `warn` | suggest | a run of `fixture.componentRef.setInput('title', v)` on one fixture → `await setInputs(fixture, { title: v })`, which resolves every name against the compiled definition before it writes one — an undeclared name is an `NG0303` on the console and no change at all — and types the value. The suggestion collapses the run, drops a `detectChanges()` under it and makes the callback `async`; it is offered rather than applied, because `stable()` ticks and a zone.js suite can answer that with `NG0101` |
@@ -3542,7 +3549,12 @@ while a binding it re-exports is still `undefined`, so the spread throws
3542
3549
  `Spread syntax requires ...iterable[Symbol.iterator] to be a function`. An AST pass found exactly
3543
3550
  seven sites in an 8 673-file workspace, which is small enough to flag at the cursor. A function body
3544
3551
  and an instance field are deliberately not reported — they run later than the module does — while a
3545
- `static` field is.
3552
+ `static` field is. An **object** spread of the same binding gets a message of its own,
3553
+ because it fails without failing: `[...undefined]` throws, `{ ...undefined }` is `{}`, so
3554
+ `export const ItemType = { ...ShelfItemTypeEnum, ...Local }` loads clean and every key it meant to
3555
+ copy reads `undefined` for the rest of the run. Told to look for `Spread syntax requires …`, a
3556
+ reader finds no such error and takes the report for a false positive — so the object message opens
3557
+ by saying there is nothing to find.
3546
3558
 
3547
3559
  `no-unregistered-inject-spy` catches `injectSpy(X)` for a token nothing in the file
3548
3560
  registered as an auto-spy. What comes back is whatever Angular DI already had — the real service, or
package/dist/angular.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { o as AngularTokenProvider, A as AngularValueProvider, a as AutoSpiedInstance, C as ComponentInputs, b as CreateWithAutoSpiesOptions, R as RenderShallowOptions, c as ResourceStatusLike, d as RunCounter, S as SettleResourceOptions, e as ShallowRender, f as SpyRegistry, g as StableOptions, h as createWithAutoSpies, i as flushEffects, j as injectSpy, p as provideAutoSpy, q as provideAutoSpyForToken, r as renderShallow, k as runEffect, s as setInputs, l as settleResource, m as stable, t as trackEffectRuns, n as trackRecomputations } from './track-signal-runs-C-DgtCj9.js';
1
+ export { o as AngularTokenProvider, A as AngularValueProvider, a as AutoSpiedInstance, C as ComponentInputs, b as CreateWithAutoSpiesOptions, R as RenderShallowOptions, c as ResourceStatusLike, d as RunCounter, S as SettleResourceOptions, e as ShallowRender, f as SpyRegistry, g as StableOptions, h as createWithAutoSpies, i as flushEffects, j as injectSpy, p as provideAutoSpy, q as provideAutoSpyForToken, r as renderShallow, k as runEffect, s as setInputs, l as settleResource, m as stable, t as trackEffectRuns, n as trackRecomputations } from './track-signal-runs-BOfsdYsC.js';
2
2
  import { InjectionToken, Provider, Type, WritableSignal, Signal, ProviderToken, FactoryProvider, AbstractType, Injector, ValueProvider } from '@angular/core';
3
3
  import { A as AutoMockConfiguration, D as DeepReadonly } from './expect-emission-CmN9ADyN.js';
4
4
  export { C as CallbackSubscribable, E as EmissionObserver, b as EmissionOptions, c as EmissionSource, S as SubscribableLike, g as expectCompletion, h as expectEmission, i as expectEmissions, j as expectError, k as expectNoEmission, s as setEmissionTimeout } from './expect-emission-CmN9ADyN.js';
package/dist/angular.js CHANGED
@@ -3306,15 +3306,38 @@ function importsOf(definition2) {
3306
3306
  function isChildComponent(dependency) {
3307
3307
  return Reflect.get(dependency, "\u0275cmp") !== void 0;
3308
3308
  }
3309
- function keptScope(definition2, options) {
3309
+ var DECLARATION_KEYS = ["\u0275cmp", "\u0275dir", "\u0275pipe"];
3310
+ function refusedInImports(dependency) {
3311
+ if (Reflect.get(dependency, "\u0275mod") !== void 0) {
3312
+ return void 0;
3313
+ }
3314
+ const definition2 = DECLARATION_KEYS.map((key) => Reflect.get(dependency, key)).find((value) => value !== void 0);
3315
+ return definition2 !== void 0 && Reflect.get(definition2, "standalone") !== true ? dependency.name : void 0;
3316
+ }
3317
+ function assertScopeIsImportable(component, kept) {
3318
+ const refused = kept.map(refusedInImports).filter((name) => name !== void 0);
3319
+ if (refused.length === 0) {
3320
+ return;
3321
+ }
3322
+ throw new Error(
3323
+ withDocs(
3324
+ `[vitest-auto-spy] renderShallow(${component.name}, { keepTemplate: true }): ${refused.length} of its template dependencies are declared by an NgModule rather than standalone \u2014 ${refused.join(", ")} \u2014 and Angular takes only standalone declarations and NgModules in \`imports\`.
3325
+ Nothing is wrong with those declarations, and the same call works under JIT. This is an AOT dependency list: ngtsc resolves an imported NgModule at compile time and flattens its exported declarations into the component, so the module that would carry them is not in the list to keep. The scope cannot be rebuilt from what the definition holds.
3326
+ Drop \`keepTemplate\` when the spec reads TypeScript state only \u2014 that is the case \`renderShallow\` is for \u2014 or build ${component.name} with \`TestBed\` directly, which keeps its compiled scope untouched, and hold the cost down by seeding the services its children inject rather than by trimming the template.`,
3327
+ DOCS_LINKS.angular
3328
+ )
3329
+ );
3330
+ }
3331
+ function keptScope(component, definition2, options) {
3310
3332
  const kept = importsOf(definition2).filter((dependency) => !isChildComponent(dependency));
3311
3333
  kept.push(...options.keepChildren ?? NOTHING);
3334
+ assertScopeIsImportable(component, kept);
3312
3335
  return kept;
3313
3336
  }
3314
- function buildOverride(definition2, options) {
3337
+ function buildOverride(component, definition2, options) {
3315
3338
  const override = {};
3316
3339
  if (isStandalone3(definition2)) {
3317
- override.imports = options.keepTemplate ? keptScope(definition2, options) : options.keepChildren ?? NOTHING;
3340
+ override.imports = options.keepTemplate ? keptScope(component, definition2, options) : options.keepChildren ?? NOTHING;
3318
3341
  override.schemas = PERMISSIVE_SCHEMAS;
3319
3342
  }
3320
3343
  if (!options.keepTemplate) {
@@ -3341,7 +3364,7 @@ function renderShallow(component, options = {}) {
3341
3364
  // is right to report one, which made these two features of the same package cancel each other out.
3342
3365
  ...standalone ? {} : { schemas: PERMISSIVE_SCHEMAS }
3343
3366
  });
3344
- TestBed.overrideComponent(component, { set: buildOverride(definition2, options) });
3367
+ TestBed.overrideComponent(component, { set: buildOverride(component, definition2, options) });
3345
3368
  options.beforeCreate?.();
3346
3369
  const fixture = TestBed.createComponent(component);
3347
3370
  applyInputs(fixture, options.inputs);
@@ -3,7 +3,7 @@ export { ArgCaptor, AsInstances, AssertMockedOptions, ConstructorMock, Construct
3
3
  export { A as AutoMockConfiguration, a as AutoSpyDefaultEntry, C as CallbackSubscribable, E as EmissionObserver, b as EmissionOptions, c as EmissionSource, S as SubscribableLike, d as autoMocked, e as clearAutoSpyDefaults, f as createAutoMock, g as expectCompletion, h as expectEmission, i as expectEmissions, j as expectError, k as expectNoEmission, r as registerAutoSpyDefaults, s as setEmissionTimeout } from './expect-emission-CmN9ADyN.js';
4
4
  export { c as createFunctionSpy, d as describeDuplicateCopies, g as getPackageCopies } from './package-identity-lm-gqZAR.js';
5
5
  export { A as AccessorImplementations, O as OutsideHookReaction, R as RestoreProp, c as countMockedProps, m as mockAccessorsProp, a as mockReadonlyProp, b as mockReadonlyPropGetter, d as mockValueProp, r as reportPropsOutsideHooks, e as restoreMockedProps } from './prop-mock-DnYNBNJL.js';
6
- export { A as AngularValueProvider, a as AutoSpiedInstance, C as ComponentInputs, b as CreateWithAutoSpiesOptions, R as RenderShallowOptions, c as ResourceStatusLike, d as RunCounter, S as SettleResourceOptions, e as ShallowRender, f as SpyRegistry, g as StableOptions, h as createWithAutoSpies, i as flushEffects, j as injectSpy, p as provideAutoSpy, r as renderShallow, k as runEffect, s as setInputs, l as settleResource, m as stable, t as trackEffectRuns, n as trackRecomputations } from './track-signal-runs-C-DgtCj9.js';
6
+ export { A as AngularValueProvider, a as AutoSpiedInstance, C as ComponentInputs, b as CreateWithAutoSpiesOptions, R as RenderShallowOptions, c as ResourceStatusLike, d as RunCounter, S as SettleResourceOptions, e as ShallowRender, f as SpyRegistry, g as StableOptions, h as createWithAutoSpies, i as flushEffects, j as injectSpy, p as provideAutoSpy, r as renderShallow, k as runEffect, s as setInputs, l as settleResource, m as stable, t as trackEffectRuns, n as trackRecomputations } from './track-signal-runs-BOfsdYsC.js';
7
7
  import 'vitest';
8
8
  import '@angular/core';
9
9
  import '@angular/core/testing';
@@ -1,7 +1,7 @@
1
1
  import { stable, flushEffects } from './chunk-M5BULP7X.js';
2
2
  export { flushEffects, stable } from './chunk-M5BULP7X.js';
3
3
  import './chunk-M6VMOOZ2.js';
4
- export { asInstance, asInstances, asSpy, assertMocked, captureArg, createFixture, createFixtureFactory, createMock, createSpyClass, createSpyFromInstance, describeDuplicateCopies, expectCompletion, expectEmission, expectEmissions, expectError, expectNoEmission, flushEventLoop, flushEventLoopUntil, getPackageCopies, mockConstructor, mockDeep, moduleNamespace, narrow, restoreSpiedInstance, setEmissionTimeout, settleDynamicImport, stubConstructor, withOverrides } from './chunk-5XLI5JYC.js';
4
+ export { asInstance, asInstances, asSpy, assertMocked, captureArg, createFixture, createFixtureFactory, createMock, createSpyClass, createSpyFromInstance, describeDuplicateCopies, expectCompletion, expectEmission, expectEmissions, expectError, expectNoEmission, flushEventLoop, flushEventLoopUntil, getPackageCopies, mockConstructor, mockDeep, moduleNamespace, narrow, restoreSpiedInstance, setEmissionTimeout, settleDynamicImport, stubConstructor, withOverrides } from './chunk-4SJ7EGIY.js';
5
5
  import { currentSpecFile, mockValueProp } from './chunk-N3T7KWWA.js';
6
6
  export { countMockedProps, mockAccessorsProp, mockReadonlyProp, mockReadonlyPropGetter, mockValueProp, reportPropsOutsideHooks, restoreMockedProps } from './chunk-N3T7KWWA.js';
7
7
  import { createSpyForToken } from './chunk-DUFMKOKF.js';
@@ -317,15 +317,38 @@ function importsOf(definition) {
317
317
  function isChildComponent(dependency) {
318
318
  return Reflect.get(dependency, "\u0275cmp") !== void 0;
319
319
  }
320
- function keptScope(definition, options) {
320
+ var DECLARATION_KEYS = ["\u0275cmp", "\u0275dir", "\u0275pipe"];
321
+ function refusedInImports(dependency) {
322
+ if (Reflect.get(dependency, "\u0275mod") !== void 0) {
323
+ return void 0;
324
+ }
325
+ const definition = DECLARATION_KEYS.map((key) => Reflect.get(dependency, key)).find((value) => value !== void 0);
326
+ return definition !== void 0 && Reflect.get(definition, "standalone") !== true ? dependency.name : void 0;
327
+ }
328
+ function assertScopeIsImportable(component, kept) {
329
+ const refused = kept.map(refusedInImports).filter((name) => name !== void 0);
330
+ if (refused.length === 0) {
331
+ return;
332
+ }
333
+ throw new Error(
334
+ withDocs(
335
+ `[vitest-auto-spy] renderShallow(${component.name}, { keepTemplate: true }): ${refused.length} of its template dependencies are declared by an NgModule rather than standalone \u2014 ${refused.join(", ")} \u2014 and Angular takes only standalone declarations and NgModules in \`imports\`.
336
+ Nothing is wrong with those declarations, and the same call works under JIT. This is an AOT dependency list: ngtsc resolves an imported NgModule at compile time and flattens its exported declarations into the component, so the module that would carry them is not in the list to keep. The scope cannot be rebuilt from what the definition holds.
337
+ Drop \`keepTemplate\` when the spec reads TypeScript state only \u2014 that is the case \`renderShallow\` is for \u2014 or build ${component.name} with \`TestBed\` directly, which keeps its compiled scope untouched, and hold the cost down by seeding the services its children inject rather than by trimming the template.`,
338
+ DOCS_LINKS.angular
339
+ )
340
+ );
341
+ }
342
+ function keptScope(component, definition, options) {
321
343
  const kept = importsOf(definition).filter((dependency) => !isChildComponent(dependency));
322
344
  kept.push(...options.keepChildren ?? NOTHING);
345
+ assertScopeIsImportable(component, kept);
323
346
  return kept;
324
347
  }
325
- function buildOverride(definition, options) {
348
+ function buildOverride(component, definition, options) {
326
349
  const override = {};
327
350
  if (isStandalone(definition)) {
328
- override.imports = options.keepTemplate ? keptScope(definition, options) : options.keepChildren ?? NOTHING;
351
+ override.imports = options.keepTemplate ? keptScope(component, definition, options) : options.keepChildren ?? NOTHING;
329
352
  override.schemas = PERMISSIVE_SCHEMAS;
330
353
  }
331
354
  if (!options.keepTemplate) {
@@ -352,7 +375,7 @@ function renderShallow(component, options = {}) {
352
375
  // is right to report one, which made these two features of the same package cancel each other out.
353
376
  ...standalone ? {} : { schemas: PERMISSIVE_SCHEMAS }
354
377
  });
355
- TestBed.overrideComponent(component, { set: buildOverride(definition, options) });
378
+ TestBed.overrideComponent(component, { set: buildOverride(component, definition, options) });
356
379
  options.beforeCreate?.();
357
380
  const fixture = TestBed.createComponent(component);
358
381
  applyInputs(fixture, options.inputs);
package/dist/bun.d.ts CHANGED
@@ -551,6 +551,27 @@ type Subscribable<T> = [Extract<T, {
551
551
  }>] extends [never] ? T : Extract<T, {
552
552
  subscribe: Func;
553
553
  }>;
554
+ /**
555
+ * The narrowing every optional read needs: neither `null` nor `undefined`, handed back.
556
+ *
557
+ * ```ts
558
+ * const covers = narrow.defined(row.content?.covers);
559
+ *
560
+ * expect(covers.map((cover) => cover.code)).toEqual(['a', 'b']);
561
+ * ```
562
+ *
563
+ * `expect(value).toBeDefined()` and `assert.exists(value)` both assert this, and neither **returns**
564
+ * it — so under a strict spec type-check each optional read costs two statements and a local that
565
+ * exists for the narrowing alone (`const x = obj.member; assert.exists(x); … x …`). That shape
566
+ * multiplies: one suite hit it fifteen times across four files, and twice it grew a helper function
567
+ * per member of a stub. This returns the value, so the narrowing sits inside the expression that
568
+ * needed it.
569
+ *
570
+ * It is not a replacement for `assert.exists` where the *assertion* is the point of the test. The
571
+ * difference is which of the two the line is about: asserting that a value arrived, or reading a
572
+ * value the test already knows arrived.
573
+ */
574
+ declare function defined<T>(value: T, label?: string): NonNullable<T>;
554
575
  /**
555
576
  * The most common narrowing, without writing the guard: the branch that has this key.
556
577
  *
@@ -581,6 +602,8 @@ interface Narrow {
581
602
  <T>(value: T, predicate: (candidate: T) => boolean, label?: string): T;
582
603
  /** {@link byKey} */
583
604
  byKey: typeof byKey;
605
+ /** {@link defined} */
606
+ defined: typeof defined;
584
607
  /** {@link observable} */
585
608
  observable: typeof observable;
586
609
  }
package/dist/bun.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import './chunk-M6VMOOZ2.js';
2
- export { asInstance, asInstances, asSpy, assertMocked, captureArg, createFixture, createFixtureFactory, createMock, createSpyClass, createSpyFromInstance, describeDuplicateCopies, expectCompletion, expectEmission, expectEmissions, expectError, expectNoEmission, flushEventLoop, flushEventLoopUntil, getPackageCopies, mockConstructor, mockDeep, moduleNamespace, narrow, restoreSpiedInstance, setEmissionTimeout, settleDynamicImport, stubConstructor, withOverrides } from './chunk-5XLI5JYC.js';
2
+ export { asInstance, asInstances, asSpy, assertMocked, captureArg, createFixture, createFixtureFactory, createMock, createSpyClass, createSpyFromInstance, describeDuplicateCopies, expectCompletion, expectEmission, expectEmissions, expectError, expectNoEmission, flushEventLoop, flushEventLoopUntil, getPackageCopies, mockConstructor, mockDeep, moduleNamespace, narrow, restoreSpiedInstance, setEmissionTimeout, settleDynamicImport, stubConstructor, withOverrides } from './chunk-4SJ7EGIY.js';
3
3
  export { countMockedProps, mockAccessorsProp, mockReadonlyProp, mockReadonlyPropGetter, mockValueProp, reportPropsOutsideHooks, restoreMockedProps } from './chunk-N3T7KWWA.js';
4
4
  export { autoMocked, clearAutoSpy, clearAutoSpyDefaults, createAutoMock, createSpyFromClass, registerAutoSpyDefaults, resetAutoSpy } from './chunk-WAMFS3UI.js';
5
5
  import './chunk-OS4PTU6D.js';
@@ -554,6 +554,12 @@ var narrowValue = defineHelper((value, predicate, label) => {
554
554
  }
555
555
  return value;
556
556
  });
557
+ function defined(value, label = "a value that is neither null nor undefined") {
558
+ if (value === null || value === void 0) {
559
+ throw narrowingFailed(label, value);
560
+ }
561
+ return value;
562
+ }
557
563
  function byKey(value, key) {
558
564
  if (typeof value !== "object" || value === null || !(key in value)) {
559
565
  throw narrowingFailed(`an object with a '${String(key)}' property`, value);
@@ -568,6 +574,7 @@ function observable(value) {
568
574
  }
569
575
  var narrow = Object.assign(narrowValue, {
570
576
  byKey: defineHelper(byKey),
577
+ defined: defineHelper(defined),
571
578
  observable: defineHelper(observable)
572
579
  });
573
580
 
package/dist/cli.js CHANGED
@@ -2455,7 +2455,7 @@ function checkForeignPragma(graph) {
2455
2455
  }
2456
2456
 
2457
2457
  // src/cli/checks/export-map.generated.ts
2458
- var EXPORT_MAP_VERSION = "5.15.1";
2458
+ var EXPORT_MAP_VERSION = "5.16.0";
2459
2459
  var ENTRY_SPECIFIERS = "vitest-auto-spy vitest-auto-spy/bun vitest-auto-spy/bun-angular vitest-auto-spy/node vitest-auto-spy/rstest vitest-auto-spy/rxjs vitest-auto-spy/console vitest-auto-spy/dom-stubs vitest-auto-spy/diagnostics vitest-auto-spy/jasmine vitest-auto-spy/jasmine-compat vitest-auto-spy/observer-spy vitest-auto-spy/angular vitest-auto-spy/angular-http vitest-auto-spy/angular-router vitest-auto-spy/signal-forms vitest-auto-spy/nestjs vitest-auto-spy/react vitest-auto-spy/vue vitest-auto-spy/svelte vitest-auto-spy/setup vitest-auto-spy/zone vitest-auto-spy/eslint-plugin";
2460
2460
  var EXPORTED_BY = {
2461
2461
  AccessorImplementations: "0 1 2 3 4 12 17 18 19",
@@ -779,6 +779,8 @@ var SPY_FACTORIES = /* @__PURE__ */ new Set([
779
779
  "autoMocked",
780
780
  "createActivatedRoute",
781
781
  "createAutoMock",
782
+ "createComponentStub",
783
+ "createDirectiveHost",
782
784
  "createDocumentDouble",
783
785
  "createMock",
784
786
  "createRouterDouble",
@@ -904,6 +906,9 @@ function spreadOfImport(context, node) {
904
906
  const binding = findBinding(context.sourceCode.getScope(node), argument.name);
905
907
  return binding?.defs.some((definition) => definition.type === IMPORT_DEFINITION2) ? argument : void 0;
906
908
  }
909
+ function spreadFailureMode(node) {
910
+ return isObjectExpression(node.parent) ? "object" : "iterable";
911
+ }
907
912
  function isDeferred(node) {
908
913
  return isFunctionNode(node) || node.type === "PropertyDefinition" && Reflect.get(node, "static") !== true;
909
914
  }
@@ -3311,7 +3316,8 @@ var noImportTimeSpread = defineRule({
3311
3316
  description: "Do not spread an imported binding at module scope \u2014 inside a bundle it can still be undefined",
3312
3317
  hasSuggestions: true,
3313
3318
  messages: {
3314
- noImportTimeSpread: "This spreads `{{name}}`, a binding another module owns, while this module is still being evaluated. Under `tsc` and under a browser\u2019s ESM loader that is safe \u2014 nothing runs before its dependency. Inside one bundle it is not: the spec bundle emits shared chunks, a chunk can be evaluated while the binding it re-exports is still `undefined`, and `[...undefined]` throws `Spread syntax requires ...iterable[Symbol.iterator] to be a function` before a single test runs \u2014 on a tree whose every test passes. Build the value lazily (a function, called where it is read), or inline the constant so nothing has to be imported for this line to work. Nothing inside a function body is reported: that runs later, which is the whole repair."
3319
+ noImportTimeSpread: "This spreads `{{name}}`, a binding another module owns, while this module is still being evaluated. Under `tsc` and under a browser\u2019s ESM loader that is safe \u2014 nothing runs before its dependency. Inside one bundle it is not: the spec bundle emits shared chunks, a chunk can be evaluated while the binding it re-exports is still `undefined`, and `[...undefined]` throws `Spread syntax requires ...iterable[Symbol.iterator] to be a function` before a single test runs \u2014 on a tree whose every test passes. Build the value lazily (a function, called where it is read), or inline the constant so nothing has to be imported for this line to work. Nothing inside a function body is reported: that runs later, which is the whole repair.",
3320
+ noImportTimeSpreadObject: "This spreads `{{name}}`, a binding another module owns, into an object literal while this module is still being evaluated. Under `tsc` and under a browser\u2019s ESM loader that is safe \u2014 nothing runs before its dependency. Inside one bundle it is not: the spec bundle emits shared chunks, and a chunk can be evaluated while the binding it re-exports is still `undefined`. Do not look for an error \u2014 an object spread of `undefined` throws nothing, `{ ...undefined }` is `{}`, so the module loads and every key this line was meant to copy reads `undefined` for the rest of the run, on a tree whose every test passes. Build the value lazily (a function, called where it is read), or write the keys out so nothing has to be imported for this line to work. Nothing inside a function body is reported: that runs later, which is the whole repair."
3315
3321
  },
3316
3322
  create: (context) => ({
3317
3323
  SpreadElement: (node) => {
@@ -3320,7 +3326,8 @@ var noImportTimeSpread = defineRule({
3320
3326
  return;
3321
3327
  }
3322
3328
  const suggestion = lazyValueSuggestion(context, node);
3323
- const report = { node, messageId: "noImportTimeSpread", data: { name: imported.name } };
3329
+ const messageId = spreadFailureMode(node) === "object" ? "noImportTimeSpreadObject" : "noImportTimeSpread";
3330
+ const report = { node, messageId, data: { name: imported.name } };
3324
3331
  context.report(suggestion ? { ...report, suggest: [suggestion] } : report);
3325
3332
  }
3326
3333
  })
package/dist/index.js CHANGED
@@ -2885,6 +2885,12 @@ var narrowValue = defineHelper((value, predicate, label) => {
2885
2885
  }
2886
2886
  return value;
2887
2887
  });
2888
+ function defined(value, label = "a value that is neither null nor undefined") {
2889
+ if (value === null || value === void 0) {
2890
+ throw narrowingFailed(label, value);
2891
+ }
2892
+ return value;
2893
+ }
2888
2894
  function byKey(value, key) {
2889
2895
  if (typeof value !== "object" || value === null || !(key in value)) {
2890
2896
  throw narrowingFailed(`an object with a '${String(key)}' property`, value);
@@ -2899,6 +2905,7 @@ function observable(value) {
2899
2905
  }
2900
2906
  var narrow = Object.assign(narrowValue, {
2901
2907
  byKey: defineHelper(byKey),
2908
+ defined: defineHelper(defined),
2902
2909
  observable: defineHelper(observable)
2903
2910
  });
2904
2911
 
package/dist/node.cjs CHANGED
@@ -2618,6 +2618,12 @@ var narrowValue = defineHelper((value, predicate, label) => {
2618
2618
  }
2619
2619
  return value;
2620
2620
  });
2621
+ function defined(value, label = "a value that is neither null nor undefined") {
2622
+ if (value === null || value === void 0) {
2623
+ throw narrowingFailed(label, value);
2624
+ }
2625
+ return value;
2626
+ }
2621
2627
  function byKey(value, key) {
2622
2628
  if (typeof value !== "object" || value === null || !(key in value)) {
2623
2629
  throw narrowingFailed(`an object with a '${String(key)}' property`, value);
@@ -2632,6 +2638,7 @@ function observable(value) {
2632
2638
  }
2633
2639
  var narrow = Object.assign(narrowValue, {
2634
2640
  byKey: defineHelper(byKey),
2641
+ defined: defineHelper(defined),
2635
2642
  observable: defineHelper(observable)
2636
2643
  });
2637
2644
 
package/dist/node.js CHANGED
@@ -1,4 +1,4 @@
1
- export { asInstance, asInstances, asSpy, assertMocked, captureArg, createFixture, createFixtureFactory, createMock, createSpyClass, createSpyFromInstance, describeDuplicateCopies, expectCompletion, expectEmission, expectEmissions, expectError, expectNoEmission, flushEventLoop, flushEventLoopUntil, getPackageCopies, mockConstructor, mockDeep, moduleNamespace, narrow, restoreSpiedInstance, setEmissionTimeout, settleDynamicImport, stubConstructor, withOverrides } from './chunk-5XLI5JYC.js';
1
+ export { asInstance, asInstances, asSpy, assertMocked, captureArg, createFixture, createFixtureFactory, createMock, createSpyClass, createSpyFromInstance, describeDuplicateCopies, expectCompletion, expectEmission, expectEmissions, expectError, expectNoEmission, flushEventLoop, flushEventLoopUntil, getPackageCopies, mockConstructor, mockDeep, moduleNamespace, narrow, restoreSpiedInstance, setEmissionTimeout, settleDynamicImport, stubConstructor, withOverrides } from './chunk-4SJ7EGIY.js';
2
2
  export { countMockedProps, mockAccessorsProp, mockReadonlyProp, mockReadonlyPropGetter, mockValueProp, reportPropsOutsideHooks, restoreMockedProps } from './chunk-N3T7KWWA.js';
3
3
  export { autoMocked, clearAutoSpy, clearAutoSpyDefaults, createAutoMock, createSpyFromClass, registerAutoSpyDefaults, resetAutoSpy } from './chunk-WAMFS3UI.js';
4
4
  import './chunk-OS4PTU6D.js';
package/dist/react.js CHANGED
@@ -1,4 +1,4 @@
1
- export { asInstance, asInstances, asSpy, assertMocked, captureArg, createFixture, createFixtureFactory, createMock, createSpyClass, createSpyFromInstance, describeDuplicateCopies, expectCompletion, expectEmission, expectEmissions, expectError, expectNoEmission, flushEventLoop, flushEventLoopUntil, getPackageCopies, mockConstructor, mockDeep, moduleNamespace, narrow, restoreSpiedInstance, setEmissionTimeout, settleDynamicImport, stubConstructor, withOverrides } from './chunk-5XLI5JYC.js';
1
+ export { asInstance, asInstances, asSpy, assertMocked, captureArg, createFixture, createFixtureFactory, createMock, createSpyClass, createSpyFromInstance, describeDuplicateCopies, expectCompletion, expectEmission, expectEmissions, expectError, expectNoEmission, flushEventLoop, flushEventLoopUntil, getPackageCopies, mockConstructor, mockDeep, moduleNamespace, narrow, restoreSpiedInstance, setEmissionTimeout, settleDynamicImport, stubConstructor, withOverrides } from './chunk-4SJ7EGIY.js';
2
2
  export { countMockedProps, mockAccessorsProp, mockReadonlyProp, mockReadonlyPropGetter, mockValueProp, reportPropsOutsideHooks, restoreMockedProps } from './chunk-N3T7KWWA.js';
3
3
  export { autoMocked, clearAutoSpy, clearAutoSpyDefaults, createAutoMock, createSpyFromClass, registerAutoSpyDefaults, resetAutoSpy } from './chunk-WAMFS3UI.js';
4
4
  import './chunk-OS4PTU6D.js';
package/dist/rstest.js CHANGED
@@ -1,4 +1,4 @@
1
- export { asInstance, asInstances, asSpy, assertMocked, captureArg, createFixture, createFixtureFactory, createMock, createSpyClass, createSpyFromInstance, describeDuplicateCopies, expectCompletion, expectEmission, expectEmissions, expectError, expectNoEmission, flushEventLoop, flushEventLoopUntil, getPackageCopies, mockConstructor, mockDeep, moduleNamespace, narrow, restoreSpiedInstance, setEmissionTimeout, settleDynamicImport, stubConstructor, withOverrides } from './chunk-5XLI5JYC.js';
1
+ export { asInstance, asInstances, asSpy, assertMocked, captureArg, createFixture, createFixtureFactory, createMock, createSpyClass, createSpyFromInstance, describeDuplicateCopies, expectCompletion, expectEmission, expectEmissions, expectError, expectNoEmission, flushEventLoop, flushEventLoopUntil, getPackageCopies, mockConstructor, mockDeep, moduleNamespace, narrow, restoreSpiedInstance, setEmissionTimeout, settleDynamicImport, stubConstructor, withOverrides } from './chunk-4SJ7EGIY.js';
2
2
  export { countMockedProps, mockAccessorsProp, mockReadonlyProp, mockReadonlyPropGetter, mockValueProp, reportPropsOutsideHooks, restoreMockedProps } from './chunk-N3T7KWWA.js';
3
3
  export { autoMocked, clearAutoSpy, clearAutoSpyDefaults, createAutoMock, createSpyFromClass, registerAutoSpyDefaults, resetAutoSpy } from './chunk-WAMFS3UI.js';
4
4
  import './chunk-OS4PTU6D.js';
package/dist/svelte.js CHANGED
@@ -1,4 +1,4 @@
1
- export { asInstance, asInstances, asSpy, assertMocked, captureArg, createFixture, createFixtureFactory, createMock, createSpyClass, createSpyFromInstance, describeDuplicateCopies, expectCompletion, expectEmission, expectEmissions, expectError, expectNoEmission, flushEventLoop, flushEventLoopUntil, getPackageCopies, mockConstructor, mockDeep, moduleNamespace, narrow, restoreSpiedInstance, setEmissionTimeout, settleDynamicImport, stubConstructor, withOverrides } from './chunk-5XLI5JYC.js';
1
+ export { asInstance, asInstances, asSpy, assertMocked, captureArg, createFixture, createFixtureFactory, createMock, createSpyClass, createSpyFromInstance, describeDuplicateCopies, expectCompletion, expectEmission, expectEmissions, expectError, expectNoEmission, flushEventLoop, flushEventLoopUntil, getPackageCopies, mockConstructor, mockDeep, moduleNamespace, narrow, restoreSpiedInstance, setEmissionTimeout, settleDynamicImport, stubConstructor, withOverrides } from './chunk-4SJ7EGIY.js';
2
2
  export { countMockedProps, mockAccessorsProp, mockReadonlyProp, mockReadonlyPropGetter, mockValueProp, reportPropsOutsideHooks, restoreMockedProps } from './chunk-N3T7KWWA.js';
3
3
  export { autoMocked, clearAutoSpy, clearAutoSpyDefaults, createAutoMock, createSpyFromClass, registerAutoSpyDefaults, resetAutoSpy } from './chunk-WAMFS3UI.js';
4
4
  import './chunk-OS4PTU6D.js';
@@ -239,8 +239,14 @@ interface RenderShallowOptions<T> {
239
239
  /**
240
240
  * Keep the real template instead of blanking it — for `viewChild`, content projection, host
241
241
  * bindings. The template's own pipes and directives stay resolvable; child components are still
242
- * dropped, which is what keeps the render shallow. A child re-exported by an imported `NgModule`
243
- * is the one thing that survives, because the module is kept whole.
242
+ * dropped, which is what keeps the render shallow. Under JIT a child re-exported by an imported
243
+ * `NgModule` survives, because the module is kept whole.
244
+ *
245
+ * Under **AOT** an imported `NgModule` is not in the compiled dependency list at all — ngtsc
246
+ * resolves its exported scope and flattens the declarations into the list instead — so a
247
+ * `standalone: false` pipe or directive is what arrives, and Angular refuses one of those in
248
+ * `imports`. That case throws with the declaration named rather than letting Angular blame the
249
+ * pipe's author; see the error for what to do instead.
244
250
  */
245
251
  keepTemplate?: boolean;
246
252
  /** Child components/directives/pipes to keep resolvable in the template (everything else is dropped). */
package/dist/vue.js CHANGED
@@ -1,4 +1,4 @@
1
- export { asInstance, asInstances, asSpy, assertMocked, captureArg, createFixture, createFixtureFactory, createMock, createSpyClass, createSpyFromInstance, describeDuplicateCopies, expectCompletion, expectEmission, expectEmissions, expectError, expectNoEmission, flushEventLoop, flushEventLoopUntil, getPackageCopies, mockConstructor, mockDeep, moduleNamespace, narrow, restoreSpiedInstance, setEmissionTimeout, settleDynamicImport, stubConstructor, withOverrides } from './chunk-5XLI5JYC.js';
1
+ export { asInstance, asInstances, asSpy, assertMocked, captureArg, createFixture, createFixtureFactory, createMock, createSpyClass, createSpyFromInstance, describeDuplicateCopies, expectCompletion, expectEmission, expectEmissions, expectError, expectNoEmission, flushEventLoop, flushEventLoopUntil, getPackageCopies, mockConstructor, mockDeep, moduleNamespace, narrow, restoreSpiedInstance, setEmissionTimeout, settleDynamicImport, stubConstructor, withOverrides } from './chunk-4SJ7EGIY.js';
2
2
  export { countMockedProps, mockAccessorsProp, mockReadonlyProp, mockReadonlyPropGetter, mockValueProp, reportPropsOutsideHooks, restoreMockedProps } from './chunk-N3T7KWWA.js';
3
3
  import { createSpyFromClass } from './chunk-WAMFS3UI.js';
4
4
  export { autoMocked, clearAutoSpy, clearAutoSpyDefaults, createAutoMock, createSpyFromClass, registerAutoSpyDefaults, resetAutoSpy } from './chunk-WAMFS3UI.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vitest-auto-spy",
3
- "version": "5.15.1",
3
+ "version": "5.16.0",
4
4
  "description": "Auto-generate fully-typed test spies from a class — across Vitest, Bun, node:test and Rstest, with Angular/NestJS/React/Vue/Svelte recipes. Drop-in replacement for jest-auto-spies and jasmine-auto-spies.",
5
5
  "keywords": [
6
6
  "auto-mock",