vitest-auto-spy 5.49.0 → 5.50.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,25 @@ The latest released version here must always match the one published on
10
10
 
11
11
  ## [Unreleased]
12
12
 
13
+ ## [5.50.0] - 2026-09-29
14
+
15
+ ### Changed
16
+
17
+ - **`doctor` and `perf` color their findings by severity** — the `error` label red, `warn` yellow,
18
+ `info` plain — and the `perf --gate` verdict line turns red when something fails the run. The
19
+ same switch as the `perf` tables decides it (`NO_COLOR`, `FORCE_COLOR`, `TERM=dumb`, a terminal);
20
+ `--format json`, `--format markdown` and `--code-quality` stay free of escapes.
21
+ - **Color in GitLab CI and GitHub Actions logs.** Both render ANSI color although the job's stdout
22
+ is a pipe, so the CLI now paints when `GITLAB_CI` or `GITHUB_ACTIONS` is set; `NO_COLOR`,
23
+ `FORCE_COLOR=0` and `TERM=dumb` still turn it off.
24
+
25
+ ### Documentation
26
+
27
+ - **`returns` vs `overrides` has a page of its own**, with a landing card and a sidebar entry: one
28
+ table of what each seeds, whether the member stays a spy, how it behaves under `strict` and which
29
+ wins when a member is named in both. `agent-docs/configuration.md` gains the two rows its option
30
+ table was missing.
31
+
13
32
  ## [5.49.0] - 2026-09-28
14
33
 
15
34
  ### Added
@@ -6093,7 +6112,8 @@ moved under 1 %: `/angular` +192 B (+1.0 %) for the override queue and the HTTP
6093
6112
  This copy ships with the package and carries 5.x only. Releases before 5.0.0 are in the
