vitest-auto-spy 5.13.0 → 5.14.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.
Files changed (2) hide show
  1. package/dist/cli.js +84 -17
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -2455,7 +2455,7 @@ function checkForeignPragma(graph) {
2455
2455
  }
2456
2456
 
2457
2457
  // src/cli/checks/export-map.generated.ts
2458
- var EXPORT_MAP_VERSION = "5.13.0";
2458
+ var EXPORT_MAP_VERSION = "5.14.0";
2459
2459
  var ENTRY_SPECIFIERS = "vitest-auto-spy vitest-auto-spy/bun vitest-auto-spy/bun-angular vitest-auto-spy/node vitest-auto-spy/rstest vitest-auto-spy/rxjs vitest-auto-spy/console vitest-auto-spy/dom-stubs vitest-auto-spy/diagnostics vitest-auto-spy/jasmine vitest-auto-spy/jasmine-compat vitest-auto-spy/observer-spy vitest-auto-spy/angular vitest-auto-spy/angular-http vitest-auto-spy/angular-router vitest-auto-spy/signal-forms vitest-auto-spy/nestjs vitest-auto-spy/react vitest-auto-spy/vue vitest-auto-spy/svelte vitest-auto-spy/setup vitest-auto-spy/zone vitest-auto-spy/eslint-plugin";
