vitest-auto-spy 3.11.0 → 3.12.1

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
@@ -77,7 +77,8 @@ Do you have a real class at runtime?
77
77
  ├── yes, and reads chain (a.b.c()) → mockDeep<T>(overrides?) → DeepMockProxy<T>
78
78
  ├── yes, and CALLS chain (a.b().c()) → mockDeep<T>({}, { selfReturning: true })
79
79
  └── no, it is only READ (DTO, config, route snapshot)
80
- → createMock<T>(partial?) → T (no spies)
80
+ ├── one spec, a couple of fields → createMock<T>(partial?) → T (no spies)
81
+ └── many specs, one shared model → createFixtureFactory<T>(defaults) → (overrides?) => T
81
82
 
82
83
  One standalone function? → createFunctionSpy<Fn>('name')
83
84
  Code under test does `new Foo()`? → a real class? createSpyClass(Foo)
@@ -97,6 +98,16 @@ noise scales with the number of them.
97
98
  `createMock<T>()` is the one to reach for on data shapes — it returns a plain `T`, so it satisfies a
98
99
  `no-type-assertion` lint rule without an `eslint-disable` on every fixture.
99
100
 
101
+ **`createFixture<T>(defaults, overrides?)` / `createFixtureFactory<T>(defaults)` are for the model
102
+ that more than one spec builds.** The difference from `createMock` is the `defaults` argument: it is
103
+ a **complete** `T`, checked in full, in one place — so a field the model dropped fails there instead
104
+ of in eight copies of a hundred-line literal. Overrides are deep-partial-checked and merge leaf by
105
+ leaf; an overridden array replaces the default one. Every call hands back a fresh object and the
106
+ defaults are copied at build time, which is what keeps one test's mutation out of the next test —
107
+ across files, under `isolate: false`. The copy is deep through plain objects and arrays only: a
108
+ `Date`, a `Map` or a class instance travels by reference, because rebuilding it would strip its
109
+ prototype. Defaults that are a class instance with getters go through `withOverrides()` first.
110
+
100
111
  **`mockDeep` builds depth on property access, not on calls** — the distinction the tree now spells
101
112
  out, and the one that costs an afternoon otherwise. `mock.repo.user.find()` chains because every hop
102
113
  but the last is a _read_. A node that is **called** returns what it was configured to return, and by
@@ -657,6 +668,14 @@ sub-tree). The key is non-enumerable, so a spread does not copy it, and there is
657
668
  `[Symbol.dispose]` on a host mock restores the original implementation instead — call
658
669
  `resetAutoSpy(spy)` there. If the project does not transpile `using`, call `spy[Symbol.dispose]()`.
659
670
 
671
+ `src/lib/dispose-symbol.ts` installs `Symbol.dispose` when the realm has none, and `DISPOSE` — not
672
+ `Symbol.dispose` — is what library code compares against. Node 22 has no explicit resource
673
+ management in V8: it patches the symbol in itself, as `Symbol.for('nodejs.dispose')`, onto the main
674
+ realm only, so under `jsdom` / `happy-dom` (a bare `vm` context) it is absent, the downlevelled
675
+ `using` throws out of `tslib.__addDisposableResource`, and `spy[Symbol.dispose]` degrades into a
676
+ property named `"undefined"`. The shim is the same registry symbol, so it is identical to Node's
677
+ across realms.
678
+
660
679
  ---
661
680
 
662
681
  ## 8. Observable assertions (core entry — no rxjs needed)
@@ -1539,6 +1558,37 @@ assertNgModuleScopes(DirectivesModule, PipesModule); // throws, naming the modul
1539
1558
  Then declare what the spec needs in the TestBed module directly. Pass only modules you expect to
1540
1559
  bring declarations — a providers-only module is legitimately empty.
1541
1560
 
1561
+ ### A component whose own definition has a hole in it
1562
+
1563
+ The same bundle, one level down. A component's `providers`, `viewProviders` and compiled scope are
1564
+ **baked into `ɵcmp` when its module executes**, not read at `createComponent` time — so a barrel
1565
+ split into a chunk that has not run yet produces a definition with `undefined` in those lists.
1566
+ Angular finds out much later, from inside itself, with a stack that names neither the barrel nor the
1567
+ component:
1568
+
1569
+ ```
1570
+ TypeError: Cannot read properties of undefined (reading 'provide')
1571
+ ❯ resolveProvider render3/di_setup.ts:95
1572
+ ```
1573
+
1574
+ Both obvious cures fail: `await import('@scope/lib')` at the top of `beforeEach` is already too late,
1575
+ and a static import at the top of the spec does not fix the order this bundler emits. Worse, the spec
1576
+ that breaks is one nobody touched — chunk boundaries move with file *contents*, so editing a type
1577
+ next door is enough.
1578
+
1579
+ ```ts
1580
+ import { assertComponentDefIntact } from 'vitest-auto-spy/angular';
1581
+
1582
+ assertComponentDefIntact(HoverMenuComponent); // HoverMenuComponent.ɵcmp.providers[0] is undefined
1583
+ const fixture = TestBed.createComponent(HoverMenuComponent);
1584
+ ```
1585
+
1586
+ It walks the three lists, nested arrays and forward-reference thunks included, and answers the
1587
+ related `Cannot read properties of undefined (reading 'ɵcmp')` from `imports: [Cmp]` as well — there
1588
+ the class reference itself never arrived, and the message names the argument position. Directives
1589
+ (`ɵdir`) are checked the same way. Neither this nor `assertNgModuleScopes` fixes the build; both
1590
+ replace a stack inside `@angular/core` with a line naming what is missing.
1591
+
1542
1592
  ### Four silent failures, as one setup line
1543
1593
 
