vitest-auto-spy 5.36.0 → 5.37.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/CHANGELOG.md CHANGED
@@ -10,6 +10,52 @@ The latest released version here must always match the one published on
10
10
 
11
11
  ## [Unreleased]
12
12
 
13
+ ## [5.37.0] - 2026-09-26
14
+
15
+ ### Added
16
+
17
+ - **`stable()` takes Angular 22.2's `TestBed.createDirective` fixture.** It only ever awaited
18
+ `whenStable()`, but was typed on `ComponentFixture`, so `await stable(TestBed.createDirective(Dir,
19
+ { tagName }))` failed with `TS2345` and specs fell back to `detectChanges()`, which flushes no
20
+ effects. It now accepts any fixture with `whenStable()`; nothing in the signature names
21
+ `DirectiveFixture`, so Angular 20 and 21 compile as before.
22
+
23
+ ### Fixed
24
+
25
+ - **`toHaveDirectiveApplied` finds a directive on the element a fixture is rooted at.** It searched
26
+ with `queryAll`, which never returns the root, so on a `TestBed.createDirective` fixture — rooted at
27
+ the element that carries the directive — it failed with `it is not on any element of this fixture`
28
+ and advised `createDirectiveHost`. The root is searched too; on a `ComponentFixture` that also counts
29
+ a `hostDirectives` entry of the component under test.
30
+ - **`vitest-auto-spy/eslint-plugin` is assignable to ESLint's own `Plugin` and flat-config types.**
31
+ `plugins: { 'vitest-auto-spy': autoSpy }` in a typed `eslint.config.ts`, or in
32
+ `new Linter().verify(code, [...])`, failed with `TS2322` / `TS2769`: `configs.*.plugins` was
33
+ `Record<string, unknown>` and each rule's `create` was typed on the library's own context. The
34
+ configs now name the plugin type, and a published rule is typed the way ESLint calls it. The plugin
35
+ still imports nothing from ESLint.
36
+ - **`strayListeners` no longer blames a spec file for jsdom's own wiring, and no longer strips it.**
37
+ Under Vitest 5, assigning a window global in jsdom or happy-dom also writes it to the environment's
38
+ real window, so the tracking wrapper sat where jsdom's internals call it too. The first
39
+ `querySelectorAll`, `matches` or `getComputedStyle` on a document makes jsdom's selector engine
40
+ register nine focus, keyboard and mouse listeners on the window, once; the file that happened to
41
+ run it failed under `onStrayListeners: 'throw'` with `left 9 window/document listeners attached`,
42
+ and the sweep took them off, which broke `:focus` and `:hover` matching for every later file in
43
+ the worker. The same held under a VM pool (`vmThreads`, the Analog default) on any Vitest version.
44
+ A registration whose first caller frame is inside `jsdom`, `happy-dom` or `@asamuzakjp/*` is now
45
+ the environment's and is neither counted nor swept — which also covers the `resize` listener
46
+ happy-dom's `matchMedia` adds per `change` listener. A `document.querySelectorAll('*')` warm-up
47
+ placed before `setupAutoSpy()` to work around this can go.
48
+ - **Stray timer and listener reports quote the caller instead of `__VITEST_HELPER__`.** When every
49
+ frame of a registration was inside dependencies, the first one shown was the `vi.defineHelper`
50
+ wrapper around the tracker. It is now skipped like the library's own frames.
51
+
52
+ ### Docs
53
+
54
+ - **`no-redundant-mock-reset` says what it cannot read.** A runner config assembled by a factory or
55
+ a `mergeConfig` from another module sets `clearMocks` in a file the rule never opens; the docs now
56
+ say to write the flag beside `configFile`. The Vitest 5 default for a config that leaves it out
57
+ arrived in 5.35.0.
58
+
13
59
  ## [5.36.0] - 2026-09-26
14
60
 
15
61
  **Why upgrade.** Eleven entries that crashed on import under `bun:test` load now, and every spec
@@ -5374,7 +5420,8 @@ moved under 1 %: `/angular` +192 B (+1.0 %) for the override queue and the HTTP
5374
5420
  This copy ships with the package and carries 5.x only. Releases before 5.0.0 are in the
5375
5421
  [full changelog](https://github.com/ASDAlexey/vitest-auto-spy/blob/master/CHANGELOG.md).
5376
5422
 
5377
- [Unreleased]: https://github.com/ASDAlexey/vitest-auto-spy/compare/v5.36.0...HEAD
5423
+ [Unreleased]: https://github.com/ASDAlexey/vitest-auto-spy/compare/v5.37.0...HEAD
5424
+ [5.37.0]: https://github.com/ASDAlexey/vitest-auto-spy/compare/v5.36.0...v5.37.0
5378
5425
  [5.36.0]: https://github.com/ASDAlexey/vitest-auto-spy/compare/v5.35.0...v5.36.0
5379
5426
  [5.35.0]: https://github.com/ASDAlexey/vitest-auto-spy/compare/v5.34.0...v5.35.0
5380
5427
  [5.34.0]: https://github.com/ASDAlexey/vitest-auto-spy/compare/v5.33.0...v5.34.0
@@ -977,6 +977,7 @@ spies.get(PricingService).total.mockReturnValue(100);
977
977
  // zoneless waiting
978
978
  await stable(fixture); // flush effects, then await the fixture; fails at 2000 ms naming the cause
979
979
  await stable(fixture, { timeout: 5000, label: 'the products fixture' });
980
+ await stable(TestBed.createDirective(TooltipDirective, { tagName: 'button' })); // any fixture with whenStable()
980
981
  flushEffects(); // the no-fixture half: services, stores, runInInjectionContext
981
982
 
982
983
  // resources — one wait for httpResource(), resource() and rxResource()
@@ -1177,6 +1178,11 @@ _is_ declared; `NG0304` calls a missing directive a missing component; a bare at
1177
1178
  nothing at all). `schemas: [NO_ERRORS_SCHEMA]` next to a standalone component is a dead entry —
1178
1179
  schemas apply to a testing module's `declarations` only.
1179
1180
 
1181
+ Angular ≥ 22.2: when the directive needs no static host attribute, no `TemplateRef` (structural) and
1182
+ no sibling markup, `TestBed.createDirective(Dir, { tagName, bindings })` builds the host itself.
1183
+ `stable(fixture)`, `hostElement(fixture)` and `toHaveDirectiveApplied` take its `DirectiveFixture`;
1184
+ `setInputs` does not (no `componentRef`) — bind a signal with `inputBinding` and set it.
1185
+
1180
1186
  ### A stand-in for a child — `createComponentStub`
1181
1187
 
1182
1188
  Do not hand-write `class MockChartComponent` with a copied selector: nothing checks the copy, so a
@@ -126,7 +126,7 @@ Part of the agent reference [`AGENTS.md`](../AGENTS.md), which maps every sectio
126
126
  | `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 |
127
127
  | `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) |
128
128
  | `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) |
129
- | `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) |
129
+ | `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] })`; on Angular ≥ 22.2, for a directive with no static host attribute, `TemplateRef` or sibling markup, `TestBed.createDirective(Dir, { tagName, bindings })` (§13) |
130
130
  | `setInputs: … declares no input named …` (with a `Did you mean` for a near typo) | the key is not an input of that component — a typo, an input renamed since, or one of its own fields | take the suggestion, or set a name the message lists; a signal the component owns is moved through the signal (§13) |
131
131
  | `setInputs: X carries no ɵcmp, so there are no inputs to set` (or `the component class is undefined`, `X is a @Pipe`) | the fixture's `componentType` is not a compiled `@Component` — a `@Pipe`, a class Angular never compiled, or `undefined` from a barrel split across chunks | pass the `@Component` class the fixture was created from; call a pipe's `transform()` directly; for `undefined` import the class from its own file (§13) |
