vitest-auto-spy 5.17.0 → 5.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/AGENTS.md CHANGED
@@ -1676,7 +1676,10 @@ share a worker and never alone. The guard records the attributes of `<html>`, `<
1676
1676
  before each test and compares them from `onTestFinished` — **after** the TestBed destroyed the
1677
1677
  fixtures in its own `afterEach`, so what a component removes in `ngOnDestroy` / `DestroyRef.onDestroy`
1678
1678
  is never reported. Every attribute added, changed or removed is named with both values, put back,
1679
- and fails that test. A change made in a `beforeAll` and never undone fails the file, checked from a
1679
+ and fails that test. An empty `class` or `style` reads as the attribute being absent, in both
1680
+ directions — `classList.add` followed by `remove` leaves `class=""` where there was none and nothing
1681
+ observable changed; any other empty value, `data-reset-focus=""` included, is still reported.
1682
+ A change made in a `beforeAll` and never undone fails the file, checked from a
1680
1683
  `beforeAll` cleanup after every `afterAll`. `nodes: true` also watches the child elements of `<head>`
1681
1684
  and `<body>`; it is off by default because a module that injects a stylesheet on first import does
1682
1685
  so once per worker. Blind spots: a write made while the spec file is imported, and a fixture kept
@@ -3726,9 +3729,16 @@ files of a confirmation pass go.
3726
3729
 
3727
3730
  **`--gate` is the only part of this command that fails anything**, and three rules keep it honest.
3728
3731
  It judges the `tests` phase alone, because the other five are the harness and the machine rather than
3729
- anybody's code. A file is over budget only when it is over both an absolute floor and `--factor` ×
3730
- the median file **of the same run**, which is what makes the verdict the same on a loaded CI runner
3731
- and on an idle laptop. And every candidate is re-measured on its own before it may fail anything: a
3732
+ anybody's code. A file budget is counted in the median test **of the same run** — the largest of
3733
+ `--max-file-tests` (2 000) median tests, `--factor` (10) × the median test for each test in the file,
3734
+ and a `--max-file-ms` (5 000) floor that can only spare a file — which is what makes the verdict the
3735
+ same on a loaded CI runner and on an idle laptop, and what keeps a large file of ordinary tests out of
3736
+ it: the rule it replaced, `--factor` × the median **file**, flagged 0 files of a 2 023-file consumer
3737
+ suite at ×1 slowdown and 19 at ×9, a 209-test service spec at 5 ms a test among them. A confirmed finding
3738
+ also says why: the confirmation pass records a CPU profile of each suspect file and every one of its
3739
+ test bodies, and the gate prints the slowest tests, the share in hooks against bodies, and where the
3740
+ time went in the spec, in your code and by package. The report ends in two tables of what is over
3741
+ budget and nothing else. And every candidate is re-measured on its own before it may fail anything: a
3732
3742
  file that is fast when it has the machine to itself is reported as _not reproduced_, an `info` rather
3733
3743
  than a finding. Without a way to re-measure, findings are warnings that fail nothing unless
3734
3744
  `--no-confirm` says one reading is enough. Exit `1` is "over budget", exit `2` is "there was nothing
package/README.md CHANGED
@@ -70,6 +70,7 @@ faster at suite scale ([benchmarks](#benchmarks)) — and for
70
70
  - 🪆 NestJS units from their own DI metadata — `createNestUnit(Target, { expose, providers })`, the solitary / sociable model of `@suites/unit` with the real prototype behind every spy and no `@nestjs` dependency
71
71
  - 🧱 The providers a testing module cannot reach — `overrideComponentProvider` (which verifies the override actually applied), `provideAutoSpyForToken`, `assertNgModuleScopes`, `assertComponentDefIntact`, `createDirectiveHost`, and `createComponentStub` for a child whose selector and inputs are read off the real one, so the stub cannot drift
72
72
  - 🚨 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. `injectSpy` already warns once per token when DI handed back a real instance, naming the token and the missing `provideAutoSpy` — where Spectator's `inject<T>(token): SpyObject<T>` types **every** token as a spy whether it was mocked or not, so the compiler hides the same mistake
73
+ - 🐢 Which test is slow, and why — [`npx vitest-auto-spy perf --gate`](#perf--where-the-cpu-time-actually-goes) fails CI over a file whose tests each cost many times the median test of the same run, so a laptop and a runner nine times slower agree; it re-measures every suspect on its own first, and a confirmed one comes with a CPU profile card: the slowest tests, hooks against bodies, the time by package and in your own code, and a likely cause. Vitest 5.0 marks a test over a fixed `slowTestThreshold` of 300 ms and says nothing about where the time went
73
74
  - 🔒 [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`; `unconfiguredReads` reports the getter and the stream nobody configured once the test is over