1544
1594
  ```ts
@@ -2015,6 +2065,9 @@ packages, which a subpath export can never be.
2015
2065
  | `overrideComponentProvider(…): the override did not apply` | the component injects a different token, or a later `overrideProvider` won | pass the token the component actually injects (a base class, an `InjectionToken`), and override after nothing else re-configures |
2016
2066
  | `the test ended with N unflushed HttpTestingController request(s)` | `enableAngularDiagnostics({ pendingRequests })` — nothing answered them | flush each (`controller.expectOne(url).flush(body)`), or `controller.verify()` where absence is the assertion (§13) |
2017
2067
  | `NgModule(s) with an empty runtime scope: …` | `ngModuleScopes` (or `assertNgModuleScopes`) — an AOT test bundle stripped `ɵɵsetNgModuleScope`, so the import contributes nothing | declare what the spec needs in the TestBed module directly; pass only modules expected to bring declarations (§13) |
2068
+ | `Cannot read properties of undefined (reading 'provide')`, stack inside `render3/di_setup` | a barrel chunk had not run when the component's definition was built, so a provider slot is `undefined` | `assertComponentDefIntact(Cmp)` before `createComponent` — it names the list and the index (§13) |
2069
+ | `assertComponentDefIntact(): argument N is undefined, which carries no ɵcmp or ɵdir` | the class reference itself never arrived — the `Cannot read properties of undefined (reading 'ɵcmp')` case | the same split barrel; import the component from its own module, not the barrel (§13) |
2070
+ | `TS1117: An object literal cannot have multiple properties with the same name`, in a fixture | a hundred-line model literal copied into eight specs and edited independently | one `createFixtureFactory<T>(defaults)`; note the runtime keeps the **second** key, so do not auto-fix by dropping one (§12) |
2018
2071
  | `configureTestingModule was given N schema(s) that can never apply` | `enableAngularDiagnostics({ deadSchemas })` — `schemas` next to a standalone component, with `declarations` empty | drop the `schemas` entry and put the missing directive/pipe in the component's own `imports`, or use `createDirectiveHost` (§13) |
2019
2072
  | `injectSpy(X): the injector returned a plain instance, not an auto-spy` | the token is provided for real; a `console.warn` on its own, a **throw** under `enableAngularDiagnostics({ unspiedProviders })` | `provideAutoSpy(X)` / `provideAutoSpyForToken(TOKEN)`, or `TestBed.inject(X)` if the real thing is what the spec wants |
2020
2073
  | `trackInjections(...).get(X): that token is not tracked by this log` | `get` only answers for tokens whose providers the log created | add the token to the `trackInjections([...])` list, or read it from the injector directly (§13) |
package/README.md CHANGED
@@ -16,7 +16,7 @@ identical API, with **RxJS** spies and **Angular / NestJS / React / Vue·Pinia /
16
16
  [![npm version](https://img.shields.io/npm/v/vitest-auto-spy?color=brightgreen&logo=npm)](https://www.npmjs.com/package/vitest-auto-spy)
17
17
  [![npm downloads](https://img.shields.io/npm/dm/vitest-auto-spy?color=brightgreen&logo=npm)](https://www.npmjs.com/package/vitest-auto-spy)
18
18
  [![CI](https://github.com/ASDAlexey/vitest-auto-spy/actions/workflows/ci.yml/badge.svg)](https://github.com/ASDAlexey/vitest-auto-spy/actions/workflows/ci.yml)
19
- [![minzipped size](https://img.shields.io/badge/minzip-14.3%20kB-brightgreen)](#install)
19
+ [![minzipped size](https://img.shields.io/badge/minzip-14.6%20kB-brightgreen)](#install)
20
20
  [![types](https://img.shields.io/npm/types/vitest-auto-spy?logo=typescript&logoColor=white)](https://www.npmjs.com/package/vitest-auto-spy)
21
21
  [![coverage](https://img.shields.io/badge/coverage-100%25-brightgreen)](https://github.com/ASDAlexey/vitest-auto-spy/actions/workflows/ci.yml)
22
22
  [![license](https://img.shields.io/npm/l/vitest-auto-spy?color=blue)](./LICENSE)
@@ -49,7 +49,7 @@ identical API, with **RxJS** spies and **Angular / NestJS / React / Vue·Pinia /
49
49
  - ⚙️ Getter / setter spies via `accessorSpies`
50
50
  - 🧰 DI & mocking utilities — `provideAutoSpy` / `injectSpy` (Angular, NestJS, Vue), `createFunctionSpy`, `mockReadonlyProp` for signals
51
51
  - ⚡ Angular speed & zoneless helpers — `renderShallow` (**1.7×** on real component specs), `createWithAutoSpies`, `stable` / `flushEffects`, `settleResource` for `httpResource()`, `toHaveSignalValue`, per-file `TestBed` timings
52
- - 🧱 The providers a testing module cannot reach — `overrideComponentProvider` (which verifies the override actually applied), `provideAutoSpyForToken`, `assertNgModuleScopes`, `createDirectiveHost`
52
+ - 🧱 The providers a testing module cannot reach — `overrideComponentProvider` (which verifies the override actually applied), `provideAutoSpyForToken`, `assertNgModuleScopes`, `assertComponentDefIntact`, `createDirectiveHost`
53
53
  - 🚨 Failures that used to be silence — `enableAngularDiagnostics()` for dead NgModule imports, dead `schemas`, an unspied provider and unflushed HTTP requests; `trackInjections` for which collaborators the code actually asked for
54
54
  - 🔒 [Strict doubles](#strict-doubles--fail-on-a-method-nobody-configured) — `strict: true` / `onUnstubbedCall` fail on a method nobody configured, naming the class, the method and the arguments instead of answering `undefined`
55
55
  - ♻️ `using spy = createSpyFromClass(X)` — every double carries `[Symbol.dispose]`, so the `afterEach` that only reset one spy can go
@@ -58,7 +58,7 @@ identical API, with **RxJS** spies and **Angular / NestJS / React / Vue·Pinia /
58
58
  - ⏳ Waiting that is not a guess — `flushEventLoop`, `settleDynamicImport`, `flushEventLoopUntil`, and a clock that survives fake timers (`mockSystemTime`, `useCountingClock`)
59
59
  - 🌀 `fakeAsync` / `waitForAsync` on Vitest — one import of `vitest-auto-spy/zone`; zone.js stays out of every other entry
60
60
  - 🧩 Module mocks that prove they applied — `assertMocked`, `moduleNamespace`, for a `vi.mock()` a bundler quietly ignored
61
- - 🧾 Fixtures without casts — deep-partial `createMock`, `narrow()`, `withOverrides()`, `asInstances()`, `captureArg()`
61
+ - 🧾 Fixtures without casts — deep-partial `createMock`, `createFixture` / `createFixtureFactory`, `narrow()`, `withOverrides()`, `asInstances()`, `captureArg()`
62
62
  - 🚚 A migration you can verify — `compareTestRuns` on the two JSON reports, `diffByField` for the assertion the reporter collapses
63
63
  - 📏 Lint rules and one-line test-run hygiene — fourteen rules in `vitest-auto-spy/eslint-plugin` (two `--fix`, six suggestions), `setupAutoSpy()`
64
64
  - 🩺 [Editor diagnostics](#editor-diagnostics--webstorm--vs-code) — the same anti-patterns underlined while you type: native ESLint inspections in **WebStorm** and the other JetBrains IDEs, the ESLint extension in **VS Code**, no extra plugin either way
@@ -1171,6 +1171,40 @@ Rule of thumb: `createAutoMock` for a collaborator you **call** and assert on, `
1171
1171
  data shape you **read**. `createMock` is also the one place the `as` lives, so a suite under a
1172
1172
  `no-type-assertion` lint rule stops sprinkling `eslint-disable` over its fixtures.
1173
1173
 