132
132
  | `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 |
@@ -75,7 +75,7 @@ blanket downgrade so those keep their severity; do not copy the two names into a
75
75
  | `prefer-settle-dynamic-import` | `error` | suggest | `await import('./thing')` and `import('./thing').then(…)` inside an `it` / `test` body or a `beforeEach` / `beforeAll` / `afterEach` / `afterAll` hook (the `.only`, `.skip` and `.each` spellings included) → `await settleDynamicImport(() => import('./thing'))`, which is the same load plus `flushEventLoop(1)` and returns the namespace, so the destructured form keeps reading the same way. The bare `await` waits for the **module** and not for the continuation of the code that was loading it, so the assertion runs a turn early and the test is green only while that continuation is short. Syntax only. Silent wherever a function of its own sits between the runner's callback and the import — a `vi.mock` factory, a lazy route's `loadComponent` / `loadChildren`, a callback handed to production code, `settleDynamicImport`'s own `() => import(…)` — and, by the same reading, silent on a spec-local `const load = async () => { await import('…') }`, which is written identically whether the spec calls it or the code under test does |
76
76
  | `prefer-create-mock` | `warn` | suggest | an object literal under `as SomeType` — `{ id: '1', isOffline: false } as Device`, `<Device>{ … }` too → `createMock<Device>({ … })`. A cast asks whether the two types overlap rather than whether the value is one of them, so it passes a key the type does not declare (the excess-property check is skipped) **and** a required field the fixture never sets; both type gates stay silent, and the same object is then spread into the expected payload of a call assertion. Where the literal already sits in a typed slot the first repair is to delete the cast. Silent on `as const`, `as unknown` / `as any` and the double cast built from them, on a cast of anything but a literal, and on a literal inside one of this library's own factories. `warn`, because accepting the suggestion makes a drifted fixture red on the next type-check — the finding, and a migration taken file by file |
77
77
  | `no-mock-cast` | `error` | suggest | a cast to Vitest's `Mock` / `MockInstance` over a **member access** — `TestBed.inject(S).m as Mock` → `injectSpy(S).m`, which the suggestion writes whenever the token is in view (directly, or through a `const` the file settled with a `TestBed.inject`). `Mock` with no parameters is `Mock<any>`: `mockReturnValue` takes anything and `toHaveBeenCalledWith` stops comparing arguments. `(spy.m.mockReturnValue as Mock)(…)` gets its own message — the cast is on the member that installs the answer, so neither the value nor the method's return type is checked. The name must resolve to an import from `vitest` / `@rstest/core` / `bun:test` / `jest`, or to nothing at all, so a domain `Mock` is left alone |
78
- | `no-redundant-mock-reset` | `error` | `--fix` / suggest | a mock reset the runner already performs between tests — `vi.clearAllMocks()` / `resetAllMocks()` / `restoreAllMocks()`, `x.mockClear()` / `mockReset()` / `mockRestore()` — standing where nothing the file wrote ran since the runner's own: the first statement of a `beforeEach` no other `beforeEach` precedes, or a clear as the last statement of an `afterEach` → delete it, and the hook with it where that is all it held. A restore or reset in `afterEach` / `afterAll` is never reported: after the last test it is what takes a spy off before the `afterAll` hooks. **Silent** until the flags are known: options `{ clearMocks, restoreMocks, mockReset }` (or `{ configFile }` naming the runner config) first, then a `vitest.config.*` / `vite.config.*` searched upwards from the file and read as text. Flag matches call, never family — `restoreMocks` walks only the spies `vi.spyOn` installed. `--fix` only in a file whose only before-hook it is; a suggestion otherwise; never inside a test body |
78
+ | `no-redundant-mock-reset` | `error` | `--fix` / suggest | a mock reset the runner already performs between tests — `vi.clearAllMocks()` / `resetAllMocks()` / `restoreAllMocks()`, `x.mockClear()` / `mockReset()` / `mockRestore()` — standing where nothing the file wrote ran since the runner's own: the first statement of a `beforeEach` no other `beforeEach` precedes, or a clear as the last statement of an `afterEach` → delete it, and the hook with it where that is all it held. A restore or reset in `afterEach` / `afterAll` is never reported: after the last test it is what takes a spy off before the `afterAll` hooks. **Silent** until the flags are known: options `{ clearMocks, restoreMocks, mockReset }` (or `{ configFile }` naming the runner config) first, then a `vitest.config.*` / `vite.config.*` searched upwards from the file and read as text; a config that leaves `clearMocks` out counts it on from Vitest 5 (its default). A config built by a factory or `mergeConfig` from another module is not read through — write its flags beside `configFile`. Flag matches call, never family — `restoreMocks` walks only the spies `vi.spyOn` installed. `--fix` only in a file whose only before-hook it is; a suggestion otherwise; never inside a test body |
79
79
  | `no-unasserted-argument` | `warn` | — | a bare `expect(spy).toHaveBeenCalled()` on evidence from the file: the same subject asserted with `toHaveBeenCalledWith(…)` in another test, or a title containing `with` over a body whose every assertion is a bare call → `toHaveBeenCalledWith(…)` / `mustBeCalledWith(…)`. `not.toHaveBeenCalled()` and the counting matchers are never reported |
80
80
  | `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. **Keep the call for a component whose template holds a `@defer` block** — that ships async class metadata, which `TestBed` resolves in this very call whatever the builder did, and dropping it fails the test with `has unresolved metadata`; the rule cannot see another file's template, so list those classes in `{ ignoreComponents: ['CardComponent'] }` (a spec naming one is left alone) or keep a `// eslint-disable-next-line vitest-auto-spy/no-compile-components -- @defer: async class metadata` |
81
81
  | `no-sync-testbed-await` | `error` | suggest | `await` on a TestBed call that answers the TestBed or a fixture — `configureTestingModule`, `override*`, `resetTestingModule`, `createComponent`, `getLastFixture`, through `TestBed`, `getTestBed()`, a chain of those, or a name holding one → drop the `await`, and the `async` of a hook that then awaits nothing else; the suggestion does both. Reads **no types**, so it reports without `parserOptions.project`, where `@typescript-eslint/await-thenable` cannot. `inject` and `runInInjectionContext` are never reported: each answers whatever the token or the callback holds, which can be a promise |
@@ -202,7 +202,9 @@ setupAutoSpy({ strayListeners: true }); // wrap addEventListener, sweep what the
202
202
 
203
203
  The split is the one `pruneMockRegistry` uses: a `beforeAll` marks the listeners already on
204
204
  `window`/`document` — registered while the module graph was evaluated, a framework's one-time
205
- initialisation — and the `afterAll` takes off everything added since. The pieces are exported too:
205
+ initialisation — and the `afterAll` takes off everything added since. A registration jsdom or
206
+ happy-dom makes itself (jsdom wires nine focus, keyboard and mouse listeners the first time a
207
+ document is queried) is the environment's: neither counted nor swept. The pieces are exported too:
206
208
  `trackStrayListeners()` (idempotent, returns the undo), `baselineStrayListeners()`,
207
209
  `removeStrayListeners()` (returns how many), `countStrayListeners()` (throws before
208
210
  `trackStrayListeners()` has run, like the timer counter) and `describeStrayListeners()` — each
@@ -1,12 +1,12 @@
1
1
  import { pendingRequestsReport, assertAngularInternals, instrumentTestBed, onTestingModuleConfigured, onComponentCreated, beforeTestBedReset, verifyOnTeardown } from './chunk-GKSVYPZQ.js';
2
2
  export { disableTestBedDiagnostics, enableTestBedDiagnostics, formatSpecTiming, getTestBedTiming, instrumentTestBed, reportSpecTiming } from './chunk-GKSVYPZQ.js';