2460
2460
  var EXPORTED_BY = {
2461
2461
  AccessorImplementations: "0 1 2 3 4 12 17 18 19",
@@ -4312,6 +4312,17 @@ var NO_PER_TEST = "\u2014";
4312
4312
  var INDENT = " ";
4313
4313
  var GAP = 4;
4314
4314
  var ELLIPSIS = "\u2026";
4315
+ var RED = "\x1B[31m";
4316
+ var DIM = "\x1B[2m";
4317
+ var OFF = "\x1B[0m";
4318
+ var MONOCHROME = { red: (text) => text, dim: (text) => text };
4319
+ var TERMINAL = {
4320
+ red: (text) => `${RED}${text}${OFF}`,
4321
+ dim: (text) => `${DIM}${text}${OFF}`
4322
+ };
4323
+ function colorWanted(env) {
4324
+ return env["NO_COLOR"] === void 0 && env["FORCE_COLOR"] !== "0" && env["TERM"] !== "dumb";
4325
+ }
4315
4326
  function fileHotspots(run, cwd, limit) {
4316
4327
  const found = [];
4317
4328
  for (const [file, measured] of measuredFiles(run, cwd)) {
@@ -4338,28 +4349,58 @@ function caseHotspots(run, cwd, limit) {
4338
4349
  function totalTestMs(run, cwd) {
4339
4350
  return [...measuredFiles(run, cwd).values()].reduce((total, file) => total + file.tests, 0);
4340
4351
  }
4341
- function truncateLeft(text, width) {
4342
- return text.length <= width ? text : `${ELLIPSIS}${text.slice(text.length - width + ELLIPSIS.length)}`;
4352
+ function truncateLeft(text, width, paint) {
4353
+ if (text.length <= width) {
4354
+ return text;
4355
+ }
4356
+ return `${paint.dim(ELLIPSIS)}${text.slice(text.length - width + ELLIPSIS.length)}`;
4357
+ }
4358
+ function truncatePath(path, width, paint) {
4359
+ if (path.length <= width) {
4360
+ return path;
4361
+ }
4362
+ const first = path.indexOf("/");
4363
+ if (first === -1) {
4364
+ return truncateLeft(path, width, paint);
4365
+ }
4366
+ const head = path.slice(0, first);
4367
+ const rest = path.slice(first + 1).split("/");
4368
+ const budget = width - head.length - 3;
4369
+ const tail = [];
4370
+ let used = 0;
4371
+ for (const segment of [...rest].reverse()) {
4372
+ const next = used === 0 ? segment.length : used + 1 + segment.length;
4373
+ if (next > budget) {
4374
+ break;
4375
+ }
4376
+ tail.unshift(segment);
4377
+ used = next;
4378
+ }
4379
+ if (tail.length === 0) {
4380
+ return truncateLeft(path, width, paint);
4381
+ }
4382
+ return `${head}/${paint.dim(ELLIPSIS)}/${tail.join("/")}`;
4343
4383
  }
4344
4384
  function widthOf(head, cells) {
4345
4385
  return cells.reduce((widest, cell) => Math.max(widest, cell.length), head.length);
4346
4386
  }
4347
- function table(label, numbers, width) {
4387
+ function table(label, numbers, width, truncate, decorate) {
4348
4388
  const spent = numbers.reduce((sum, column) => sum + widthOf(column.head, column.cells) + GAP, 0);
4349
4389
  const labelWidth = Math.max(Math.min(widthOf(label.head, label.cells), width - INDENT.length - spent), label.head.length);
4350
4390
  const laid = [
4351
- { width: labelWidth, left: true, texts: [label.head, ...label.cells.map((cell) => truncateLeft(cell, labelWidth))] },
4391
+ { width: labelWidth, left: true, texts: [label.head, ...label.cells.map((cell) => truncate(cell, labelWidth))] },
4352
4392
  ...numbers.map((column) => ({ width: widthOf(column.head, column.cells) + GAP, left: false, texts: [column.head, ...column.cells] }))
4353
4393
  ];
4354
4394
  return laid.reduce(
4355
- (lines, column) => column.texts.map(
4356
- (text, index) => `${lines[index] ?? INDENT}${column.left ? text.padEnd(column.width) : text.padStart(column.width)}`
4395
+ (lines, column, current) => column.texts.map(
4396
+ (text, index) => `${lines[index] ?? INDENT}${decorate(index, current, column.left ? text.padEnd(column.width) : text.padStart(column.width))}`
4357
4397
  ),
4358
4398
  []
4359
4399
  ).join("\n");
4360
4400
  }
4361
- function fileSection(files, total, width) {
4401
+ function fileSection(files, total, width, floorMs, paint) {
4362
4402
  const listed = files.reduce((sum, hotspot) => sum + hotspot.ms, 0);
4403
+ const overBudget = new Set(files.flatMap((hotspot, index) => hotspot.testCount > 0 && hotspot.perTest >= floorMs ? [index + 1] : []));
4363
4404
  const drawn = table(
4364
4405
  { head: "file", cells: files.map((hotspot) => hotspot.file) },
4365
4406
  [
@@ -4367,7 +4408,9 @@ function fileSection(files, total, width) {
4367
4408
  { head: "ms/test", cells: files.map((hotspot) => hotspot.testCount === 0 ? NO_PER_TEST : formatMs(hotspot.perTest)) },
4368
4409
  { head: "share", cells: files.map((hotspot) => formatShare(hotspot.ms / total)) }
4369
4410
  ],
4370
- width
4411
+ width,
4412
+ (cell, columnWidth) => truncatePath(cell, columnWidth, paint),
4413
+ (row, column, padded) => row === 0 ? paint.dim(padded) : column === 2 && overBudget.has(row) ? paint.red(padded) : padded
4371
4414
  );
4372
4415
  const counted = files.length === 1 ? "That one file is" : `Those ${files.length} files are`;
4373
4416
  const prose = [
@@ -4377,27 +4420,51 @@ function fileSection(files, total, width) {
4377
4420
  ].join("\n");
4378
4421
  return ["slowest files \u2014 what every body in the file cost, and what one of them cost", drawn, prose];
4379
4422
  }
4380
- function caseSection(cases, width) {
4423
+ function caseSection(cases, width, floorMs, paint) {
4381
4424
  if (cases.length === 0) {
4382
4425
  return [];
4383
4426
  }
4427
+ const timeWidth = widthOf(
4428
+ "time",
4429
+ cases.map((entry) => formatMs(entry.ms))
4430
+ ) + GAP;
4431
+ const nameWidth = Math.max(width - 2 * INDENT.length - timeWidth, "test".length);
4432
+ const groups = /* @__PURE__ */ new Map();
4433
+ for (const entry of cases) {
4434
+ const bucket = groups.get(entry.file);
4435
+ bucket === void 0 ? groups.set(entry.file, [entry]) : bucket.push(entry);
4436
+ }
4437
+ const lines = [];
4438
+ for (const [file, entries] of groups) {
4439
+ lines.push(`${INDENT}${truncatePath(file, width - INDENT.length, paint)}`);
4440
+ for (const entry of entries) {
4441
+ const time = formatMs(entry.ms).padStart(timeWidth);
4442
+ lines.push(
4443
+ `${INDENT}${INDENT}${truncateLeft(entry.name, nameWidth, paint).padEnd(nameWidth)}${entry.ms >= floorMs ? paint.red(time) : time}`
4444
+ );
4445
+ }
4446
+ }
4384
4447
  return [
4385
4448
  `slowest test bodies \u2014 a body under ${formatMs(CASE_FLOOR_MS)} is not in the report at all, so a fast one is absent rather than cheap`,
4386
- table(
4387
- { head: "test", cells: cases.map((entry) => `${entry.file} \u203A ${entry.name}`) },
4388
- [{ head: "time", cells: cases.map((entry) => formatMs(entry.ms)) }],
4389
- width
4390
- )
4449
+ lines.join("\n")
4391
4450
  ];
4392
4451
  }
4393
4452
  function formatHotspots(run, cwd, options = {}) {
4394
- const { limit, floorMs, width } = { ...HOTSPOT_DEFAULTS, ...options };
4453
+ const { limit, floorMs, width, colors } = { ...HOTSPOT_DEFAULTS, ...options };
4454
+ const paint = withColors(colors ?? colorWanted(process.env));
4395
4455
  const files = fileHotspots(run, cwd, limit);
4396
4456
  const [slowest] = files;
4397
4457
  if (slowest === void 0 || slowest.ms < floorMs) {
4398
4458
  return "";
4399
4459
  }
4400
- return [...fileSection(files, totalTestMs(run, cwd), width), ...caseSection(caseHotspots(run, cwd, limit), width)].join("\n\n");
4460
+ const sections = [
4461
+ ...fileSection(files, totalTestMs(run, cwd), width, floorMs, paint),
4462
+ ...caseSection(caseHotspots(run, cwd, limit), width, floorMs, paint)
4463
+ ];
4464
+ return sections.join("\n\n");
4465
+ }
4466
+ function withColors(on) {
4467
+ return on ? TERMINAL : MONOCHROME;
4401
4468
  }
4402
4469
  function hotspotFloorNote(run, cwd, floorMs = HOTSPOT_DEFAULTS.floorMs) {
4403
4470
  const [slowest] = fileHotspots(run, cwd, 1);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vitest-auto-spy",
3
- "version": "5.13.0",
3
+ "version": "5.14.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",