modestbench 0.2.0 → 0.3.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/CHANGELOG.md +27 -0
- package/README.md +131 -34
- package/dist/cli/commands/analyze.cjs +60 -0
- package/dist/cli/commands/analyze.cjs.map +1 -0
- package/dist/cli/commands/analyze.d.cts +35 -0
- package/dist/cli/commands/analyze.d.cts.map +1 -0
- package/dist/cli/commands/analyze.d.ts +35 -0
- package/dist/cli/commands/analyze.d.ts.map +1 -0
- package/dist/cli/commands/analyze.js +56 -0
- package/dist/cli/commands/analyze.js.map +1 -0
- package/dist/cli/commands/baseline.cjs +404 -0
- package/dist/cli/commands/baseline.cjs.map +1 -0
- package/dist/cli/commands/baseline.d.cts +72 -0
- package/dist/cli/commands/baseline.d.cts.map +1 -0
- package/dist/cli/commands/baseline.d.ts +72 -0
- package/dist/cli/commands/baseline.d.ts.map +1 -0
- package/dist/cli/commands/baseline.js +396 -0
- package/dist/cli/commands/baseline.js.map +1 -0
- package/dist/cli/commands/history.d.cts +1 -1
- package/dist/cli/commands/history.d.cts.map +1 -1
- package/dist/cli/commands/history.d.ts +1 -1
- package/dist/cli/commands/history.d.ts.map +1 -1
- package/dist/cli/commands/init.cjs +99 -166
- package/dist/cli/commands/init.cjs.map +1 -1
- package/dist/cli/commands/init.d.cts +4 -4
- package/dist/cli/commands/init.d.cts.map +1 -1
- package/dist/cli/commands/init.d.ts +4 -4
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +99 -166
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/run.cjs +146 -127
- package/dist/cli/commands/run.cjs.map +1 -1
- package/dist/cli/commands/run.d.cts +16 -3
- package/dist/cli/commands/run.d.cts.map +1 -1
- package/dist/cli/commands/run.d.ts +16 -3
- package/dist/cli/commands/run.d.ts.map +1 -1
- package/dist/cli/commands/run.js +145 -93
- package/dist/cli/commands/run.js.map +1 -1
- package/dist/cli/index.cjs +583 -394
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.d.cts +4 -16
- package/dist/cli/index.d.cts.map +1 -1
- package/dist/cli/index.d.ts +4 -16
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +575 -386
- package/dist/cli/index.js.map +1 -1
- package/dist/config/budget-schema.cjs +172 -0
- package/dist/config/budget-schema.cjs.map +1 -0
- package/dist/config/budget-schema.d.cts +59 -0
- package/dist/config/budget-schema.d.cts.map +1 -0
- package/dist/config/budget-schema.d.ts +59 -0
- package/dist/config/budget-schema.d.ts.map +1 -0
- package/dist/config/budget-schema.js +166 -0
- package/dist/config/budget-schema.js.map +1 -0
- package/dist/config/schema.cjs +182 -2
- package/dist/config/schema.cjs.map +1 -1
- package/dist/config/schema.d.cts +122 -3
- package/dist/config/schema.d.cts.map +1 -1
- package/dist/config/schema.d.ts +122 -3
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/config/schema.js +180 -1
- package/dist/config/schema.js.map +1 -1
- package/dist/constants.cjs +45 -2
- package/dist/constants.cjs.map +1 -1
- package/dist/constants.d.cts +41 -0
- package/dist/constants.d.cts.map +1 -1
- package/dist/constants.d.ts +41 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +44 -1
- package/dist/constants.js.map +1 -1
- package/dist/core/engine.cjs +114 -23
- package/dist/core/engine.cjs.map +1 -1
- package/dist/core/engine.d.cts +7 -7
- package/dist/core/engine.d.cts.map +1 -1
- package/dist/core/engine.d.ts +7 -7
- package/dist/core/engine.d.ts.map +1 -1
- package/dist/core/engine.js +115 -24
- package/dist/core/engine.js.map +1 -1
- package/dist/core/engines/accurate-engine.cjs +171 -36
- package/dist/core/engines/accurate-engine.cjs.map +1 -1
- package/dist/core/engines/accurate-engine.d.cts +5 -0
- package/dist/core/engines/accurate-engine.d.cts.map +1 -1
- package/dist/core/engines/accurate-engine.d.ts +5 -0
- package/dist/core/engines/accurate-engine.d.ts.map +1 -1
- package/dist/core/engines/accurate-engine.js +171 -36
- package/dist/core/engines/accurate-engine.js.map +1 -1
- package/dist/core/engines/tinybench-engine.cjs +3 -2
- package/dist/core/engines/tinybench-engine.cjs.map +1 -1
- package/dist/core/engines/tinybench-engine.d.cts.map +1 -1
- package/dist/core/engines/tinybench-engine.d.ts.map +1 -1
- package/dist/core/engines/tinybench-engine.js +3 -2
- package/dist/core/engines/tinybench-engine.js.map +1 -1
- package/dist/core/output-path-resolver.cjs +8 -1
- package/dist/core/output-path-resolver.cjs.map +1 -1
- package/dist/core/output-path-resolver.d.cts.map +1 -1
- package/dist/core/output-path-resolver.d.ts.map +1 -1
- package/dist/core/output-path-resolver.js +9 -2
- package/dist/core/output-path-resolver.js.map +1 -1
- package/dist/errors/base.cjs +12 -3
- package/dist/errors/base.cjs.map +1 -1
- package/dist/errors/base.d.cts +7 -0
- package/dist/errors/base.d.cts.map +1 -1
- package/dist/errors/base.d.ts +7 -0
- package/dist/errors/base.d.ts.map +1 -1
- package/dist/errors/base.js +10 -2
- package/dist/errors/base.js.map +1 -1
- package/dist/errors/budget.cjs +37 -0
- package/dist/errors/budget.cjs.map +1 -0
- package/dist/errors/budget.d.cts +31 -0
- package/dist/errors/budget.d.cts.map +1 -0
- package/dist/errors/budget.d.ts +31 -0
- package/dist/errors/budget.d.ts.map +1 -0
- package/dist/errors/budget.js +33 -0
- package/dist/errors/budget.js.map +1 -0
- package/dist/errors/index.cjs +4 -1
- package/dist/errors/index.cjs.map +1 -1
- package/dist/errors/index.d.cts +1 -0
- package/dist/errors/index.d.cts.map +1 -1
- package/dist/errors/index.d.ts +1 -0
- package/dist/errors/index.d.ts.map +1 -1
- package/dist/errors/index.js +2 -0
- package/dist/errors/index.js.map +1 -1
- package/dist/index.cjs +13 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -0
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -1
- package/dist/reporters/csv.cjs +37 -17
- package/dist/reporters/csv.cjs.map +1 -1
- package/dist/reporters/csv.d.cts +3 -6
- package/dist/reporters/csv.d.cts.map +1 -1
- package/dist/reporters/csv.d.ts +3 -6
- package/dist/reporters/csv.d.ts.map +1 -1
- package/dist/reporters/csv.js +37 -17
- package/dist/reporters/csv.js.map +1 -1
- package/dist/reporters/human.cjs +290 -67
- package/dist/reporters/human.cjs.map +1 -1
- package/dist/reporters/human.d.cts +25 -13
- package/dist/reporters/human.d.cts.map +1 -1
- package/dist/reporters/human.d.ts +25 -13
- package/dist/reporters/human.d.ts.map +1 -1
- package/dist/reporters/human.js +290 -67
- package/dist/reporters/human.js.map +1 -1
- package/dist/reporters/json.cjs +23 -48
- package/dist/reporters/json.cjs.map +1 -1
- package/dist/reporters/json.d.cts +2 -28
- package/dist/reporters/json.d.cts.map +1 -1
- package/dist/reporters/json.d.ts +2 -28
- package/dist/reporters/json.d.ts.map +1 -1
- package/dist/reporters/json.js +25 -50
- package/dist/reporters/json.js.map +1 -1
- package/dist/reporters/profile-human.cjs +154 -0
- package/dist/reporters/profile-human.cjs.map +1 -0
- package/dist/reporters/profile-human.d.cts +44 -0
- package/dist/reporters/profile-human.d.cts.map +1 -0
- package/dist/reporters/profile-human.d.ts +44 -0
- package/dist/reporters/profile-human.d.ts.map +1 -0
- package/dist/reporters/profile-human.js +147 -0
- package/dist/reporters/profile-human.js.map +1 -0
- package/dist/reporters/simple.cjs +67 -45
- package/dist/reporters/simple.cjs.map +1 -1
- package/dist/reporters/simple.d.cts +14 -14
- package/dist/reporters/simple.d.cts.map +1 -1
- package/dist/reporters/simple.d.ts +14 -14
- package/dist/reporters/simple.d.ts.map +1 -1
- package/dist/reporters/simple.js +67 -45
- package/dist/reporters/simple.js.map +1 -1
- package/dist/schema/modestbench-config.schema.json +153 -0
- package/dist/services/baseline-storage.cjs +151 -0
- package/dist/services/baseline-storage.cjs.map +1 -0
- package/dist/services/baseline-storage.d.cts +55 -0
- package/dist/services/baseline-storage.d.cts.map +1 -0
- package/dist/services/baseline-storage.d.ts +55 -0
- package/dist/services/baseline-storage.d.ts.map +1 -0
- package/dist/services/baseline-storage.js +147 -0
- package/dist/services/baseline-storage.js.map +1 -0
- package/dist/services/budget-evaluator.cjs +146 -0
- package/dist/services/budget-evaluator.cjs.map +1 -0
- package/dist/services/budget-evaluator.d.cts +29 -0
- package/dist/services/budget-evaluator.d.cts.map +1 -0
- package/dist/services/budget-evaluator.d.ts +29 -0
- package/dist/services/budget-evaluator.d.ts.map +1 -0
- package/dist/services/budget-evaluator.js +142 -0
- package/dist/services/budget-evaluator.js.map +1 -0
- package/dist/services/config-manager.cjs +24 -10
- package/dist/services/config-manager.cjs.map +1 -1
- package/dist/services/config-manager.d.cts +6 -1
- package/dist/services/config-manager.d.cts.map +1 -1
- package/dist/services/config-manager.d.ts +6 -1
- package/dist/services/config-manager.d.ts.map +1 -1
- package/dist/services/config-manager.js +24 -10
- package/dist/services/config-manager.js.map +1 -1
- package/dist/services/file-loader.cjs +3 -6
- package/dist/services/file-loader.cjs.map +1 -1
- package/dist/services/file-loader.d.cts.map +1 -1
- package/dist/services/file-loader.d.ts.map +1 -1
- package/dist/services/file-loader.js +3 -6
- package/dist/services/file-loader.js.map +1 -1
- package/dist/services/profiler/profile-filter.cjs +116 -0
- package/dist/services/profiler/profile-filter.cjs.map +1 -0
- package/dist/services/profiler/profile-filter.d.cts +20 -0
- package/dist/services/profiler/profile-filter.d.cts.map +1 -0
- package/dist/services/profiler/profile-filter.d.ts +20 -0
- package/dist/services/profiler/profile-filter.d.ts.map +1 -0
- package/dist/services/profiler/profile-filter.js +112 -0
- package/dist/services/profiler/profile-filter.js.map +1 -0
- package/dist/services/profiler/profile-parser.cjs +139 -0
- package/dist/services/profiler/profile-parser.cjs.map +1 -0
- package/dist/services/profiler/profile-parser.d.cts +18 -0
- package/dist/services/profiler/profile-parser.d.cts.map +1 -0
- package/dist/services/profiler/profile-parser.d.ts +18 -0
- package/dist/services/profiler/profile-parser.d.ts.map +1 -0
- package/dist/services/profiler/profile-parser.js +132 -0
- package/dist/services/profiler/profile-parser.js.map +1 -0
- package/dist/services/profiler/profile-runner.cjs +90 -0
- package/dist/services/profiler/profile-runner.cjs.map +1 -0
- package/dist/services/profiler/profile-runner.d.cts +29 -0
- package/dist/services/profiler/profile-runner.d.cts.map +1 -0
- package/dist/services/profiler/profile-runner.d.ts +29 -0
- package/dist/services/profiler/profile-runner.d.ts.map +1 -0
- package/dist/services/profiler/profile-runner.js +86 -0
- package/dist/services/profiler/profile-runner.js.map +1 -0
- package/dist/services/progress-manager.cjs +10 -2
- package/dist/services/progress-manager.cjs.map +1 -1
- package/dist/services/progress-manager.d.cts +2 -0
- package/dist/services/progress-manager.d.cts.map +1 -1
- package/dist/services/progress-manager.d.ts +2 -0
- package/dist/services/progress-manager.d.ts.map +1 -1
- package/dist/services/progress-manager.js +10 -2
- package/dist/services/progress-manager.js.map +1 -1
- package/dist/services/reporter-registry.cjs +18 -24
- package/dist/services/reporter-registry.cjs.map +1 -1
- package/dist/services/reporter-registry.d.cts +18 -40
- package/dist/services/reporter-registry.d.cts.map +1 -1
- package/dist/services/reporter-registry.d.ts +18 -40
- package/dist/services/reporter-registry.d.ts.map +1 -1
- package/dist/services/reporter-registry.js +18 -24
- package/dist/services/reporter-registry.js.map +1 -1
- package/dist/types/budgets.cjs +8 -0
- package/dist/types/budgets.cjs.map +1 -0
- package/dist/types/budgets.d.cts +149 -0
- package/dist/types/budgets.d.cts.map +1 -0
- package/dist/types/budgets.d.ts +149 -0
- package/dist/types/budgets.d.ts.map +1 -0
- package/dist/types/budgets.js +7 -0
- package/dist/types/budgets.js.map +1 -0
- package/dist/types/cli.cjs +2 -11
- package/dist/types/cli.cjs.map +1 -1
- package/dist/types/cli.d.cts +3 -227
- package/dist/types/cli.d.cts.map +1 -1
- package/dist/types/cli.d.ts +3 -227
- package/dist/types/cli.d.ts.map +1 -1
- package/dist/types/cli.js +2 -11
- package/dist/types/cli.js.map +1 -1
- package/dist/types/core.cjs +6 -1
- package/dist/types/core.cjs.map +1 -1
- package/dist/types/core.d.cts +15 -2
- package/dist/types/core.d.cts.map +1 -1
- package/dist/types/core.d.ts +15 -2
- package/dist/types/core.d.ts.map +1 -1
- package/dist/types/core.js +2 -1
- package/dist/types/core.js.map +1 -1
- package/dist/types/index.cjs +5 -0
- package/dist/types/index.cjs.map +1 -1
- package/dist/types/index.d.cts +2 -0
- package/dist/types/index.d.cts.map +1 -1
- package/dist/types/index.d.ts +2 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -1
- package/dist/types/interfaces.d.cts +19 -8
- package/dist/types/interfaces.d.cts.map +1 -1
- package/dist/types/interfaces.d.ts +19 -8
- package/dist/types/interfaces.d.ts.map +1 -1
- package/dist/types/profiler.cjs +11 -0
- package/dist/types/profiler.cjs.map +1 -0
- package/dist/types/profiler.d.cts +102 -0
- package/dist/types/profiler.d.cts.map +1 -0
- package/dist/types/profiler.d.ts +102 -0
- package/dist/types/profiler.d.ts.map +1 -0
- package/dist/types/profiler.js +10 -0
- package/dist/types/profiler.js.map +1 -0
- package/dist/types/utility.cjs.map +1 -1
- package/dist/types/utility.d.cts +0 -8
- package/dist/types/utility.d.cts.map +1 -1
- package/dist/types/utility.d.ts +0 -8
- package/dist/types/utility.d.ts.map +1 -1
- package/dist/types/utility.js.map +1 -1
- package/dist/utils/identifiers.cjs +32 -0
- package/dist/utils/identifiers.cjs.map +1 -0
- package/dist/utils/identifiers.d.cts +32 -0
- package/dist/utils/identifiers.d.cts.map +1 -0
- package/dist/utils/identifiers.d.ts +32 -0
- package/dist/utils/identifiers.d.ts.map +1 -0
- package/dist/utils/identifiers.js +27 -0
- package/dist/utils/identifiers.js.map +1 -0
- package/dist/utils/package.cjs +40 -0
- package/dist/utils/package.cjs.map +1 -0
- package/dist/utils/package.d.cts +15 -0
- package/dist/utils/package.d.cts.map +1 -0
- package/dist/utils/package.d.ts +15 -0
- package/dist/utils/package.d.ts.map +1 -0
- package/dist/utils/package.js +33 -0
- package/dist/utils/package.js.map +1 -0
- package/dist/utils/type-guards.cjs +48 -0
- package/dist/utils/type-guards.cjs.map +1 -0
- package/dist/utils/type-guards.d.cts +22 -0
- package/dist/utils/type-guards.d.cts.map +1 -0
- package/dist/utils/type-guards.d.ts +22 -0
- package/dist/utils/type-guards.d.ts.map +1 -0
- package/dist/utils/type-guards.js +43 -0
- package/dist/utils/type-guards.js.map +1 -0
- package/package.json +18 -19
- package/src/cli/commands/analyze.ts +101 -0
- package/src/cli/commands/baseline.ts +577 -0
- package/src/cli/commands/history.ts +1 -1
- package/src/cli/commands/init.ts +116 -194
- package/src/cli/commands/run.ts +183 -113
- package/src/cli/index.ts +425 -183
- package/src/config/budget-schema.ts +189 -0
- package/src/config/schema.ts +260 -1
- package/src/constants.ts +53 -1
- package/src/core/engine.ts +169 -22
- package/src/core/engines/accurate-engine.ts +195 -44
- package/src/core/engines/tinybench-engine.ts +3 -2
- package/src/core/output-path-resolver.ts +10 -2
- package/src/errors/base.ts +11 -2
- package/src/errors/budget.ts +38 -0
- package/src/errors/index.ts +3 -0
- package/src/index.ts +9 -0
- package/src/reporters/csv.ts +54 -25
- package/src/reporters/human.ts +434 -115
- package/src/reporters/json.ts +26 -71
- package/src/reporters/profile-human.ts +210 -0
- package/src/reporters/simple.ts +88 -54
- package/src/services/baseline-storage.ts +199 -0
- package/src/services/budget-evaluator.ts +182 -0
- package/src/services/config-manager.ts +24 -9
- package/src/services/file-loader.ts +3 -6
- package/src/services/profiler/profile-filter.ts +147 -0
- package/src/services/profiler/profile-parser.ts +194 -0
- package/src/services/profiler/profile-runner.ts +121 -0
- package/src/services/progress-manager.ts +12 -2
- package/src/services/reporter-registry.ts +46 -81
- package/src/types/budgets.ts +180 -0
- package/src/types/cli.ts +5 -238
- package/src/types/core.ts +52 -10
- package/src/types/index.ts +5 -0
- package/src/types/interfaces.ts +24 -6
- package/src/types/profiler.ts +135 -0
- package/src/types/utility.ts +0 -10
- package/src/utils/identifiers.ts +58 -0
- package/src/utils/package.ts +35 -0
- package/src/utils/type-guards.ts +51 -0
package/dist/reporters/human.js
CHANGED
|
@@ -13,8 +13,10 @@ import { ansiChars, colors } from "../utils/ansi.js";
|
|
|
13
13
|
export class HumanReporter extends BaseReporter {
|
|
14
14
|
currentFile = '';
|
|
15
15
|
currentSuite = '';
|
|
16
|
+
currentSuiteMaxNameLen = 0; // Track max name length for current suite alignment
|
|
16
17
|
failures = [];
|
|
17
18
|
lastProgressLine = '';
|
|
19
|
+
maxTimePadWidth = 0; // Track maximum time padding width to prevent jitter
|
|
18
20
|
progressWindowActive = false; // Track if progress window is rendered
|
|
19
21
|
quiet;
|
|
20
22
|
showProgress;
|
|
@@ -34,6 +36,63 @@ export class HumanReporter extends BaseReporter {
|
|
|
34
36
|
this.quiet = options.quiet ?? false;
|
|
35
37
|
this.showProgress = options.progress ?? true;
|
|
36
38
|
}
|
|
39
|
+
/**
|
|
40
|
+
* Format bytes in human-readable format
|
|
41
|
+
*/
|
|
42
|
+
static formatBytes(bytes) {
|
|
43
|
+
const units = ['B', 'KB', 'MB', 'GB', 'TB'];
|
|
44
|
+
let size = bytes;
|
|
45
|
+
let unitIndex = 0;
|
|
46
|
+
while (size >= 1024 && unitIndex < units.length - 1) {
|
|
47
|
+
size /= 1024;
|
|
48
|
+
unitIndex++;
|
|
49
|
+
}
|
|
50
|
+
return `${size.toFixed(1)} ${units[unitIndex]}`;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Format file path - show relative path if within CWD, otherwise absolute
|
|
54
|
+
*/
|
|
55
|
+
static formatPath(filePath) {
|
|
56
|
+
const cwd = process.cwd();
|
|
57
|
+
const absolutePath = path.resolve(filePath);
|
|
58
|
+
// Check if the file is within the current working directory
|
|
59
|
+
if (absolutePath.startsWith(cwd + path.sep) || absolutePath === cwd) {
|
|
60
|
+
return path.relative(cwd, absolutePath);
|
|
61
|
+
}
|
|
62
|
+
return absolutePath;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Simple pluralization helper
|
|
66
|
+
*/
|
|
67
|
+
static pluralize(str, count) {
|
|
68
|
+
return count === 1 ? str : `${str}s`;
|
|
69
|
+
}
|
|
70
|
+
onBudgetResult(summary) {
|
|
71
|
+
if (summary.total === 0 || this.quiet) {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
this.clearProgress();
|
|
75
|
+
this.printLine();
|
|
76
|
+
const budgetHeader = `${this.colorize('magenta', ansiChars.block.full.repeat(2))} ${this.colorize('brightWhite', this.colorize('bold', 'Performance Budgets'))}`;
|
|
77
|
+
this.printLine(budgetHeader);
|
|
78
|
+
this.printLine();
|
|
79
|
+
for (const result of summary.results) {
|
|
80
|
+
const icon = result.passed ? ansiChars.checkmark : ansiChars.cross;
|
|
81
|
+
const iconColor = result.passed ? 'brightCyan' : 'brightRed';
|
|
82
|
+
this.printLine(` ${this.colorize(iconColor, icon)} ${this.colorize('white', result.taskId)}`);
|
|
83
|
+
if (!result.passed && result.violations.length > 0) {
|
|
84
|
+
for (const violation of result.violations) {
|
|
85
|
+
this.printLine(` ${this.colorize('brightRed', violation.message)}`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
this.printLine();
|
|
90
|
+
const statusText = summary.failed === 0
|
|
91
|
+
? `${this.colorize('brightCyan', ansiChars.checkmark)} All ${summary.total} budget(s) passed`
|
|
92
|
+
: `${this.colorize('brightRed', ansiChars.cross)} ${summary.failed} of ${summary.total} budget(s) failed`;
|
|
93
|
+
this.printLine(` ${statusText}`);
|
|
94
|
+
this.printLine();
|
|
95
|
+
}
|
|
37
96
|
onEnd(run) {
|
|
38
97
|
if (this.quiet) {
|
|
39
98
|
return;
|
|
@@ -45,11 +104,13 @@ export class HumanReporter extends BaseReporter {
|
|
|
45
104
|
let totalSuites = 0;
|
|
46
105
|
let totalPassed = 0;
|
|
47
106
|
let totalFailed = 0;
|
|
107
|
+
let totalAborted = 0;
|
|
48
108
|
for (const file of run.files) {
|
|
49
109
|
totalSuites += file.suites.length;
|
|
50
110
|
for (const suite of file.suites) {
|
|
51
|
-
totalPassed += suite.tasks.filter((t) => !t.error).length;
|
|
111
|
+
totalPassed += suite.tasks.filter((t) => !t.error && !t.aborted).length;
|
|
52
112
|
totalFailed += suite.tasks.filter((t) => t.error).length;
|
|
113
|
+
totalAborted += suite.tasks.filter((t) => t.aborted).length;
|
|
53
114
|
}
|
|
54
115
|
}
|
|
55
116
|
// Results header
|
|
@@ -58,15 +119,22 @@ export class HumanReporter extends BaseReporter {
|
|
|
58
119
|
this.printLine();
|
|
59
120
|
this.printLine(`${this.colorize('brightBlue', ' Files:')} ${this.colorize('brightWhite', String(totalFiles))}`);
|
|
60
121
|
this.printLine(`${this.colorize('brightBlue', ' Suites:')} ${this.colorize('brightWhite', String(totalSuites))}`);
|
|
61
|
-
this.printLine(`${this.colorize('brightBlue', ' Tasks:')} ${this.colorize('brightWhite', String(totalPassed + totalFailed))}`);
|
|
62
|
-
if (totalFailed > 0) {
|
|
63
|
-
|
|
64
|
-
|
|
122
|
+
this.printLine(`${this.colorize('brightBlue', ' Tasks:')} ${this.colorize('brightWhite', String(totalPassed + totalFailed + totalAborted))}`);
|
|
123
|
+
if (totalFailed > 0 || totalAborted > 0) {
|
|
124
|
+
if (totalFailed > 0) {
|
|
125
|
+
this.printLine(`${this.colorize('brightRed', ansiChars.cross + ' Failed:')} ${this.colorize('brightWhite', String(totalFailed))}`);
|
|
126
|
+
}
|
|
127
|
+
if (totalPassed > 0) {
|
|
128
|
+
this.printLine(`${this.colorize('brightCyan', ansiChars.checkmark + ' Passed:')} ${this.colorize('brightWhite', String(totalPassed))}`);
|
|
129
|
+
}
|
|
130
|
+
if (totalAborted > 0) {
|
|
131
|
+
this.printLine(`${this.colorize('brightYellow', ansiChars.approx + ' Aborted:')} ${this.colorize('brightWhite', String(totalAborted))}`);
|
|
132
|
+
}
|
|
65
133
|
}
|
|
66
134
|
else {
|
|
67
135
|
this.printLine(`${this.colorize('brightCyan', ansiChars.checkmark + ' All tasks passed:')} ${this.colorize('brightWhite', String(totalPassed))}`);
|
|
68
136
|
}
|
|
69
|
-
this.printLine(`${this.colorize('cyan', ansiChars.approx + ' Duration:')} ${this.colorize('brightWhite',
|
|
137
|
+
this.printLine(`${this.colorize('cyan', ansiChars.approx + ' Duration:')} ${this.colorize('brightWhite', BaseReporter.formatDuration(duration * 1000000))}`);
|
|
70
138
|
this.printLine();
|
|
71
139
|
if (totalFailed > 0) {
|
|
72
140
|
// Display failed tasks with details
|
|
@@ -75,14 +143,15 @@ export class HumanReporter extends BaseReporter {
|
|
|
75
143
|
this.printLine(this.colorize('brightRed', this.colorize('bold', 'Failed Tasks:')));
|
|
76
144
|
this.printLine();
|
|
77
145
|
for (const failure of this.failures) {
|
|
78
|
-
const displayPath =
|
|
146
|
+
const displayPath = HumanReporter.formatPath(failure.file);
|
|
79
147
|
this.printLine(` ${this.colorize('dim', displayPath)} ${this.colorize('dim', '›')} ${this.colorize('white', failure.suite)} ${this.colorize('dim', '›')} ${this.colorize('brightWhite', failure.task)}`);
|
|
80
148
|
this.printLine(` ${this.colorize('brightRed', failure.error)}`);
|
|
81
149
|
this.printLine();
|
|
82
150
|
}
|
|
83
151
|
}
|
|
84
152
|
}
|
|
85
|
-
else {
|
|
153
|
+
else if (totalAborted === 0) {
|
|
154
|
+
// Only show "Rad" if no failures AND no aborts
|
|
86
155
|
const successMessage = `${this.colorize('brightMagenta', 'Rad. ☮')}`;
|
|
87
156
|
this.printLine(successMessage);
|
|
88
157
|
}
|
|
@@ -108,7 +177,7 @@ export class HumanReporter extends BaseReporter {
|
|
|
108
177
|
this.printLine(this.colorize('red', ` ${ansiChars.cross} ${totalFailed} failed, ${totalPassed} passed`));
|
|
109
178
|
}
|
|
110
179
|
else {
|
|
111
|
-
this.printLine(` ${this.colorize('magenta', ansiChars.checkmark)} ${totalPassed > 1 ? this.colorize('brightMagenta', 'All ') : ''}${this.colorize('bold', this.colorize('brightMagenta', `${totalPassed}`))} ${this.colorize('brightMagenta', `${
|
|
180
|
+
this.printLine(` ${this.colorize('magenta', ansiChars.checkmark)} ${totalPassed > 1 ? this.colorize('brightMagenta', 'All ') : ''}${this.colorize('bold', this.colorize('brightMagenta', `${totalPassed}`))} ${this.colorize('brightMagenta', `${HumanReporter.pluralize('task', totalPassed)} passed`)}`);
|
|
112
181
|
}
|
|
113
182
|
this.printLine();
|
|
114
183
|
}
|
|
@@ -117,7 +186,7 @@ export class HumanReporter extends BaseReporter {
|
|
|
117
186
|
if (this.quiet) {
|
|
118
187
|
return;
|
|
119
188
|
}
|
|
120
|
-
const displayPath =
|
|
189
|
+
const displayPath = HumanReporter.formatPath(file);
|
|
121
190
|
const fileMarker = `${colors.magenta}${ansiChars.block.dark}${ansiChars.block.dark}${colors.reset}`;
|
|
122
191
|
this.printLine(`${fileMarker} ${colors.underline}${this.colorize('brightMagenta', this.colorize('bold', displayPath))}${colors.reset}`);
|
|
123
192
|
}
|
|
@@ -130,7 +199,7 @@ export class HumanReporter extends BaseReporter {
|
|
|
130
199
|
if (!process.stdout.isTTY) {
|
|
131
200
|
return;
|
|
132
201
|
}
|
|
133
|
-
const { elapsed, percentage, tasksCompleted, totalTasks } = state;
|
|
202
|
+
const { currentTask, elapsed, percentage, tasksCompleted, totalTasks } = state;
|
|
134
203
|
// Pad task counts for alignment
|
|
135
204
|
const totalTasksWidth = String(totalTasks).length;
|
|
136
205
|
const paddedTasksCompleted = String(tasksCompleted).padStart(totalTasksWidth, ' ');
|
|
@@ -139,7 +208,7 @@ export class HumanReporter extends BaseReporter {
|
|
|
139
208
|
const elapsedStrRaw = this.formatTimeRemaining(elapsedSeconds);
|
|
140
209
|
// Calculate ETA if we have completed tasks and determine padding width
|
|
141
210
|
let etaStr = '';
|
|
142
|
-
let padWidth = elapsedStrRaw.length;
|
|
211
|
+
let padWidth = Math.max(this.maxTimePadWidth, elapsedStrRaw.length);
|
|
143
212
|
if (tasksCompleted > 0) {
|
|
144
213
|
const avgTimePerTask = elapsed / tasksCompleted;
|
|
145
214
|
const remainingTasks = totalTasks - tasksCompleted;
|
|
@@ -147,12 +216,21 @@ export class HumanReporter extends BaseReporter {
|
|
|
147
216
|
const etaSeconds = Math.round(etaMs / 1000);
|
|
148
217
|
const etaTimeStr = this.formatTimeRemaining(etaSeconds);
|
|
149
218
|
padWidth = Math.max(padWidth, etaTimeStr.length);
|
|
150
|
-
etaStr = ` ${this.colorize('
|
|
219
|
+
etaStr = ` ${this.colorize('gray', '|')} ${this.colorize('gray', 'ETA:')} ${this.colorize('brightBlue', etaTimeStr)}`;
|
|
151
220
|
}
|
|
221
|
+
// Remember the maximum width we've ever used to prevent jitter
|
|
222
|
+
this.maxTimePadWidth = Math.max(this.maxTimePadWidth, padWidth);
|
|
152
223
|
// Pad elapsed time to match the longest time string
|
|
153
|
-
const elapsedStr = elapsedStrRaw.padStart(
|
|
224
|
+
const elapsedStr = elapsedStrRaw.padStart(this.maxTimePadWidth, ' ');
|
|
154
225
|
const roundedPercentage = percentage.toFixed(2);
|
|
155
|
-
|
|
226
|
+
// Build progress line with current task if available
|
|
227
|
+
let line = `${this.colorize('brightCyan', ansiChars.approx)} ${this.colorize('white', paddedTasksCompleted)}${this.colorize('gray', '/')}${this.colorize('white', String(totalTasks))} ${this.colorize('gray', 'tasks')} ${this.colorize('gray', '(')}${this.colorize('brightBlue', roundedPercentage + '%')}${this.colorize('gray', ')')} ${this.colorize('gray', '|')} ${this.colorize('gray', 'Elapsed:')} ${this.colorize('cyan', elapsedStr)}${etaStr}`;
|
|
228
|
+
if (currentTask) {
|
|
229
|
+
const truncatedTask = currentTask.length > 60
|
|
230
|
+
? currentTask.substring(0, 57) + '...'
|
|
231
|
+
: currentTask;
|
|
232
|
+
line += ` ${this.colorize('gray', '|')} ${this.colorize('white', truncatedTask)}`;
|
|
233
|
+
}
|
|
156
234
|
this.lastProgressLine = line;
|
|
157
235
|
this.renderProgressWindow();
|
|
158
236
|
}
|
|
@@ -160,6 +238,7 @@ export class HumanReporter extends BaseReporter {
|
|
|
160
238
|
this.startTime = Date.now();
|
|
161
239
|
this.failures = []; // Reset failures for new run
|
|
162
240
|
this.lastProgressLine = ''; // Reset for new run
|
|
241
|
+
this.maxTimePadWidth = 0; // Reset time padding width for new run
|
|
163
242
|
if (this.quiet) {
|
|
164
243
|
return;
|
|
165
244
|
}
|
|
@@ -175,7 +254,7 @@ export class HumanReporter extends BaseReporter {
|
|
|
175
254
|
\x1b[48;5;0m \x1b[48;5;14m \x1b[38;5;30;48;5;38m▄\x1b[38;5;14;48;5;14m▄\x1b[48;5;14m \x1b[38;5;45;48;5;14m▄\x1b[38;5;89;48;5;14m▄\x1b[38;5;89;48;5;89m▄\x1b[38;5;14;48;5;31m▄\x1b[48;5;14m \x1b[38;5;37;48;5;89m▄\x1b[48;5;198m \x1b[38;5;198;48;5;198m▄\x1b[38;5;31;48;5;14m▄\x1b[48;5;14m \x1b[48;5;0m \x1b[m \x1b[2mnode.js:\x1b[m \x1b[36m${run.environment.nodeVersion} \x1b[m
|
|
176
255
|
\x1b[48;5;0m \x1b[48;5;14m \x1b[38;5;44;48;5;31m▄\x1b[48;5;14m \x1b[38;5;126;48;5;38m▄\x1b[38;5;198;48;5;237m▄\x1b[38;5;237;48;5;37m▄\x1b[48;5;14m \x1b[38;5;14;48;5;14m▄\x1b[38;5;162;48;5;198m▄▄\x1b[38;5;53;48;5;240m▄\x1b[48;5;14m \x1b[48;5;0m \x1b[m \x1b[2mplatform:\x1b[m \x1b[36m${run.environment.platform} ${run.environment.arch} \x1b[m
|
|
177
256
|
\x1b[48;5;0m \x1b[38;5;45;48;5;14m▄\x1b[48;5;14m \x1b[38;5;14;48;5;37m▄\x1b[38;5;14;48;5;5m▄\x1b[38;5;14;48;5;44m▄\x1b[48;5;14m \x1b[38;5;45;48;5;14m▄\x1b[48;5;0m \x1b[m \x1b[2mcpu:\x1b[m \x1b[36m${run.environment.cpu.model} \x1b[2m(\x1b[m\x1b[36m${run.environment.cpu.cores} cores\x1b[2m) \x1b[m
|
|
178
|
-
\x1b[49;38;5;0m▀▀\x1b[38;5;0;48;5;6m▄\x1b[38;5;232;48;5;14m▄\x1b[38;5;38;48;5;14m▄\x1b[48;5;14m \x1b[38;5;30;48;5;14m▄\x1b[38;5;0;48;5;14m▄\x1b[38;5;0;48;5;23m▄\x1b[49;38;5;0m▀▀\x1b[m \x1b[2mmem:\x1b[m \x1b[36m${
|
|
257
|
+
\x1b[49;38;5;0m▀▀\x1b[38;5;0;48;5;6m▄\x1b[38;5;232;48;5;14m▄\x1b[38;5;38;48;5;14m▄\x1b[48;5;14m \x1b[38;5;30;48;5;14m▄\x1b[38;5;0;48;5;14m▄\x1b[38;5;0;48;5;23m▄\x1b[49;38;5;0m▀▀\x1b[m \x1b[2mmem:\x1b[m \x1b[36m${HumanReporter.formatBytes(run.environment.memory.total)} \x1b[m
|
|
179
258
|
\x1b[49m \x1b[49;38;5;0m▀\x1b[38;5;0;48;5;236m▄\x1b[38;5;0;48;5;45m▄\x1b[38;5;23;48;5;14m▄\x1b[48;5;14m \x1b[38;5;236;48;5;14m▄\x1b[38;5;0;48;5;44m▄\x1b[38;5;0;48;5;232m▄\x1b[49;38;5;0m▀\x1b[49m \x1b[m
|
|
180
259
|
\x1b[49m \x1b[49;38;5;0m▀▀\x1b[38;5;0;48;5;37m▄\x1b[38;5;0;48;5;14m▄\x1b[38;5;0;48;5;30m▄\x1b[49;38;5;0m▀▀\x1b[49m \x1b[m
|
|
181
260
|
`;
|
|
@@ -210,22 +289,53 @@ export class HumanReporter extends BaseReporter {
|
|
|
210
289
|
if (this.quiet) {
|
|
211
290
|
return;
|
|
212
291
|
}
|
|
213
|
-
//
|
|
214
|
-
this.printAlignedSuiteResults();
|
|
292
|
+
// Tasks are printed immediately in onTaskResult, so just print suite summary
|
|
215
293
|
// Skip displaying summary for the implicit "default" suite
|
|
216
294
|
if (result.name === 'default') {
|
|
217
295
|
return;
|
|
218
296
|
}
|
|
219
|
-
const passed = result.tasks.filter((t) => !t.error).length;
|
|
297
|
+
const passed = result.tasks.filter((t) => !t.error && !t.aborted).length;
|
|
220
298
|
const failed = result.tasks.filter((t) => t.error).length;
|
|
299
|
+
const aborted = result.tasks.filter((t) => t.aborted).length;
|
|
300
|
+
const durationStr = BaseReporter.formatDuration(result.duration * 1000000); // ms to ns
|
|
301
|
+
// Build summary parts
|
|
302
|
+
const parts = [];
|
|
221
303
|
if (failed > 0) {
|
|
222
|
-
|
|
304
|
+
parts.push(this.colorize('red', `${ansiChars.cross} ${failed} failed`));
|
|
305
|
+
}
|
|
306
|
+
if (passed > 0) {
|
|
307
|
+
parts.push(this.colorize('green', `${passed} passed`));
|
|
308
|
+
}
|
|
309
|
+
if (aborted > 0) {
|
|
310
|
+
parts.push(this.colorize('brightYellow', `${aborted} aborted`));
|
|
311
|
+
}
|
|
312
|
+
const summary = parts.join(', ');
|
|
313
|
+
const timeInfo = `${this.colorize('gray', 'in')} ${this.colorize('cyan', durationStr)}`;
|
|
314
|
+
if (failed > 0 || aborted > 0) {
|
|
315
|
+
this.printLine(` ${summary} ${timeInfo}`);
|
|
223
316
|
}
|
|
224
317
|
else {
|
|
225
|
-
this.printLine(` ${this.colorize('magenta', ansiChars.checkmark)} ${this.colorize('bold', this.colorize('brightWhite', `${passed}`))} ${this.colorize('brightWhite', `${
|
|
318
|
+
this.printLine(` ${this.colorize('magenta', ansiChars.checkmark)} ${this.colorize('bold', this.colorize('brightWhite', `${passed}`))} ${this.colorize('brightWhite', `${HumanReporter.pluralize('task', passed)} passed`)} ${timeInfo}`);
|
|
226
319
|
}
|
|
227
320
|
this.printLine();
|
|
228
321
|
}
|
|
322
|
+
onSuiteInit(suite, taskNames) {
|
|
323
|
+
// Pre-calculate max name length for optimal alignment
|
|
324
|
+
const terminalWidth = process.stdout.columns || 80;
|
|
325
|
+
const STATS_RESERVED_WIDTH = 70;
|
|
326
|
+
const MAX_NAME_WIDTH = Math.max(40, Math.min(60, terminalWidth - 4 - 2 - 2 - STATS_RESERVED_WIDTH));
|
|
327
|
+
// Calculate the actual max name length from non-wrapped names
|
|
328
|
+
let maxLen = 0;
|
|
329
|
+
for (const name of taskNames) {
|
|
330
|
+
const nameLen = this.getVisibleLength(name.trim());
|
|
331
|
+
// Only count names that won't wrap
|
|
332
|
+
if (nameLen <= MAX_NAME_WIDTH) {
|
|
333
|
+
maxLen = Math.max(maxLen, nameLen);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
// Use the max of actual names or MAX_NAME_WIDTH for consistency
|
|
337
|
+
this.currentSuiteMaxNameLen = Math.max(maxLen, MAX_NAME_WIDTH);
|
|
338
|
+
}
|
|
229
339
|
onSuiteStart(suite) {
|
|
230
340
|
this.currentSuite = suite;
|
|
231
341
|
if (this.quiet) {
|
|
@@ -244,8 +354,14 @@ export class HumanReporter extends BaseReporter {
|
|
|
244
354
|
if (this.quiet) {
|
|
245
355
|
return;
|
|
246
356
|
}
|
|
247
|
-
//
|
|
357
|
+
// Always buffer the result for suite summary (including aborted tasks)
|
|
248
358
|
this.suiteResults.push(result);
|
|
359
|
+
// Skip printing aborted tasks (they're counted in summary but not shown individually)
|
|
360
|
+
if (result.aborted) {
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
// Print immediately with current alignment
|
|
364
|
+
this.printTaskResult(result);
|
|
249
365
|
}
|
|
250
366
|
onTaskStart(task) {
|
|
251
367
|
if (this.quiet) {
|
|
@@ -285,31 +401,6 @@ export class HumanReporter extends BaseReporter {
|
|
|
285
401
|
}
|
|
286
402
|
return `${colors[color]}${text}${colors.reset}`;
|
|
287
403
|
}
|
|
288
|
-
/**
|
|
289
|
-
* Format bytes in human-readable format
|
|
290
|
-
*/
|
|
291
|
-
formatBytes(bytes) {
|
|
292
|
-
const units = ['B', 'KB', 'MB', 'GB', 'TB'];
|
|
293
|
-
let size = bytes;
|
|
294
|
-
let unitIndex = 0;
|
|
295
|
-
while (size >= 1024 && unitIndex < units.length - 1) {
|
|
296
|
-
size /= 1024;
|
|
297
|
-
unitIndex++;
|
|
298
|
-
}
|
|
299
|
-
return `${size.toFixed(1)} ${units[unitIndex]}`;
|
|
300
|
-
}
|
|
301
|
-
/**
|
|
302
|
-
* Format file path - show relative path if within CWD, otherwise absolute
|
|
303
|
-
*/
|
|
304
|
-
formatPath(filePath) {
|
|
305
|
-
const cwd = process.cwd();
|
|
306
|
-
const absolutePath = path.resolve(filePath);
|
|
307
|
-
// Check if the file is within the current working directory
|
|
308
|
-
if (absolutePath.startsWith(cwd + path.sep) || absolutePath === cwd) {
|
|
309
|
-
return path.relative(cwd, absolutePath);
|
|
310
|
-
}
|
|
311
|
-
return absolutePath;
|
|
312
|
-
}
|
|
313
404
|
/**
|
|
314
405
|
* Format duration in human-readable format for progress display
|
|
315
406
|
*/
|
|
@@ -336,12 +427,6 @@ export class HumanReporter extends BaseReporter {
|
|
|
336
427
|
// eslint-disable-next-line no-control-regex
|
|
337
428
|
return str.replace(/\x1b\[[0-9;]*m/g, '').length;
|
|
338
429
|
}
|
|
339
|
-
/**
|
|
340
|
-
* Simple pluralization helper
|
|
341
|
-
*/
|
|
342
|
-
pluralize(str, count) {
|
|
343
|
-
return count === 1 ? str : `${str}s`;
|
|
344
|
-
}
|
|
345
430
|
/**
|
|
346
431
|
* Print all task results in a suite with aligned columns
|
|
347
432
|
*/
|
|
@@ -349,10 +434,17 @@ export class HumanReporter extends BaseReporter {
|
|
|
349
434
|
if (this.suiteResults.length === 0) {
|
|
350
435
|
return;
|
|
351
436
|
}
|
|
352
|
-
const MAX_NAME_WIDTH = 60;
|
|
353
437
|
const BASE_INDENT = ' '; // 4 spaces
|
|
354
438
|
const bullet = this.colorize('dim', this.colorize('gray', ansiChars.bullet));
|
|
355
|
-
|
|
439
|
+
// Calculate maximum name width based on terminal width
|
|
440
|
+
// Reserve space for: indent (4) + status (1) + space (1) + name + ": " (2) + stats (~60 chars)
|
|
441
|
+
const terminalWidth = process.stdout.columns || 80;
|
|
442
|
+
const STATS_RESERVED_WIDTH = 70; // Approx space for duration + rme + ops/sec with padding
|
|
443
|
+
const MAX_NAME_WIDTH = Math.max(40, Math.min(60, terminalWidth - BASE_INDENT.length - 2 - 2 - STATS_RESERVED_WIDTH));
|
|
444
|
+
// Filter out aborted tasks (they're counted in suite summary but not printed)
|
|
445
|
+
const formatted = this.suiteResults
|
|
446
|
+
.filter((result) => !result.aborted)
|
|
447
|
+
.map((result) => {
|
|
356
448
|
const status = result.error
|
|
357
449
|
? this.colorize('red', ansiChars.cross)
|
|
358
450
|
: this.colorize('brightCyan', ansiChars.checkmark);
|
|
@@ -374,9 +466,9 @@ export class HumanReporter extends BaseReporter {
|
|
|
374
466
|
status,
|
|
375
467
|
};
|
|
376
468
|
}
|
|
377
|
-
const duration =
|
|
378
|
-
const opsPerSec =
|
|
379
|
-
const rme =
|
|
469
|
+
const duration = BaseReporter.formatDuration(result.mean); // already in nanoseconds
|
|
470
|
+
const opsPerSec = BaseReporter.formatOpsPerSecond(result.opsPerSecond);
|
|
471
|
+
const rme = BaseReporter.formatPercentage(result.marginOfError); // already a percentage
|
|
380
472
|
return {
|
|
381
473
|
durationLen: this.getVisibleLength(duration),
|
|
382
474
|
durationStr: duration,
|
|
@@ -399,9 +491,6 @@ export class HumanReporter extends BaseReporter {
|
|
|
399
491
|
const maxDurationLen = Math.max(...formatted.filter((t) => !t.error).map((t) => t.durationLen), 0);
|
|
400
492
|
const maxRmeLen = Math.max(...formatted.filter((t) => !t.error).map((t) => t.rmeLen), 0);
|
|
401
493
|
const maxOpsLen = Math.max(...formatted.filter((t) => !t.error).map((t) => t.opsPerSecLen), 0);
|
|
402
|
-
// Calculate the position where numbers start for unwrapped lines
|
|
403
|
-
// BASE_INDENT (4) + status (1 char) + space (1) + maxNameLen + ": " (2) = 8 + maxNameLen
|
|
404
|
-
const numbersStartPos = BASE_INDENT.length + 2 + maxNameLen + 2;
|
|
405
494
|
// Print each task with aligned columns
|
|
406
495
|
for (const task of formatted) {
|
|
407
496
|
if (task.error) {
|
|
@@ -415,15 +504,33 @@ export class HumanReporter extends BaseReporter {
|
|
|
415
504
|
this.printLine(`${BASE_INDENT}${task.status} ${this.colorize('white', task.name)} ${this.colorize('red', 'FAILED')}`);
|
|
416
505
|
}
|
|
417
506
|
else if (task.nameLength > MAX_NAME_WIDTH) {
|
|
418
|
-
// Long name - wrap to
|
|
419
|
-
this.
|
|
420
|
-
|
|
421
|
-
//
|
|
422
|
-
const leadingPad = ' '.repeat(numbersStartPos);
|
|
507
|
+
// Long name - wrap to multiple lines, align last line with short names
|
|
508
|
+
const wrappedLines = this.wrapText(task.name, MAX_NAME_WIDTH);
|
|
509
|
+
const continueIndent = BASE_INDENT + ' '; // 6 spaces for continuation lines
|
|
510
|
+
// Format stats string
|
|
423
511
|
const durationPad = ' '.repeat(maxDurationLen - task.durationLen);
|
|
424
512
|
const rmePad = ' '.repeat(maxRmeLen - task.rmeLen);
|
|
425
513
|
const opsPad = ' '.repeat(maxOpsLen - task.opsPerSecLen);
|
|
426
|
-
|
|
514
|
+
const statsStr = `${durationPad}${this.colorize('cyan', task.durationStr)} ${bullet} ${ansiChars.plusMinus}${rmePad}${this.colorize('brightBlue', task.rmeStr)} ${bullet} ${opsPad}${this.colorize('magenta', task.opsPerSecStr)}`;
|
|
515
|
+
// Print first line with status
|
|
516
|
+
this.printLine(`${BASE_INDENT}${task.status} ${this.colorize('white', wrappedLines[0])}`);
|
|
517
|
+
// Print middle continuation lines (all but first and last)
|
|
518
|
+
for (let i = 1; i < wrappedLines.length - 1; i++) {
|
|
519
|
+
this.printLine(`${continueIndent}${this.colorize('white', wrappedLines[i])}`);
|
|
520
|
+
}
|
|
521
|
+
// Print last line with colon and stats aligned with short names
|
|
522
|
+
if (wrappedLines.length > 1) {
|
|
523
|
+
const lastLine = wrappedLines[wrappedLines.length - 1];
|
|
524
|
+
const lastLineLen = this.getVisibleLength(lastLine);
|
|
525
|
+
// Pad the last line to align the ':' with short names
|
|
526
|
+
const lastLinePad = ' '.repeat(Math.max(0, maxNameLen - lastLineLen));
|
|
527
|
+
this.printLine(`${continueIndent}${this.colorize('white', lastLine)}${lastLinePad}: ${statsStr}`);
|
|
528
|
+
}
|
|
529
|
+
else {
|
|
530
|
+
// Single wrapped line
|
|
531
|
+
const lastLinePad = ' '.repeat(maxNameLen - task.nameLength);
|
|
532
|
+
this.printLine(`${BASE_INDENT}${task.status} ${this.colorize('white', task.name)}${lastLinePad}: ${statsStr}`);
|
|
533
|
+
}
|
|
427
534
|
if (this.verbose && task.iterations > 0) {
|
|
428
535
|
this.printLine(` ${this.colorize('dim', `${task.iterations} iterations`)}`);
|
|
429
536
|
}
|
|
@@ -451,6 +558,84 @@ export class HumanReporter extends BaseReporter {
|
|
|
451
558
|
console.log(message);
|
|
452
559
|
this.renderProgressWindow();
|
|
453
560
|
}
|
|
561
|
+
/**
|
|
562
|
+
* Print a single task result immediately with current alignment
|
|
563
|
+
*/
|
|
564
|
+
printTaskResult(result) {
|
|
565
|
+
// Clear progress bar temporarily
|
|
566
|
+
this.clearProgress();
|
|
567
|
+
const BASE_INDENT = ' '; // 4 spaces
|
|
568
|
+
const bullet = this.colorize('dim', this.colorize('gray', ansiChars.bullet));
|
|
569
|
+
// Calculate terminal width constraints
|
|
570
|
+
const terminalWidth = process.stdout.columns || 80;
|
|
571
|
+
const STATS_RESERVED_WIDTH = 70;
|
|
572
|
+
const MAX_NAME_WIDTH = Math.max(40, Math.min(60, terminalWidth - BASE_INDENT.length - 2 - 2 - STATS_RESERVED_WIDTH));
|
|
573
|
+
// Status marker
|
|
574
|
+
const status = result.error
|
|
575
|
+
? this.colorize('red', ansiChars.cross)
|
|
576
|
+
: this.colorize('brightCyan', ansiChars.checkmark);
|
|
577
|
+
const name = result.name.trim();
|
|
578
|
+
const nameLength = this.getVisibleLength(name);
|
|
579
|
+
// Handle errors
|
|
580
|
+
if (result.error) {
|
|
581
|
+
this.failures.push({
|
|
582
|
+
error: result.error?.message || String(result.error),
|
|
583
|
+
file: this.currentFile,
|
|
584
|
+
suite: this.currentSuite,
|
|
585
|
+
task: name,
|
|
586
|
+
});
|
|
587
|
+
this.printLine(`${BASE_INDENT}${status} ${this.colorize('white', name)} ${this.colorize('red', 'FAILED')}`);
|
|
588
|
+
return;
|
|
589
|
+
}
|
|
590
|
+
// Format stats
|
|
591
|
+
const duration = BaseReporter.formatDuration(result.mean);
|
|
592
|
+
const opsPerSec = BaseReporter.formatOpsPerSecond(result.opsPerSecond);
|
|
593
|
+
const rme = BaseReporter.formatPercentage(result.marginOfError);
|
|
594
|
+
// Use fixed widths for stats columns (reasonable maximums)
|
|
595
|
+
const DURATION_WIDTH = 10; // "999.99ms" max
|
|
596
|
+
const RME_WIDTH = 8; // "±999.99%" max
|
|
597
|
+
const OPS_WIDTH = 15; // "999.99K ops/sec" max
|
|
598
|
+
const durationLen = this.getVisibleLength(duration);
|
|
599
|
+
const rmeLen = this.getVisibleLength(rme);
|
|
600
|
+
const opsLen = this.getVisibleLength(opsPerSec);
|
|
601
|
+
// Stats formatting with fixed widths
|
|
602
|
+
const durationPad = ' '.repeat(DURATION_WIDTH - durationLen);
|
|
603
|
+
const rmePad = ' '.repeat(RME_WIDTH - rmeLen);
|
|
604
|
+
const opsPad = ' '.repeat(OPS_WIDTH - opsLen);
|
|
605
|
+
const statsStr = `${durationPad}${this.colorize('cyan', duration)} ${bullet} ${ansiChars.plusMinus}${rmePad}${this.colorize('brightBlue', rme)} ${bullet} ${opsPad}${this.colorize('magenta', opsPerSec)}`;
|
|
606
|
+
// Handle long names (wrap)
|
|
607
|
+
if (nameLength > MAX_NAME_WIDTH) {
|
|
608
|
+
const wrappedLines = this.wrapText(name, MAX_NAME_WIDTH);
|
|
609
|
+
const continueIndent = BASE_INDENT + ' '; // 6 spaces for continuation lines
|
|
610
|
+
// Print first line with status
|
|
611
|
+
this.printLine(`${BASE_INDENT}${status} ${this.colorize('white', wrappedLines[0])}`);
|
|
612
|
+
// Print middle lines (all but first and last)
|
|
613
|
+
for (let i = 1; i < wrappedLines.length - 1; i++) {
|
|
614
|
+
this.printLine(`${continueIndent}${this.colorize('white', wrappedLines[i])}`);
|
|
615
|
+
}
|
|
616
|
+
// Print last line with colon and stats aligned
|
|
617
|
+
// Use pre-calculated currentSuiteMaxNameLen for perfect alignment
|
|
618
|
+
if (wrappedLines.length > 1) {
|
|
619
|
+
const lastLine = wrappedLines[wrappedLines.length - 1];
|
|
620
|
+
const lastLineLen = this.getVisibleLength(lastLine);
|
|
621
|
+
const lastLinePad = ' '.repeat(Math.max(0, this.currentSuiteMaxNameLen - lastLineLen));
|
|
622
|
+
this.printLine(`${continueIndent}${this.colorize('white', lastLine)}${lastLinePad}: ${statsStr}`);
|
|
623
|
+
}
|
|
624
|
+
else {
|
|
625
|
+
// Single wrapped line (shouldn't happen if nameLength > MAX but handle it)
|
|
626
|
+
const lastLinePad = ' '.repeat(Math.max(0, this.currentSuiteMaxNameLen - nameLength));
|
|
627
|
+
this.printLine(`${BASE_INDENT}${status} ${this.colorize('white', name)}${lastLinePad}: ${statsStr}`);
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
else {
|
|
631
|
+
// Normal length - print on same line with pre-calculated alignment
|
|
632
|
+
const namePad = ' '.repeat(Math.max(0, this.currentSuiteMaxNameLen - nameLength));
|
|
633
|
+
this.printLine(`${BASE_INDENT}${status} ${this.colorize('white', name)}${namePad}: ${statsStr}`);
|
|
634
|
+
}
|
|
635
|
+
if (this.verbose && result.iterations > 0) {
|
|
636
|
+
this.printLine(` ${this.colorize('dim', `${result.iterations} iterations`)}`);
|
|
637
|
+
}
|
|
638
|
+
}
|
|
454
639
|
/**
|
|
455
640
|
* Render the progress window at the bottom
|
|
456
641
|
*/
|
|
@@ -467,5 +652,43 @@ export class HumanReporter extends BaseReporter {
|
|
|
467
652
|
console.log(this.lastProgressLine);
|
|
468
653
|
this.progressWindowActive = true;
|
|
469
654
|
}
|
|
655
|
+
/**
|
|
656
|
+
* Wrap text to a maximum width, breaking at word boundaries when possible
|
|
657
|
+
*/
|
|
658
|
+
wrapText(text, maxWidth) {
|
|
659
|
+
if (this.getVisibleLength(text) <= maxWidth) {
|
|
660
|
+
return [text];
|
|
661
|
+
}
|
|
662
|
+
const lines = [];
|
|
663
|
+
let currentLine = '';
|
|
664
|
+
const words = text.split(/(\s+)/); // Keep whitespace in split
|
|
665
|
+
for (const word of words) {
|
|
666
|
+
const testLine = currentLine + word;
|
|
667
|
+
if (this.getVisibleLength(testLine) <= maxWidth) {
|
|
668
|
+
currentLine = testLine;
|
|
669
|
+
}
|
|
670
|
+
else {
|
|
671
|
+
// If current line has content, save it
|
|
672
|
+
if (currentLine.trim()) {
|
|
673
|
+
lines.push(currentLine.trimEnd());
|
|
674
|
+
currentLine = word.trim() + ' ';
|
|
675
|
+
}
|
|
676
|
+
else {
|
|
677
|
+
// Single word is too long, force break it
|
|
678
|
+
if (this.getVisibleLength(word) > maxWidth) {
|
|
679
|
+
lines.push(word.substring(0, maxWidth));
|
|
680
|
+
currentLine = word.substring(maxWidth);
|
|
681
|
+
}
|
|
682
|
+
else {
|
|
683
|
+
currentLine = word;
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
if (currentLine.trim()) {
|
|
689
|
+
lines.push(currentLine.trimEnd());
|
|
690
|
+
}
|
|
691
|
+
return lines;
|
|
692
|
+
}
|
|
470
693
|
}
|
|
471
694
|
//# sourceMappingURL=human.js.map
|