3
- import { failOnUnspiedProvider } from './chunk-VWNFNWTG.js';
3
+ import { failOnUnspiedProvider } from './chunk-2KBRDNIB.js';
4
4
  import './chunk-GG7BQKEK.js';
5
5
  import './chunk-FWXDSPZD.js';
6
- import './chunk-F4URI3UE.js';
6
+ import './chunk-6KP5QMMV.js';
7
7
  import './chunk-FOL5RH22.js';
8
8
  import './chunk-PC3FAZT3.js';
9
- import './chunk-LA2CMIPM.js';
9
+ import './chunk-RYFF55PV.js';
10
10
  import { isAutoSpyLike } from './chunk-Q7L4AWI7.js';
11
11
  import { count, taskName } from './chunk-M3NTXLZG.js';
12
12
  import { angularPendingRequests, angularShadowedProviders, angularDeadSchemas, angularNgModuleScopesAuto } from './chunk-5FQZGJLJ.js';
@@ -1,11 +1,11 @@
1
1
  import { describeInstance } from './chunk-BS432KB2.js';
2
- import { useVitestAdapter } from './chunk-YWSOJH5C.js';
3
- import './chunk-GTDJ22IQ.js';
2
+ import { useVitestAdapter } from './chunk-YNXQ2WFH.js';
3
+ import './chunk-B6C3K27C.js';
4
4
  import './chunk-YHDBRBGU.js';
5
5
  import './chunk-TXDBDMIL.js';
6
6
  import './chunk-FOL5RH22.js';
7
7
  import './chunk-PC3FAZT3.js';
8
- import { createFunctionSpy } from './chunk-LA2CMIPM.js';
8
+ import { createFunctionSpy } from './chunk-RYFF55PV.js';
9
9
  import './chunk-Q7L4AWI7.js';
10
10
  import './chunk-M3NTXLZG.js';
11
11
  import { angularDialog } from './chunk-5FQZGJLJ.js';
@@ -17,11 +17,14 @@ function rootOf(received) {
17
17
  candidate
18
18
  ) : void 0;
19
19
  }