1174
+ ### A model many specs build — `createFixture` / `createFixtureFactory`
1175
+
1176
+ `createMock` answers "this spec reads two fields of a big shape". The other habit is more expensive:
1177
+ a model with seventeen required fields, each with its own nested interface, copied into every spec
1178
+ that needs one. Measured on one migration shard, those copies alone produced **28 `TS1117`**
1179
+ diagnostics — a duplicate key in a literal, where the runtime keeps the *second* one.
1180
+
1181
+ ```ts
1182
+ import { createFixtureFactory } from 'vitest-auto-spy';
1183
+
1184
+ // article.fixture.ts — the model, written out once and checked in full
1185
+ export const anArticle = createFixtureFactory<Article>({
1186
+ id: '1',
1187
+ header: { title: '', subtitle: 'none' },
1188
+ tags: [],
1189
+ publishedAt: new Date(0),
1190
+ });
1191
+
1192
+ // in a spec — name only what this test is about
1193
+ const draft = anArticle({ header: { title: 'Draft' } }); // header.subtitle survives
1194
+ ```
1195
+
1196
+ `defaults` is a **complete** `T`, and that is the point rather than a chore: a field the model
1197
+ dropped six months ago fails in one place instead of in eight copies nobody re-checks. `Partial<T>`
1198
+ and `as T` both delete that diagnostic. Overrides are deep-partial-checked and merge leaf by leaf; an
1199
+ overridden array replaces the default one outright.
1200
+
1201
+ Every call returns a **new object**, and the defaults are copied when the factory is built — a
1202
+ fixture shared by reference is the most common way one test's mutation decides another's outcome, and
1203
+ under `isolate: false` that reaches across files. The copy is deep through plain objects and arrays
1204
+ and stops there: a `Date`, a `Map` or a class instance is carried across by reference rather than
1205
+ rebuilt without its prototype. For defaults that *are* a class instance with getters, snapshot them
1206
+ with `withOverrides()` first.
1207
+
1174
1208
  ## Synchronous methods
1175
1209
 