6094
6113
  [full changelog](https://github.com/ASDAlexey/vitest-auto-spy/blob/master/CHANGELOG.md).
6095
6114
 
6096
- [Unreleased]: https://github.com/ASDAlexey/vitest-auto-spy/compare/v5.49.0...HEAD
6115
+ [Unreleased]: https://github.com/ASDAlexey/vitest-auto-spy/compare/v5.50.0...HEAD
6116
+ [5.50.0]: https://github.com/ASDAlexey/vitest-auto-spy/compare/v5.49.0...v5.50.0
6097
6117
  [5.49.0]: https://github.com/ASDAlexey/vitest-auto-spy/compare/v5.48.0...v5.49.0
6098
6118
  [5.48.0]: https://github.com/ASDAlexey/vitest-auto-spy/compare/v5.47.0...v5.48.0
6099
6119
  [5.47.0]: https://github.com/ASDAlexey/vitest-auto-spy/compare/v5.46.0...v5.47.0
@@ -92,7 +92,7 @@ provideAutoSpyForToken(PRODUCTS, undefined, { returns: { getProducts: of([]), ge
92
92
  ```
93
93
 
94
94
  A seeded `overrides` value is stored verbatim and is **no longer a spy**, so seed data there and
95
- name methods in `returns` when they must stay assertable. On `provideAutoSpy` a plain function
95
+ name methods in `returns` when they must stay assertable (the two compared in §5, [`configuration.md`](./configuration.md)). On `provideAutoSpy` a plain function
96
96
  seeded on a method is the exception: it becomes that method's spy, running the function until the
97
97
  test configures it, so `toHaveBeenCalled` works on it; a `vi.fn()` is kept as it is
98
98
  (`provideAutoSpyForToken` still stores every seed verbatim). The reason to prefer this over a second
@@ -31,6 +31,8 @@ createSpyFromClass(MyService, {
31
31
  | `lazySpies` | Unset: `true` (accessor placeholders) below 8 methods, `'proxy'` (a `Proxy`, lighter once touched) from 8, counting the methods the double spies (after `onlyMethodsToSpyOn` / `methodsToSpyOn`). Explicit `true` / `false` / `'proxy'` apply at any width. Calls, `Object.keys`, spread and snapshots behave the same in every mode. |
32
32
  | `strict` | Throw on a method nobody configured, instead of answering `undefined` (below). |
33
33
  | `onUnstubbedCall` | The general form of `strict`; its return value becomes the call's return value. |
34
+ | `returns` | What a spied **method** answers. The member stays a spy (`toHaveBeenCalled` works); a default that `calledWith` / `mockReturnValue` / `resolveWith` replace. A key that is not a spied method is reported. |
35
+ | `overrides` | Replaces **any member** with the value as written — a field, a signal, an `Observable` property. The member is no longer a spy, except a plain function seeded on a method (class factories only), which becomes its spy. Named in both, `overrides` wins. |
34
36
  | `selfReturning` | The named methods answer the double itself — a default like `returns`; a name in both answers `returns`. |
35
37
 
36
38
  **`instanceMethodsToSpyOn` is not an edge case — it is a top-5 option** (103 of ~370 spec files in
package/dist/cli.js CHANGED
@@ -607,11 +607,12 @@ var wrap = (open) => (text) => `${ESC}[${open}m${text}${ESC}[0m`;
607
607
  var TERMINAL = { red: wrap("31"), yellow: wrap("33"), cyan: wrap("36"), bold: wrap("1"), dim: wrap("2") };
608
608
  var same = (text) => text;
609
609
  var MONOCHROME = { red: same, yellow: same, cyan: same, bold: same, dim: same };
610
+ var COLOR_LOGS = ["GITLAB_CI", "GITHUB_ACTIONS"];
610
611
  function colorWanted(env, isTTY = process.stdout.isTTY === true) {
611
612
  if (env["NO_COLOR"] !== void 0 || env["FORCE_COLOR"] === "0" || env["TERM"] === "dumb") {
612
613
  return false;
613
614
  }
614
- return env["FORCE_COLOR"] !== void 0 || isTTY;
615
+ return env["FORCE_COLOR"] !== void 0 || COLOR_LOGS.some((name) => env[name] !== void 0) || isTTY;
615
616
  }
616
617
  function painterFor(colors) {
617
618
  return colors ?? colorWanted(process.env) ? TERMINAL : MONOCHROME;
@@ -658,7 +659,15 @@ function wrapLine(text, width, indent, firstIndent) {
658
659
 
659
660
  // src/cli/report.ts
660
661
  var SEVERITY_ORDER = { error: 0, warning: 1, info: 2 };
661
- var SEVERITY_LABEL = { error: "error", warning: "warn ", info: "info " };
662
+ var SEVERITY_LABEL = { error: "error", warning: "warn", info: "info" };
663
+ var SEVERITY_PAINT = {
664
+ error: (paint, text) => paint.red(text),
665
+ warning: (paint, text) => paint.yellow(text),
666
+ info: (_paint, text) => text
667
+ };
668
+ function severityLabel(severity, paint) {
669
+ return padEnd(SEVERITY_PAINT[severity](paint, SEVERITY_LABEL[severity]), 5);
670
+ }
662
671
  var BODY = " ";
663
672
  var ITEM = `${BODY} `;
664
673
  var ITEM_BODY = `${ITEM} `;
@@ -679,23 +688,23 @@ function fixLines(finding2, width) {
679
688
  const docs = docsFor(finding2.check);
680
689
  return [...wrapText(finding2.fix, width, `${BODY} `, `${BODY}\u2192 `), ...docs === void 0 ? [] : [`${BODY}Docs: ${docs}`]];
681
690
  }
682
- function formatOne(finding2, width) {
691
+ function formatOne(finding2, width, paint) {
683
692
  const where2 = finding2.file === void 0 ? "" : ` ${finding2.file}`;
684
693
  return [
685
- `${SEVERITY_LABEL[finding2.severity]} ${finding2.check}${where2}`,
694
+ `${severityLabel(finding2.severity, paint)} ${finding2.check}${where2}`,
686
695
  ...wrapText(finding2.message, width, BODY),
687
696
  ...(finding2.details ?? []).map((line) => `${BODY}${line}`),
688
697
  ...fixLines(finding2, width)
689
698
  ].join("\n");
690
699
  }
691
- function formatGroup({ first, members: group3 }, width) {
700
+ function formatGroup({ first, members: group3 }, width, paint) {
692
701
  const sameMessage = group3.every((finding2) => finding2.message === first.message);
693
702
  const places = group3.flatMap((finding2) => [
694
703
  `${ITEM}${String(finding2.file)}`,
695
704
  ...sameMessage ? [] : wrapText(finding2.message, width, ITEM_BODY)
696
705
  ]);
697
706
  return [
698
- `${SEVERITY_LABEL[first.severity]} ${first.check} \u2014 ${placesOf(group3)}`,
707
+ `${severityLabel(first.severity, paint)} ${first.check} \u2014 ${placesOf(group3)}`,
699
708
  ...sameMessage ? wrapText(first.message, width, BODY) : [],
700
709
  ...places,
701
710
  ...fixLines(first, width)
@@ -723,8 +732,8 @@ function groupFindings(findings) {
723
732
  function filterBySeverity(findings, minSeverity = "info") {
724
733
  return findings.filter((finding2) => SEVERITY_ORDER[finding2.severity] <= SEVERITY_ORDER[minSeverity]);
725
734
  }
726
- function formatFindings(findings, minSeverity = "info", width = outputWidth()) {
727
- return groupFindings(filterBySeverity(findings, minSeverity)).map((group3) => group3.members.length === 1 ? formatOne(group3.first, width) : formatGroup(group3, width)).join("\n\n");
735
+ function formatFindings(findings, minSeverity = "info", width = outputWidth(), paint = MONOCHROME) {
736
+ return groupFindings(filterBySeverity(findings, minSeverity)).map((group3) => group3.members.length === 1 ? formatOne(group3.first, width, paint) : formatGroup(group3, width, paint)).join("\n\n");
728
737
  }
729
738
  function tallyOf(findings) {
730
739
  return {
@@ -3865,7 +3874,7 @@ function checkForeignPragma(graph) {
3865
3874
  }
3866
3875
 
3867
3876
  // src/cli/checks/export-map.generated.ts
3868
- var EXPORT_MAP_VERSION = "5.48.0";
3877
+ var EXPORT_MAP_VERSION = "5.49.0";
3869
3878
  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";
3870
3879
  var EXPORTED_BY = {
3871
3880
  AccessorImplementations: "0 1 2 3 4 12 20 21 22",
@@ -8236,7 +8245,8 @@ function formatVerdict(rows, paint) {
8236
8245
  ],
8237
8246
  paint
8238
8247
  );
8239
- return [`perf gate verdict \u2014 ${rows.length} judged, ${failing} ${failing === 1 ? "fails" : "fail"} the run`, ...table];
8248
+ const title = `perf gate verdict \u2014 ${rows.length} judged, ${failing} ${failing === 1 ? "fails" : "fail"} the run`;
8249
+ return [failing > 0 ? paint.red(title) : title, ...table];
8240
8250
  }
8241
8251
 
8242
8252
  // src/cli/report-markdown.ts
@@ -8464,7 +8474,7 @@ ${nothingToDo(analysis)}`);
8464
8474
  reportOnly(analysis.findings, io, minSeverity);
8465
8475
  }
8466
8476
  function reportOnly(findings, io, minSeverity) {
8467
- const report2 = formatFindings(findings, minSeverity);
8477
+ const report2 = formatFindings(findings, minSeverity, outputWidth(), painterFor(void 0));
8468
8478
  if (report2 !== "") {
8469
8479
  io.out(`
8470
8480
  ${report2}`);
@@ -9085,7 +9095,7 @@ function doctorCommand(cwd, argv, io) {
9085
9095
  io.out(`vitest-auto-spy doctor \u2014 ${cwd}`);
9086
9096
  io.out(`${profile.files.length} files scanned, ${specFiles} of them spec files \u2014 runner: ${profile.runner}, entry: ${profile.entry}
9087
9097
  `);
9088
- const report2 = findings.length === 0 ? "No problems found." : formatFindings(findings, minSeverity);
9098
+ const report2 = findings.length === 0 ? "No problems found." : formatFindings(findings, minSeverity, outputWidth(), painterFor(void 0));
9089
9099
  if (report2 !== "") {
9090
9100
  io.out(`${report2}
9091
9101
  `);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vitest-auto-spy",
3
- "version": "5.49.0",
3
+ "version": "5.50.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",