74
75
  - ♻️ `using spy = createSpyFromClass(X)` — every double carries `[Symbol.dispose]`, so the `afterEach` that only reset one spy can go
75
76
  - 📡 Observable assertions that fail on silence — `expectEmission` / `expectEmissions` / `expectNoEmission` / `expectCompletion` / `expectError`, no rxjs required, Angular `output()` included
@@ -345,7 +346,7 @@ The package ships one executable, with no dependencies and nothing to configure:
345
346
 
346
347
  ```bash
347
348
  npx vitest-auto-spy doctor # read-only. Exits 1 when it finds something
348
- npx vitest-auto-spy perf # where the suite's CPU time goes. --gate fails a budget
349
+ npx vitest-auto-spy perf # where the suite's CPU time goes. --gate fails a budget and says why
349
350
  npx vitest-auto-spy codemod # prints the migration diff. Writes nothing without --write
350
351
  npx vitest-auto-spy init # writes the agent instructions pointer
351
352
  ```
@@ -462,14 +463,46 @@ cost 2.8 % of wall clock and about 2 GB less on a 16-core machine.
462
463
 
463
464
  **`--gate` is the half that may fail a pipeline**, and it is built so that it only ever fails over
464
465
  somebody's code. It judges the `tests` phase alone — the other five are the harness and the machine
465
- — it asks for both an absolute budget and a multiple of the median file **of the same run**, so the
466
- verdict survives a change of hardware, and it re-measures every candidate on its own before failing
467
- anything. A file that is not slow when it has the machine to itself is reported as _not reproduced_
468
- rather than as a defect.
466
+ — it counts a file's budget in the median test **of the same run** (2 000 of them, or 10× that
467
+ median for each test in the file, whichever is larger), so the verdict survives a change of hardware
468
+ and a large file of ordinary tests never fails it, and it re-measures every candidate on its own
469
+ before failing anything. A file that is not slow when it has the machine to itself is reported as
470
+ _not reproduced_ rather than as a defect; one that is gets a card saying why — its slowest tests,
471
+ hooks against bodies, and where the CPU profile says the time went.
469
472
 
470
473
  ```bash
471
- npx vitest-auto-spy perf --command 'npm test -- {paths:--include=}' --gate
472
- ```
474
+ npx vitest-auto-spy perf --gate # a plain Vitest suite
475
+ npx vitest-auto-spy perf --command 'npm test -- {paths:--include=}' --gate # a suite behind a script
476
+ ```
477
+
478
+ ```
479
+ error perf-gate-slow-file libs/player/src/lib/vod/vod.component.spec.ts
480
+ The test bodies in this file add up to 9.20s, over the 5.00s budget (…). Re-measured on its own: 8.70s, still over budget.
481
+
482
+ ┌─ measurements ────────────────────────────────────────────────
483
+ │ tests 38 242ms each 20× the median test
484
+ ├─ slowest tests ───────────────────────────────────────────────
485
+ │ 527ms focus > moves through the controls
486
+ ├─ where the time went · CPU profile, 8.41s sampled ────────────
487
+ │ hooks ███████████░░░░░░░░░ 54% test bodies 46%
488
+ │ by package ██████░░░░░░░░░░░░░░ 28% jsdom
489
+ │ in the spec setUpWith 38% · VodComponent_Template 17% · assertFocus 8%
490
+ ├─ likely cause ────────────────────────────────────────────────
491
+ │ Most of the time is set-up that every test repeats: 54% is in hooks — setUpWith alone is 38%.
492
+ └───────────────────────────────────────────────────────────────
493
+ ```
494
+
495
+ The profiler behind the card is loaded only for the confirmation pass; an ordinary run never pays
496
+ for it. The whole card, and the rule the budget is counted by, are in
497
+ [the CLI docs](https://asdalexey.github.io/vitest-auto-spy/utilities/cli#the-gate).
498
+
499
+ On an Angular spec the card adds a row of Angular's own costs — TestBed set-up, component creation,
500
+ change detection, JIT compilation, computed styles — and the likely cause reads it first; on Vitest
501
+ 4.1+ it also lists the spec's heaviest imports. Three things need no network in CI: `--baseline
502
+ perf-history.jsonl` keeps the last 30 runs in a cache and fails a file only past twice its mean share
503
+ and above every share it was recorded at; `--fail-on-flaky` fails a test that passed only on a retry;
504
+ and `--code-quality <path>`, on `perf` and on `doctor`, writes the findings for the GitLab merge
505
+ request widget.
473
506
 
474
507
  `--command` is also the answer to a bare `vitest run` not being your suite at all: where the suite is
475
508
  built by an Angular builder, an Nx target or a script, there is no root config, the defaults sweep up