headlamp 0.1.14 → 0.1.16
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/README.md +28 -0
- package/dist/cli.cjs +100 -26
- package/dist/cli.cjs.map +2 -2
- package/dist/index.js +100 -26
- package/dist/index.js.map +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -69,6 +69,22 @@ npx headlamp --onlyFailures
|
|
|
69
69
|
npx headlamp --changed --onlyFailures
|
|
70
70
|
```
|
|
71
71
|
|
|
72
|
+
- `--showConsole[=true|false]`:
|
|
73
|
+
- When enabled, Headlamp prints a dedicated "Logs" section under each failing test and failing file with the full console output captured by the runner.
|
|
74
|
+
- By default (without this flag), Headlamp shows a condensed "Console errors" snippet with only the most relevant error messages. `--showConsole` includes all console entries (log/info/warn/error).
|
|
75
|
+
- Supported forms: `--showConsole`, `--showConsole=true`, `--showConsole=false`.
|
|
76
|
+
- Works alongside `--onlyFailures`, coverage flags, and selection flags.
|
|
77
|
+
|
|
78
|
+
Examples:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
# Always include the full console output for each failure
|
|
82
|
+
npx headlamp --showConsole
|
|
83
|
+
|
|
84
|
+
# Combine with only failures visible during the run
|
|
85
|
+
npx headlamp --onlyFailures --showConsole
|
|
86
|
+
```
|
|
87
|
+
|
|
72
88
|
## Changed-file selection
|
|
73
89
|
|
|
74
90
|
- `--changed[=mode]` selects tests by files changed in your working tree or branch.
|
|
@@ -83,6 +99,8 @@ npx headlamp --changed --onlyFailures
|
|
|
83
99
|
- Effects:
|
|
84
100
|
- Uses changed production files as seeds to discover related tests by import-graph.
|
|
85
101
|
- Coverage tables prioritize and annotate files related to selection/changed files.
|
|
102
|
+
- Additional flags:
|
|
103
|
+
- `--changed.depth=<n>`: cap the transitive import scan depth when refining related tests from changed production files. Default: 5. Increase to include more indirectly-related tests (slower), decrease for speed.
|
|
86
104
|
|
|
87
105
|
Examples:
|
|
88
106
|
|
|
@@ -100,6 +118,16 @@ npx headlamp --changed=branch
|
|
|
100
118
|
npx headlamp --coverage --changed=branch
|
|
101
119
|
```
|
|
102
120
|
|
|
121
|
+
Depth examples:
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
# Scan imports up to 10 levels deep when resolving related tests for changed files
|
|
125
|
+
npx headlamp --changed=all --changed.depth=10
|
|
126
|
+
|
|
127
|
+
# With branch mode
|
|
128
|
+
npx headlamp --changed=branch --changed.depth=12
|
|
129
|
+
```
|
|
130
|
+
|
|
103
131
|
## Coverage flags
|
|
104
132
|
|
|
105
133
|
- `--coverage`: enables coverage collection and prints merged coverage output after test execution. Uses your project's Jest/Vitest setup and reads coverage JSON from Jest.
|
package/dist/cli.cjs
CHANGED
|
@@ -249,6 +249,7 @@ var init_args = __esm({
|
|
|
249
249
|
coverageUi: (value) => ({ type: "coverageUi", value }),
|
|
250
250
|
coverageAbortOnFailure: (value) => ({ type: "coverageAbortOnFailure", value }),
|
|
251
251
|
onlyFailures: (value) => ({ type: "onlyFailures", value }),
|
|
252
|
+
showConsole: (value) => ({ type: "showConsole", value }),
|
|
252
253
|
jestArg: (value) => ({ type: "jestArg", value }),
|
|
253
254
|
jestArgs: (values) => ({ type: "jestArgs", values }),
|
|
254
255
|
vitestArg: (value) => ({ type: "vitestArg", value }),
|
|
@@ -265,7 +266,8 @@ var init_args = __esm({
|
|
|
265
266
|
coverageMaxFiles: (value) => ({ type: "coverageMaxFiles", value }),
|
|
266
267
|
coverageMaxHotspots: (value) => ({ type: "coverageMaxHotspots", value }),
|
|
267
268
|
coveragePageFit: (value) => ({ type: "coveragePageFit", value }),
|
|
268
|
-
changed: (value) => ({ type: "changed", value })
|
|
269
|
+
changed: (value) => ({ type: "changed", value }),
|
|
270
|
+
changedDepth: (value) => ({ type: "changedDepth", value })
|
|
269
271
|
};
|
|
270
272
|
Some = (value) => ({ _tag: "some", value });
|
|
271
273
|
None = { _tag: "none" };
|
|
@@ -414,6 +416,18 @@ var init_args = __esm({
|
|
|
414
416
|
"--onlyFailures",
|
|
415
417
|
(_flag, lookahead) => step([ActionBuilders.onlyFailures(isTruthy(String(lookahead)))], true)
|
|
416
418
|
),
|
|
419
|
+
// --showConsole flag (boolean)
|
|
420
|
+
rule.eq("--showConsole", () => step([ActionBuilders.showConsole(true)])),
|
|
421
|
+
rule.startsWith(
|
|
422
|
+
"--showConsole=",
|
|
423
|
+
(value) => step([
|
|
424
|
+
ActionBuilders.showConsole(isTruthy((value.split("=")[1] ?? "").trim().toLowerCase()))
|
|
425
|
+
])
|
|
426
|
+
),
|
|
427
|
+
rule.withLookahead(
|
|
428
|
+
"--showConsole",
|
|
429
|
+
(_flag, lookahead) => step([ActionBuilders.showConsole(isTruthy(String(lookahead)))], true)
|
|
430
|
+
),
|
|
417
431
|
rule.withLookahead(
|
|
418
432
|
"--testPathPattern",
|
|
419
433
|
(flag, lookahead) => step([ActionBuilders.jestArgs([flag, lookahead])], true)
|
|
@@ -486,6 +500,16 @@ var init_args = __esm({
|
|
|
486
500
|
const mode = raw === "staged" ? "staged" : raw === "unstaged" ? "unstaged" : raw === "branch" ? "branch" : "all";
|
|
487
501
|
return step([ActionBuilders.changed(mode)], true);
|
|
488
502
|
}),
|
|
503
|
+
// --changed.depth flag: maximum transitive import depth for changed selection refinement
|
|
504
|
+
rule.startsWith("--changed.depth=", (value) => {
|
|
505
|
+
const raw = (value.split("=")[1] ?? "").trim();
|
|
506
|
+
const num = Number(raw);
|
|
507
|
+
return step(Number.isFinite(num) && num > 0 ? [ActionBuilders.changedDepth(num)] : []);
|
|
508
|
+
}),
|
|
509
|
+
rule.withLookahead("--changed.depth", (_flag, lookahead) => {
|
|
510
|
+
const num = Number(String(lookahead).trim());
|
|
511
|
+
return step(Number.isFinite(num) && num > 0 ? [ActionBuilders.changedDepth(num)] : [], true);
|
|
512
|
+
}),
|
|
489
513
|
rule.withLookahead(
|
|
490
514
|
"-t",
|
|
491
515
|
(flag, lookahead) => step(
|
|
@@ -562,6 +586,8 @@ var init_args = __esm({
|
|
|
562
586
|
return { vitest: [], jest: [], coverage: false, coverageAbortOnFailure: action.value };
|
|
563
587
|
case "onlyFailures":
|
|
564
588
|
return { vitest: [], jest: [], coverage: false, onlyFailures: action.value };
|
|
589
|
+
case "showConsole":
|
|
590
|
+
return { vitest: [], jest: [], coverage: false, showConsole: action.value };
|
|
565
591
|
case "jestArgs":
|
|
566
592
|
return { vitest: [], jest: action.values, coverage: false };
|
|
567
593
|
case "selectionHint":
|
|
@@ -592,6 +618,8 @@ var init_args = __esm({
|
|
|
592
618
|
return { vitest: [], jest: [], coverage: false, coveragePageFit: action.value };
|
|
593
619
|
case "changed":
|
|
594
620
|
return { vitest: [], jest: [], coverage: false, changed: action.value };
|
|
621
|
+
case "changedDepth":
|
|
622
|
+
return { vitest: [], jest: [], coverage: false, changedDepth: action.value };
|
|
595
623
|
case "jestArg":
|
|
596
624
|
return { vitest: [], jest: [action.value], coverage: false };
|
|
597
625
|
case "vitestArg":
|
|
@@ -632,8 +660,10 @@ var init_args = __esm({
|
|
|
632
660
|
return {
|
|
633
661
|
...next,
|
|
634
662
|
...right.changed !== void 0 || left.changed !== void 0 ? { changed: right.changed ?? left.changed } : {},
|
|
663
|
+
...right.changedDepth !== void 0 || left.changedDepth !== void 0 ? { changedDepth: right.changedDepth ?? left.changedDepth } : {},
|
|
635
664
|
...right.coverageAbortOnFailure !== void 0 || left.coverageAbortOnFailure !== void 0 ? { coverageAbortOnFailure: right.coverageAbortOnFailure ?? left.coverageAbortOnFailure } : {},
|
|
636
665
|
...right.onlyFailures !== void 0 || left.onlyFailures !== void 0 ? { onlyFailures: right.onlyFailures ?? left.onlyFailures } : {},
|
|
666
|
+
...right.showConsole !== void 0 || left.showConsole !== void 0 ? { showConsole: right.showConsole ?? left.showConsole } : {},
|
|
637
667
|
...right.coverageDetail !== void 0 || left.coverageDetail !== void 0 ? { coverageDetail: right.coverageDetail ?? left.coverageDetail } : {},
|
|
638
668
|
...right.coverageShowCode !== void 0 || left.coverageShowCode !== void 0 ? { coverageShowCode: right.coverageShowCode ?? left.coverageShowCode } : {},
|
|
639
669
|
...right.coverageMode !== void 0 || left.coverageMode !== void 0 ? { coverageMode: right.coverageMode ?? left.coverageMode } : {},
|
|
@@ -658,6 +688,7 @@ var init_args = __esm({
|
|
|
658
688
|
let coverageUi = "both";
|
|
659
689
|
let coverageAbortOnFailure = false;
|
|
660
690
|
let onlyFailures = false;
|
|
691
|
+
let showConsole = false;
|
|
661
692
|
let coverageShowCode = Boolean(process.stdout.isTTY);
|
|
662
693
|
let coverageMode = "auto";
|
|
663
694
|
const coverageMaxFilesLocalInit = void 0;
|
|
@@ -676,6 +707,7 @@ var init_args = __esm({
|
|
|
676
707
|
coverageUi = contrib.coverageUi ?? coverageUi;
|
|
677
708
|
coverageAbortOnFailure = contrib.coverageAbortOnFailure ?? coverageAbortOnFailure;
|
|
678
709
|
onlyFailures = contrib.onlyFailures ?? onlyFailures;
|
|
710
|
+
showConsole = contrib.showConsole ?? showConsole;
|
|
679
711
|
coverageShowCode = contrib.coverageShowCode ?? coverageShowCode;
|
|
680
712
|
const coverageDetailComputed = contrib.coverageDetail ?? (contrib.selection ? "auto" : void 0);
|
|
681
713
|
coverageMode = contrib.coverageMode ?? (contrib.selection ? "compact" : "auto");
|
|
@@ -710,6 +742,7 @@ var init_args = __esm({
|
|
|
710
742
|
coverageUi,
|
|
711
743
|
coverageAbortOnFailure,
|
|
712
744
|
onlyFailures,
|
|
745
|
+
showConsole,
|
|
713
746
|
selectionSpecified: Boolean(contrib.selection),
|
|
714
747
|
selectionPaths: [...contrib.selectionPaths ?? []],
|
|
715
748
|
includeGlobs,
|
|
@@ -722,7 +755,8 @@ var init_args = __esm({
|
|
|
722
755
|
coveragePageFit,
|
|
723
756
|
...contrib.editorCmd !== void 0 ? { editorCmd: contrib.editorCmd } : {},
|
|
724
757
|
...contrib.workspaceRoot !== void 0 ? { workspaceRoot: contrib.workspaceRoot } : {},
|
|
725
|
-
...contrib.changed !== void 0 ? { changed: contrib.changed } : {}
|
|
758
|
+
...contrib.changed !== void 0 ? { changed: contrib.changed } : {},
|
|
759
|
+
...contrib.changedDepth !== void 0 ? { changedDepth: contrib.changedDepth } : {}
|
|
726
760
|
};
|
|
727
761
|
return out;
|
|
728
762
|
};
|
|
@@ -5074,24 +5108,43 @@ var buildStackSection = (mergedForStack, ctx, fallbackLoc) => {
|
|
|
5074
5108
|
};
|
|
5075
5109
|
var MAX_CONSOLE_ERRORS_TO_SHOW = 3;
|
|
5076
5110
|
var isConsoleEntry = (candidate) => typeof candidate === "object" && candidate !== null;
|
|
5077
|
-
var buildConsoleSection = (maybeConsole) => {
|
|
5111
|
+
var buildConsoleSection = (maybeConsole, opts) => {
|
|
5078
5112
|
const out = [];
|
|
5079
5113
|
if (!Array.isArray(maybeConsole)) {
|
|
5080
5114
|
return out;
|
|
5081
5115
|
}
|
|
5082
5116
|
const entries = maybeConsole.filter(isConsoleEntry);
|
|
5083
|
-
|
|
5084
|
-
|
|
5085
|
-
|
|
5086
|
-
|
|
5087
|
-
|
|
5088
|
-
|
|
5089
|
-
|
|
5090
|
-
|
|
5091
|
-
|
|
5092
|
-
|
|
5117
|
+
if (opts?.full) {
|
|
5118
|
+
const toMsg = (entry) => {
|
|
5119
|
+
const type = String(entry?.type ?? "").toLowerCase();
|
|
5120
|
+
const raw = entry?.message;
|
|
5121
|
+
const msg = Array.isArray(raw) ? raw.map(String).join(" ") : typeof raw === "string" ? raw : String(raw ?? "");
|
|
5122
|
+
const origin = String(entry?.origin ?? "");
|
|
5123
|
+
const typeFmt = type ? `${ansi.white(type)}: ` : "";
|
|
5124
|
+
const originFmt = origin ? ` ${ansi.dim(`(${origin})`)}` : "";
|
|
5125
|
+
return ` ${ansi.dim("\u2022")} ${typeFmt}${msg}${originFmt}`;
|
|
5126
|
+
};
|
|
5127
|
+
const lines = entries.map(toMsg).filter((ln) => stripAnsiSimple(ln).trim().length > 0);
|
|
5128
|
+
if (lines.length) {
|
|
5129
|
+
out.push(ansi.dim(" Logs:"));
|
|
5130
|
+
out.push(...lines, "");
|
|
5131
|
+
}
|
|
5132
|
+
} else {
|
|
5133
|
+
const errorsOnly = entries.filter(
|
|
5134
|
+
(entry) => String(entry?.type ?? "").toLowerCase() === "error"
|
|
5135
|
+
);
|
|
5136
|
+
const scored = errorsOnly.map((entry) => {
|
|
5137
|
+
const raw = entry?.message;
|
|
5138
|
+
const msg = Array.isArray(raw) ? raw.map(String).join(" ") : typeof raw === "string" ? raw : String(raw ?? "");
|
|
5139
|
+
return { msg, score: msg.length };
|
|
5140
|
+
}).filter((item) => item.msg.trim().length > 0).sort((left, right) => right.score - left.score).slice(0, MAX_CONSOLE_ERRORS_TO_SHOW);
|
|
5141
|
+
if (scored.length) {
|
|
5142
|
+
out.push(ansi.dim(" Console errors:"));
|
|
5143
|
+
for (const item of scored) {
|
|
5144
|
+
out.push(` ${ansi.dim("\u2022")} ${item.msg}`);
|
|
5145
|
+
}
|
|
5146
|
+
out.push("");
|
|
5093
5147
|
}
|
|
5094
|
-
out.push("");
|
|
5095
5148
|
}
|
|
5096
5149
|
return out;
|
|
5097
5150
|
};
|
|
@@ -5153,8 +5206,8 @@ var buildThrownSection = (details) => {
|
|
|
5153
5206
|
}
|
|
5154
5207
|
};
|
|
5155
5208
|
const candidates = [];
|
|
5156
|
-
for (const
|
|
5157
|
-
const obj =
|
|
5209
|
+
for (const detailEntry of details) {
|
|
5210
|
+
const obj = detailEntry && typeof detailEntry === "object" ? detailEntry : null;
|
|
5158
5211
|
if (obj && obj.error && typeof obj.error === "object") {
|
|
5159
5212
|
const err = obj.error;
|
|
5160
5213
|
if (typeof err.name === "string") {
|
|
@@ -5351,7 +5404,7 @@ var renderChunks = (chunks, ctx, fns, opts) => {
|
|
|
5351
5404
|
|
|
5352
5405
|
// src/lib/formatter/context.ts
|
|
5353
5406
|
var fs5 = __toESM(require("node:fs"), 1);
|
|
5354
|
-
var makeCtx = (opts, showStacks = false) => {
|
|
5407
|
+
var makeCtx = (opts, showStacks = false, showConsole = false) => {
|
|
5355
5408
|
const cwd = (opts?.cwd ?? process.cwd()).replace(/\\/g, "/");
|
|
5356
5409
|
const width = Math.max(
|
|
5357
5410
|
40,
|
|
@@ -5367,7 +5420,15 @@ var makeCtx = (opts, showStacks = false) => {
|
|
|
5367
5420
|
return [];
|
|
5368
5421
|
}
|
|
5369
5422
|
};
|
|
5370
|
-
return {
|
|
5423
|
+
return {
|
|
5424
|
+
cwd,
|
|
5425
|
+
width,
|
|
5426
|
+
showStacks,
|
|
5427
|
+
showConsole,
|
|
5428
|
+
projectHint,
|
|
5429
|
+
editorCmd: opts?.editorCmd,
|
|
5430
|
+
readSource: readSource2
|
|
5431
|
+
};
|
|
5371
5432
|
};
|
|
5372
5433
|
|
|
5373
5434
|
// src/lib/formatter/bridge/tryBridgeFallback.ts
|
|
@@ -5689,7 +5750,9 @@ var renderFileLevelFailure = (file, ctx) => {
|
|
|
5689
5750
|
suppressDiff: pretty.length > 0,
|
|
5690
5751
|
stackPreview
|
|
5691
5752
|
});
|
|
5692
|
-
const consoleBlock = buildConsoleSection(stripBridgeEventsFromConsole(file.console ?? null)
|
|
5753
|
+
const consoleBlock = buildConsoleSection(stripBridgeEventsFromConsole(file.console ?? null), {
|
|
5754
|
+
full: Boolean(ctx.showConsole)
|
|
5755
|
+
});
|
|
5693
5756
|
const stackTail = ctx.showStacks && stackPreview.length === 0 ? (() => {
|
|
5694
5757
|
const tail = mergedForStack.filter((ln) => isStackLine(stripAnsiSimple(ln))).slice(-4).map((ln) => ` ${colorStackLine(String(ln), ctx.projectHint)}`);
|
|
5695
5758
|
return tail.length ? [ansi.dim(" Stack:"), ...tail, ""] : empty;
|
|
@@ -6004,7 +6067,9 @@ var renderFailedAssertion = (args) => {
|
|
|
6004
6067
|
return empty;
|
|
6005
6068
|
}
|
|
6006
6069
|
})() : empty;
|
|
6007
|
-
const consoleBlock = buildConsoleSection(stripBridgeEventsFromConsole(file.console ?? null)
|
|
6070
|
+
const consoleBlock = buildConsoleSection(stripBridgeEventsFromConsole(file.console ?? null), {
|
|
6071
|
+
full: Boolean(ctx.showConsole)
|
|
6072
|
+
});
|
|
6008
6073
|
const stackTail = ctx.showStacks && stackPreview.length === 0 ? (() => {
|
|
6009
6074
|
const merged = collapseStacks([...msgLines, ...details.stacks]);
|
|
6010
6075
|
const tail = collapseStacks(merged).filter((ln) => isStackLine(stripAnsiSimple(ln))).slice(-4).map((ln) => ` ${colorStackLine(String(ln), ctx.projectHint)}`);
|
|
@@ -6101,7 +6166,11 @@ var formatJestOutputVitest = (raw, opts) => pipe(
|
|
|
6101
6166
|
{ raw, opts },
|
|
6102
6167
|
(state) => ({
|
|
6103
6168
|
...state,
|
|
6104
|
-
ctx: makeCtx(
|
|
6169
|
+
ctx: makeCtx(
|
|
6170
|
+
state.opts,
|
|
6171
|
+
/\bFAIL\b/.test(stripAnsiSimple(state.raw)),
|
|
6172
|
+
Boolean(state.opts?.showConsole)
|
|
6173
|
+
)
|
|
6105
6174
|
}),
|
|
6106
6175
|
(state) => ({ ...state, chunks: parseChunks(state.raw) }),
|
|
6107
6176
|
(state) => ({
|
|
@@ -6456,6 +6525,7 @@ var program = async () => {
|
|
|
6456
6525
|
coverageUi,
|
|
6457
6526
|
coverageAbortOnFailure,
|
|
6458
6527
|
onlyFailures,
|
|
6528
|
+
showConsole,
|
|
6459
6529
|
selectionSpecified,
|
|
6460
6530
|
selectionPaths,
|
|
6461
6531
|
includeGlobs,
|
|
@@ -6468,7 +6538,8 @@ var program = async () => {
|
|
|
6468
6538
|
coverageMaxFiles: coverageMaxFilesArg,
|
|
6469
6539
|
coverageMaxHotspots: coverageMaxHotspotsArg,
|
|
6470
6540
|
coveragePageFit,
|
|
6471
|
-
changed
|
|
6541
|
+
changed,
|
|
6542
|
+
changedDepth
|
|
6472
6543
|
} = deriveArgs(argv);
|
|
6473
6544
|
const getChangedFiles = async (mode, cwd) => {
|
|
6474
6545
|
const collect = async (cmd, args) => {
|
|
@@ -6940,7 +7011,7 @@ var program = async () => {
|
|
|
6940
7011
|
resolutionCache.set(key, resolved);
|
|
6941
7012
|
return resolved;
|
|
6942
7013
|
};
|
|
6943
|
-
const MAX_DEPTH = 5;
|
|
7014
|
+
const MAX_DEPTH = Number.isFinite(Number(changedDepth)) && Number(changedDepth) > 0 ? Number(changedDepth) : 5;
|
|
6944
7015
|
const seen = /* @__PURE__ */ new Set();
|
|
6945
7016
|
const matchesTransitively = async (absTestPath, depth) => {
|
|
6946
7017
|
if (depth > MAX_DEPTH) {
|
|
@@ -7157,7 +7228,8 @@ var program = async () => {
|
|
|
7157
7228
|
reordered,
|
|
7158
7229
|
makeCtx(
|
|
7159
7230
|
{ cwd: repoRootForDiscovery, ...editorCmd !== void 0 ? { editorCmd } : {} },
|
|
7160
|
-
/\bFAIL\b/.test(stripAnsiSimple(output))
|
|
7231
|
+
/\bFAIL\b/.test(stripAnsiSimple(output)),
|
|
7232
|
+
Boolean(showConsole)
|
|
7161
7233
|
),
|
|
7162
7234
|
{ onlyFailures }
|
|
7163
7235
|
);
|
|
@@ -7166,7 +7238,8 @@ var program = async () => {
|
|
|
7166
7238
|
bridge,
|
|
7167
7239
|
makeCtx(
|
|
7168
7240
|
{ cwd: repoRootForDiscovery, ...editorCmd !== void 0 ? { editorCmd } : {} },
|
|
7169
|
-
/\bFAIL\b/.test(stripAnsiSimple(output))
|
|
7241
|
+
/\bFAIL\b/.test(stripAnsiSimple(output)),
|
|
7242
|
+
Boolean(showConsole)
|
|
7170
7243
|
),
|
|
7171
7244
|
{ onlyFailures }
|
|
7172
7245
|
);
|
|
@@ -7268,7 +7341,8 @@ ${stripFooter(rawAlso)}`.trimEnd();
|
|
|
7268
7341
|
unified,
|
|
7269
7342
|
makeCtx(
|
|
7270
7343
|
{ cwd: repoRootForDiscovery, ...editorCmd !== void 0 ? { editorCmd } : {} },
|
|
7271
|
-
showStacks
|
|
7344
|
+
showStacks,
|
|
7345
|
+
Boolean(showConsole)
|
|
7272
7346
|
),
|
|
7273
7347
|
{ onlyFailures }
|
|
7274
7348
|
);
|