20
+ function matching(root, predicate) {
21
+ return predicate(root) ? [root, ...root.queryAll(predicate)] : root.queryAll(predicate);
22
+ }
20
23
  function diagnose(directive, selector, root) {
21
- const matching = selector === void 0 ? [] : root.queryAll(By.css(selector));
24
+ const elements = selector === void 0 ? [] : matching(root, By.css(selector));
22
25
  const where = selector === void 0 ? "" : ` on '${selector}'`;
23
26
  const prefix = `[vitest-auto-spy] expected ${directive.name} to be applied${where}`;
24
- if (selector !== void 0 && matching.length === 0) {
27
+ if (selector !== void 0 && elements.length === 0) {
25
28
  return withDocs(
26
29
  `${prefix}, but no element matches that selector.
27
30
  Run fixture.detectChanges() before asserting, and check the selector against the template.`,
@@ -47,14 +50,14 @@ function directiveResult(received, directive, selector) {
47
50
  if (!root) {
48
51
  throw new Error(
49
52
  withDocs(
50
- `[vitest-auto-spy] toHaveDirectiveApplied: expected a ComponentFixture or a DebugElement, received ${describeReceived(received)}.
53
+ `[vitest-auto-spy] toHaveDirectiveApplied: expected a ComponentFixture, a DirectiveFixture or a DebugElement, received ${describeReceived(received)}.
51
54
  Pass the fixture itself, or fixture.debugElement \u2014 not fixture.nativeElement.`,
52
55
  angularDirectiveApplied
53
56
  )
54
57
  );
55
58
  }
56
- const withDirective = root.queryAll(By.directive(directive));
57
- const applied = selector === void 0 ? withDirective.length > 0 : root.queryAll(By.css(selector)).some((element) => element.providerTokens.includes(directive));
59
+ const withDirective = matching(root, By.directive(directive));
60
+ const applied = selector === void 0 ? withDirective.length > 0 : matching(root, By.css(selector)).some((element) => element.providerTokens.includes(directive));
58
61
  return {
59
62
  pass: applied,
60
63
  message: () => applied ? `[vitest-auto-spy] expected ${directive.name} not to be applied, but it is on ${count(withDirective.length, "element")}.` : diagnose(directive, selector, root)
@@ -1,6 +1,6 @@
1
1
  import { describeInstance } from './chunk-BS432KB2.js';
2
2
  import { angularInternalsError } from './chunk-GG7BQKEK.js';
3
- import { createFunctionSpy } from './chunk-LA2CMIPM.js';
3
+ import { createFunctionSpy } from './chunk-RYFF55PV.js';
4
4
  import './chunk-Q7L4AWI7.js';
5
5
  import { count } from './chunk-M3NTXLZG.js';
6
6
  import { angularRouteDouble, angularRouterInject, angularRouterEvents, angularLocationDouble, angularRouteWiring, angularRouterDouble } from './chunk-5FQZGJLJ.js';
package/dist/angular.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { u as AngularTokenProvider, A as AngularValueProvider, a as AutoSpiedInstance, C as ComponentInputs, b as CreateWithAutoSpiesOptions, E as ElementConstructor, N as NativeElementHolder, 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 hostElement, k as injectSpy, p as provideAutoSpy, v as provideAutoSpyForToken, q as queryElement, r as renderShallow, l as runEffect, s as setInputs, m as settleResource, n as stable, t as trackEffectRuns, o as trackRecomputations } from './track-signal-runs-BuJk_Yi8.js';
1
+ export { u as AngularTokenProvider, A as AngularValueProvider, a as AutoSpiedInstance, C as ComponentInputs, b as CreateWithAutoSpiesOptions, E as ElementConstructor, N as NativeElementHolder, 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 hostElement, k as injectSpy, p as provideAutoSpy, v as provideAutoSpyForToken, q as queryElement, r as renderShallow, l as runEffect, s as setInputs, m as settleResource, n as stable, t as trackEffectRuns, o as trackRecomputations } from './track-signal-runs-C7SrW3Ub.js';
2
2
  import { o as DeepPartial, C as ClassType, m as ClassSpyConfiguration, S as Spy, s as OnlyMethodKeysOf, h as AddSpyMethodsByReturnTypes, r as NotAPublicKey } from './types-BVKEkhWj.js';
3
3
  import { InjectionToken, Provider, Type, WritableSignal, Signal } from '@angular/core';
4
4
  import { A as AutoMockConfiguration, D as DeepReadonly } from './expect-emission-wUaoy0NW.js';
@@ -3,7 +3,7 @@ export { AdoptMockOptions, AdoptedMock, ArgCaptor, AsInstances, AssertMockedOpti
3
3
  export { A as AutoMockConfiguration, a as AutoMocked, b as AutoSpyDefaultEntry, C as CallbackSubscribable, E as EmissionObserver, c as EmissionOptions, d as EmissionSource, S as SubscribableLike, e as autoMocked, f as clearAutoSpyDefaults, g as createAutoMock, h as expectAllEmissions, i as expectCompletion, j as expectEmission, k as expectEmissions, l as expectError, m as expectNoEmission, r as registerAutoSpyDefaults, s as setEmissionTimeout } from './expect-emission-wUaoy0NW.js';
4
4
  export { c as createFunctionSpy, d as describeDuplicateCopies, g as getPackageCopies } from './package-identity-5pFBRWW9.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-8Ps9usRa.js';
6
- export { A as AngularValueProvider, a as AutoSpiedInstance, C as ComponentInputs, b as CreateWithAutoSpiesOptions, E as ElementConstructor, N as NativeElementHolder, 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 hostElement, k as injectSpy, p as provideAutoSpy, q as queryElement, r as renderShallow, l as runEffect, s as setInputs, m as settleResource, n as stable, t as trackEffectRuns, o as trackRecomputations } from './track-signal-runs-BuJk_Yi8.js';
6
+ export { A as AngularValueProvider, a as AutoSpiedInstance, C as ComponentInputs, b as CreateWithAutoSpiesOptions, E as ElementConstructor, N as NativeElementHolder, 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 hostElement, k as injectSpy, p as provideAutoSpy, q as queryElement, r as renderShallow, l as runEffect, s as setInputs, m as settleResource, n as stable, t as trackEffectRuns, o as trackRecomputations } from './track-signal-runs-C7SrW3Ub.js';
7
7
  import 'vitest';
8
8
  import '@angular/core';
9
9
  import '@angular/core/testing';
@@ -1,19 +1,19 @@
1
- import { createSpyForToken } from './chunk-464WKNEQ.js';
2
- export { injectSpy, provideAutoSpy } from './chunk-VWNFNWTG.js';
1
+ import { createSpyForToken } from './chunk-2TZCJ3MM.js';
2
+ export { injectSpy, provideAutoSpy } from './chunk-2KBRDNIB.js';
3
3
  import { stable, flushEffects } from './chunk-DBC6526L.js';
4
4
  export { flushEffects, stable } from './chunk-DBC6526L.js';
5
5
  import { angularInternalsError } from './chunk-GG7BQKEK.js';
6
6
  import './chunk-QDBRZQJO.js';
7
- export { adoptMock, asInstance, asInstances, asSpy, assertMocked, captureArg, createFixture, createFixtureFactory, createLog, createMock, createSpyClass, createSpyFromInstance, describeDuplicateCopies, expectAllEmissions, expectCompletion, expectEmission, expectEmissions, expectError, expectNoEmission, flushEventLoop, flushEventLoopUntil, getPackageCopies, mockConstructor, mockDeep, moduleNamespace, narrow, outOfType, restoreSpiedInstance, setEmissionTimeout, settleDynamicImport, spyOnOwnMethod, spyOnVoidMethod, stubConstructor, withOverrides } from './chunk-GAE5EGO7.js';
7
+ export { adoptMock, asInstance, asInstances, asSpy, assertMocked, captureArg, createFixture, createFixtureFactory, createLog, createMock, createSpyClass, createSpyFromInstance, describeDuplicateCopies, expectAllEmissions, expectCompletion, expectEmission, expectEmissions, expectError, expectNoEmission, flushEventLoop, flushEventLoopUntil, getPackageCopies, mockConstructor, mockDeep, moduleNamespace, narrow, outOfType, restoreSpiedInstance, setEmissionTimeout, settleDynamicImport, spyOnOwnMethod, spyOnVoidMethod, stubConstructor, withOverrides } from './chunk-6VPSS4BA.js';
8
8
  import { mockValueProp } from './chunk-FWXDSPZD.js';
9
9
  export { countMockedProps, mockAccessorsProp, mockReadonlyProp, mockReadonlyPropGetter, mockValueProp, reportPropsOutsideHooks, restoreMockedProps } from './chunk-FWXDSPZD.js';
10
- import { closestName } from './chunk-F4URI3UE.js';
11
- export { autoMocked, clearAutoSpy, clearAutoSpyDefaults, createAutoMock, createSpyFromClass, registerAutoSpyDefaults, resetAutoSpy } from './chunk-F4URI3UE.js';
10
+ import { closestName } from './chunk-6KP5QMMV.js';
11
+ export { autoMocked, clearAutoSpy, clearAutoSpyDefaults, createAutoMock, createSpyFromClass, registerAutoSpyDefaults, resetAutoSpy } from './chunk-6KP5QMMV.js';
12
12
  import { fakeClockBacklog } from './chunk-W5EXSI22.js';
13
13
  import './chunk-TXDBDMIL.js';
14
14
  import './chunk-FOL5RH22.js';
15
15
  import './chunk-PC3FAZT3.js';
16
- export { createFunctionSpy, errorHandler } from './chunk-LA2CMIPM.js';
16
+ export { createFunctionSpy, errorHandler } from './chunk-RYFF55PV.js';
17
17
  import { libraryWarn } from './chunk-Q7L4AWI7.js';
18
18
  import { displayPath, count } from './chunk-M3NTXLZG.js';
19
19
  import { angularEffects, angularTrackRuns, bunAngular, angularUnreadResource, angularCreateWithAutoSpies, angularResources, angularTypedElements, angularInputs, angularKeepTemplate } from './chunk-5FQZGJLJ.js';
package/dist/bun.js CHANGED
@@ -1,12 +1,12 @@
1
1
  import './chunk-QDBRZQJO.js';
2
- export { adoptMock, asInstance, asInstances, asSpy, assertMocked, captureArg, createFixture, createFixtureFactory, createLog, createMock, createSpyClass, createSpyFromInstance, describeDuplicateCopies, expectAllEmissions, expectCompletion, expectEmission, expectEmissions, expectError, expectNoEmission, flushEventLoop, flushEventLoopUntil, getPackageCopies, mockConstructor, mockDeep, moduleNamespace, narrow, outOfType, restoreSpiedInstance, setEmissionTimeout, settleDynamicImport, spyOnOwnMethod, spyOnVoidMethod, stubConstructor, withOverrides } from './chunk-GAE5EGO7.js';
2
+ export { adoptMock, asInstance, asInstances, asSpy, assertMocked, captureArg, createFixture, createFixtureFactory, createLog, createMock, createSpyClass, createSpyFromInstance, describeDuplicateCopies, expectAllEmissions, expectCompletion, expectEmission, expectEmissions, expectError, expectNoEmission, flushEventLoop, flushEventLoopUntil, getPackageCopies, mockConstructor, mockDeep, moduleNamespace, narrow, outOfType, restoreSpiedInstance, setEmissionTimeout, settleDynamicImport, spyOnOwnMethod, spyOnVoidMethod, stubConstructor, withOverrides } from './chunk-6VPSS4BA.js';
3
3
  export { countMockedProps, mockAccessorsProp, mockReadonlyProp, mockReadonlyPropGetter, mockValueProp, reportPropsOutsideHooks, restoreMockedProps } from './chunk-FWXDSPZD.js';
4
- export { autoMocked, clearAutoSpy, clearAutoSpyDefaults, createAutoMock, createSpyFromClass, registerAutoSpyDefaults, resetAutoSpy } from './chunk-F4URI3UE.js';
4
+ export { autoMocked, clearAutoSpy, clearAutoSpyDefaults, createAutoMock, createSpyFromClass, registerAutoSpyDefaults, resetAutoSpy } from './chunk-6KP5QMMV.js';
5
5
  import './chunk-W5EXSI22.js';
6
6
  import './chunk-TXDBDMIL.js';
7
7
  import './chunk-FOL5RH22.js';
8
8
  import './chunk-PC3FAZT3.js';
9
- export { createFunctionSpy, errorHandler } from './chunk-LA2CMIPM.js';
9
+ export { createFunctionSpy, errorHandler } from './chunk-RYFF55PV.js';
10
10
  import './chunk-Q7L4AWI7.js';
11
11
  import './chunk-M3NTXLZG.js';
12
12
  import './chunk-5FQZGJLJ.js';
@@ -1,5 +1,5 @@
1
1
  import { currentSpecFile } from './chunk-FWXDSPZD.js';
2
- import { createSpyFromClass } from './chunk-F4URI3UE.js';
2
+ import { createSpyFromClass } from './chunk-6KP5QMMV.js';
3
3
  import { isAutoSpyLike, misconfigurationThrows, reportMisconfiguration } from './chunk-Q7L4AWI7.js';
4
4
  import { angularInjectSpyReal } from './chunk-5FQZGJLJ.js';
5
5
  import { withDocs } from './chunk-ENCEEH23.js';
@@ -1,4 +1,4 @@
1
- import { createSpyFromClass, createAutoMock } from './chunk-F4URI3UE.js';
1
+ import { createSpyFromClass, createAutoMock } from './chunk-6KP5QMMV.js';
2
2
  import { trackInjectionsUntracked } from './chunk-5FQZGJLJ.js';
3
3
  import { withDocs } from './chunk-ENCEEH23.js';
4
4
 
@@ -1,5 +1,5 @@
1
1
  import { DISPOSE } from './chunk-PC3FAZT3.js';
2
- import { defaultStrict, resolveUnstubbedGuard, seedReturnValue, createFunctionSpy } from './chunk-LA2CMIPM.js';
2
+ import { defaultStrict, resolveUnstubbedGuard, seedReturnValue, createFunctionSpy } from './chunk-RYFF55PV.js';
3
3
  import { runClearHook, runConfigReset, reportMisconfiguration, AUTO_SPY_MARK, markAsMock, isMarkedMock, readDeepChildren } from './chunk-Q7L4AWI7.js';
4
4
  import { autoMockHeldBack, createSpyFromClassInstanceMethods, createSpyFromClassAccessors, createSpyFromClassReturns, createSpyFromClassLazySpies } from './chunk-5FQZGJLJ.js';
5
5
  import { withDocs } from './chunk-ENCEEH23.js';
@@ -1,9 +1,9 @@
1
1
  import { mockValueProp, mockAccessorsProp } from './chunk-FWXDSPZD.js';
2
- import { resolveConfiguration, mergeAutoSpyDefaults, resolveReadGuard, mergeMethodNames, getCallableMemberNames, createTrackedPropSpy, applyConfiguredReturns, createSpyFromClass, warnOnUnknownMethods, warnOnAccessorNamingAMethod, resolveAccessors, createAccessorsSpies, ownerOf, createProxyPropStore, storeWriteTraps, describeStoredProp, hasStoredProp, isProtocolKey, isDeletedProp, readStoredAccessor, NOT_STORED, disposeAutoSpy, writeStoredValue } from './chunk-F4URI3UE.js';
2
+ import { resolveConfiguration, mergeAutoSpyDefaults, resolveReadGuard, mergeMethodNames, getCallableMemberNames, createTrackedPropSpy, applyConfiguredReturns, createSpyFromClass, warnOnUnknownMethods, warnOnAccessorNamingAMethod, resolveAccessors, createAccessorsSpies, ownerOf, createProxyPropStore, storeWriteTraps, describeStoredProp, hasStoredProp, isProtocolKey, isDeletedProp, readStoredAccessor, NOT_STORED, disposeAutoSpy, writeStoredValue } from './chunk-6KP5QMMV.js';
3
3
  import { unpatchedSetTimeout, unpatchedClearTimeout, fakeClockAdvice, fakeClockBacklog } from './chunk-W5EXSI22.js';
4
4
  import { redefineFailure } from './chunk-FOL5RH22.js';
5
5
  import { DISPOSE } from './chunk-PC3FAZT3.js';
6
- import { defineHelper, adoptFunctionSpy, reinstallDispatch, serializeValue, resolveUnstubbedGuard, createFunctionSpy } from './chunk-LA2CMIPM.js';
6
+ import { defineHelper, adoptFunctionSpy, reinstallDispatch, serializeValue, resolveUnstubbedGuard, createFunctionSpy } from './chunk-RYFF55PV.js';
7
7
  import { isMarkedMock, reportMisconfiguration, DEEP_CHILDREN } from './chunk-Q7L4AWI7.js';
8
8
  import { count, displayPath } from './chunk-M3NTXLZG.js';
9
9
  import { moduleMocksAssert, moduleMocksNoOp, narrow, adoptMock, observableFailures, strictModePassthrough, createSpyFromClassReturns, captureArg, eventLoopUntil, mockConstructor, createSpyFromClassLiveDomNode } from './chunk-5FQZGJLJ.js';
@@ -1,6 +1,6 @@
1
1
  import { guardAccessorSpies, spyOnAccessorByRedefine } from './chunk-TXDBDMIL.js';
2
2
  import { DISPOSE } from './chunk-PC3FAZT3.js';
3
- import { isFastSpy, FAST_SPY_BRAND, isThenable } from './chunk-LA2CMIPM.js';
3
+ import { isFastSpy, FAST_SPY_BRAND, isThenable } from './chunk-RYFF55PV.js';
4
4
  import { setSharedHelperSink, hooksOf } from './chunk-Q7L4AWI7.js';
5
5
  import { mockConstructor } from './chunk-5FQZGJLJ.js';
6
6
  import { withDocs } from './chunk-ENCEEH23.js';
@@ -768,11 +768,15 @@ function installSettledResultsPolyfill(mockFn) {
768
768
  // src/lib/stack-frames.ts
769
769
  var FOREIGN_FRAME = /node_modules|node:internal|\(node:/;
770
770
  var LIBRARY_FRAME = /[/\\]vitest-auto-spy[/\\]dist[/\\]|[/\\]\.vite[/\\]deps[^/\\]*[/\\]vitest-auto-spy[^/\\]*\.js/;
771
+ var HELPER_FRAME = /\b__VITEST_HELPER__\b/;
772
+ function isLibraryFrame(frame) {
773
+ return LIBRARY_FRAME.test(frame) || HELPER_FRAME.test(frame);
774
+ }
771
775
  function stackFrames(stack) {
772
776
  return String(stack).split("\n").filter((line) => /^\s*at\s/.test(line)).map((line) => line.trim());
773
777
  }
774
778
  function ownFrames(frames, limit) {
775
- const outside = frames.filter((frame) => !LIBRARY_FRAME.test(frame));
779
+ const outside = frames.filter((frame) => !isLibraryFrame(frame));
776
780
  const own = outside.filter((frame) => !FOREIGN_FRAME.test(frame));
777
781
  const shown = [own, outside].find((candidates) => candidates.length > 0) ?? frames;
778
782
  return shown.slice(0, limit);
@@ -1,4 +1,4 @@
1
- import { createRunnerMockAdapter } from './chunk-GTDJ22IQ.js';
1
+ import { createRunnerMockAdapter } from './chunk-B6C3K27C.js';
2
2
  import { SWEEP_SENTINEL } from './chunk-YHDBRBGU.js';
3
3
  import { hasMockAdapter, registerMockAdapter } from './shared-state.js';
4
4
  import { vi } from 'vitest';
package/dist/cli.js CHANGED
@@ -3421,7 +3421,7 @@ function checkForeignPragma(graph) {
3421
3421
  }
3422
3422
 
3423
3423
  // src/cli/checks/export-map.generated.ts
3424
- var EXPORT_MAP_VERSION = "5.35.0";
3424
+ var EXPORT_MAP_VERSION = "5.36.0";
3425
3425
  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/diagnostics vitest-auto-spy/angular/doubles vitest-auto-spy/angular/matchers 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 vitest-auto-spy/perf-reporter";
3426
3426
  var EXPORTED_BY = {
3427
3427
  AccessorImplementations: "0 1 2 3 4 12 20 21 22",
@@ -3701,6 +3701,7 @@ var EXPORTED_BY = {
3701
3701
  PendingRequestsOptions: "13",
3702
3702
  PerTestHandle: "23",
3703
3703
  PlatformOverrides: "14",
3704
+ PluginRule: "25",
3704
3705
  PropStubValue: "0 1 2 3 4 20 21 22",
3705
3706
  PrototypePollutionReaction: "23",
3706
3707
  provideActivatedRoute: "17",
package/dist/console.js CHANGED
@@ -1,10 +1,10 @@
1
- import { useVitestAdapter } from './chunk-YWSOJH5C.js';
2
- import './chunk-GTDJ22IQ.js';
1
+ import { useVitestAdapter } from './chunk-YNXQ2WFH.js';
2
+ import './chunk-B6C3K27C.js';
3
3
  import './chunk-YHDBRBGU.js';
4
4
  import './chunk-TXDBDMIL.js';
5
5
  import './chunk-FOL5RH22.js';
6
6
  import './chunk-PC3FAZT3.js';
7
- import { createFunctionSpy } from './chunk-LA2CMIPM.js';
7
+ import { createFunctionSpy } from './chunk-RYFF55PV.js';
8
8
  import './chunk-Q7L4AWI7.js';
9
9
  import './chunk-M3NTXLZG.js';
10
10
  import './chunk-5FQZGJLJ.js';
@@ -11,6 +11,7 @@ declare const plugin: Plugin.AutoSpyEslintPlugin;
11
11
  declare namespace plugin {
12
12
  export type AutoSpyEslintPlugin = Plugin.AutoSpyEslintPlugin;
13
13
  export type FlatConfig = Plugin.FlatConfig;
14
+ export type PluginRule = Plugin.PluginRule;
14
15
  export type RuleSeverity = Plugin.RuleSeverity;
15
16
  }
16
17
 
@@ -248,12 +248,24 @@ interface RuleModule {
248
248
  type RuleSeverity = 'error' | 'off' | 'warn';
249
249
  /** A flat-config object: the plugin under its name, plus the rules it turns on. */
250
250
  interface FlatConfig {
251
- plugins: Record<string, unknown>;
251
+ plugins: Record<string, AutoSpyEslintPlugin>;
252
252
  rules: Record<string, RuleSeverity>;
253
253
  }
254
+ /**
255
+ * One visitor of a published rule. A method signature is bivariant in its parameter, which lets a
256
+ * visitor typed on its selector's node fit ESLint's `(...args: any[]) => void`.
257
+ */
258
+ type PluginRuleVisitor = {
259
+ visit(node: unknown): void;
260
+ }['visit'];
261
+ /** A rule as the plugin publishes it: ESLint calls `create`, so no caller needs its context type. */
262
+ interface PluginRule {
263
+ meta: RuleModule['meta'];
264
+ create(context: never): Record<string, PluginRuleVisitor>;
265
+ }
254
266
  /** The plugin object, as ESLint consumes it. */
255
267
  interface AutoSpyEslintPlugin {
256
- rules: Record<string, RuleModule>;
268
+ rules: Record<string, PluginRule>;
257
269
  configs: {
258
270
  recommended: FlatConfig;
259
271
  strict: FlatConfig;
@@ -262,4 +274,4 @@ interface AutoSpyEslintPlugin {
262
274
  }
263
275
  declare const plugin: AutoSpyEslintPlugin;
264
276
 
265
- export { type AutoSpyEslintPlugin, type FlatConfig, type RuleSeverity, plugin as default };
277
+ export { type AutoSpyEslintPlugin, type FlatConfig, type PluginRule, type RuleSeverity, plugin as default };
package/dist/index.js CHANGED
@@ -1609,11 +1609,15 @@ function installSettledResultsPolyfill(mockFn) {
1609
1609
  // src/lib/stack-frames.ts
1610
1610
  var FOREIGN_FRAME = /node_modules|node:internal|\(node:/;
1611
1611
  var LIBRARY_FRAME = /[/\\]vitest-auto-spy[/\\]dist[/\\]|[/\\]\.vite[/\\]deps[^/\\]*[/\\]vitest-auto-spy[^/\\]*\.js/;
1612
+ var HELPER_FRAME = /\b__VITEST_HELPER__\b/;
1613
+ function isLibraryFrame(frame) {
1614
+ return LIBRARY_FRAME.test(frame) || HELPER_FRAME.test(frame);
1615
+ }
1612
1616
  function stackFrames(stack) {
1613
1617
  return String(stack).split("\n").filter((line) => /^\s*at\s/.test(line)).map((line) => line.trim());
1614
1618
  }
1615
1619
  function ownFrames(frames, limit) {
1616
- const outside = frames.filter((frame) => !LIBRARY_FRAME.test(frame));
1620
+ const outside = frames.filter((frame) => !isLibraryFrame(frame));
1617
1621
  const own = outside.filter((frame) => !FOREIGN_FRAME.test(frame));
1618
1622
  const shown = [own, outside].find((candidates) => candidates.length > 0) ?? frames;
1619
1623
  return shown.slice(0, limit);
package/dist/jasmine.js CHANGED
@@ -1,13 +1,13 @@
1
1
  import { enableJasmineCompat } from './chunk-3ZBRBPOL.js';
2
2
  export { enableJasmineCompat } from './chunk-3ZBRBPOL.js';
3
- import { createSpyFromClass } from './chunk-F4URI3UE.js';
4
- import { useVitestAdapter } from './chunk-YWSOJH5C.js';
5
- import './chunk-GTDJ22IQ.js';
3
+ import { createSpyFromClass } from './chunk-6KP5QMMV.js';
4
+ import { useVitestAdapter } from './chunk-YNXQ2WFH.js';
5
+ import './chunk-B6C3K27C.js';
6
6
  import './chunk-YHDBRBGU.js';
7
7
  import './chunk-TXDBDMIL.js';
8
8
  import './chunk-FOL5RH22.js';
9
9
  import './chunk-PC3FAZT3.js';
10
- import { createFunctionSpy } from './chunk-LA2CMIPM.js';
10
+ import { createFunctionSpy } from './chunk-RYFF55PV.js';
11
11
  import { reportMisconfiguration, misconfigurationThrows } from './chunk-Q7L4AWI7.js';
12
12
  import './chunk-M3NTXLZG.js';
13
13
  import { jasmineGlobals, jasmineClock, jasmineAutoSpies, jasmineTimeout } from './chunk-5FQZGJLJ.js';
package/dist/nestjs.js CHANGED
@@ -1,13 +1,13 @@
1
- import { createSpyForToken } from './chunk-464WKNEQ.js';
2
- export { trackInjections } from './chunk-464WKNEQ.js';
3
- import { createSpyFromClass } from './chunk-F4URI3UE.js';
4
- import { useVitestAdapter } from './chunk-YWSOJH5C.js';
5
- import './chunk-GTDJ22IQ.js';
1
+ import { createSpyForToken } from './chunk-2TZCJ3MM.js';
2
+ export { trackInjections } from './chunk-2TZCJ3MM.js';
3
+ import { createSpyFromClass } from './chunk-6KP5QMMV.js';
4
+ import { useVitestAdapter } from './chunk-YNXQ2WFH.js';
5
+ import './chunk-B6C3K27C.js';
6
6
  import './chunk-YHDBRBGU.js';
7
7
  import './chunk-TXDBDMIL.js';
8
8
  import './chunk-FOL5RH22.js';
9
9
  import './chunk-PC3FAZT3.js';
10
- import './chunk-LA2CMIPM.js';
10
+ import './chunk-RYFF55PV.js';
11
11
  import './chunk-Q7L4AWI7.js';
12
12
  import './chunk-M3NTXLZG.js';
13
13
  import { nestUnitRefusals } from './chunk-5FQZGJLJ.js';
package/dist/node.cjs CHANGED
@@ -1240,11 +1240,15 @@ function installSettledResultsPolyfill(mockFn) {
1240
1240
  // src/lib/stack-frames.ts
1241
1241
  var FOREIGN_FRAME = /node_modules|node:internal|\(node:/;
1242
1242
  var LIBRARY_FRAME = /[/\\]vitest-auto-spy[/\\]dist[/\\]|[/\\]\.vite[/\\]deps[^/\\]*[/\\]vitest-auto-spy[^/\\]*\.js/;
1243
+ var HELPER_FRAME = /\b__VITEST_HELPER__\b/;
1244
+ function isLibraryFrame(frame) {
1245
+ return LIBRARY_FRAME.test(frame) || HELPER_FRAME.test(frame);
1246
+ }
1243
1247
  function stackFrames(stack) {
1244
1248
  return String(stack).split("\n").filter((line) => /^\s*at\s/.test(line)).map((line) => line.trim());
1245
1249
  }
1246
1250
  function ownFrames(frames, limit) {
1247
- const outside = frames.filter((frame) => !LIBRARY_FRAME.test(frame));
1251
+ const outside = frames.filter((frame) => !isLibraryFrame(frame));
1248
1252
  const own = outside.filter((frame) => !FOREIGN_FRAME.test(frame));
1249
1253
  const shown = [own, outside].find((candidates) => candidates.length > 0) ?? frames;
1250
1254
  return shown.slice(0, limit);
package/dist/node.js CHANGED
@@ -1129,11 +1129,15 @@ function installSettledResultsPolyfill(mockFn) {
1129
1129
  // src/lib/stack-frames.ts
1130
1130
  var FOREIGN_FRAME = /node_modules|node:internal|\(node:/;
1131
1131
  var LIBRARY_FRAME = /[/\\]vitest-auto-spy[/\\]dist[/\\]|[/\\]\.vite[/\\]deps[^/\\]*[/\\]vitest-auto-spy[^/\\]*\.js/;
1132
+ var HELPER_FRAME = /\b__VITEST_HELPER__\b/;
1133
+ function isLibraryFrame(frame) {
1134
+ return LIBRARY_FRAME.test(frame) || HELPER_FRAME.test(frame);
1135
+ }
1132
1136
  function stackFrames(stack) {
1133
1137
  return String(stack).split("\n").filter((line) => /^\s*at\s/.test(line)).map((line) => line.trim());
1134
1138
  }
1135
1139
  function ownFrames(frames, limit) {
1136
- const outside = frames.filter((frame) => !LIBRARY_FRAME.test(frame));
1140
+ const outside = frames.filter((frame) => !isLibraryFrame(frame));
1137
1141
  const own = outside.filter((frame) => !FOREIGN_FRAME.test(frame));
1138
1142
  const shown = [own, outside].find((candidates) => candidates.length > 0) ?? frames;
1139
1143
  return shown.slice(0, limit);
package/dist/rstest.js CHANGED
@@ -1,12 +1,12 @@
1
- export { adoptMock, asInstance, asInstances, asSpy, assertMocked, captureArg, createFixture, createFixtureFactory, createLog, createMock, createSpyClass, createSpyFromInstance, describeDuplicateCopies, expectAllEmissions, expectCompletion, expectEmission, expectEmissions, expectError, expectNoEmission, flushEventLoop, flushEventLoopUntil, getPackageCopies, mockConstructor, mockDeep, moduleNamespace, narrow, outOfType, restoreSpiedInstance, setEmissionTimeout, settleDynamicImport, spyOnOwnMethod, spyOnVoidMethod, stubConstructor, withOverrides } from './chunk-GAE5EGO7.js';
1
+ export { adoptMock, asInstance, asInstances, asSpy, assertMocked, captureArg, createFixture, createFixtureFactory, createLog, createMock, createSpyClass, createSpyFromInstance, describeDuplicateCopies, expectAllEmissions, expectCompletion, expectEmission, expectEmissions, expectError, expectNoEmission, flushEventLoop, flushEventLoopUntil, getPackageCopies, mockConstructor, mockDeep, moduleNamespace, narrow, outOfType, restoreSpiedInstance, setEmissionTimeout, settleDynamicImport, spyOnOwnMethod, spyOnVoidMethod, stubConstructor, withOverrides } from './chunk-6VPSS4BA.js';
2
2
  export { countMockedProps, mockAccessorsProp, mockReadonlyProp, mockReadonlyPropGetter, mockValueProp, reportPropsOutsideHooks, restoreMockedProps } from './chunk-FWXDSPZD.js';
3
- export { autoMocked, clearAutoSpy, clearAutoSpyDefaults, createAutoMock, createSpyFromClass, registerAutoSpyDefaults, resetAutoSpy } from './chunk-F4URI3UE.js';
3
+ export { autoMocked, clearAutoSpy, clearAutoSpyDefaults, createAutoMock, createSpyFromClass, registerAutoSpyDefaults, resetAutoSpy } from './chunk-6KP5QMMV.js';
4
4
  import './chunk-W5EXSI22.js';
5
- import { createRunnerMockAdapter } from './chunk-GTDJ22IQ.js';
5
+ import { createRunnerMockAdapter } from './chunk-B6C3K27C.js';
6
6
  import './chunk-TXDBDMIL.js';
7
7
  import './chunk-FOL5RH22.js';
8
8
  import './chunk-PC3FAZT3.js';
9
- export { createFunctionSpy, errorHandler } from './chunk-LA2CMIPM.js';
9
+ export { createFunctionSpy, errorHandler } from './chunk-RYFF55PV.js';
10
10
  import './chunk-Q7L4AWI7.js';
11
11
  import './chunk-M3NTXLZG.js';
12
12
  import './chunk-5FQZGJLJ.js';
package/dist/setup.js CHANGED
@@ -1245,11 +1245,15 @@ function storagePrototype() {
1245
1245
  // src/lib/stack-frames.ts
1246
1246
  var FOREIGN_FRAME = /node_modules|node:internal|\(node:/;
1247
1247
  var LIBRARY_FRAME = /[/\\]vitest-auto-spy[/\\]dist[/\\]|[/\\]\.vite[/\\]deps[^/\\]*[/\\]vitest-auto-spy[^/\\]*\.js/;
1248
+ var HELPER_FRAME = /\b__VITEST_HELPER__\b/;
1249
+ function isLibraryFrame(frame) {
1250
+ return LIBRARY_FRAME.test(frame) || HELPER_FRAME.test(frame);
1251
+ }
1248
1252
  function stackFrames(stack) {
1249
1253
  return String(stack).split("\n").filter((line) => /^\s*at\s/.test(line)).map((line) => line.trim());
1250
1254
  }
1251
1255
  function ownFrames(frames, limit) {
1252
- const outside = frames.filter((frame) => !LIBRARY_FRAME.test(frame));
1256
+ const outside = frames.filter((frame) => !isLibraryFrame(frame));
1253
1257
  const own = outside.filter((frame) => !FOREIGN_FRAME.test(frame));
1254
1258
  const shown = [own, outside].find((candidates) => candidates.length > 0) ?? frames;
1255
1259
  return shown.slice(0, limit);
@@ -1442,12 +1446,24 @@ function recordEntry(tracking, type, listener, options) {
1442
1446
  function forgetEntries(tracking, type, listener, capture) {
1443
1447
  tracking.entries = tracking.entries.filter((entry) => entry.type !== type || entry.listener !== listener || entry.capture !== capture);
1444
1448
  }
1449
+ var OWN_MODULE_FRAME = /stray-listeners\.[jt]s/;
1450
+ var ENVIRONMENT_FRAME = /[/\\]node_modules[/\\](?:jsdom|happy-dom|@asamuzakjp[/\\][^/\\]+)[/\\]/;
1451
+ var environmentVerdicts = /* @__PURE__ */ new WeakMap();
1452
+ function isEnvironmentEntry(entry) {
1453
+ let verdict = environmentVerdicts.get(entry);
1454
+ if (verdict === void 0) {
1455
+ const caller = stackFrames(entry.trace.stack).find((frame) => !OWN_MODULE_FRAME.test(frame) && !isLibraryFrame(frame));
1456
+ verdict = caller !== void 0 && ENVIRONMENT_FRAME.test(caller);
1457
+ environmentVerdicts.set(entry, verdict);
1458
+ }
1459
+ return verdict;
1460
+ }
1445
1461
  function strayEntries(tracking) {
1446
1462
  const { baseline } = tracking;
1447
1463
  if (!baseline) {
1448
1464
  return [];
1449
1465
  }
1450
- return tracking.entries.filter((entry) => !baseline.has(entry));
1466
+ return tracking.entries.filter((entry) => !baseline.has(entry) && !isEnvironmentEntry(entry));
1451
1467
  }
1452
1468
  function installTracking({ name, target }) {
1453
1469
  const host = target;
@@ -1549,7 +1565,6 @@ function countStrayListeners(targets) {
1549
1565
  }
1550
1566
  return total;
1551
1567
  }
1552
- var OWN_MODULE_FRAME = /stray-listeners\.[jt]s/;
1553
1568
  function describeEntry(name, entry) {
1554
1569
  return { target: name, type: entry.type, ...describeOriginOf(entry, OWN_MODULE_FRAME) };
1555
1570
  }
@@ -456,8 +456,10 @@ interface StableOptions {
456
456
  * one component. On expiry this throws the cause instead, and names the two things that produce it.
457
457
  *
458
458
  * Pass `{ label }` when a spec awaits more than one fixture, so the failure says which.
459
+ *
460
+ * Any fixture with `whenStable()` is taken — Angular 22.2's `TestBed.createDirective` fixture too.
459
461
  */
460
- declare function stable(fixture: ComponentFixture<unknown>, options?: StableOptions): Promise<void>;
462
+ declare function stable(fixture: Pick<ComponentFixture<unknown>, 'whenStable'>, options?: StableOptions): Promise<void>;
461
463
 
462
464
  /**
463
465
  * `setInputs` — change a component's inputs after it is rendered, and let Angular catch up.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vitest-auto-spy",
3
- "version": "5.36.0",
3
+ "version": "5.37.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",
@@ -281,7 +281,7 @@ it('loads', async () => {
281
281
  | the same 100-line model literal copied into eight specs (`TS1117`) | one `createFixtureFactory<T>(defaults)`; specs call it with what they change |
282
282
  | `'params' in link` ladders, or a cast, to pick a union branch | `narrow.byKey(link, 'params')` / `narrow.observable(x)` |
283
283
  | `{ ...modelInstance, flag: true }` losing every getter | `withOverrides(modelInstance, { flag: true })` |
284
- | `NG0303` / `NG0304` / silence from a directive spec | `createDirectiveHost({ template, scope: [Module] })` |
284
+ | `NG0303` / `NG0304` / silence from a directive spec | `createDirectiveHost({ template, scope: [Module] })`; Angular ≥ 22.2, no host markup needed: `TestBed.createDirective(Dir, { tagName, bindings })`, which `stable` takes |
285
285
  | a hand-written `class MockChartComponent` restating a child's selector | `createComponentStub(ChartComponent)` (`/angular`) — selector, inputs, outputs read from `ɵcmp` |
286
286
  | a `TestingStorage` class for `localStorage` / `sessionStorage` | `stubWebStorage('localStorage', { items })` from `/dom-stubs` — `snapshot()` to assert |
287
287
  | a hand-written `MockWorker` whose `addEventListener` sets `onmessage` | `stubWorker({ respond })` from `/dom-stubs` — `last.emit(data)`, `last.messages` |
@@ -544,7 +544,7 @@ from `vitest-auto-spy/angular-router` rather than at `provideAutoSpy`.
544
544
 
545
545
  **`no-self-called-spy` reports a test that calls the spied method itself and then asserts that it was called** — `vi.spyOn(obj, 'm')`, then `obj.m(…)` written by the test body after it, then a positive `toHaveBeenCalled*`. Such a test proves that `emit` calls `emit`: delete the template binding or subscription its title names and it stays green. Drive the real trigger instead and keep the assertion. Order decides it, so a call written **before** the spy is arrangement and is never reported; neither is a negated matcher or `toHaveBeenCalledTimes(0)`, a call whose record a `mockClear` / `vi.clearAllMocks()` drops before the assertion reads it, an assertion whose arguments the call did not pass, a spy installed in a hook, or a call made from inside a callback.
546
546
 
547
- **`no-redundant-mock-reset` reports a mock reset in a hook that the runner already performs** — `vi.clearAllMocks()` / `vi.resetAllMocks()` / `vi.restoreAllMocks()` and the per-mock `mockClear()` / `mockReset()` / `mockRestore()` — as the first statement of a `beforeEach` that no other `beforeEach` precedes, or a clear as the last statement of an `afterEach`. Vitest resets in `onBeforeTryTask`, ahead of every `beforeEach` chain and never after a test, so a reset that anything ran before, or a restore in `afterEach` / `afterAll` (the only thing that takes the last test's spies off before the `afterAll` hooks), is left alone. **It is silent until it knows the configuration**: options first — `['error', { clearMocks: true, restoreMocks: true }]`, or `{ configFile: 'path/to/runner.config.ts' }` for a runner config the search cannot find — then a `vitest.config.*` / `vite.config.*` searched upwards from the file and read as text; with neither, it reports nothing. The flag has to match the call, not the family: `restoreMocks` walks only the spies `vi.spyOn` installed, so it makes a `vi.clearAllMocks()` dead only where the receiver is such a spy. `--fix` deletes the line only in a file whose only before-hook it is, and offers a suggestion everywhere else; a reset in the middle of a test body is never reported, because there it separates two arrangements inside one test.
547
+ **`no-redundant-mock-reset` reports a mock reset in a hook that the runner already performs** — `vi.clearAllMocks()` / `vi.resetAllMocks()` / `vi.restoreAllMocks()` and the per-mock `mockClear()` / `mockReset()` / `mockRestore()` — as the first statement of a `beforeEach` that no other `beforeEach` precedes, or a clear as the last statement of an `afterEach`. Vitest resets in `onBeforeTryTask`, ahead of every `beforeEach` chain and never after a test, so a reset that anything ran before, or a restore in `afterEach` / `afterAll` (the only thing that takes the last test's spies off before the `afterAll` hooks), is left alone. **It is silent until it knows the configuration**: options first — `['error', { clearMocks: true, restoreMocks: true }]`, or `{ configFile: 'path/to/runner.config.ts' }` for a runner config the search cannot find — then a `vitest.config.*` / `vite.config.*` searched upwards from the file and read as text; with neither, it reports nothing. A config that leaves `clearMocks` out counts it on from Vitest 5, whose default it is; a config a factory or `mergeConfig` assembles in another module is not read through, so write its flags beside it — `{ configFile: 'vitest.config.ts', clearMocks: true }`. The flag has to match the call, not the family: `restoreMocks` walks only the spies `vi.spyOn` installed, so it makes a `vi.clearAllMocks()` dead only where the receiver is such a spy. `--fix` deletes the line only in a file whose only before-hook it is, and offers a suggestion everywhere else; a reset in the middle of a test body is never reported, because there it separates two arrangements inside one test.
548
548
 
549
549
  **`no-unasserted-argument` reports a bare `expect(spy).toHaveBeenCalled()` where the file itself says the arguments are the point** — either the same subject is pinned with `toHaveBeenCalledWith(…)` in another test of the file, or the test's title says `with` and its body asserts nothing but bare calls. Narrower than `vitest/prefer-called-with` on purpose: on one 2032-file suite that reports 1941 times in 360 files and this one 175 in 90. `not.toHaveBeenCalled()` and the counting matchers are never reported. `warn` rather than `error`, because the repair is the argument list the test should have named — `toHaveBeenCalledWith(…)`, `toHaveBeenCalledExactlyOnceWith(…)`, or `mustBeCalledWith(…)` where the double is configured — and that is the one thing the rule cannot write.
550
550