vitest-auto-spy 5.12.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.
- package/dist/cli.js +125 -36
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -19,6 +19,7 @@ var VALUE_FLAGS = /* @__PURE__ */ new Set([
|
|
|
19
19
|
"max-file-ms",
|
|
20
20
|
"max-test-ms",
|
|
21
21
|
"max-wall-ms",
|
|
22
|
+
"min-severity",
|
|
22
23
|
"only",
|
|
23
24
|
"out",
|
|
24
25
|
"skip",
|
|
@@ -416,11 +417,12 @@ function formatOne(finding2) {
|
|
|
416
417
|
"\n"
|
|
417
418
|
);
|
|
418
419
|
}
|
|
419
|
-
function formatFindings(findings) {
|
|
420
|
-
|
|
420
|
+
function formatFindings(findings, minSeverity = "info") {
|
|
421
|
+
const reported = findings.filter((finding2) => SEVERITY_ORDER[finding2.severity] <= SEVERITY_ORDER[minSeverity]);
|
|
422
|
+
if (reported.length === 0) {
|
|
421
423
|
return "";
|
|
422
424
|
}
|
|
423
|
-
return sortFindings(
|
|
425
|
+
return sortFindings(reported).map(formatOne).join("\n\n");
|
|
424
426
|
}
|
|
425
427
|
function summarize(findings) {
|
|
426
428
|
const counts = { error: 0, warning: 0, info: 0 };
|
|
@@ -2453,7 +2455,7 @@ function checkForeignPragma(graph) {
|
|
|
2453
2455
|
}
|
|
2454
2456
|
|
|
2455
2457
|
// src/cli/checks/export-map.generated.ts
|
|
2456
|
-
var EXPORT_MAP_VERSION = "5.
|
|
2458
|
+
var EXPORT_MAP_VERSION = "5.14.0";
|
|
2457
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";
|
|
2458
2460
|
var EXPORTED_BY = {
|
|
2459
2461
|
AccessorImplementations: "0 1 2 3 4 12 17 18 19",
|
|
@@ -3484,6 +3486,11 @@ Options
|
|
|
3484
3486
|
perf only. How many times its recorded share a file has to
|
|
3485
3487
|
take (2), and the absolute floor under which it is noise
|
|
3486
3488
|
(500 ms).
|
|
3489
|
+
--min-severity <error|warning|info>
|
|
3490
|
+
The quietest findings the report prints. Default info, which
|
|
3491
|
+
prints everything. The tally line still counts what was hidden,
|
|
3492
|
+
and nothing about the exit code moves \u2014 a note never failed a
|
|
3493
|
+
run. Works for doctor and for perf.
|
|
3487
3494
|
--top <n> perf only. Rows in the "slowest files" and "slowest bodies"
|
|
3488
3495
|
tables. 0 turns them off. Asked for explicitly, a run whose
|
|
3489
3496
|
slowest file is under the one-second floor says so rather
|
|
@@ -4305,6 +4312,17 @@ var NO_PER_TEST = "\u2014";
|
|
|
4305
4312
|
var INDENT = " ";
|
|
4306
4313
|
var GAP = 4;
|
|
4307
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
|
+
}
|
|
4308
4326
|
function fileHotspots(run, cwd, limit) {
|
|
4309
4327
|
const found = [];
|
|
4310
4328
|
for (const [file, measured] of measuredFiles(run, cwd)) {
|
|
@@ -4331,28 +4349,58 @@ function caseHotspots(run, cwd, limit) {
|
|
|
4331
4349
|
function totalTestMs(run, cwd) {
|
|
4332
4350
|
return [...measuredFiles(run, cwd).values()].reduce((total, file) => total + file.tests, 0);
|
|
4333
4351
|
}
|
|
4334
|
-
function truncateLeft(text, width) {
|
|
4335
|
-
|
|
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("/")}`;
|
|
4336
4383
|
}
|
|
4337
4384
|
function widthOf(head, cells) {
|
|
4338
4385
|
return cells.reduce((widest, cell) => Math.max(widest, cell.length), head.length);
|
|
4339
4386
|
}
|
|
4340
|
-
function table(label, numbers, width) {
|
|
4387
|
+
function table(label, numbers, width, truncate, decorate) {
|
|
4341
4388
|
const spent = numbers.reduce((sum, column) => sum + widthOf(column.head, column.cells) + GAP, 0);
|
|
4342
4389
|
const labelWidth = Math.max(Math.min(widthOf(label.head, label.cells), width - INDENT.length - spent), label.head.length);
|
|
4343
4390
|
const laid = [
|
|
4344
|
-
{ width: labelWidth, left: true, texts: [label.head, ...label.cells.map((cell) =>
|
|
4391
|
+
{ width: labelWidth, left: true, texts: [label.head, ...label.cells.map((cell) => truncate(cell, labelWidth))] },
|
|
4345
4392
|
...numbers.map((column) => ({ width: widthOf(column.head, column.cells) + GAP, left: false, texts: [column.head, ...column.cells] }))
|
|
4346
4393
|
];
|
|
4347
4394
|
return laid.reduce(
|
|
4348
|
-
(lines, column) => column.texts.map(
|
|
4349
|
-
(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))}`
|
|
4350
4397
|
),
|
|
4351
4398
|
[]
|
|
4352
4399
|
).join("\n");
|
|
4353
4400
|
}
|
|
4354
|
-
function fileSection(files, total, width) {
|
|
4401
|
+
function fileSection(files, total, width, floorMs, paint) {
|
|
4355
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] : []));
|
|
4356
4404
|
const drawn = table(
|
|
4357
4405
|
{ head: "file", cells: files.map((hotspot) => hotspot.file) },
|
|
4358
4406
|
[
|
|
@@ -4360,7 +4408,9 @@ function fileSection(files, total, width) {
|
|
|
4360
4408
|
{ head: "ms/test", cells: files.map((hotspot) => hotspot.testCount === 0 ? NO_PER_TEST : formatMs(hotspot.perTest)) },
|
|
4361
4409
|
{ head: "share", cells: files.map((hotspot) => formatShare(hotspot.ms / total)) }
|
|
4362
4410
|
],
|
|
4363
|
-
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
|
|
4364
4414
|
);
|
|
4365
4415
|
const counted = files.length === 1 ? "That one file is" : `Those ${files.length} files are`;
|
|
4366
4416
|
const prose = [
|
|
@@ -4370,27 +4420,51 @@ function fileSection(files, total, width) {
|
|
|
4370
4420
|
].join("\n");
|
|
4371
4421
|
return ["slowest files \u2014 what every body in the file cost, and what one of them cost", drawn, prose];
|
|
4372
4422
|
}
|
|
4373
|
-
function caseSection(cases, width) {
|
|
4423
|
+
function caseSection(cases, width, floorMs, paint) {
|
|
4374
4424
|
if (cases.length === 0) {
|
|
4375
4425
|
return [];
|
|
4376
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
|
+
}
|
|
4377
4447
|
return [
|
|
4378
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`,
|
|
4379
|
-
|
|
4380
|
-
{ head: "test", cells: cases.map((entry) => `${entry.file} \u203A ${entry.name}`) },
|
|
4381
|
-
[{ head: "time", cells: cases.map((entry) => formatMs(entry.ms)) }],
|
|
4382
|
-
width
|
|
4383
|
-
)
|
|
4449
|
+
lines.join("\n")
|
|
4384
4450
|
];
|
|
4385
4451
|
}
|
|
4386
4452
|
function formatHotspots(run, cwd, options = {}) {
|
|
4387
|
-
const { limit, floorMs, width } = { ...HOTSPOT_DEFAULTS, ...options };
|
|
4453
|
+
const { limit, floorMs, width, colors } = { ...HOTSPOT_DEFAULTS, ...options };
|
|
4454
|
+
const paint = withColors(colors ?? colorWanted(process.env));
|
|
4388
4455
|
const files = fileHotspots(run, cwd, limit);
|
|
4389
4456
|
const [slowest] = files;
|
|
4390
4457
|
if (slowest === void 0 || slowest.ms < floorMs) {
|
|
4391
4458
|
return "";
|
|
4392
4459
|
}
|
|
4393
|
-
|
|
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;
|
|
4394
4468
|
}
|
|
4395
4469
|
function hotspotFloorNote(run, cwd, floorMs = HOTSPOT_DEFAULTS.floorMs) {
|
|
4396
4470
|
const [slowest] = fileHotspots(run, cwd, 1);
|
|
@@ -4709,16 +4783,22 @@ function formatPhases(phases) {
|
|
|
4709
4783
|
const rows = phases.map((phase) => ` ${phase.name.padEnd(14)}${formatMs(phase.ms).padStart(10)}${formatShare(phase.share).padStart(9)}`);
|
|
4710
4784
|
return [header2, ...rows].join("\n");
|
|
4711
4785
|
}
|
|
4712
|
-
function reportFindings(analysis, io) {
|
|
4786
|
+
function reportFindings(analysis, io, minSeverity) {
|
|
4713
4787
|
if (analysis.findings.length === 0) {
|
|
4714
4788
|
io.out(`
|
|
4715
4789
|
${nothingToDo(analysis)}`);
|
|
4716
4790
|
return;
|
|
4717
4791
|
}
|
|
4792
|
+
reportOrTally(analysis.findings, io, minSeverity);
|
|
4793
|
+
}
|
|
4794
|
+
function reportOrTally(findings, io, minSeverity) {
|
|
4795
|
+
const report2 = formatFindings(findings, minSeverity);
|
|
4796
|
+
if (report2 !== "") {
|
|
4797
|
+
io.out(`
|
|
4798
|
+
${report2}`);
|
|
4799
|
+
}
|
|
4718
4800
|
io.out(`
|
|
4719
|
-
${
|
|
4720
|
-
io.out(`
|
|
4721
|
-
${summarize(analysis.findings)}`);
|
|
4801
|
+
${summarize(findings)}`);
|
|
4722
4802
|
}
|
|
4723
4803
|
var PERF_GATE_FAILED = 1;
|
|
4724
4804
|
var PERF_NO_MEASUREMENT = 2;
|
|
@@ -4771,7 +4851,7 @@ function unmatchedScope(run, cwd, only) {
|
|
|
4771
4851
|
const paths = [...measuredFiles(run, cwd).keys()];
|
|
4772
4852
|
return only.filter((entry) => !paths.some((path) => isJudged(path, [entry])));
|
|
4773
4853
|
}
|
|
4774
|
-
function runGate(run, cwd, io, gate, extra) {
|
|
4854
|
+
function runGate(run, cwd, io, gate, extra, minSeverity) {
|
|
4775
4855
|
const unmatched = unmatchedScope(run, cwd, gate.options.only);
|
|
4776
4856
|
if (unmatched.length > 0) {
|
|
4777
4857
|
io.err(
|
|
@@ -4789,10 +4869,7 @@ perf gate: nothing over budget \u2014 no test body over ${formatMs(gate.options.
|
|
|
4789
4869
|
return 0;
|
|
4790
4870
|
}
|
|
4791
4871
|
const verdict = gateVerdict(candidates2, confirmRun(gate, suspectFiles(candidates2), cwd, io), cwd, gate.trustSingle);
|
|
4792
|
-
io
|
|
4793
|
-
${formatFindings(verdict.findings)}`);
|
|
4794
|
-
io.out(`
|
|
4795
|
-
${summarize(verdict.findings)}`);
|
|
4872
|
+
reportOrTally(verdict.findings, io, minSeverity);
|
|
4796
4873
|
return verdict.failed ? PERF_GATE_FAILED : 0;
|
|
4797
4874
|
}
|
|
4798
4875
|
function baselineCandidates(run, cwd, request, io) {
|
|
@@ -4868,7 +4945,7 @@ function renderPerf(source, profile, io, options = {}) {
|
|
|
4868
4945
|
}
|
|
4869
4946
|
io.out(formatPhases(analysis.phases));
|
|
4870
4947
|
reportHotspots(source.run, profile.cwd, options.top, io);
|
|
4871
|
-
reportFindings(analysis, io);
|
|
4948
|
+
reportFindings(analysis, io, options.minSeverity);
|
|
4872
4949
|
if (options.baseline?.update === true) {
|
|
4873
4950
|
recordBaseline(source.run, profile.cwd, options.baseline.path, io);
|
|
4874
4951
|
return 0;
|
|
@@ -4877,7 +4954,7 @@ function renderPerf(source, profile, io, options = {}) {
|
|
|
4877
4954
|
if (gate === void 0) {
|
|
4878
4955
|
if (regressions.length > 0) {
|
|
4879
4956
|
io.out(`
|
|
4880
|
-
${formatFindings(gateVerdict(regressions, void 0, profile.cwd, false).findings)}`);
|
|
4957
|
+
${formatFindings(gateVerdict(regressions, void 0, profile.cwd, false).findings, options.minSeverity)}`);
|
|
4881
4958
|
}
|
|
4882
4959
|
return 0;
|
|
4883
4960
|
}
|
|
@@ -4887,7 +4964,7 @@ ${formatFindings(gateVerdict(regressions, void 0, profile.cwd, false).findings)}
|
|
|
4887
4964
|
);
|
|
4888
4965
|
return PERF_NO_MEASUREMENT;
|
|
4889
4966
|
}
|
|
4890
|
-
return runGate(source.run, profile.cwd, io, gate, regressions);
|
|
4967
|
+
return runGate(source.run, profile.cwd, io, gate, regressions, options.minSeverity);
|
|
4891
4968
|
}
|
|
4892
4969
|
var ROOT_CONFIGS = [
|
|
4893
4970
|
"vitest.config.ts",
|
|
@@ -5198,7 +5275,14 @@ function perfRemeasure(options, spawn = spawnProcess, packageRoot = ownPackageRo
|
|
|
5198
5275
|
|
|
5199
5276
|
// src/cli/main.ts
|
|
5200
5277
|
var STATUS_WIDTH = 10;
|
|
5201
|
-
function
|
|
5278
|
+
function minSeverityOf(args) {
|
|
5279
|
+
const raw = flagValue(args, "min-severity")?.trim().toLowerCase();
|
|
5280
|
+
if (raw === "error" || raw === "info") {
|
|
5281
|
+
return raw;
|
|
5282
|
+
}
|
|
5283
|
+
return raw === "warning" || raw === "warn" ? "warning" : void 0;
|
|
5284
|
+
}
|
|
5285
|
+
function doctorCommand(cwd, argv, io) {
|
|
5202
5286
|
const profile = readProfile(cwd);
|
|
5203
5287
|
const findings = runDoctor(profile);
|
|
5204
5288
|
io.out(`vitest-auto-spy doctor \u2014 ${cwd}`);
|
|
@@ -5208,7 +5292,10 @@ function doctorCommand(cwd, io) {
|
|
|
5208
5292
|
io.out("No problems found.");
|
|
5209
5293
|
return 0;
|
|
5210
5294
|
}
|
|
5211
|
-
|
|
5295
|
+
const report2 = formatFindings(findings, minSeverityOf(parseArgs(argv)));
|
|
5296
|
+
if (report2 !== "") {
|
|
5297
|
+
io.out(report2);
|
|
5298
|
+
}
|
|
5212
5299
|
io.out(`
|
|
5213
5300
|
${summarize(findings)}`);
|
|
5214
5301
|
return hasFailures(findings) ? 1 : 0;
|
|
@@ -5252,10 +5339,12 @@ function perfCommand(cwd, argv, io) {
|
|
|
5252
5339
|
const gate = flagEnabled(args, "gate") ? { options: gateOptions(args), remeasure: trustSingle ? void 0 : perfRemeasure(options), trustSingle } : void 0;
|
|
5253
5340
|
const baseline = baselineRequest(args, cwd);
|
|
5254
5341
|
const top = flagNumber(args, "top");
|
|
5342
|
+
const minSeverity = minSeverityOf(args);
|
|
5255
5343
|
return renderPerf(readPerfRun(options), profile, io, {
|
|
5256
5344
|
...gate === void 0 ? {} : { gate },
|
|
5257
5345
|
...baseline === void 0 ? {} : { baseline },
|
|
5258
|
-
...top === void 0 ? {} : { top }
|
|
5346
|
+
...top === void 0 ? {} : { top },
|
|
5347
|
+
...minSeverity === void 0 ? {} : { minSeverity }
|
|
5259
5348
|
});
|
|
5260
5349
|
}
|
|
5261
5350
|
function formatAction(action) {
|
|
@@ -5318,7 +5407,7 @@ function runCli(argv, io) {
|
|
|
5318
5407
|
return args.command === void 0 && !flagEnabled(args, "help") ? 2 : 0;
|
|
5319
5408
|
}
|
|
5320
5409
|
if (args.command === "doctor") {
|
|
5321
|
-
return doctorCommand(cwd, io);
|
|
5410
|
+
return doctorCommand(cwd, argv, io);
|
|
5322
5411
|
}
|
|
5323
5412
|
if (args.command === "init") {
|
|
5324
5413
|
return initCommand(cwd, argv, io);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vitest-auto-spy",
|
|
3
|
-
"version": "5.
|
|
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",
|