1176
1210
  ```ts
@@ -1302,10 +1336,14 @@ non-enumerable, so `{ ...spy }` does not carry it into a snapshot, and there is
1302
1336
  `[Symbol.asyncDispose]` — `resetAutoSpy` is synchronous, and `await using` falls back to `@@dispose`
1303
1337
  anyway.
1304
1338
 
1305
- **The method is ours; the `using` declaration is your toolchain's.** `Symbol.dispose` exists on every
1306
- runtime this package supports, so there is no feature detection; esbuild and `tsc` both downlevel the
1307
- declaration. If your setup does not transpile, call `spy[Symbol.dispose]()` or `resetAutoSpy(spy)`
1308
- directly. A standalone `createFunctionSpy` is **not** covered: it is a host-runner mock, and Vitest
1339
+ **The method is ours; the `using` declaration is your toolchain's.** esbuild and `tsc` both downlevel
1340
+ the declaration. If your setup does not transpile, call `spy[Symbol.dispose]()` or
1341
+ `resetAutoSpy(spy)` directly. On **Node 22** the package installs the missing `Symbol.dispose` when
1342
+ it loads: the downlevelled form reads the symbol off the global `Symbol`, and Node 22 patches it in
1343
+ only on its main realm — under Vitest's `jsdom` environment, whose globals come from a `vm` context,
1344
+ it is absent and `using` throws `TypeError: Symbol.dispose is not defined.`. The shim is
1345
+ `Symbol.for('nodejs.dispose')`, the same registry symbol Node uses, so the key is identical across
1346
+ realms; a runtime that already has the symbol (Node 24+, Bun) is left untouched. A standalone `createFunctionSpy` is **not** covered: it is a host-runner mock, and Vitest
1309
1347
  puts its own `[Symbol.dispose]` on those, which restores the original implementation rather than
1310
1348
  reverting this library's configuration.
1311
1349
 
@@ -1813,6 +1851,8 @@ single-purpose utility you can pick up independently — they all ride on the sa
1813
1851
  | `createFunctionSpy(name)` | core | A single standalone function spy with the full helper set (`calledWith`, `resolveWith`, `nextWith`, …) — no class needed |
1814
1852
  | `createAutoMock<T>(overrides?)` | core | Proxy-based spy from a **type/interface** alone ([details](#auto-mock-by-type-no-class-needed)) |
1815
1853
  | `createMock<T>(partial?)` | core | A plain, spy-free `T` built from the fields a test seeds — for data shapes, not collaborators |
1854
+ | `createFixture<T>(defaults, overrides?)` | core | One `T` from a complete, checked default plus what this test changes — a fresh copy every call |
1855
+ | `createFixtureFactory<T>(defaults)` | core | Somewhere to put that default: returns `(overrides?) => T`, with the defaults pinned at build time |
1816
1856
  | `createObservableWithValues(configs, opts?)` | `/rxjs` | Build a fake `Observable` emitting a precise sequence of values / errors / completion |
1817
1857
  | `consoleInfoSpy` / `consoleWarnSpy` / … | `/console` | Silent typed spies over the global `console`, installed on import ([details](#console-spies--vitest-auto-spyconsole)) |
1818
1858
  | `mockReadonlyProp(obj, prop, value)` | `/angular` | Overwrite a `readonly` property (incl. Angular signals) with a static value |
@@ -1850,6 +1890,7 @@ single-purpose utility you can pick up independently — they all ride on the sa
1850
1890
  | `registerFocusMatchers()` | `/setup` | Adds `expect(el).toHaveFocus()`, which names _why_ focus is elsewhere |
1851
1891
  | `overrideAutoSpy(Token, config?)` / `overrideComponentProvider(Cmp, Token, config?)` | `/angular` | Replace a dependency a component declares in its own `providers` |
1852
1892
  | `assertNgModuleScopes(...modules)` | `/angular` | Fail early when an AOT test bundle left an NgModule with no runtime declarations |
1893
+ | `assertComponentDefIntact(...components)` | `/angular` | Fail before rendering when a half-loaded barrel chunk left a hole in a component's own `providers` or scope |
1853
1894
  | `enableAngularDiagnostics(opts?)` / `assertNoPendingRequests()` | `/angular` | Dead NgModule imports, dead `schemas`, an unspied provider and unflushed HTTP requests, as failures ([details](#diagnostics--four-silent-failures-made-loud)) |
1854
1895
  | `trackInjections(tokens, opts?)` | `/angular`, `/nestjs` | Which collaborators DI actually constructed, recorded through provider factories — with the doubles attached |
1855
1896
  | `mockSignalProp(obj, prop, initial)` | `/angular` | Replace a signal-valued property with a real `WritableSignal`, and hand the writable handle back |
@@ -2342,6 +2383,31 @@ asSpy(TestBed.inject(CartService)); // CartService → Spy<CartService>, for the
2342
2383
 
2343
2384
  Both are the same object at runtime; only the view changes.
2344
2385
 
2386
+ ### Error → cure
2387
+
2388
+ Keyed by what the compiler prints, because that is what you have when you get there. The helper
2389
+ names are unfindable from these messages otherwise — no `TS2739` text contains the word `asInstance`.
2390
+
2391
+ | Message | What actually happened | Cure |
2392
+ | ------------------------------------------------------------------------------- | ----------------------------------------- | -------------------------------------------------- |
2393
+ | `TS2352 … 'Spy<X>' … Property 'accessorSpies' is missing in type 'X'` | `x as Spy<X>`, written by hand | `asSpy(x)` — never a double assertion |
2394
+ | `TS2739` / `TS2740`: `'Spy<X>' is missing … ` + a list of **private** fields | a spy handed to an API typed against `X` | `asInstance(spy)` |
2395
+ | `TS2345: Argument of type 'Spy<X>' is not assignable to parameter of type 'X'` | the same, in an argument | `asInstance(spy)`, or `asInstances(a, b, c)` |
2396
+ | `TS2322: Type 'Spy<X>' is not assignable to type 'Mocked<X>' …` | the variable was declared `Mocked<T>` | declare it `Spy<T>` |
2397
+ | `'AddPromiseSpyMethods<unknown>' is missing … from type 'WithMockReturnValue<…>'` | a generic class inferred as `Service<any>` | `asSpy<Service>(…)` / `injectSpy<Service>(…)` |
2398
+ | `TS2739 … 'Spy<X>' is missing …` **on a line with `injectSpy`** | the provider handed back the real object | `provideAutoSpy(X)`, or an honest `TestBed.inject` |
2399
+
2400
+ Two notes that cost real time when they are missing.
2401
+
2402
+ **The last row is word for word the second one**, and that is a property of the language rather than
2403
+ a flaw in the table: one message serves two different mistakes. The tell is the line it lands on.
2404
+
2405
+ **The error count does not go down monotonically.** TypeScript stops checking a call at the first bad
2406
+ argument, so "one error left" means "as many as there are unfixed arguments" — one file counted
2407
+ 40 → 1 → 1 → 1 → 0. Excess-property checking stops at the first unknown key of a literal for the same
2408
+ reason. The useful heuristic: if a file already contains one `asInstance`, it almost certainly needs
2409
+ another, and the next one is in that same file.
2410
+
2345
2411
  ## API reference
2346
2412
 
2347
2413
  | Export | Description |
@@ -2349,6 +2415,7 @@ Both are the same object at runtime; only the view changes.
2349
2415
  | `createSpyFromClass(Class, methodsOrConfig?)` | Build a fully-typed `Spy<T>` from a class |
2350
2416
  | `createAutoMock<T>(overrides?, config?)` | Build a `Spy<T>` from a **type/interface** alone (Proxy, no class); `{ returns, observablePropsToSpyOn }` configure it |
2351
2417
  | `createMock<T>(partial?)` | Build a plain, spy-free `T` from the fields a test seeds — for data shapes the code under test reads |
2418
+ | `createFixture<T>(defaults, overrides?)` / `createFixtureFactory<T>(defaults)` | A model written out and checked once, stamped into a fresh copy per test — for the fixture eight specs would otherwise each keep a copy of |
2352
2419
  | `mockDeep<T>(overrides?, options?)` | Build a **recursive** auto-mock — `mock.repo.user.find()` chains without seeding; `{ selfReturning: true }` chains through calls too |
2353
2420
  | `resetAutoSpy(spy)` / `clearAutoSpy(spy)` | Reset every spy in an auto-spy at once — `reset` also reverts return-value config (`calledWith` **and** a bare `mockReturnValue`); `clear` keeps it |
2354
2421
  | `spy[Symbol.dispose]()` | What `using spy = createSpyFromClass(X)` runs at the end of the block — `resetAutoSpy(this)`, on every double including each `mockDeep` node |
@@ -2371,7 +2438,7 @@ Both are the same object at runtime; only the view changes.
2371
2438
  | `autoMocked<T>(overrides?)` | `createAutoMock` typed as `T & Spy<T>` |
2372
2439
  | `mockSystemTime` / `withSystemTime` / `mockNow` / `useCountingClock` _(`/setup`)_ | Clock control that survives fake timers being re-installed per test |
2373
2440
  | `registerFocusMatchers()` _(`/setup`)_ | Adds `expect(el).toHaveFocus()` |
2374
- | `overrideAutoSpy` / `overrideComponentProvider` / `assertNgModuleScopes` _(Angular)_ | Override a component-level provider — and verify on the next fixture that the override applied; diagnose an NgModule with an empty runtime scope |
2441
+ | `overrideAutoSpy` / `overrideComponentProvider` / `assertNgModuleScopes` / `assertComponentDefIntact` _(Angular)_ | Override a component-level provider — and verify on the next fixture that the override applied; diagnose an NgModule with an empty runtime scope |
2375
2442
  | `enableAngularDiagnostics(opts?)` / `disableAngularDiagnostics()` / `assertNoPendingRequests()` _(Angular)_ | Dead NgModule imports, dead `schemas`, an unspied provider and unflushed HTTP requests, as failures |
2376
2443
  | `trackInjections(tokens, opts?)` _(Angular, NestJS)_ | Providers that record which collaborators DI constructed, with an auto-spy behind each token |
2377
2444
  | `setupAutoSpy(opts?)` _(`/setup`)_ | Property restore + duplicate-copy detection + mock-registry hygiene, in one call |
package/dist/angular.d.ts CHANGED
@@ -35,6 +35,12 @@ import 'vitest';
35
35
  * landed. So {@link overrideComponentProvider} also checks, on the next `TestBed.createComponent`,
36
36
  * that the component's own injector really answers with the spy — see {@link verifyOnNextCreate}
37
37
  * for why that check is always on rather than an opt-in diagnostic.
38
+ *
39
+ * The same AOT bundle is behind the two assertions at the end of the file.
40
+ * {@link assertNgModuleScopes} covers the module whose scope the bundler stripped;
41
+ * {@link assertComponentDefIntact} covers the component whose own definition was built while the
42
+ * chunk holding one of its providers had not run yet. Neither fixes a build — both replace a stack
43
+ * inside `@angular/core` with a line naming the thing that is missing.
38
44
  */
39
45
 
40
46
  /** The `{ useValue }` shape `TestBed.overrideProvider` expects, carrying an auto-spy. */
@@ -75,6 +81,39 @@ declare function overrideAutoSpy<T>(ObjectClass: ClassType<T>, methodsToSpyOnOrC
75
81
  * runtime scope the bundler has stripped — leaving the component with none of them.
76
82
  */
77
83
  declare function overrideComponentProvider<T>(component: Type<unknown>, ObjectClass: ClassType<T>, methodsToSpyOnOrConfig?: ClassSpyConfiguration<T> | OnlyMethodKeysOf<T>[]): Spy<T>;
84
+ /**
85
+ * Fail before rendering when a component's own definition has holes in it.
86
+ *
87
+ * Providers are **baked into `ɵcmp` when the component's module executes**, not read at
88
+ * `createComponent` time. So when a bundler splits a barrel into a chunk that has not run yet, the
89
+ * definition is built with `undefined` where a provider or a scope dependency should be, and Angular
90
+ * discovers it much later, from inside itself:
91
+ *
92
+ * ```
93
+ * TypeError: Cannot read properties of undefined (reading 'provide')
94
+ * ❯ resolveProvider render3/di_setup.ts:95
95
+ * ```
96
+ *
97
+ * The stack names neither the barrel, nor the symbol, nor the component — and the spec it breaks is
98
+ * usually one nobody touched, because chunk boundaries move with file *contents*: editing a type in
99
+ * a neighbouring file is enough. Both documented cures fail, too, and for the same reason: an
100
+ * `await import()` in `beforeEach` is already too late, and a static import at the top of the spec
101
+ * does not fix the order this bundler emits.
102
+ *
103
+ * ```ts
104
+ * assertComponentDefIntact(HoverMenuComponent);
105
+ * const fixture = TestBed.createComponent(HoverMenuComponent);
106
+ * ```
107
+ *
108
+ * The same call answers the related `Cannot read properties of undefined (reading 'ɵcmp')` from
109
+ * `imports: [Cmp]`, where the class reference itself is the thing that never arrived.
110
+ *
111
+ * This does not fix the build — that is a bundler configuration question — but it turns a
112
+ * half-hour investigation into one line, and points it away from the spec.
113
+ *
114
+ * @param components The component (or directive) classes a spec is about to render or import.
115
+ */
116
+ declare function assertComponentDefIntact(...components: unknown[]): void;
78
117
  /**
79
118
  * Fail early when an NgModule imported into the TestBed contributes nothing at runtime.
80
119
  *
@@ -576,4 +615,4 @@ declare function reportSpecTiming(timing: SpecTiming): void;
576
615
  */
577
616
  declare function enableTestBedDiagnostics(options?: TestBedDiagnosticsOptions): void;
578
617
 
579
- export { type AngularDiagnosticsOptions, type AngularTestEnvMode, type AngularTestEnvOptions, type AutoSpyOverride, type DirectiveHostOptions, type MockedResource, type ResourceDouble, type ResourceDoubleStatus, type ResourceLike, type SignalLike, type SpecTiming, type TestBedDiagnosticsOptions, assertNgModuleScopes, assertNoPendingRequests, createDirectiveHost, disableAngularDiagnostics, disableTestBedDiagnostics, enableAngularDiagnostics, enableTestBedDiagnostics, formatSpecTiming, getTestBedTiming, instrumentTestBed, mockResourceProp, mockSignalProp, overrideAutoSpy, overrideComponentProvider, registerDirectiveMatchers, registerResourceMatchers, registerSignalMatchers, reportSpecTiming, setupAngularTestEnv };
618
+ export { type AngularDiagnosticsOptions, type AngularTestEnvMode, type AngularTestEnvOptions, type AutoSpyOverride, type DirectiveHostOptions, type MockedResource, type ResourceDouble, type ResourceDoubleStatus, type ResourceLike, type SignalLike, type SpecTiming, type TestBedDiagnosticsOptions, assertComponentDefIntact, assertNgModuleScopes, assertNoPendingRequests, createDirectiveHost, disableAngularDiagnostics, disableTestBedDiagnostics, enableAngularDiagnostics, enableTestBedDiagnostics, formatSpecTiming, getTestBedTiming, instrumentTestBed, mockResourceProp, mockSignalProp, overrideAutoSpy, overrideComponentProvider, registerDirectiveMatchers, registerResourceMatchers, registerSignalMatchers, reportSpecTiming, setupAngularTestEnv };
package/dist/angular.js CHANGED
@@ -42,6 +42,19 @@ function useVitestAdapter() {
42
42
  registerMockAdapter(vitestMockAdapter);
43
43
  }
44
44
 
45
+ // src/lib/dispose-symbol.ts
46
+ var NODE_DISPOSE_KEY = "nodejs.dispose";
47
+ function installDisposeSymbol(host) {
48
+ const existing = Reflect.get(host, "dispose");
49
+ if (typeof existing === "symbol") {
50
+ return existing;
51
+ }
52
+ const shim = Symbol.for(NODE_DISPOSE_KEY);
53
+ Object.defineProperty(host, "dispose", { value: shim, enumerable: false, configurable: true, writable: false });
54
+ return shim;
55
+ }
56
+ var DISPOSE = installDisposeSymbol(Symbol);
57
+
45
58
  // src/lib/docs-links.ts
46
59
  var DOCS = "https://asdalexey.github.io/vitest-auto-spy";
47
60
  var DOCS_LINKS = {
@@ -692,7 +705,7 @@ function disposeAutoSpy() {
692
705
  resetAutoSpy(this);
693
706
  }
694
707
  function attachDispose(spy) {
695
- Object.defineProperty(spy, Symbol.dispose, { value: disposeAutoSpy, enumerable: false, configurable: true, writable: true });
708
+ Object.defineProperty(spy, DISPOSE, { value: disposeAutoSpy, enumerable: false, configurable: true, writable: true });
696
709
  }
697
710
 
698
711
  // src/lib/auto-mock.ts
@@ -750,7 +763,7 @@ function createAutoMockHandler(seed, unstubbed) {
750
763
  defineProperty: (_target, key, descriptor) => storeDefinedProp(store, key, descriptor),
751
764
  deleteProperty: (_target, key) => dropStoredProp(store, key),
752
765
  has(_target, key) {
753
- return key === AUTO_SPY_MARK || key === Symbol.dispose || hasStoredProp(store, key);
766
+ return key === AUTO_SPY_MARK || key === DISPOSE || hasStoredProp(store, key);
754
767
  },
755
768
  ownKeys() {
756
769
  return [.../* @__PURE__ */ new Set([...store.values.keys(), ...store.accessors.keys()])];
@@ -769,7 +782,7 @@ function readKey(store, key, receiver, unstubbed) {
769
782
  if (key === AUTO_SPY_MARK) {
770
783
  return true;
771
784
  }
772
- if (key === Symbol.dispose) {
785
+ if (key === DISPOSE) {
773
786
  return disposeAutoSpy;
774
787
  }
775
788
  if (typeof key === "symbol" || key === "then" || key === "constructor" || isProtocolKey(key) || isDeletedProp(store, key)) {
@@ -1402,6 +1415,66 @@ function moduleName(module) {
1402
1415
  const name = readProperty(module, "name");
1403
1416
  return typeof name === "string" ? name : String(module);
1404
1417
  }
1418
+ var DEFINITION_LISTS = ["providers", "viewProviders", "dependencies"];
1419
+ function resolveList(list) {
1420
+ if (typeof list !== "function") {
1421
+ return list;
1422
+ }
1423
+ try {
1424
+ const produced = Reflect.apply(list, void 0, []);
1425
+ return produced;
1426
+ } catch {
1427
+ return void 0;
1428
+ }
1429
+ }
1430
+ function collectHoles(list, path, holes) {
1431
+ const resolved = resolveList(list);
1432
+ if (!Array.isArray(resolved)) {
1433
+ return;
1434
+ }
1435
+ resolved.forEach((entry, index) => {
1436
+ const at = `${path}[${index}]`;
1437
+ if (entry === void 0 || entry === null) {
1438
+ holes.push(at);
1439
+ } else if (Array.isArray(entry)) {
1440
+ collectHoles(entry, at, holes);
1441
+ }
1442
+ });
1443
+ }
1444
+ function definitionOf(type) {
1445
+ const component = readProperty(type, "\u0275cmp");
1446
+ if (component !== void 0) {
1447
+ return { definition: component, key: "\u0275cmp" };
1448
+ }
1449
+ const directive = readProperty(type, "\u0275dir");
1450
+ return directive === void 0 ? void 0 : { definition: directive, key: "\u0275dir" };
1451
+ }
1452
+ function assertComponentDefIntact(...components) {
1453
+ components.forEach((component, position) => {
1454
+ const named = moduleName(component);
1455
+ const found = definitionOf(component);
1456
+ if (!found) {
1457
+ throw new Error(
1458
+ withDocs(
1459
+ `[vitest-auto-spy] assertComponentDefIntact(): argument ${position} is ${named}, which carries no \u0275cmp or \u0275dir.
1460
+ Either it is not a component or directive, or the chunk that defines it has not executed yet \u2014 the import resolved to nothing. A barrel split across chunks is the usual cause.`,
1461
+ DOCS_LINKS.angularOverrides
1462
+ )
1463
+ );
1464
+ }
1465
+ const holes = [];
1466
+ DEFINITION_LISTS.forEach((list) => collectHoles(readProperty(found.definition, list), `${named}.${found.key}.${list}`, holes));
1467
+ if (holes.length > 0) {
1468
+ throw new Error(
1469
+ withDocs(
1470
+ `[vitest-auto-spy] ${holes.join(", ")} ${holes.length === 1 ? "is" : "are"} undefined.
1471
+ A component bakes its providers and its scope into the definition when its module executes, so a hole there means the chunk holding that symbol had not run at that moment \u2014 an uninitialised barrel chunk. Angular reports this much later as "Cannot read properties of undefined (reading 'provide')", from inside its own provider resolution.`,
1472
+ DOCS_LINKS.angularOverrides
1473
+ )
1474
+ );
1475
+ }
1476
+ });
1477
+ }
1405
1478
  function assertNgModuleScopes(...modules) {
1406
1479
  const empty = modules.filter(hasEmptyRuntimeScope).map(moduleName);
1407
1480
  if (empty.length === 0) {
@@ -2009,4 +2082,4 @@ function registerSignalMatchers() {
2009
2082
  }
2010
2083
  useVitestAdapter();
2011
2084
 
2012
- export { assertNgModuleScopes, assertNoPendingRequests, countMockedProps, createDirectiveHost, createWithAutoSpies, disableAngularDiagnostics, disableTestBedDiagnostics, enableAngularDiagnostics, enableTestBedDiagnostics, flushEffects, formatSpecTiming, getTestBedTiming, injectSpy, instrumentTestBed, mockAccessorsProp, mockReadonlyProp, mockReadonlyPropGetter, mockResourceProp, mockSignalProp, mockValueProp, overrideAutoSpy, overrideComponentProvider, provideAutoSpy, provideAutoSpyForToken, registerDirectiveMatchers, registerResourceMatchers, registerSignalMatchers, renderShallow, reportSpecTiming, restoreMockedProps, runEffect, settleResource, setupAngularTestEnv, stable, trackInjections };
2085
+ export { assertComponentDefIntact, assertNgModuleScopes, assertNoPendingRequests, countMockedProps, createDirectiveHost, createWithAutoSpies, disableAngularDiagnostics, disableTestBedDiagnostics, enableAngularDiagnostics, enableTestBedDiagnostics, flushEffects, formatSpecTiming, getTestBedTiming, injectSpy, instrumentTestBed, mockAccessorsProp, mockReadonlyProp, mockReadonlyPropGetter, mockResourceProp, mockSignalProp, mockValueProp, overrideAutoSpy, overrideComponentProvider, provideAutoSpy, provideAutoSpyForToken, registerDirectiveMatchers, registerResourceMatchers, registerSignalMatchers, renderShallow, reportSpecTiming, restoreMockedProps, runEffect, settleResource, setupAngularTestEnv, stable, trackInjections };
@@ -1,5 +1,5 @@
1
1
  export { d as AccessorKeysOf, e as AddAccessorsSpies, f as AddCalledWithObservable, g as AddCalledWithPromise, h as AddCalledWithSpyMethods, A as AddObservableSpyMethods, i as AddPromiseSpyMethods, j as AddSpyMethodsByReturnTypes, k as AddVoidReturnHelpers, a as ClassSpyConfiguration, C as ClassType, b as CompleteValueConfig, D as DeepMockProxy, l as DeepPartial, E as ErrorValueConfig, F as Func, M as MethodReturns, m as Mutable, N as NextValueConfig, O as OnlyMethodKeysOf, n as OnlyObservablePropsOf, o as OnlyPropsOf, p as Overload, q as Overloads, P as PropStubValue, S as Spy, r as SpyDisposable, s as SpyOptions, t as StrictSpyConfiguration, U as UnstubbedCall, u as UnstubbedCallHandler, V as ValueConfig, c as ValueConfigPerCall, W as WithMockReturnValue } from './types-it6CnDkI.js';
2
- export { ArgCaptor, AsInstances, AssertMockedOptions, ConstructorMock, ConstructorSpy, FlushUntilOptions, IntersectionObserverStubOptions, MediaElementState, MediaElementStub, MediaElementStubOptions, MockDeepOptions, ModuleNamespace, ModuleNamespaceOptions, MutationRecordInit, ObserverGlobal, ObserverInstance, ObserverStub, ObserverStubOptions, ResizeEntryRect, TestRunComparison, TestRunReport, TestRunSummary, asInstance, asInstances, asSpy, assertMocked, captureArg, clearAutoSpy, compareTestRuns, createFunctionSpy, createMock, createSpyClass, createSpyFromClass, diffByField, errorHandler, flushEventLoop, flushEventLoopUntil, formatTestRunComparison, intersectionEntry, mockConstructor, mockDeep, moduleNamespace, mutationRecord, narrow, resetAutoSpy, resizeEntry, settleDynamicImport, stubAbortController, stubConstructor, stubIntersectionObserver, stubMediaElement, stubMutationObserver, stubObserver, stubResizeObserver, summarizeTestRun, withOverrides } from './bun.js';
2
+ export { ArgCaptor, AsInstances, AssertMockedOptions, ConstructorMock, ConstructorSpy, FixtureFactory, FlushUntilOptions, IntersectionObserverStubOptions, MediaElementState, MediaElementStub, MediaElementStubOptions, MockDeepOptions, ModuleNamespace, ModuleNamespaceOptions, MutationRecordInit, ObserverGlobal, ObserverInstance, ObserverStub, ObserverStubOptions, ResizeEntryRect, TestRunComparison, TestRunReport, TestRunSummary, asInstance, asInstances, asSpy, assertMocked, captureArg, clearAutoSpy, compareTestRuns, createFixture, createFixtureFactory, createFunctionSpy, createMock, createSpyClass, createSpyFromClass, diffByField, errorHandler, flushEventLoop, flushEventLoopUntil, formatTestRunComparison, intersectionEntry, mockConstructor, mockDeep, moduleNamespace, mutationRecord, narrow, resetAutoSpy, resizeEntry, settleDynamicImport, stubAbortController, stubConstructor, stubIntersectionObserver, stubMediaElement, stubMutationObserver, stubObserver, stubResizeObserver, summarizeTestRun, withOverrides } from './bun.js';
3
3
  export { A as AutoMockConfiguration, C as CallbackSubscribable, E as EmissionObserver, a as EmissionOptions, b as EmissionSource, S as SubscribableLike, c as autoMocked, d as createAutoMock, e as expectCompletion, f as expectEmission, g as expectEmissions, h as expectError, i as expectNoEmission, s as setEmissionTimeout } from './expect-emission-CuGjE0YR.js';
4
4
  export { A as AccessorImplementations, R as RestoreProp, c as countMockedProps, m as mockAccessorsProp, a as mockReadonlyProp, b as mockReadonlyPropGetter, d as mockValueProp, r as restoreMockedProps } from './prop-mock-IgCwv_40.js';
5
5
  export { d as describeDuplicateCopies, g as getPackageCopies } from './package-identity-B1pqa-Sh.js';
@@ -1,10 +1,10 @@
1
1
  import './chunk-Y4YG7UOK.js';
2
- import { createSpyForToken } from './chunk-LVLSMH6T.js';
2
+ import { createSpyForToken } from './chunk-JV5XRAS4.js';
3
3
  import './chunk-NQXXUJPS.js';
4
- export { asInstance, asInstances, asSpy, assertMocked, captureArg, compareTestRuns, createMock, createSpyClass, describeDuplicateCopies, diffByField, expectCompletion, expectEmission, expectEmissions, expectError, expectNoEmission, flushEventLoop, flushEventLoopUntil, formatTestRunComparison, getPackageCopies, intersectionEntry, mockConstructor, mockDeep, moduleNamespace, mutationRecord, narrow, resizeEntry, setEmissionTimeout, settleDynamicImport, stubAbortController, stubConstructor, stubIntersectionObserver, stubMediaElement, stubMutationObserver, stubObserver, stubResizeObserver, summarizeTestRun, withOverrides } from './chunk-2U3J65A4.js';
4
+ export { asInstance, asInstances, asSpy, assertMocked, captureArg, compareTestRuns, createFixture, createFixtureFactory, createMock, createSpyClass, describeDuplicateCopies, diffByField, expectCompletion, expectEmission, expectEmissions, expectError, expectNoEmission, flushEventLoop, flushEventLoopUntil, formatTestRunComparison, getPackageCopies, intersectionEntry, mockConstructor, mockDeep, moduleNamespace, mutationRecord, narrow, resizeEntry, setEmissionTimeout, settleDynamicImport, stubAbortController, stubConstructor, stubIntersectionObserver, stubMediaElement, stubMutationObserver, stubObserver, stubResizeObserver, summarizeTestRun, withOverrides } from './chunk-ZBVBJFNW.js';
5
5
  export { countMockedProps, mockAccessorsProp, mockReadonlyProp, mockReadonlyPropGetter, mockValueProp, restoreMockedProps } from './chunk-R4EWAG7Y.js';
6
- import { createSpyFromClass } from './chunk-B7PDIHEG.js';
7
- export { autoMocked, clearAutoSpy, createAutoMock, createSpyFromClass, resetAutoSpy } from './chunk-B7PDIHEG.js';
6
+ import { createSpyFromClass } from './chunk-7YTPHYP7.js';
7
+ export { autoMocked, clearAutoSpy, createAutoMock, createSpyFromClass, resetAutoSpy } from './chunk-7YTPHYP7.js';
8
8
  import { withDocs, DOCS_LINKS, isAutoSpyLike } from './chunk-3AWKPZCE.js';
9
9
  export { createFunctionSpy, errorHandler } from './chunk-3AWKPZCE.js';
10
10
  import './chunk-J5O72UHX.js';
package/dist/bun.d.ts CHANGED
@@ -76,6 +76,88 @@ declare function createSpyFromClass<T, Options extends SpyOptions = SpyOptions>(
76
76
  */
77
77
  declare function createMock<T>(partial?: DeepPartial<T>): T;
78
78
 
79
+ /**
80
+ * `createFixture` / `createFixtureFactory` — one checked copy of a data model, handed out fresh.
81
+ *
82
+ * Specs do not only spy; they *build data*, and that is where a suite meets the type checker for
83
+ * the first time. A content model with seventeen required fields, each with its own nested
84
+ * interface, gets copied into every spec that needs it: eight files, a hundred-line literal apiece.
85
+ * The copies then rot independently. Measured on one migration shard, that single habit produced
86
+ * **28 `TS1117`** diagnostics (a duplicate key in a literal — the runtime keeps the *second*, so an
87
+ * automated "drop one" fix silently changes values) and half of the shard's `TS2741`.
88
+ *
89
+ * The two obvious escapes are worse than the problem:
90
+ *
91
+ * - `as T` and `Partial<T>` both delete the diagnostic that made the migration worth doing —
92
+ * `TS2353`, "this fixture still sets a field the model dropped six months ago". A fixture that
93
+ * cannot be wrong about the model is a fixture that never reports the model changed.
94
+ * - {@link createAutoMock} builds an object from a type, but only its **methods**. A data model has
95
+ * none, so the machine walks straight past the place the errors actually are.
96
+ *
97
+ * So the answer is not another way to skip the fields. It is somewhere to *put* them once:
98
+ * {@link createFixtureFactory} takes a complete, fully checked `T` — every required field, every
99
+ * removed field rejected, one place for the compiler to point at — and returns a function that
100
+ * stamps out copies with per-test changes applied. The check happens once; the fifty call sites
101
+ * that follow name only what they care about.
102
+ *
103
+ * **Each call returns a new object.** That is not a performance footnote — a fixture shared by
104
+ * reference across a file is the most common way one test's mutation decides another test's
105
+ * outcome, and under `isolate: false` the sharing reaches across files. The copy is deep through
106
+ * plain objects and arrays and stops there: a `Date`, a `Map`, a DOM node or a class instance is
107
+ * carried across by reference, because rebuilding one would strip its prototype — accessors
108
+ * included — and hand back something that is no longer the model it claims to be. When the defaults
109
+ * *are* a class instance with getters, snapshot it with {@link withOverrides} first and hand the
110
+ * result here.
111
+ *
112
+ * | | `createMock<T>()` | `createFixture<T>()` |
113
+ * | --- | --- | --- |
114
+ * | Input | the fields this test reads | a complete `T`, plus what this test changes |
115
+ * | Unnamed fields | `undefined` at runtime | the default's value |
116
+ * | Use it for | a shape read shallowly, once | a model many specs build, read deeply, and mutate |
117
+ */
118
+
119
+ /** Stamps out one fixture per call. What {@link createFixtureFactory} returns. */
120
+ type FixtureFactory<T> = (overrides?: DeepPartial<T>) => T;
121
+ /**
122
+ * Build one `T` from a complete default and the fields this test changes.
123
+ *
124
+ * ```ts
125
+ * const article = createFixture<Article>(ARTICLE_DEFAULTS, { header: { title: 'Draft' } });
126
+ * ```
127
+ *
128
+ * `defaults` is a whole `T`, so a field the model removed is a compile error here rather than a
129
+ * value nothing reads; `overrides` is checked at every depth against the same type. Sibling fields
130
+ * of an overridden leaf are kept — `header.subtitle` above survives — while an overridden array
131
+ * replaces the default one entirely.
132
+ *
133
+ * Reach for {@link createFixtureFactory} as soon as a second spec needs the same defaults; reach
134
+ * for {@link createMock} when there are no meaningful defaults to begin with and the test reads two
135
+ * fields of a large shape.
136
+ *
137
+ * @param defaults A complete `T`. Checked in full: missing and removed fields both fail here.
138
+ * @param overrides The fields this fixture changes, checked against `T` at every depth.
139
+ */
140
+ declare function createFixture<T>(defaults: T, overrides?: DeepPartial<T>): T;
141
+ /**
142
+ * Somewhere to put a fixture, so that the model is written out and checked exactly once.
143
+ *
144
+ * ```ts
145
+ * // article.fixture.ts — one file, one checked literal
146
+ * export const anArticle = createFixtureFactory<Article>({ id: '1', header: { title: '', subtitle: '' }, tags: [], … });
147
+ *
148
+ * // in a spec
149
+ * const draft = anArticle({ header: { title: 'Draft' } });
150
+ * const tagged = anArticle({ tags: ['news'] });
151
+ * ```
152
+ *
153
+ * The defaults are copied when the factory is built, so a later edit to the object that was passed
154
+ * in cannot reach a fixture already handed out — and every call returns a fresh copy, so one test
155
+ * mutating what it was given cannot decide what the next one sees.
156
+ *
157
+ * @param defaults A complete `T`, checked once, here.
158
+ */
159
+ declare function createFixtureFactory<T>(defaults: T): FixtureFactory<T>;
160
+
79
161
  /**
80
162
  * Behaviour switches for {@link mockDeep}.
81
163
  *
@@ -1105,4 +1187,4 @@ interface ResizeEntryRect {
1105
1187
  */
1106
1188
  declare function resizeEntry(target: Element, rect?: ResizeEntryRect): ResizeObserverEntry;
1107
1189
 
1108
- export { AddSpyMethodsByReturnTypes, type ArgCaptor, type AsInstances, type AssertMockedOptions, ClassSpyConfiguration, ClassType, type ConstructorMock, type ConstructorSpy, DeepMockProxy, DeepPartial, type FlushUntilOptions, Func, type IntersectionObserverStubOptions, type MediaElementState, type MediaElementStub, type MediaElementStubOptions, type MockDeepOptions, type ModuleNamespace, type ModuleNamespaceOptions, type MutationRecordInit, type ObserverGlobal, type ObserverInstance, type ObserverStub, type ObserverStubOptions, OnlyMethodKeysOf, type ResizeEntryRect, Spy, SpyOptions, type TestRunComparison, type TestRunReport, type TestRunSummary, UnstubbedCallHandler, asInstance, asInstances, asSpy, assertMocked, captureArg, clearAutoSpy, compareTestRuns, createFunctionSpy, createMock, createSpyClass, createSpyFromClass, diffByField, errorHandler, flushEventLoop, flushEventLoopUntil, formatTestRunComparison, intersectionEntry, mockConstructor, mockDeep, moduleNamespace, mutationRecord, narrow, resetAutoSpy, resizeEntry, settleDynamicImport, stubAbortController, stubConstructor, stubIntersectionObserver, stubMediaElement, stubMutationObserver, stubObserver, stubResizeObserver, summarizeTestRun, withOverrides };
1190
+ export { AddSpyMethodsByReturnTypes, type ArgCaptor, type AsInstances, type AssertMockedOptions, ClassSpyConfiguration, ClassType, type ConstructorMock, type ConstructorSpy, DeepMockProxy, DeepPartial, type FixtureFactory, type FlushUntilOptions, Func, type IntersectionObserverStubOptions, type MediaElementState, type MediaElementStub, type MediaElementStubOptions, type MockDeepOptions, type ModuleNamespace, type ModuleNamespaceOptions, type MutationRecordInit, type ObserverGlobal, type ObserverInstance, type ObserverStub, type ObserverStubOptions, OnlyMethodKeysOf, type ResizeEntryRect, Spy, SpyOptions, type TestRunComparison, type TestRunReport, type TestRunSummary, UnstubbedCallHandler, asInstance, asInstances, asSpy, assertMocked, captureArg, clearAutoSpy, compareTestRuns, createFixture, createFixtureFactory, createFunctionSpy, createMock, createSpyClass, createSpyFromClass, diffByField, errorHandler, flushEventLoop, flushEventLoopUntil, formatTestRunComparison, intersectionEntry, mockConstructor, mockDeep, moduleNamespace, mutationRecord, narrow, resetAutoSpy, resizeEntry, settleDynamicImport, stubAbortController, stubConstructor, stubIntersectionObserver, stubMediaElement, stubMutationObserver, stubObserver, stubResizeObserver, summarizeTestRun, withOverrides };
package/dist/bun.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import './chunk-Y4YG7UOK.js';
2
2
  import './chunk-NQXXUJPS.js';
3
- export { asInstance, asInstances, asSpy, assertMocked, captureArg, compareTestRuns, createMock, createSpyClass, describeDuplicateCopies, diffByField, expectCompletion, expectEmission, expectEmissions, expectError, expectNoEmission, flushEventLoop, flushEventLoopUntil, formatTestRunComparison, getPackageCopies, intersectionEntry, mockConstructor, mockDeep, moduleNamespace, mutationRecord, narrow, resizeEntry, setEmissionTimeout, settleDynamicImport, stubAbortController, stubConstructor, stubIntersectionObserver, stubMediaElement, stubMutationObserver, stubObserver, stubResizeObserver, summarizeTestRun, withOverrides } from './chunk-2U3J65A4.js';
3
+ export { asInstance, asInstances, asSpy, assertMocked, captureArg, compareTestRuns, createFixture, createFixtureFactory, createMock, createSpyClass, describeDuplicateCopies, diffByField, expectCompletion, expectEmission, expectEmissions, expectError, expectNoEmission, flushEventLoop, flushEventLoopUntil, formatTestRunComparison, getPackageCopies, intersectionEntry, mockConstructor, mockDeep, moduleNamespace, mutationRecord, narrow, resizeEntry, setEmissionTimeout, settleDynamicImport, stubAbortController, stubConstructor, stubIntersectionObserver, stubMediaElement, stubMutationObserver, stubObserver, stubResizeObserver, summarizeTestRun, withOverrides } from './chunk-ZBVBJFNW.js';
4
4
  export { countMockedProps, mockAccessorsProp, mockReadonlyProp, mockReadonlyPropGetter, mockValueProp, restoreMockedProps } from './chunk-R4EWAG7Y.js';
5
- export { autoMocked, clearAutoSpy, createAutoMock, createSpyFromClass, resetAutoSpy } from './chunk-B7PDIHEG.js';
5
+ export { autoMocked, clearAutoSpy, createAutoMock, createSpyFromClass, resetAutoSpy } from './chunk-7YTPHYP7.js';
6
6
  export { createFunctionSpy, errorHandler } from './chunk-3AWKPZCE.js';
7
7
  import './chunk-J5O72UHX.js';