headlamp 0.1.9 → 0.1.11
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 +32 -0
- package/dist/cli.cjs +1904 -714
- package/dist/cli.cjs.map +4 -4
- package/dist/index.js +1969 -809
- package/dist/index.js.map +4 -4
- package/package.json +3 -2
package/dist/index.js
CHANGED
|
@@ -76,7 +76,7 @@ var init_args = __esm({
|
|
|
76
76
|
isSome = (opt) => opt._tag === "some";
|
|
77
77
|
step = (actions, skipNext = false) => [actions, skipNext];
|
|
78
78
|
rule = {
|
|
79
|
-
when: (predicate, build) => (value,
|
|
79
|
+
when: (predicate, build) => (value, env) => predicate(value, env) ? Some(build(value, env)) : None,
|
|
80
80
|
eq: (flag, build) => rule.when(
|
|
81
81
|
(value) => value === flag,
|
|
82
82
|
() => build()
|
|
@@ -86,12 +86,12 @@ var init_args = __esm({
|
|
|
86
86
|
(value) => build(value)
|
|
87
87
|
),
|
|
88
88
|
inSet: (select, build) => rule.when(
|
|
89
|
-
(value,
|
|
89
|
+
(value, env) => select(env).has(value),
|
|
90
90
|
(value) => build(value)
|
|
91
91
|
),
|
|
92
92
|
withLookahead: (lookaheadFlag, build) => rule.when(
|
|
93
|
-
(value,
|
|
94
|
-
(value,
|
|
93
|
+
(value, env) => value === lookaheadFlag && typeof env.lookahead === "string" && env.lookahead.length > 0,
|
|
94
|
+
(value, env) => build(value, env.lookahead)
|
|
95
95
|
)
|
|
96
96
|
};
|
|
97
97
|
STRING_EMPTY = "";
|
|
@@ -224,7 +224,7 @@ var init_args = __esm({
|
|
|
224
224
|
),
|
|
225
225
|
rule.startsWith("--testPathPattern=", (value) => step([ActionBuilders.jestArg(value)])),
|
|
226
226
|
rule.inSet(
|
|
227
|
-
(
|
|
227
|
+
(env) => env.jestFlags,
|
|
228
228
|
(value) => step([ActionBuilders.jestArg(value)])
|
|
229
229
|
),
|
|
230
230
|
rule.when(
|
|
@@ -282,12 +282,12 @@ var init_args = __esm({
|
|
|
282
282
|
rule.eq("--changed", () => step([ActionBuilders.changed("all")])),
|
|
283
283
|
rule.startsWith("--changed=", (value) => {
|
|
284
284
|
const raw = (value.split("=")[1] ?? "").trim().toLowerCase();
|
|
285
|
-
const mode = raw === "staged" ? "staged" : raw === "unstaged" ? "unstaged" : "all";
|
|
285
|
+
const mode = raw === "staged" ? "staged" : raw === "unstaged" ? "unstaged" : raw === "branch" ? "branch" : "all";
|
|
286
286
|
return step([ActionBuilders.changed(mode)]);
|
|
287
287
|
}),
|
|
288
288
|
rule.withLookahead("--changed", (_flag, lookahead) => {
|
|
289
289
|
const raw = String(lookahead).trim().toLowerCase();
|
|
290
|
-
const mode = raw === "staged" ? "staged" : raw === "unstaged" ? "unstaged" : "all";
|
|
290
|
+
const mode = raw === "staged" ? "staged" : raw === "unstaged" ? "unstaged" : raw === "branch" ? "branch" : "all";
|
|
291
291
|
return step([ActionBuilders.changed(mode)], true);
|
|
292
292
|
}),
|
|
293
293
|
rule.withLookahead(
|
|
@@ -320,9 +320,9 @@ var init_args = __esm({
|
|
|
320
320
|
}
|
|
321
321
|
const tokenValue = token ?? STRING_EMPTY;
|
|
322
322
|
const nextToken = tokens[index + INDEX_STEP];
|
|
323
|
-
let
|
|
323
|
+
let env = { jestFlags: jestOnlyFlags };
|
|
324
324
|
if (typeof nextToken === "string" && nextToken.length > 0) {
|
|
325
|
-
|
|
325
|
+
env = { jestFlags: jestOnlyFlags, lookahead: nextToken };
|
|
326
326
|
}
|
|
327
327
|
const firstMatch = (rs, value, envForRules) => {
|
|
328
328
|
for (const ruleFn of rs) {
|
|
@@ -333,7 +333,7 @@ var init_args = __esm({
|
|
|
333
333
|
}
|
|
334
334
|
return None;
|
|
335
335
|
};
|
|
336
|
-
const matched = firstMatch(rules, tokenValue,
|
|
336
|
+
const matched = firstMatch(rules, tokenValue, env);
|
|
337
337
|
const isTestFileToken = (candidate) => /\.(test|spec)\.[tj]sx?$/.test(candidate) || /(^|\/)tests?\//.test(candidate);
|
|
338
338
|
const isPathLike = (candidate) => /[\\/]/.test(candidate) || /\.(m?[tj]sx?)$/i.test(candidate);
|
|
339
339
|
const [matchedActions, shouldSkipNext] = isSome(matched) ? matched.value : (() => {
|
|
@@ -593,7 +593,7 @@ var init_TimeoutError = __esm({
|
|
|
593
593
|
|
|
594
594
|
// node_modules/es-toolkit/dist/promise/delay.mjs
|
|
595
595
|
function delay(ms, { signal } = {}) {
|
|
596
|
-
return new Promise((
|
|
596
|
+
return new Promise((resolve10, reject) => {
|
|
597
597
|
const abortError = () => {
|
|
598
598
|
reject(new AbortError());
|
|
599
599
|
};
|
|
@@ -606,7 +606,7 @@ function delay(ms, { signal } = {}) {
|
|
|
606
606
|
}
|
|
607
607
|
const timeoutId = setTimeout(() => {
|
|
608
608
|
signal?.removeEventListener("abort", abortHandler);
|
|
609
|
-
|
|
609
|
+
resolve10();
|
|
610
610
|
}, ms);
|
|
611
611
|
signal?.addEventListener("abort", abortHandler, { once: true });
|
|
612
612
|
});
|
|
@@ -671,11 +671,11 @@ var init_exec = __esm({
|
|
|
671
671
|
child.stderr?.on("data", (chunk) => {
|
|
672
672
|
stderr += String(chunk);
|
|
673
673
|
});
|
|
674
|
-
const exec = new Promise((
|
|
674
|
+
const exec = new Promise((resolve10, reject) => {
|
|
675
675
|
child.on("error", reject);
|
|
676
676
|
child.on(
|
|
677
677
|
"close",
|
|
678
|
-
(code) => Number(code) === 0 ?
|
|
678
|
+
(code) => Number(code) === 0 ? resolve10(stdout) : reject(new Error(stderr || `exit ${code}`))
|
|
679
679
|
);
|
|
680
680
|
});
|
|
681
681
|
try {
|
|
@@ -695,7 +695,7 @@ var init_exec = __esm({
|
|
|
695
695
|
throw caughtError;
|
|
696
696
|
}
|
|
697
697
|
};
|
|
698
|
-
runExitCode = async (cmd, args, opts = {}) => new Promise((
|
|
698
|
+
runExitCode = async (cmd, args, opts = {}) => new Promise((resolve10, reject) => {
|
|
699
699
|
const child = spawn(cmd, [...args], {
|
|
700
700
|
cwd: opts.cwd,
|
|
701
701
|
env: opts.env,
|
|
@@ -704,9 +704,9 @@ var init_exec = __esm({
|
|
|
704
704
|
windowsHide: true
|
|
705
705
|
});
|
|
706
706
|
child.on("error", reject);
|
|
707
|
-
child.on("close", (code) =>
|
|
707
|
+
child.on("close", (code) => resolve10(Number(code)));
|
|
708
708
|
});
|
|
709
|
-
runWithCapture = async (cmd, args, opts) => new Promise((
|
|
709
|
+
runWithCapture = async (cmd, args, opts) => new Promise((resolve10, reject) => {
|
|
710
710
|
const child = spawn(cmd, [...args], {
|
|
711
711
|
cwd: opts.cwd,
|
|
712
712
|
env: opts.env,
|
|
@@ -722,7 +722,7 @@ var init_exec = __esm({
|
|
|
722
722
|
buf += String(chunk);
|
|
723
723
|
});
|
|
724
724
|
child.on("error", reject);
|
|
725
|
-
child.on("close", (code) =>
|
|
725
|
+
child.on("close", (code) => resolve10({ code: Number(code), output: buf }));
|
|
726
726
|
});
|
|
727
727
|
}
|
|
728
728
|
});
|
|
@@ -1133,8 +1133,8 @@ var require_utils = __commonJS({
|
|
|
1133
1133
|
}
|
|
1134
1134
|
return output;
|
|
1135
1135
|
};
|
|
1136
|
-
exports.basename = (
|
|
1137
|
-
const segs =
|
|
1136
|
+
exports.basename = (path12, { windows } = {}) => {
|
|
1137
|
+
const segs = path12.split(windows ? /[\\/]/ : "/");
|
|
1138
1138
|
const last = segs[segs.length - 1];
|
|
1139
1139
|
if (last === "") {
|
|
1140
1140
|
return segs[segs.length - 2];
|
|
@@ -2250,7 +2250,7 @@ var require_picomatch = __commonJS({
|
|
|
2250
2250
|
var parse = require_parse();
|
|
2251
2251
|
var utils = require_utils();
|
|
2252
2252
|
var constants = require_constants();
|
|
2253
|
-
var
|
|
2253
|
+
var isObject2 = (val) => val && typeof val === "object" && !Array.isArray(val);
|
|
2254
2254
|
var picomatch2 = (glob, options, returnState = false) => {
|
|
2255
2255
|
if (Array.isArray(glob)) {
|
|
2256
2256
|
const fns = glob.map((input) => picomatch2(input, options, returnState));
|
|
@@ -2263,7 +2263,7 @@ var require_picomatch = __commonJS({
|
|
|
2263
2263
|
};
|
|
2264
2264
|
return arrayMatcher;
|
|
2265
2265
|
}
|
|
2266
|
-
const isState =
|
|
2266
|
+
const isState = isObject2(glob) && glob.tokens && glob.input;
|
|
2267
2267
|
if (glob === "" || typeof glob !== "string" && !isState) {
|
|
2268
2268
|
throw new TypeError("Expected pattern to be a non-empty string");
|
|
2269
2269
|
}
|
|
@@ -3503,11 +3503,11 @@ var require_lib = __commonJS({
|
|
|
3503
3503
|
"node_modules/json5/lib/index.js"(exports, module) {
|
|
3504
3504
|
var parse = require_parse2();
|
|
3505
3505
|
var stringify = require_stringify();
|
|
3506
|
-
var
|
|
3506
|
+
var JSON53 = {
|
|
3507
3507
|
parse,
|
|
3508
3508
|
stringify
|
|
3509
3509
|
};
|
|
3510
|
-
module.exports =
|
|
3510
|
+
module.exports = JSON53;
|
|
3511
3511
|
}
|
|
3512
3512
|
});
|
|
3513
3513
|
|
|
@@ -4705,6 +4705,19 @@ var missedFunctions = (file) => {
|
|
|
4705
4705
|
}
|
|
4706
4706
|
return out.sort((firstFunction, secondFunction) => firstFunction.line - secondFunction.line);
|
|
4707
4707
|
};
|
|
4708
|
+
var clamp = (value, lowerBound, upperBound) => Math.max(lowerBound, Math.min(upperBound, value));
|
|
4709
|
+
var renderCodeFrame = (source, miss, context = 3) => {
|
|
4710
|
+
const lines = source.split(/\r?\n/);
|
|
4711
|
+
const from = clamp(miss.start - context, 1, lines.length);
|
|
4712
|
+
const to = clamp(miss.end + context, 1, lines.length);
|
|
4713
|
+
const out = [];
|
|
4714
|
+
for (let ln = from; ln <= to; ln += 1) {
|
|
4715
|
+
const body = lines[ln - 1] ?? "";
|
|
4716
|
+
const tag = ln >= miss.start && ln <= miss.end ? `> ${ln.toString().padStart(4)}|` : ` ${ln.toString().padStart(4)}|`;
|
|
4717
|
+
out.push(`${tag} ${body}`);
|
|
4718
|
+
}
|
|
4719
|
+
return out.join("\n");
|
|
4720
|
+
};
|
|
4708
4721
|
var compositeBarPct = (summary, hotspots) => {
|
|
4709
4722
|
const base = Math.min(
|
|
4710
4723
|
Number.isFinite(summary.lines.pct) ? summary.lines.pct : 0,
|
|
@@ -4970,7 +4983,7 @@ var printCompactCoverage = async (opts) => {
|
|
|
4970
4983
|
);
|
|
4971
4984
|
}
|
|
4972
4985
|
};
|
|
4973
|
-
var shortenPathPreservingFilename = (
|
|
4986
|
+
var shortenPathPreservingFilename = (relPath2, maxWidth, opts) => {
|
|
4974
4987
|
const ellipsis = opts?.ellipsis ?? "\u2026";
|
|
4975
4988
|
const START_HEAD = Math.max(0, opts?.keepHead ?? 1);
|
|
4976
4989
|
const START_TAIL = Math.max(0, opts?.keepTail ?? 1);
|
|
@@ -5142,7 +5155,7 @@ var shortenPathPreservingFilename = (relPath, maxWidth, opts) => {
|
|
|
5142
5155
|
}
|
|
5143
5156
|
return null;
|
|
5144
5157
|
};
|
|
5145
|
-
const normalized =
|
|
5158
|
+
const normalized = relPath2.replace(/\\/g, "/");
|
|
5146
5159
|
if (visibleWidth(normalized) <= maxWidth) {
|
|
5147
5160
|
return normalized;
|
|
5148
5161
|
}
|
|
@@ -5598,34 +5611,81 @@ var printPerFileCompositeTables = async (opts) => {
|
|
|
5598
5611
|
}
|
|
5599
5612
|
};
|
|
5600
5613
|
|
|
5601
|
-
// src/lib/
|
|
5602
|
-
|
|
5603
|
-
|
|
5604
|
-
|
|
5605
|
-
|
|
5606
|
-
var
|
|
5607
|
-
|
|
5608
|
-
|
|
5609
|
-
)
|
|
5610
|
-
|
|
5611
|
-
|
|
5614
|
+
// src/lib/fp.ts
|
|
5615
|
+
function pipe(initial, ...fns) {
|
|
5616
|
+
return fns.reduce((acc, fn) => fn(acc), initial);
|
|
5617
|
+
}
|
|
5618
|
+
var some = (value) => ({ tag: "Some", value });
|
|
5619
|
+
var none = { tag: "None" };
|
|
5620
|
+
var unfoldr = (initial, step2) => {
|
|
5621
|
+
const out = [];
|
|
5622
|
+
for (let state = initial; ; ) {
|
|
5623
|
+
const result = step2(state);
|
|
5624
|
+
if (result.tag === "None") {
|
|
5625
|
+
break;
|
|
5626
|
+
}
|
|
5627
|
+
const [element, next] = result.value;
|
|
5628
|
+
out.push(element);
|
|
5629
|
+
state = next;
|
|
5612
5630
|
}
|
|
5613
|
-
|
|
5614
|
-
return path9.isAbsolute(jsonPath) ? jsonPath : path9.resolve(cwd, jsonPath);
|
|
5631
|
+
return out;
|
|
5615
5632
|
};
|
|
5616
|
-
|
|
5617
|
-
|
|
5618
|
-
|
|
5619
|
-
|
|
5620
|
-
|
|
5621
|
-
|
|
5622
|
-
|
|
5633
|
+
|
|
5634
|
+
// src/lib/formatter/parse.ts
|
|
5635
|
+
var isFailureStart = (lineText) => /^\s*●\s+/.test(lineText);
|
|
5636
|
+
var isSuiteLine = (lineText) => /^\s*(PASS|FAIL)\s+/.test(lineText);
|
|
5637
|
+
var isSummaryLine = (lineText) => /^\s*(Test Suites:|Tests:|Snapshots:|Time:|Ran all)/.test(lineText);
|
|
5638
|
+
var collectFailure = (allLines, startIndex) => {
|
|
5639
|
+
const title = stripAnsiSimple(allLines[startIndex]).replace(/^\s*●\s+/, "").trim();
|
|
5640
|
+
const buf = [allLines[startIndex]];
|
|
5641
|
+
let i = startIndex + 1;
|
|
5642
|
+
for (; i < allLines.length; i += 1) {
|
|
5643
|
+
const simple = stripAnsiSimple(allLines[i]);
|
|
5644
|
+
const nextIsStart = isFailureStart(simple) || isSuiteLine(simple) || isSummaryLine(simple);
|
|
5645
|
+
const prevBlank = stripAnsiSimple(allLines[i - 1] ?? "").trim() === "";
|
|
5646
|
+
if (nextIsStart && prevBlank) {
|
|
5647
|
+
break;
|
|
5648
|
+
}
|
|
5649
|
+
buf.push(allLines[i]);
|
|
5623
5650
|
}
|
|
5624
|
-
|
|
5625
|
-
|
|
5626
|
-
|
|
5651
|
+
return [{ tag: "FailureBlock", title, lines: buf }, i];
|
|
5652
|
+
};
|
|
5653
|
+
var parseSuite = (lineText) => {
|
|
5654
|
+
const match = lineText.match(/^\s*(PASS|FAIL)\s+(.+)$/);
|
|
5655
|
+
return { tag: "PassFail", badge: match[1], rel: match[2] };
|
|
5627
5656
|
};
|
|
5628
|
-
var
|
|
5657
|
+
var parseChunks = (raw) => {
|
|
5658
|
+
const lines = raw.split(/\r?\n/);
|
|
5659
|
+
return unfoldr({ index: 0 }, (state) => {
|
|
5660
|
+
if (state.index >= lines.length) {
|
|
5661
|
+
return none;
|
|
5662
|
+
}
|
|
5663
|
+
const line = lines[state.index];
|
|
5664
|
+
const simple = stripAnsiSimple(line);
|
|
5665
|
+
if (isFailureStart(simple)) {
|
|
5666
|
+
const [chunk, next] = collectFailure(lines, state.index);
|
|
5667
|
+
return some([chunk, { index: next }]);
|
|
5668
|
+
}
|
|
5669
|
+
if (isSuiteLine(simple)) {
|
|
5670
|
+
return some([parseSuite(simple), { index: state.index + 1 }]);
|
|
5671
|
+
}
|
|
5672
|
+
if (isSummaryLine(simple)) {
|
|
5673
|
+
return some([{ tag: "Summary", line }, { index: state.index + 1 }]);
|
|
5674
|
+
}
|
|
5675
|
+
if (isStackLine(simple)) {
|
|
5676
|
+
return some([{ tag: "Stack", line }, { index: state.index + 1 }]);
|
|
5677
|
+
}
|
|
5678
|
+
return some([{ tag: "Other", line }, { index: state.index + 1 }]);
|
|
5679
|
+
});
|
|
5680
|
+
};
|
|
5681
|
+
|
|
5682
|
+
// src/lib/formatter/render.ts
|
|
5683
|
+
import * as path9 from "node:path";
|
|
5684
|
+
|
|
5685
|
+
// src/lib/formatter/fns.ts
|
|
5686
|
+
var import_json5 = __toESM(require_lib(), 1);
|
|
5687
|
+
import * as fs4 from "node:fs";
|
|
5688
|
+
import * as util from "node:util";
|
|
5629
5689
|
var colorTokens = {
|
|
5630
5690
|
pass: Colors.Success,
|
|
5631
5691
|
fail: Colors.Failure,
|
|
@@ -5636,80 +5696,18 @@ var colorTokens = {
|
|
|
5636
5696
|
failPill: (text) => BackgroundColors.Failure(ansi.white(` ${text} `)),
|
|
5637
5697
|
runPill: (text) => BackgroundColors.Run(ansi.white(` ${text} `))
|
|
5638
5698
|
};
|
|
5639
|
-
var
|
|
5640
|
-
|
|
5641
|
-
|
|
5642
|
-
|
|
5643
|
-
|
|
5644
|
-
|
|
5645
|
-
|
|
5646
|
-
if (!lines.length) {
|
|
5647
|
-
return raw;
|
|
5648
|
-
}
|
|
5649
|
-
const first = lines[0] ?? "";
|
|
5650
|
-
const isArrayStart = /^\s*(?:Array\s*\[|\[)\s*$/.test(first);
|
|
5651
|
-
const isObjectStart = /^\s*(?:Object\s*\{|\{)\s*$/.test(first);
|
|
5652
|
-
if (!isArrayStart && !isObjectStart) {
|
|
5653
|
-
return raw;
|
|
5654
|
-
}
|
|
5655
|
-
lines[0] = first.replace(/^(\s*)Array\s*\[/, "$1[").replace(/^(\s*)Object\s*\{/, "$1{").replace(/^\s*\[\s*$/, "[").replace(/^\s*\{\s*$/, "{");
|
|
5656
|
-
const closingChar = isArrayStart ? "]" : "}";
|
|
5657
|
-
let closeIdx = -1;
|
|
5658
|
-
for (let i = 1; i < lines.length; i += 1) {
|
|
5659
|
-
if (/^\s*[\]}]\s*$/.test(lines[i])) {
|
|
5660
|
-
closeIdx = i;
|
|
5661
|
-
break;
|
|
5662
|
-
}
|
|
5663
|
-
}
|
|
5664
|
-
if (closeIdx < 0) {
|
|
5665
|
-
return lines.join("\n");
|
|
5666
|
-
}
|
|
5667
|
-
const inner = lines.slice(1, closeIdx);
|
|
5668
|
-
const indents = [];
|
|
5669
|
-
for (const lineText of inner) {
|
|
5670
|
-
if (lineText.trim().length === 0) {
|
|
5671
|
-
continue;
|
|
5672
|
-
}
|
|
5673
|
-
indents.push(lineText.match(/^\s*/)?.[0]?.length ?? 0);
|
|
5674
|
-
}
|
|
5675
|
-
const minIndent = indents.length ? Math.min(...indents) : 0;
|
|
5676
|
-
const reindented = inner.map((lineText, idx) => {
|
|
5677
|
-
if (lineText.trim().length === 0) {
|
|
5678
|
-
return lineText;
|
|
5679
|
-
}
|
|
5680
|
-
const current = lineText.match(/^\s*/)?.[0]?.length ?? 0;
|
|
5681
|
-
const rest = lineText.slice(current);
|
|
5682
|
-
const extra = Math.max(0, current - minIndent);
|
|
5683
|
-
const base = " ";
|
|
5684
|
-
const withoutDangling = idx === inner.length - 1 ? rest.replace(/,\s*$/, "") : rest;
|
|
5685
|
-
return base + " ".repeat(extra) + withoutDangling;
|
|
5686
|
-
});
|
|
5687
|
-
lines.splice(1, inner.length, ...reindented);
|
|
5688
|
-
lines[closeIdx] = closingChar;
|
|
5689
|
-
return lines.join("\n");
|
|
5690
|
-
};
|
|
5691
|
-
var normalizeBlock = (raw) => raw.replace(/^\s*Array\s*\[/, "[").replace(/^\s*Object\s*\{/, "{").replace(/,(\s*[\]}])/g, "$1");
|
|
5692
|
-
var stringifyPrettierish = (value) => {
|
|
5693
|
-
if (typeof value === "string") {
|
|
5694
|
-
const text = normalizeBlock(value.trim());
|
|
5695
|
-
if (/^[[{]/.test(text)) {
|
|
5696
|
-
try {
|
|
5697
|
-
const parsed = import_json5.default.parse(text);
|
|
5698
|
-
return JSON.stringify(parsed, null, 2);
|
|
5699
|
-
} catch {
|
|
5700
|
-
}
|
|
5701
|
-
}
|
|
5702
|
-
return value;
|
|
5703
|
-
}
|
|
5704
|
-
if (Array.isArray(value) || value && typeof value === "object") {
|
|
5705
|
-
try {
|
|
5706
|
-
return JSON.stringify(value, null, 2);
|
|
5707
|
-
} catch {
|
|
5708
|
-
}
|
|
5699
|
+
var drawRule = (label) => {
|
|
5700
|
+
const width = Math.max(
|
|
5701
|
+
40,
|
|
5702
|
+
process.stdout && process.stdout.columns || 80
|
|
5703
|
+
);
|
|
5704
|
+
if (!label) {
|
|
5705
|
+
return ansi.dim("\u2500".repeat(width));
|
|
5709
5706
|
}
|
|
5710
|
-
|
|
5707
|
+
const plain = stripAnsiSimple(label);
|
|
5708
|
+
const pad = Math.max(1, width - plain.length - 1);
|
|
5709
|
+
return `${ansi.dim("\u2500".repeat(pad))} ${label}`;
|
|
5711
5710
|
};
|
|
5712
|
-
var drawFailRule = (label = " FAIL ") => drawRule(colorTokens.failPill(label));
|
|
5713
5711
|
var drawFailLine = () => {
|
|
5714
5712
|
const width = Math.max(
|
|
5715
5713
|
40,
|
|
@@ -5718,9 +5716,29 @@ var drawFailLine = () => {
|
|
|
5718
5716
|
return colorTokens.fail("\u2500".repeat(width));
|
|
5719
5717
|
};
|
|
5720
5718
|
var renderRunLine = (cwd) => `${colorTokens.runPill("RUN")} ${ansi.dim(cwd.replace(/\\/g, "/"))}`;
|
|
5719
|
+
var buildFileBadgeLine = (rel, failedCount) => failedCount > 0 ? `${colorTokens.failPill("FAIL")} ${ansi.white(rel)}` : `${colorTokens.passPill("PASS")} ${ansi.white(rel)}`;
|
|
5720
|
+
var buildPerFileOverview = (rel, assertions) => {
|
|
5721
|
+
const out = [];
|
|
5722
|
+
out.push(`${ansi.magenta(rel)} ${ansi.dim(`(${assertions.length})`)}`);
|
|
5723
|
+
for (const assertion of assertions) {
|
|
5724
|
+
const name = assertion.fullName;
|
|
5725
|
+
if (assertion.status === "passed") {
|
|
5726
|
+
out.push(` ${colorTokens.pass("\u2713")} ${ansi.dim(name)}`);
|
|
5727
|
+
} else if (assertion.status === "todo") {
|
|
5728
|
+
out.push(` ${colorTokens.todo("\u2610")} ${ansi.dim(name)} ${colorTokens.todo("[todo]")}`);
|
|
5729
|
+
} else if (assertion.status === "pending") {
|
|
5730
|
+
out.push(` ${colorTokens.skip("\u2193")} ${ansi.dim(name)} ${colorTokens.skip("[skipped]")}`);
|
|
5731
|
+
} else {
|
|
5732
|
+
out.push(` ${colorTokens.fail("\xD7")} ${ansi.white(name)}`);
|
|
5733
|
+
}
|
|
5734
|
+
}
|
|
5735
|
+
out.push("");
|
|
5736
|
+
return out;
|
|
5737
|
+
};
|
|
5738
|
+
var isObjectRecord = (value) => typeof value === "object" && value !== null;
|
|
5721
5739
|
var colorStackLine = (line, projectHint) => {
|
|
5722
5740
|
const plainLine = stripAnsiSimple(line);
|
|
5723
|
-
if (
|
|
5741
|
+
if (!/\s+at\s+/.test(plainLine)) {
|
|
5724
5742
|
return plainLine;
|
|
5725
5743
|
}
|
|
5726
5744
|
const match = plainLine.match(/\(?([^\s()]+):(\d+):(\d+)\)?$/);
|
|
@@ -5736,255 +5754,257 @@ var colorStackLine = (line, projectHint) => {
|
|
|
5736
5754
|
`(${coloredPath}${ansi.dim(":")}${ansi.white(`${lineNumber}:${columnNumber}`)})`
|
|
5737
5755
|
);
|
|
5738
5756
|
};
|
|
5739
|
-
var
|
|
5740
|
-
const
|
|
5741
|
-
|
|
5742
|
-
|
|
5743
|
-
|
|
5744
|
-
break;
|
|
5745
|
-
}
|
|
5746
|
-
if (/^\s*\^+\s*$/.test(raw)) {
|
|
5747
|
-
out.push(` ${colorTokens.fail(raw.trimEnd())}`);
|
|
5748
|
-
continue;
|
|
5749
|
-
}
|
|
5750
|
-
const pointerMatch = raw.match(/^\s*>(\s*\d+)\s*\|\s?(.*)$/);
|
|
5751
|
-
if (pointerMatch) {
|
|
5752
|
-
const num = ansi.dim(pointerMatch[1].trim());
|
|
5753
|
-
const code = ansi.yellow(pointerMatch[2] ?? "");
|
|
5754
|
-
out.push(` ${colorTokens.fail(">")} ${num} ${ansi.dim("|")} ${code}`);
|
|
5755
|
-
continue;
|
|
5756
|
-
}
|
|
5757
|
-
const normalMatch = raw.match(/^\s*(\d+)\s*\|\s?(.*)$/);
|
|
5758
|
-
if (normalMatch) {
|
|
5759
|
-
const num = ansi.dim(normalMatch[1]);
|
|
5760
|
-
const code = ansi.dim(normalMatch[2] ?? "");
|
|
5761
|
-
out.push(` ${num} ${ansi.dim("|")} ${code}`);
|
|
5762
|
-
continue;
|
|
5763
|
-
}
|
|
5764
|
-
out.push(` ${raw}`);
|
|
5757
|
+
var extractBridgePath = (raw, cwd) => {
|
|
5758
|
+
const re = /Test results written to:\s+([^\n\r]+jest-bridge-[^\s'"]+\.json)/g;
|
|
5759
|
+
const matches = Array.from(raw.matchAll(re));
|
|
5760
|
+
if (matches.length === 0) {
|
|
5761
|
+
return null;
|
|
5765
5762
|
}
|
|
5766
|
-
|
|
5763
|
+
const jsonPath = (matches[matches.length - 1][1] ?? "").trim().replace(/^["'`]|["'`]$/g, "");
|
|
5764
|
+
return /^\//.test(jsonPath) ? jsonPath : `${cwd.replace(/\\/g, "/")}/${jsonPath}`;
|
|
5767
5765
|
};
|
|
5766
|
+
var findCodeFrameStart = (lines) => lines.findIndex((line) => /^\s*(>?\s*\d+\s*\|)/.test(stripAnsiSimple(line)));
|
|
5768
5767
|
var _sourceCache = /* @__PURE__ */ new Map();
|
|
5769
5768
|
var readSource = (file) => {
|
|
5770
|
-
const
|
|
5771
|
-
const
|
|
5772
|
-
if (
|
|
5773
|
-
return
|
|
5769
|
+
const normalized = file.replace(/\\/g, "/");
|
|
5770
|
+
const hit = _sourceCache.get(normalized);
|
|
5771
|
+
if (hit) {
|
|
5772
|
+
return hit;
|
|
5774
5773
|
}
|
|
5775
5774
|
try {
|
|
5776
|
-
const
|
|
5777
|
-
|
|
5778
|
-
_sourceCache.set(normalizedFile, arr);
|
|
5775
|
+
const arr = fs4.readFileSync(normalized, "utf8").split(/\r?\n/);
|
|
5776
|
+
_sourceCache.set(normalized, arr);
|
|
5779
5777
|
return arr;
|
|
5780
5778
|
} catch {
|
|
5781
5779
|
return [];
|
|
5782
5780
|
}
|
|
5783
5781
|
};
|
|
5782
|
+
var renderInlineCodeFrame = (lines, start) => {
|
|
5783
|
+
const out = [];
|
|
5784
|
+
for (let i = start; i < lines.length; i += 1) {
|
|
5785
|
+
const raw = stripAnsiSimple(lines[i]);
|
|
5786
|
+
if (!raw.trim()) {
|
|
5787
|
+
break;
|
|
5788
|
+
}
|
|
5789
|
+
if (/^\s*\^+\s*$/.test(raw)) {
|
|
5790
|
+
out.push(` ${Colors.Failure(raw.trimEnd())}`);
|
|
5791
|
+
} else {
|
|
5792
|
+
const ptr = raw.match(/^\s*>(\s*\d+)\s*\|\s?(.*)$/);
|
|
5793
|
+
if (ptr) {
|
|
5794
|
+
const num = ansi.dim(ptr[1].trim());
|
|
5795
|
+
const code = ansi.yellow(ptr[2] ?? "");
|
|
5796
|
+
out.push(` ${Colors.Failure(">")} ${num} ${ansi.dim("|")} ${code}`);
|
|
5797
|
+
} else {
|
|
5798
|
+
const nor = raw.match(/^\s*(\d+)\s*\|\s?(.*)$/);
|
|
5799
|
+
if (nor) {
|
|
5800
|
+
const num = ansi.dim(nor[1]);
|
|
5801
|
+
const code = ansi.dim(nor[2] ?? "");
|
|
5802
|
+
out.push(` ${num} ${ansi.dim("|")} ${code}`);
|
|
5803
|
+
} else {
|
|
5804
|
+
out.push(` ${raw}`);
|
|
5805
|
+
}
|
|
5806
|
+
}
|
|
5807
|
+
}
|
|
5808
|
+
}
|
|
5809
|
+
return out;
|
|
5810
|
+
};
|
|
5784
5811
|
var renderSourceCodeFrame = (file, line, context = 3) => {
|
|
5785
5812
|
const lines = readSource(file);
|
|
5786
|
-
if (
|
|
5813
|
+
if (lines.length === 0 || !Number.isFinite(line)) {
|
|
5787
5814
|
return [];
|
|
5788
5815
|
}
|
|
5789
5816
|
const idx = Math.max(1, Math.min(line, lines.length));
|
|
5790
5817
|
const start = Math.max(1, idx - context);
|
|
5791
5818
|
const end = Math.min(lines.length, idx + context);
|
|
5792
5819
|
const out = [];
|
|
5793
|
-
for (let
|
|
5794
|
-
const num = ansi.dim(String(
|
|
5795
|
-
const code =
|
|
5796
|
-
if (
|
|
5797
|
-
out.push(` ${
|
|
5820
|
+
for (let current = start; current <= end; current += 1) {
|
|
5821
|
+
const num = ansi.dim(String(current));
|
|
5822
|
+
const code = current === idx ? ansi.yellow(lines[current - 1] ?? "") : ansi.dim(lines[current - 1] ?? "");
|
|
5823
|
+
if (current === idx) {
|
|
5824
|
+
out.push(` ${Colors.Failure(">")} ${num} ${ansi.dim("|")} ${code}`);
|
|
5798
5825
|
} else {
|
|
5799
5826
|
out.push(` ${num} ${ansi.dim("|")} ${code}`);
|
|
5800
5827
|
}
|
|
5801
5828
|
}
|
|
5802
|
-
out.push(` ${
|
|
5829
|
+
out.push(` ${Colors.Failure("^")}`);
|
|
5803
5830
|
return out;
|
|
5804
5831
|
};
|
|
5805
|
-
var
|
|
5806
|
-
|
|
5807
|
-
|
|
5832
|
+
var stackLocation = (line) => {
|
|
5833
|
+
const match = stripAnsiSimple(line).match(/\(?([^\s()]+):(\d+):\d+\)?$/);
|
|
5834
|
+
if (!match) {
|
|
5835
|
+
return null;
|
|
5836
|
+
}
|
|
5837
|
+
return { file: match[1].replace(/\\/g, "/"), line: Number(match[2]) };
|
|
5838
|
+
};
|
|
5839
|
+
var deepestProjectLoc = (stackLines, projectHint) => {
|
|
5840
|
+
for (let i = stackLines.length - 1; i >= 0; i -= 1) {
|
|
5841
|
+
const simple = stripAnsiSimple(stackLines[i]);
|
|
5808
5842
|
if (isStackLine(simple) && projectHint.test(simple) && !/node_modules|vitest|jest/.test(simple)) {
|
|
5809
|
-
return i;
|
|
5843
|
+
return stackLocation(stackLines[i]);
|
|
5810
5844
|
}
|
|
5811
5845
|
}
|
|
5812
|
-
return
|
|
5846
|
+
return null;
|
|
5813
5847
|
};
|
|
5814
|
-
var
|
|
5815
|
-
const
|
|
5816
|
-
|
|
5817
|
-
|
|
5848
|
+
var buildCodeFrameSection = (messageLines, ctx, synthLoc) => {
|
|
5849
|
+
const out = [];
|
|
5850
|
+
const start = findCodeFrameStart(messageLines);
|
|
5851
|
+
if (start >= 0) {
|
|
5852
|
+
out.push(...renderInlineCodeFrame(messageLines, start), "");
|
|
5853
|
+
return out;
|
|
5818
5854
|
}
|
|
5819
|
-
|
|
5820
|
-
|
|
5821
|
-
}
|
|
5822
|
-
|
|
5823
|
-
const onlyStack = lines.filter((ln) => isStackLine(stripAnsiSimple(ln)));
|
|
5824
|
-
const projectOnly = onlyStack.filter((ln) => projectHint.test(stripAnsiSimple(ln)));
|
|
5825
|
-
return projectOnly.slice(0, max).map((ln) => ` ${colorStackLine(ln, projectHint)}`);
|
|
5855
|
+
if (ctx.showStacks && synthLoc) {
|
|
5856
|
+
out.push(...renderSourceCodeFrame(synthLoc.file, synthLoc.line), "");
|
|
5857
|
+
}
|
|
5858
|
+
return out;
|
|
5826
5859
|
};
|
|
5827
|
-
var
|
|
5828
|
-
var
|
|
5829
|
-
|
|
5830
|
-
|
|
5831
|
-
|
|
5832
|
-
|
|
5833
|
-
|
|
5834
|
-
|
|
5835
|
-
|
|
5860
|
+
var normalizeBlock = (raw) => raw.replace(/^\s*Array\s*\[/, "[").replace(/^\s*Object\s*\{/, "{").replace(/,(\s*[\]}])/g, "$1");
|
|
5861
|
+
var stringifyPrettierish = (value) => {
|
|
5862
|
+
if (typeof value === "string") {
|
|
5863
|
+
const text = normalizeBlock(value.trim());
|
|
5864
|
+
try {
|
|
5865
|
+
const parsed = import_json5.default.parse(text);
|
|
5866
|
+
return JSON.stringify(parsed, null, 2);
|
|
5867
|
+
} catch {
|
|
5868
|
+
return value;
|
|
5869
|
+
}
|
|
5836
5870
|
}
|
|
5837
|
-
|
|
5838
|
-
|
|
5839
|
-
|
|
5840
|
-
|
|
5841
|
-
|
|
5842
|
-
|
|
5843
|
-
|
|
5844
|
-
|
|
5871
|
+
if (Array.isArray(value) || isObjectRecord(value)) {
|
|
5872
|
+
try {
|
|
5873
|
+
return JSON.stringify(value, null, 2);
|
|
5874
|
+
} catch {
|
|
5875
|
+
return util.inspect(value, {
|
|
5876
|
+
depth: 10,
|
|
5877
|
+
breakLength: Infinity,
|
|
5878
|
+
compact: false,
|
|
5879
|
+
sorted: true
|
|
5880
|
+
});
|
|
5845
5881
|
}
|
|
5846
|
-
};
|
|
5847
|
-
const expectedIdx = lines.findIndex(
|
|
5848
|
-
(candidateLine) => /^\s*Expected:/.test(candidateLine)
|
|
5849
|
-
);
|
|
5850
|
-
const receivedIdx = lines.findIndex(
|
|
5851
|
-
(candidateLine) => /^\s*Received:/.test(candidateLine)
|
|
5852
|
-
);
|
|
5853
|
-
const diffIdx = lines.findIndex(
|
|
5854
|
-
(candidateLine) => /^\s*(?:- Expected|\+ Received|Difference:)/.test(candidateLine)
|
|
5855
|
-
);
|
|
5856
|
-
if (expectedIdx >= 0) {
|
|
5857
|
-
collectBlock(expectedIdx);
|
|
5858
5882
|
}
|
|
5859
|
-
|
|
5860
|
-
|
|
5883
|
+
return util.inspect(value, {
|
|
5884
|
+
depth: 10,
|
|
5885
|
+
breakLength: Infinity,
|
|
5886
|
+
compact: false,
|
|
5887
|
+
sorted: true
|
|
5888
|
+
});
|
|
5889
|
+
};
|
|
5890
|
+
var isArrayOfPrimitives = (value) => Array.isArray(value) && value.every(
|
|
5891
|
+
(element) => ["string", "number", "boolean"].includes(typeof element) || element === null
|
|
5892
|
+
);
|
|
5893
|
+
var extractFromUnifiedDiff = (rawLines) => {
|
|
5894
|
+
const lines = (rawLines ?? []).map((lineText) => stripAnsiSimple(lineText));
|
|
5895
|
+
let startIndex = -1;
|
|
5896
|
+
for (let i = 0; i < lines.length; i += 1) {
|
|
5897
|
+
const lt = lines[i];
|
|
5898
|
+
if (/^\s*(?:[-+]\s*)?(Array\s*\[|Object\s*\{)/.test(lt)) {
|
|
5899
|
+
startIndex = i;
|
|
5900
|
+
break;
|
|
5901
|
+
}
|
|
5861
5902
|
}
|
|
5862
|
-
if (
|
|
5863
|
-
|
|
5903
|
+
if (startIndex < 0) {
|
|
5904
|
+
return {};
|
|
5864
5905
|
}
|
|
5865
|
-
|
|
5866
|
-
|
|
5867
|
-
|
|
5868
|
-
|
|
5869
|
-
|
|
5906
|
+
const expectedParts = [];
|
|
5907
|
+
const receivedParts = [];
|
|
5908
|
+
let opened = false;
|
|
5909
|
+
let expDone = false;
|
|
5910
|
+
let recDone = false;
|
|
5911
|
+
const canParseJsonish = (input) => {
|
|
5912
|
+
const text = normalizeBlock(input).trim();
|
|
5913
|
+
try {
|
|
5914
|
+
import_json5.default.parse(text);
|
|
5915
|
+
return true;
|
|
5916
|
+
} catch {
|
|
5917
|
+
return false;
|
|
5918
|
+
}
|
|
5919
|
+
};
|
|
5920
|
+
for (let i = startIndex; i < lines.length; i += 1) {
|
|
5921
|
+
const lineText = lines[i];
|
|
5922
|
+
const unsigned = lineText.replace(/^\s*[-+]\s?/, "");
|
|
5923
|
+
const isMinus = /^\s*-\s/.test(lineText);
|
|
5924
|
+
const isPlus = /^\s*\+\s/.test(lineText);
|
|
5925
|
+
if (!opened) {
|
|
5926
|
+
const looksLikeStart = /^\s*(Array\s*\[|Object\s*\{)/.test(unsigned);
|
|
5927
|
+
if (!looksLikeStart) {
|
|
5928
|
+
continue;
|
|
5870
5929
|
}
|
|
5871
|
-
|
|
5930
|
+
opened = true;
|
|
5931
|
+
}
|
|
5932
|
+
if (isMinus) {
|
|
5933
|
+
expectedParts.push(unsigned);
|
|
5934
|
+
} else if (isPlus) {
|
|
5935
|
+
receivedParts.push(unsigned);
|
|
5936
|
+
} else {
|
|
5937
|
+
expectedParts.push(unsigned);
|
|
5938
|
+
receivedParts.push(unsigned);
|
|
5939
|
+
}
|
|
5940
|
+
if (!expDone && expectedParts.length > 0) {
|
|
5941
|
+
expDone = canParseJsonish(expectedParts.join("\n"));
|
|
5942
|
+
}
|
|
5943
|
+
if (!recDone && receivedParts.length > 0) {
|
|
5944
|
+
recDone = canParseJsonish(receivedParts.join("\n"));
|
|
5945
|
+
}
|
|
5946
|
+
if (opened && expDone && recDone) {
|
|
5947
|
+
break;
|
|
5872
5948
|
}
|
|
5873
5949
|
}
|
|
5874
|
-
|
|
5875
|
-
|
|
5876
|
-
|
|
5877
|
-
const match = stripAnsiSimple(line).match(/\(?([^\s()]+):(\d+):\d+\)?$/);
|
|
5878
|
-
return match ? { file: match[1].replace(/\\/g, "/"), line: Number(match[2]) } : null;
|
|
5879
|
-
};
|
|
5880
|
-
var JEST_BRIDGE_REPORTER_SOURCE = `const fs = require('fs');
|
|
5881
|
-
const path = require('path');
|
|
5882
|
-
|
|
5883
|
-
class BridgeReporter {
|
|
5884
|
-
constructor(globalConfig, options) {
|
|
5885
|
-
this.out = process.env.JEST_BRIDGE_OUT || (options && options.outFile) || path.join(process.cwd(), 'coverage', 'jest-run.json');
|
|
5886
|
-
this.buf = { startTime: Date.now(), testResults: [], aggregated: null };
|
|
5887
|
-
}
|
|
5888
|
-
onRunStart() { this.buf.startTime = Date.now(); }
|
|
5889
|
-
onTestResult(_test, tr) {
|
|
5890
|
-
const mapAssertion = (a) => ({
|
|
5891
|
-
title: a.title,
|
|
5892
|
-
fullName: a.fullName || [...(a.ancestorTitles || []), a.title].join(' '),
|
|
5893
|
-
status: a.status,
|
|
5894
|
-
duration: a.duration || 0,
|
|
5895
|
-
location: a.location || null,
|
|
5896
|
-
failureMessages: (a.failureMessages || []).map(String),
|
|
5897
|
-
});
|
|
5898
|
-
this.buf.testResults.push({
|
|
5899
|
-
testFilePath: tr.testFilePath,
|
|
5900
|
-
status: tr.numFailingTests ? 'failed' : 'passed',
|
|
5901
|
-
failureMessage: tr.failureMessage || '',
|
|
5902
|
-
failureDetails: tr.failureDetails || [],
|
|
5903
|
-
console: tr.console || null,
|
|
5904
|
-
perfStats: tr.perfStats || {},
|
|
5905
|
-
testResults: (tr.testResults || []).map(mapAssertion),
|
|
5906
|
-
});
|
|
5907
|
-
}
|
|
5908
|
-
onRunComplete(_contexts, agg) {
|
|
5909
|
-
this.buf.aggregated = {
|
|
5910
|
-
numTotalTestSuites: agg.numTotalTestSuites,
|
|
5911
|
-
numPassedTestSuites: agg.numPassedTestSuites,
|
|
5912
|
-
numFailedTestSuites: agg.numFailedTestSuites,
|
|
5913
|
-
numTotalTests: agg.numTotalTests,
|
|
5914
|
-
numPassedTests: agg.numPassedTests,
|
|
5915
|
-
numFailedTests: agg.numFailedTests,
|
|
5916
|
-
numPendingTests: agg.numPendingTests,
|
|
5917
|
-
numTodoTests: agg.numTodoTests,
|
|
5918
|
-
startTime: agg.startTime,
|
|
5919
|
-
success: agg.success,
|
|
5920
|
-
runTimeMs: agg.testResults.reduce((t, r) => t + Math.max(0, (r.perfStats?.end || 0) - (r.perfStats?.start || 0)), 0),
|
|
5921
|
-
};
|
|
5922
|
-
fs.mkdirSync(path.dirname(this.out), { recursive: true });
|
|
5923
|
-
fs.writeFileSync(this.out, JSON.stringify(this.buf), 'utf8');
|
|
5924
|
-
}
|
|
5925
|
-
}
|
|
5926
|
-
module.exports = BridgeReporter;`;
|
|
5927
|
-
var asDict = (value) => value && typeof value === "object" ? value : null;
|
|
5928
|
-
var get = (objectValue, key) => objectValue ? objectValue[key] : void 0;
|
|
5929
|
-
var getStr = (objectValue, key) => {
|
|
5930
|
-
const candidate = get(objectValue, key);
|
|
5931
|
-
return typeof candidate === "string" ? candidate : void 0;
|
|
5932
|
-
};
|
|
5933
|
-
function linesFromDetails(details) {
|
|
5934
|
-
const stacks = [];
|
|
5935
|
-
const messages = [];
|
|
5936
|
-
if (!details) {
|
|
5937
|
-
return { stacks, messages };
|
|
5938
|
-
}
|
|
5939
|
-
const pushMaybe = (value, bucket) => {
|
|
5940
|
-
if (typeof value === "string" && value.trim()) {
|
|
5941
|
-
bucket.push(...value.split(/\r?\n/));
|
|
5942
|
-
}
|
|
5943
|
-
};
|
|
5944
|
-
for (const detail of details) {
|
|
5945
|
-
if (typeof detail === "string") {
|
|
5946
|
-
if (/\s+at\s.+\(.+:\d+:\d+\)/.test(detail)) {
|
|
5947
|
-
pushMaybe(detail, stacks);
|
|
5948
|
-
} else {
|
|
5949
|
-
pushMaybe(detail, messages);
|
|
5950
|
-
}
|
|
5951
|
-
continue;
|
|
5950
|
+
const toJsonLikeString = (joined) => {
|
|
5951
|
+
if (!joined) {
|
|
5952
|
+
return void 0;
|
|
5952
5953
|
}
|
|
5953
|
-
const
|
|
5954
|
-
|
|
5955
|
-
|
|
5956
|
-
|
|
5957
|
-
|
|
5958
|
-
|
|
5959
|
-
pushMaybe(getStr(err, "message"), messages);
|
|
5960
|
-
const matcherResult = asDict(get(dict, "matcherResult"));
|
|
5961
|
-
pushMaybe(getStr(matcherResult, "stack"), stacks);
|
|
5962
|
-
pushMaybe(getStr(matcherResult, "message"), messages);
|
|
5963
|
-
pushMaybe(getStr(matcherResult, "expected"), messages);
|
|
5964
|
-
pushMaybe(getStr(matcherResult, "received"), messages);
|
|
5954
|
+
const text = normalizeBlock(joined).trim();
|
|
5955
|
+
try {
|
|
5956
|
+
const parsed = import_json5.default.parse(text);
|
|
5957
|
+
return JSON.stringify(parsed, null, 2);
|
|
5958
|
+
} catch {
|
|
5959
|
+
return text;
|
|
5965
5960
|
}
|
|
5961
|
+
};
|
|
5962
|
+
const expected = expectedParts.length ? expectedParts.join("\n") : void 0;
|
|
5963
|
+
const received = receivedParts.length ? receivedParts.join("\n") : void 0;
|
|
5964
|
+
const result = {};
|
|
5965
|
+
const expStr = toJsonLikeString(expected);
|
|
5966
|
+
const recStr = toJsonLikeString(received);
|
|
5967
|
+
if (expStr !== void 0) {
|
|
5968
|
+
result.expected = expStr;
|
|
5966
5969
|
}
|
|
5967
|
-
|
|
5968
|
-
|
|
5969
|
-
|
|
5970
|
-
|
|
5971
|
-
|
|
5972
|
-
|
|
5973
|
-
const typeName = matched[1] ?? "Error";
|
|
5974
|
-
return `${typeName}:`;
|
|
5975
|
-
}
|
|
5976
|
-
return /expect\(.+?\)\.(?:to|not\.)/.test(joined) ? "Assertion:" : "Message:";
|
|
5977
|
-
}
|
|
5978
|
-
function extractExpectedReceived(details, lines) {
|
|
5970
|
+
if (recStr !== void 0) {
|
|
5971
|
+
result.received = recStr;
|
|
5972
|
+
}
|
|
5973
|
+
return result;
|
|
5974
|
+
};
|
|
5975
|
+
var extractExpectedReceived = (details, lines) => {
|
|
5979
5976
|
if (details) {
|
|
5980
5977
|
for (const detail of details) {
|
|
5981
|
-
const dict =
|
|
5982
|
-
const
|
|
5983
|
-
if (
|
|
5984
|
-
const
|
|
5985
|
-
const
|
|
5986
|
-
|
|
5987
|
-
|
|
5978
|
+
const dict = isObjectRecord(detail) ? detail : void 0;
|
|
5979
|
+
const matcher = dict && isObjectRecord(dict.matcherResult) ? dict.matcherResult : void 0;
|
|
5980
|
+
if (matcher) {
|
|
5981
|
+
const expectedValue = matcher.expected;
|
|
5982
|
+
const receivedValue = matcher.received;
|
|
5983
|
+
const matcherName = String(
|
|
5984
|
+
matcher.matcherName || ""
|
|
5985
|
+
);
|
|
5986
|
+
if (matcherName === "toHaveBeenCalledTimes" || matcherName === "toBeCalledTimes") {
|
|
5987
|
+
const getCallsCount = (actual) => {
|
|
5988
|
+
if (isObjectRecord(actual) && Array.isArray(actual.calls)) {
|
|
5989
|
+
return actual.calls.length;
|
|
5990
|
+
}
|
|
5991
|
+
if (typeof actual === "number") {
|
|
5992
|
+
return actual;
|
|
5993
|
+
}
|
|
5994
|
+
if (Array.isArray(actual)) {
|
|
5995
|
+
return actual.length;
|
|
5996
|
+
}
|
|
5997
|
+
return void 0;
|
|
5998
|
+
};
|
|
5999
|
+
const expectedNumber = getCallsCount(expectedValue);
|
|
6000
|
+
const actualValue = matcher.actual ?? receivedValue;
|
|
6001
|
+
const receivedNumber = getCallsCount(actualValue);
|
|
6002
|
+
if (expectedNumber !== void 0 || receivedNumber !== void 0) {
|
|
6003
|
+
return { expected: expectedNumber, received: receivedNumber };
|
|
6004
|
+
}
|
|
6005
|
+
}
|
|
6006
|
+
if (expectedValue !== void 0 && receivedValue !== void 0) {
|
|
6007
|
+
return { expected: expectedValue, received: receivedValue };
|
|
5988
6008
|
}
|
|
5989
6009
|
}
|
|
5990
6010
|
}
|
|
@@ -6025,116 +6045,34 @@ function extractExpectedReceived(details, lines) {
|
|
|
6025
6045
|
}
|
|
6026
6046
|
}
|
|
6027
6047
|
return {};
|
|
6028
|
-
}
|
|
6029
|
-
|
|
6030
|
-
const
|
|
6031
|
-
|
|
6032
|
-
|
|
6033
|
-
const lt = lines[i];
|
|
6034
|
-
if (/^\s*(?:[-+]\s*)?(Array\s*\[|Object\s*\{)/.test(lt)) {
|
|
6035
|
-
startIndex = i;
|
|
6036
|
-
break;
|
|
6037
|
-
}
|
|
6038
|
-
}
|
|
6039
|
-
if (startIndex < 0) {
|
|
6040
|
-
return {};
|
|
6041
|
-
}
|
|
6042
|
-
const expectedParts = [];
|
|
6043
|
-
const receivedParts = [];
|
|
6044
|
-
let opened = false;
|
|
6045
|
-
let expDone = false;
|
|
6046
|
-
let recDone = false;
|
|
6047
|
-
const canParseJsonish = (input) => {
|
|
6048
|
-
const text = normalizeBlock(input).trim();
|
|
6049
|
-
try {
|
|
6050
|
-
import_json5.default.parse(text);
|
|
6051
|
-
return true;
|
|
6052
|
-
} catch {
|
|
6053
|
-
return false;
|
|
6054
|
-
}
|
|
6055
|
-
};
|
|
6056
|
-
for (let i = startIndex; i < lines.length; i += 1) {
|
|
6057
|
-
const lineText = lines[i];
|
|
6058
|
-
const unsigned = lineText.replace(/^\s*[-+]\s?/, "");
|
|
6059
|
-
const isMinus = /^\s*-\s/.test(lineText);
|
|
6060
|
-
const isPlus = /^\s*\+\s/.test(lineText);
|
|
6061
|
-
if (!opened) {
|
|
6062
|
-
const looksLikeStart = /^\s*(Array\s*\[|Object\s*\{)/.test(unsigned);
|
|
6063
|
-
if (!looksLikeStart) {
|
|
6064
|
-
continue;
|
|
6065
|
-
}
|
|
6066
|
-
opened = true;
|
|
6067
|
-
}
|
|
6068
|
-
if (isMinus) {
|
|
6069
|
-
expectedParts.push(unsigned);
|
|
6070
|
-
} else if (isPlus) {
|
|
6071
|
-
receivedParts.push(unsigned);
|
|
6072
|
-
} else {
|
|
6073
|
-
expectedParts.push(unsigned);
|
|
6074
|
-
receivedParts.push(unsigned);
|
|
6075
|
-
}
|
|
6076
|
-
if (!expDone && expectedParts.length > 0) {
|
|
6077
|
-
const expJoined = expectedParts.join("\n");
|
|
6078
|
-
expDone = canParseJsonish(expJoined);
|
|
6079
|
-
}
|
|
6080
|
-
if (!recDone && receivedParts.length > 0) {
|
|
6081
|
-
const recJoined = receivedParts.join("\n");
|
|
6082
|
-
recDone = canParseJsonish(recJoined);
|
|
6083
|
-
}
|
|
6084
|
-
if (opened && expDone && recDone) {
|
|
6085
|
-
break;
|
|
6086
|
-
}
|
|
6087
|
-
}
|
|
6088
|
-
const toJsonLikeString = (joined) => {
|
|
6089
|
-
if (!joined) {
|
|
6090
|
-
return void 0;
|
|
6091
|
-
}
|
|
6092
|
-
const text = normalizeBlock(joined).trim();
|
|
6093
|
-
try {
|
|
6094
|
-
const parsed = import_json5.default.parse(text);
|
|
6095
|
-
return JSON.stringify(parsed, null, 2);
|
|
6096
|
-
} catch {
|
|
6097
|
-
return text;
|
|
6098
|
-
}
|
|
6099
|
-
};
|
|
6100
|
-
const expected = expectedParts.length ? expectedParts.join("\n") : void 0;
|
|
6101
|
-
const received = receivedParts.length ? receivedParts.join("\n") : void 0;
|
|
6102
|
-
const result = {};
|
|
6103
|
-
const expStr = toJsonLikeString(expected);
|
|
6104
|
-
const recStr = toJsonLikeString(received);
|
|
6105
|
-
if (expStr !== void 0) {
|
|
6106
|
-
result.expected = expStr;
|
|
6107
|
-
}
|
|
6108
|
-
if (recStr !== void 0) {
|
|
6109
|
-
result.received = recStr;
|
|
6048
|
+
};
|
|
6049
|
+
var buildPrettyDiffSection = (details, messageLines) => {
|
|
6050
|
+
const payload = extractExpectedReceived(details, messageLines);
|
|
6051
|
+
if (payload.expected === void 0 && payload.received === void 0) {
|
|
6052
|
+
return [];
|
|
6110
6053
|
}
|
|
6111
|
-
|
|
6112
|
-
|
|
6113
|
-
function renderPrettyDiff(payload) {
|
|
6054
|
+
const expectedString = stringifyPrettierish(payload.expected);
|
|
6055
|
+
const receivedString = stringifyPrettierish(payload.received);
|
|
6114
6056
|
const out = [];
|
|
6115
|
-
const
|
|
6116
|
-
|
|
6117
|
-
return out;
|
|
6118
|
-
}
|
|
6119
|
-
const expectedString = stringifyPrettierish(expected);
|
|
6120
|
-
const receivedString = stringifyPrettierish(received);
|
|
6057
|
+
const expectedLenLabel = Array.isArray(payload.expected) ? ansi.dim(` (len ${payload.expected.length})`) : "";
|
|
6058
|
+
out.push(` ${ansi.bold("Expected")}${expectedLenLabel}`);
|
|
6121
6059
|
out.push(
|
|
6122
|
-
|
|
6123
|
-
expected && Array.isArray(expected) ? `(len ${expected.length})` : ""
|
|
6124
|
-
)}`
|
|
6060
|
+
expectedString.split("\n").map((expectedLine) => ` ${Colors.Success(expectedLine)}`).join("\n")
|
|
6125
6061
|
);
|
|
6126
|
-
|
|
6062
|
+
const receivedLenLabel = Array.isArray(payload.received) ? ansi.dim(` (len ${payload.received.length})`) : "";
|
|
6063
|
+
out.push(` ${ansi.bold("Received")}${receivedLenLabel}`);
|
|
6127
6064
|
out.push(
|
|
6128
|
-
|
|
6129
|
-
received && Array.isArray(received) ? `(len ${received.length})` : ""
|
|
6130
|
-
)}`
|
|
6065
|
+
receivedString.split("\n").map((receivedLine) => ` ${Colors.Failure(receivedLine)}`).join("\n")
|
|
6131
6066
|
);
|
|
6132
|
-
|
|
6133
|
-
|
|
6134
|
-
|
|
6135
|
-
|
|
6136
|
-
const
|
|
6137
|
-
|
|
6067
|
+
if (isArrayOfPrimitives(payload.expected) && isArrayOfPrimitives(payload.received)) {
|
|
6068
|
+
const expectedSet = new Set(
|
|
6069
|
+
payload.expected.map((element) => String(element))
|
|
6070
|
+
);
|
|
6071
|
+
const receivedSet = new Set(
|
|
6072
|
+
payload.received.map((element) => String(element))
|
|
6073
|
+
);
|
|
6074
|
+
const missing = Array.from(expectedSet).filter((element) => !receivedSet.has(element));
|
|
6075
|
+
const unexpected = Array.from(receivedSet).filter((element) => !expectedSet.has(element));
|
|
6138
6076
|
const parts = [];
|
|
6139
6077
|
if (missing.length) {
|
|
6140
6078
|
parts.push(
|
|
@@ -6147,91 +6085,217 @@ function renderPrettyDiff(payload) {
|
|
|
6147
6085
|
);
|
|
6148
6086
|
}
|
|
6149
6087
|
if (parts.length) {
|
|
6150
|
-
out.push(` ${ansi.dim("Difference:")} ${
|
|
6088
|
+
out.push(` ${ansi.dim("Difference:")} ${Colors.Failure(parts.join(ansi.dim(" | ")))}`);
|
|
6151
6089
|
}
|
|
6152
6090
|
}
|
|
6153
6091
|
out.push("");
|
|
6154
6092
|
return out;
|
|
6155
|
-
}
|
|
6156
|
-
|
|
6157
|
-
const
|
|
6158
|
-
|
|
6159
|
-
|
|
6093
|
+
};
|
|
6094
|
+
var buildMessageSection = (messageLines, details, _ctx, opts) => {
|
|
6095
|
+
const out = [];
|
|
6096
|
+
const lines = messageLines.map((lineText) => stripAnsiSimple(lineText));
|
|
6097
|
+
const hintIdx = lines.findIndex(
|
|
6098
|
+
(candidate) => /expect\(.+?\)\.(?:to|not\.)/.test(candidate) || /\b(?:AssertionError|Error):/.test(candidate)
|
|
6099
|
+
);
|
|
6100
|
+
const acc = [];
|
|
6101
|
+
if (hintIdx >= 0) {
|
|
6102
|
+
acc.push(lines[hintIdx]);
|
|
6160
6103
|
}
|
|
6161
|
-
const
|
|
6162
|
-
(
|
|
6104
|
+
const pushBlock = (start) => {
|
|
6105
|
+
acc.push(lines[start]);
|
|
6106
|
+
for (let i = start + 1; i < lines.length; i += 1) {
|
|
6107
|
+
const candidate = lines[i];
|
|
6108
|
+
if (!candidate.trim() || isStackLine(candidate)) {
|
|
6109
|
+
break;
|
|
6110
|
+
}
|
|
6111
|
+
acc.push(candidate);
|
|
6112
|
+
}
|
|
6113
|
+
};
|
|
6114
|
+
const expectedIdx = lines.findIndex((lineText) => /^\s*Expected:/.test(lineText));
|
|
6115
|
+
const receivedIdx = lines.findIndex((lineText) => /^\s*Received:/.test(lineText));
|
|
6116
|
+
const diffIdx = lines.findIndex(
|
|
6117
|
+
(lineText) => /^\s*(?:- Expected|\+ Received|Difference:)/.test(lineText)
|
|
6163
6118
|
);
|
|
6164
|
-
if (
|
|
6165
|
-
|
|
6119
|
+
if (expectedIdx >= 0) {
|
|
6120
|
+
pushBlock(expectedIdx);
|
|
6166
6121
|
}
|
|
6167
|
-
|
|
6168
|
-
|
|
6169
|
-
return [firstNonEmpty];
|
|
6122
|
+
if (receivedIdx >= 0) {
|
|
6123
|
+
pushBlock(receivedIdx);
|
|
6170
6124
|
}
|
|
6171
|
-
|
|
6172
|
-
|
|
6173
|
-
function colorUnifiedDiffLine(simple) {
|
|
6174
|
-
if (/^\s*-\s/.test(simple)) {
|
|
6175
|
-
return colorTokens.fail(simple);
|
|
6125
|
+
if (diffIdx >= 0) {
|
|
6126
|
+
pushBlock(diffIdx);
|
|
6176
6127
|
}
|
|
6177
|
-
|
|
6178
|
-
|
|
6128
|
+
const filtered = opts?.suppressDiff ? acc.filter((raw) => {
|
|
6129
|
+
const simple = stripAnsiSimple(raw);
|
|
6130
|
+
return !/^\s*(Expected:|Received:|Difference:)\b/.test(simple) && !/^\s*[-+]\s/.test(simple) && !/^\s*(Array\s*\[|Object\s*\{)/.test(simple);
|
|
6131
|
+
}) : acc;
|
|
6132
|
+
const hasOnlyBareError = filtered.length === 0 || filtered.length === 1 && /^\s*(?:Error|AssertionError):?\s*$/.test(filtered[0] ?? "");
|
|
6133
|
+
const fallbackLines = [];
|
|
6134
|
+
if (hasOnlyBareError) {
|
|
6135
|
+
const startFrom = hintIdx >= 0 ? hintIdx + 1 : 0;
|
|
6136
|
+
for (let i = startFrom; i < lines.length; i += 1) {
|
|
6137
|
+
const candidate = lines[i];
|
|
6138
|
+
if (!candidate.trim()) {
|
|
6139
|
+
break;
|
|
6140
|
+
}
|
|
6141
|
+
if (isStackLine(candidate)) {
|
|
6142
|
+
break;
|
|
6143
|
+
}
|
|
6144
|
+
fallbackLines.push(candidate);
|
|
6145
|
+
}
|
|
6146
|
+
if (fallbackLines.length === 0 && details && details.messages && details.messages.length) {
|
|
6147
|
+
fallbackLines.push(
|
|
6148
|
+
...details.messages.map((messageText) => stripAnsiSimple(messageText)).filter((messageText) => messageText.trim().length > 0).slice(0, 6)
|
|
6149
|
+
);
|
|
6150
|
+
}
|
|
6179
6151
|
}
|
|
6180
|
-
|
|
6181
|
-
|
|
6182
|
-
|
|
6183
|
-
|
|
6184
|
-
|
|
6185
|
-
|
|
6152
|
+
if (filtered.length > 0) {
|
|
6153
|
+
const label = (() => {
|
|
6154
|
+
const joined = filtered.join(" ");
|
|
6155
|
+
const matchResult = joined.match(/\b(TypeError|ReferenceError|SyntaxError|RangeError|AssertionError)\b/) || joined.match(/\bError\b/);
|
|
6156
|
+
if (matchResult) {
|
|
6157
|
+
const typeName = matchResult[1] ?? "Error";
|
|
6158
|
+
return `${typeName}:`;
|
|
6159
|
+
}
|
|
6160
|
+
return /expect\(.+?\)\.(?:to|not\.)/.test(joined) ? "Assertion:" : "Message:";
|
|
6161
|
+
})();
|
|
6162
|
+
out.push(` ${ansi.bold(label)}`);
|
|
6163
|
+
const body = hasOnlyBareError ? fallbackLines : filtered;
|
|
6164
|
+
for (const lineText of body) {
|
|
6165
|
+
const colored = /^\s*-\s/.test(lineText) ? Colors.Failure(lineText) : /^\s*\+\s/.test(lineText) ? Colors.Success(lineText) : lineText;
|
|
6166
|
+
out.push(` ${ansi.yellow(colored)}`);
|
|
6167
|
+
}
|
|
6168
|
+
if (opts?.stackPreview && opts.stackPreview.length) {
|
|
6169
|
+
for (const frame of opts.stackPreview) {
|
|
6170
|
+
out.push(frame);
|
|
6171
|
+
}
|
|
6172
|
+
}
|
|
6173
|
+
out.push("");
|
|
6174
|
+
}
|
|
6175
|
+
return out;
|
|
6186
6176
|
};
|
|
6187
|
-
var
|
|
6188
|
-
const
|
|
6189
|
-
const
|
|
6190
|
-
if (
|
|
6191
|
-
|
|
6192
|
-
return lines;
|
|
6177
|
+
var linesFromDetails = (details) => {
|
|
6178
|
+
const stacks = [];
|
|
6179
|
+
const messages = [];
|
|
6180
|
+
if (!details) {
|
|
6181
|
+
return { stacks, messages };
|
|
6193
6182
|
}
|
|
6194
|
-
|
|
6195
|
-
|
|
6183
|
+
const pushMaybe = (value, bucket) => {
|
|
6184
|
+
if (typeof value === "string" && value.trim()) {
|
|
6185
|
+
bucket.push(...value.split(/\r?\n/));
|
|
6186
|
+
}
|
|
6187
|
+
};
|
|
6188
|
+
const visitDeep = (value, depth) => {
|
|
6189
|
+
if (depth > 3 || value == null) {
|
|
6190
|
+
return;
|
|
6191
|
+
}
|
|
6192
|
+
if (typeof value === "string") {
|
|
6193
|
+
pushMaybe(value, messages);
|
|
6194
|
+
return;
|
|
6195
|
+
}
|
|
6196
|
+
if (typeof value !== "object") {
|
|
6197
|
+
return;
|
|
6198
|
+
}
|
|
6199
|
+
const obj = value;
|
|
6200
|
+
if (typeof obj.message === "string") {
|
|
6201
|
+
pushMaybe(obj.message, messages);
|
|
6202
|
+
}
|
|
6203
|
+
if (typeof obj.stack === "string") {
|
|
6204
|
+
pushMaybe(obj.stack, stacks);
|
|
6205
|
+
}
|
|
6206
|
+
if (typeof obj.expected === "string") {
|
|
6207
|
+
pushMaybe(obj.expected, messages);
|
|
6208
|
+
}
|
|
6209
|
+
if (typeof obj.received === "string") {
|
|
6210
|
+
pushMaybe(obj.received, messages);
|
|
6211
|
+
}
|
|
6212
|
+
const arrays = ["errors", "causes", "aggregatedErrors"];
|
|
6213
|
+
for (const key of arrays) {
|
|
6214
|
+
const arr = obj[key];
|
|
6215
|
+
if (Array.isArray(arr)) {
|
|
6216
|
+
for (const element of arr) {
|
|
6217
|
+
visitDeep(element, depth + 1);
|
|
6218
|
+
}
|
|
6219
|
+
}
|
|
6220
|
+
}
|
|
6221
|
+
const nestedCandidates = ["error", "cause", "matcherResult"];
|
|
6222
|
+
for (const key of nestedCandidates) {
|
|
6223
|
+
if (obj[key] && typeof obj[key] === "object") {
|
|
6224
|
+
visitDeep(obj[key], depth + 1);
|
|
6225
|
+
}
|
|
6226
|
+
}
|
|
6227
|
+
};
|
|
6228
|
+
for (const detail of details) {
|
|
6229
|
+
if (typeof detail === "string") {
|
|
6230
|
+
if (/\s+at\s.+\(.+:\d+:\d+\)/.test(detail)) {
|
|
6231
|
+
pushMaybe(detail, stacks);
|
|
6232
|
+
} else {
|
|
6233
|
+
pushMaybe(detail, messages);
|
|
6234
|
+
}
|
|
6235
|
+
} else if (isObjectRecord(detail)) {
|
|
6236
|
+
pushMaybe(detail.stack, stacks);
|
|
6237
|
+
pushMaybe(detail.message, messages);
|
|
6238
|
+
const err = isObjectRecord(detail.error) ? detail.error : void 0;
|
|
6239
|
+
if (err) {
|
|
6240
|
+
pushMaybe(err.stack, stacks);
|
|
6241
|
+
pushMaybe(err.message, messages);
|
|
6242
|
+
}
|
|
6243
|
+
const matcher = isObjectRecord(detail.matcherResult) ? detail.matcherResult : void 0;
|
|
6244
|
+
if (matcher) {
|
|
6245
|
+
pushMaybe(matcher.stack, stacks);
|
|
6246
|
+
pushMaybe(matcher.message, messages);
|
|
6247
|
+
pushMaybe(matcher.expected, messages);
|
|
6248
|
+
pushMaybe(matcher.received, messages);
|
|
6249
|
+
}
|
|
6250
|
+
if (messages.length === 0 && stacks.length === 0) {
|
|
6251
|
+
visitDeep(detail, 0);
|
|
6252
|
+
}
|
|
6253
|
+
}
|
|
6196
6254
|
}
|
|
6197
|
-
return
|
|
6255
|
+
return { stacks, messages };
|
|
6198
6256
|
};
|
|
6199
|
-
var
|
|
6200
|
-
var buildMessageSection = (messageLines, details, ctx, opts) => {
|
|
6257
|
+
var buildStackSection = (mergedForStack, ctx, fallbackLoc) => {
|
|
6201
6258
|
const out = [];
|
|
6202
|
-
|
|
6203
|
-
|
|
6204
|
-
|
|
6205
|
-
return
|
|
6206
|
-
}
|
|
6207
|
-
|
|
6208
|
-
|
|
6209
|
-
|
|
6210
|
-
|
|
6211
|
-
|
|
6259
|
+
out.push(ansi.dim(" Stack:"));
|
|
6260
|
+
if (!ctx.showStacks) {
|
|
6261
|
+
out.push(` ${ansi.dim("(hidden by TEST_CLI_STACKS=)")}`, "");
|
|
6262
|
+
return out;
|
|
6263
|
+
}
|
|
6264
|
+
const onlyStack = mergedForStack.filter(
|
|
6265
|
+
(lineText) => isStackLine(stripAnsiSimple(lineText))
|
|
6266
|
+
);
|
|
6267
|
+
const tail = onlyStack.slice(-4);
|
|
6268
|
+
if (tail.length) {
|
|
6269
|
+
for (const frameLine of tail) {
|
|
6270
|
+
out.push(` ${colorStackLine(String(frameLine), ctx.projectHint)}`);
|
|
6212
6271
|
}
|
|
6213
|
-
|
|
6214
|
-
|
|
6215
|
-
|
|
6216
|
-
}
|
|
6272
|
+
const loc = deepestProjectLoc(mergedForStack, ctx.projectHint);
|
|
6273
|
+
if (loc) {
|
|
6274
|
+
const href = preferredEditorHref(loc.file, loc.line, ctx.editorCmd);
|
|
6275
|
+
out.push(` ${ansi.dim("at")} ${osc8(`${loc.file.split("/").pop()}:${loc.line}`, href)}`);
|
|
6217
6276
|
}
|
|
6218
6277
|
out.push("");
|
|
6278
|
+
return out;
|
|
6219
6279
|
}
|
|
6280
|
+
if (fallbackLoc) {
|
|
6281
|
+
out.push(
|
|
6282
|
+
` ${colorStackLine(`${fallbackLoc.file}:${fallbackLoc.line}:0`, ctx.projectHint)}`,
|
|
6283
|
+
""
|
|
6284
|
+
);
|
|
6285
|
+
return out;
|
|
6286
|
+
}
|
|
6287
|
+
out.push(` ${ansi.dim("(no stack provided)")}`, "");
|
|
6220
6288
|
return out;
|
|
6221
6289
|
};
|
|
6222
|
-
|
|
6223
|
-
|
|
6224
|
-
}
|
|
6290
|
+
var MAX_CONSOLE_ERRORS_TO_SHOW = 3;
|
|
6291
|
+
var isConsoleEntry = (candidate) => typeof candidate === "object" && candidate !== null;
|
|
6225
6292
|
var buildConsoleSection = (maybeConsole) => {
|
|
6226
6293
|
const out = [];
|
|
6227
6294
|
if (!Array.isArray(maybeConsole)) {
|
|
6228
6295
|
return out;
|
|
6229
6296
|
}
|
|
6230
6297
|
const entries = maybeConsole.filter(isConsoleEntry);
|
|
6231
|
-
const errorsOnly = entries.filter((entry) =>
|
|
6232
|
-
const val = entry?.type;
|
|
6233
|
-
return String(val ?? "").toLowerCase() === "error";
|
|
6234
|
-
});
|
|
6298
|
+
const errorsOnly = entries.filter((entry) => String(entry?.type ?? "").toLowerCase() === "error");
|
|
6235
6299
|
const scored = errorsOnly.map((entry) => {
|
|
6236
6300
|
const raw = entry?.message;
|
|
6237
6301
|
const msg = Array.isArray(raw) ? raw.map(String).join(" ") : typeof raw === "string" ? raw : String(raw ?? "");
|
|
@@ -6246,199 +6310,437 @@ var buildConsoleSection = (maybeConsole) => {
|
|
|
6246
6310
|
}
|
|
6247
6311
|
return out;
|
|
6248
6312
|
};
|
|
6249
|
-
var
|
|
6250
|
-
const
|
|
6251
|
-
|
|
6252
|
-
|
|
6253
|
-
|
|
6254
|
-
return
|
|
6313
|
+
var buildFallbackMessageBlock = (messageLines, details) => {
|
|
6314
|
+
const normalize2 = (arr) => arr.map((lineText) => stripAnsiSimple(lineText)).filter((line) => line.trim().length > 0);
|
|
6315
|
+
const normalized = normalize2(messageLines);
|
|
6316
|
+
const informative = normalized.filter((line) => !/^\s*(?:Error|AssertionError):?\s*$/.test(line));
|
|
6317
|
+
if (informative.length > 0) {
|
|
6318
|
+
return [];
|
|
6255
6319
|
}
|
|
6256
|
-
const
|
|
6257
|
-
|
|
6258
|
-
|
|
6259
|
-
|
|
6260
|
-
|
|
6261
|
-
|
|
6262
|
-
|
|
6320
|
+
const errorIdx = normalized.findIndex(
|
|
6321
|
+
(line) => /(TypeError|ReferenceError|SyntaxError|RangeError|AssertionError|Error):?/.test(line)
|
|
6322
|
+
);
|
|
6323
|
+
const collected = [];
|
|
6324
|
+
if (errorIdx >= 0) {
|
|
6325
|
+
for (let i = errorIdx; i < normalized.length && collected.length < 8; i += 1) {
|
|
6326
|
+
const ln = normalized[i];
|
|
6327
|
+
if (!ln.trim()) {
|
|
6328
|
+
break;
|
|
6329
|
+
}
|
|
6330
|
+
if (isStackLine(ln)) {
|
|
6331
|
+
break;
|
|
6332
|
+
}
|
|
6333
|
+
collected.push(ln);
|
|
6263
6334
|
}
|
|
6264
|
-
out.push("");
|
|
6265
|
-
return out;
|
|
6266
6335
|
}
|
|
6267
|
-
|
|
6268
|
-
|
|
6269
|
-
|
|
6270
|
-
|
|
6271
|
-
);
|
|
6272
|
-
return out;
|
|
6336
|
+
const fromDetails = collected.length > 0 ? [] : normalize2(details.messages).slice(0, 6);
|
|
6337
|
+
const linesToShow = collected.length > 0 ? collected : fromDetails;
|
|
6338
|
+
if (linesToShow.length === 0) {
|
|
6339
|
+
return [];
|
|
6273
6340
|
}
|
|
6274
|
-
out
|
|
6341
|
+
const out = [];
|
|
6342
|
+
out.push(` ${ansi.bold("Message:")}`);
|
|
6343
|
+
for (const lineText of linesToShow) {
|
|
6344
|
+
out.push(` ${ansi.yellow(lineText)}`);
|
|
6345
|
+
}
|
|
6346
|
+
out.push("");
|
|
6275
6347
|
return out;
|
|
6276
6348
|
};
|
|
6277
|
-
var
|
|
6278
|
-
|
|
6279
|
-
|
|
6280
|
-
|
|
6281
|
-
|
|
6282
|
-
|
|
6283
|
-
|
|
6284
|
-
out.push(` ${colorTokens.pass("\u2713")} ${ansi.dim(name)}`);
|
|
6285
|
-
} else if (assertion.status === "todo") {
|
|
6286
|
-
out.push(` ${colorTokens.todo("\u2610")} ${ansi.dim(name)} ${colorTokens.todo("[todo]")}`);
|
|
6287
|
-
} else if (assertion.status === "pending") {
|
|
6288
|
-
out.push(` ${colorTokens.skip("\u2193")} ${ansi.dim(name)} ${colorTokens.skip("[skipped]")}`);
|
|
6289
|
-
} else {
|
|
6290
|
-
out.push(` ${colorTokens.fail("\xD7")} ${ansi.white(name)}`);
|
|
6349
|
+
var buildThrownSection = (details) => {
|
|
6350
|
+
const toLines = (value) => {
|
|
6351
|
+
if (value == null) {
|
|
6352
|
+
return [];
|
|
6353
|
+
}
|
|
6354
|
+
if (typeof value === "string") {
|
|
6355
|
+
return value.split(/\r?\n/);
|
|
6291
6356
|
}
|
|
6357
|
+
try {
|
|
6358
|
+
return JSON.stringify(value, null, 2).split(/\r?\n/);
|
|
6359
|
+
} catch {
|
|
6360
|
+
return [String(value)];
|
|
6361
|
+
}
|
|
6362
|
+
};
|
|
6363
|
+
const candidates = [];
|
|
6364
|
+
for (const d of details) {
|
|
6365
|
+
const obj = d && typeof d === "object" ? d : null;
|
|
6366
|
+
if (obj && obj.error && typeof obj.error === "object") {
|
|
6367
|
+
const err = obj.error;
|
|
6368
|
+
if (typeof err.name === "string") {
|
|
6369
|
+
candidates.push(`name: ${err.name}`);
|
|
6370
|
+
}
|
|
6371
|
+
if (typeof err.message === "string") {
|
|
6372
|
+
candidates.push(`message: ${err.message}`);
|
|
6373
|
+
}
|
|
6374
|
+
if (typeof err.code === "string" || typeof err.code === "number") {
|
|
6375
|
+
candidates.push(`code: ${String(err.code)}`);
|
|
6376
|
+
}
|
|
6377
|
+
if (typeof err.cause === "string") {
|
|
6378
|
+
candidates.push(`cause: ${err.cause}`);
|
|
6379
|
+
}
|
|
6380
|
+
if (err.cause && typeof err.cause === "object") {
|
|
6381
|
+
candidates.push("cause:");
|
|
6382
|
+
candidates.push(...toLines(err.cause));
|
|
6383
|
+
}
|
|
6384
|
+
const rest = { ...err };
|
|
6385
|
+
delete rest.name;
|
|
6386
|
+
delete rest.message;
|
|
6387
|
+
delete rest.code;
|
|
6388
|
+
delete rest.stack;
|
|
6389
|
+
if (Object.keys(rest).length > 0) {
|
|
6390
|
+
candidates.push("details:");
|
|
6391
|
+
candidates.push(...toLines(rest));
|
|
6392
|
+
}
|
|
6393
|
+
}
|
|
6394
|
+
}
|
|
6395
|
+
if (!candidates.length) {
|
|
6396
|
+
return [];
|
|
6397
|
+
}
|
|
6398
|
+
const out = [];
|
|
6399
|
+
out.push(` ${ansi.bold("Thrown:")}`);
|
|
6400
|
+
for (const line of candidates.slice(0, 50)) {
|
|
6401
|
+
out.push(` ${ansi.yellow(line)}`);
|
|
6292
6402
|
}
|
|
6293
6403
|
out.push("");
|
|
6294
6404
|
return out;
|
|
6295
6405
|
};
|
|
6296
|
-
var
|
|
6297
|
-
|
|
6298
|
-
|
|
6299
|
-
|
|
6300
|
-
|
|
6301
|
-
|
|
6302
|
-
|
|
6303
|
-
|
|
6406
|
+
var mkPrettyFns = () => ({
|
|
6407
|
+
drawRule,
|
|
6408
|
+
drawFailLine,
|
|
6409
|
+
renderRunLine,
|
|
6410
|
+
buildPerFileOverview,
|
|
6411
|
+
buildFileBadgeLine,
|
|
6412
|
+
extractBridgePath,
|
|
6413
|
+
buildCodeFrameSection,
|
|
6414
|
+
buildMessageSection,
|
|
6415
|
+
buildPrettyDiffSection,
|
|
6416
|
+
buildStackSection,
|
|
6417
|
+
deepestProjectLoc,
|
|
6418
|
+
findCodeFrameStart,
|
|
6419
|
+
linesFromDetails,
|
|
6420
|
+
buildFallbackMessageBlock,
|
|
6421
|
+
buildThrownSection
|
|
6422
|
+
});
|
|
6423
|
+
|
|
6424
|
+
// src/lib/formatter/render.ts
|
|
6425
|
+
var relPath = (abs, cwd) => abs.replace(/\\/g, "/").replace(`${cwd}/`, "");
|
|
6426
|
+
var renderChunks = (chunks, ctx, fns, opts) => {
|
|
6304
6427
|
const out = [];
|
|
6305
|
-
const seenFailures = /* @__PURE__ */ new Set();
|
|
6306
6428
|
const seenFiles = /* @__PURE__ */ new Set();
|
|
6307
|
-
|
|
6308
|
-
|
|
6309
|
-
|
|
6310
|
-
|
|
6311
|
-
|
|
6312
|
-
|
|
6313
|
-
|
|
6314
|
-
|
|
6315
|
-
const nextIsStart = /^\s*●\s+/.test(scanLine) || /^\s*(PASS|FAIL)\s/.test(scanLine) || /^\s*Test Suites:/.test(scanLine);
|
|
6316
|
-
if (nextIsStart && stripAnsiSimple(lines[scanIndex - 1] ?? "").trim() === "") {
|
|
6317
|
-
break;
|
|
6318
|
-
}
|
|
6319
|
-
block.push(lines[scanIndex]);
|
|
6320
|
-
scanIndex += 1;
|
|
6321
|
-
}
|
|
6322
|
-
const codeFrameStart = block.findIndex(
|
|
6323
|
-
(candidateLine) => /^\s*(>?\s*\d+\s*\|)/.test(stripAnsiSimple(candidateLine))
|
|
6324
|
-
);
|
|
6325
|
-
const location = firstTestLocation(block, projectHint);
|
|
6326
|
-
const rel = location ? location.split(":")[0].replace(/\\/g, "/").replace(`${cwd}/`, "") : "";
|
|
6327
|
-
const key = `${rel}|${title}`;
|
|
6328
|
-
if (seenFailures.has(key)) {
|
|
6329
|
-
lineIndex = scanIndex;
|
|
6429
|
+
const seenFailures = /* @__PURE__ */ new Set();
|
|
6430
|
+
const onlyFailures = Boolean(opts?.onlyFailures);
|
|
6431
|
+
let currentRelFile = null;
|
|
6432
|
+
const escapeRegExp = (text) => text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
6433
|
+
for (const ch of chunks) {
|
|
6434
|
+
if (ch.tag === "PassFail") {
|
|
6435
|
+
const rel = relPath(ch.rel, ctx.cwd);
|
|
6436
|
+
if (seenFiles.has(rel)) {
|
|
6330
6437
|
continue;
|
|
6331
6438
|
}
|
|
6332
|
-
|
|
6333
|
-
|
|
6334
|
-
|
|
6335
|
-
|
|
6336
|
-
const linesBlock = block.map(String);
|
|
6337
|
-
const collapsedForSrc = collapseStacks(linesBlock.slice(0));
|
|
6338
|
-
if (codeFrameStart >= 0) {
|
|
6339
|
-
out.push("");
|
|
6340
|
-
out.push(...renderCodeFrame(linesBlock, codeFrameStart));
|
|
6341
|
-
out.push("");
|
|
6342
|
-
} else if (showStacks) {
|
|
6343
|
-
const deepestIdxForSrc = findLastProjectFrameIndex(collapsedForSrc, projectHint);
|
|
6344
|
-
const locForSrc = deepestIdxForSrc >= 0 ? stackLocation(collapsedForSrc[deepestIdxForSrc]) : null;
|
|
6345
|
-
if (locForSrc) {
|
|
6346
|
-
out.push("");
|
|
6347
|
-
out.push(...renderSourceCodeFrame(locForSrc.file, locForSrc.line));
|
|
6348
|
-
out.push("");
|
|
6349
|
-
}
|
|
6350
|
-
}
|
|
6351
|
-
const payload = extractExpectedReceived(void 0, linesBlock);
|
|
6352
|
-
const hasPretty = payload.expected !== void 0 || payload.received !== void 0;
|
|
6353
|
-
out.push(...renderPrettyDiff(payload));
|
|
6354
|
-
const detailsForMsg = linesFromDetails(void 0);
|
|
6355
|
-
const collapsedForTail = collapseStacks(linesBlock.slice(0));
|
|
6356
|
-
const stackPreview = showStacks ? firstProjectFrames(collapsedForTail, projectHint, 2) : [];
|
|
6357
|
-
out.push(
|
|
6358
|
-
...buildMessageSection(
|
|
6359
|
-
linesBlock,
|
|
6360
|
-
detailsForMsg,
|
|
6361
|
-
{ projectHint, editorCmd: opts?.editorCmd, showStacks },
|
|
6362
|
-
{ suppressDiff: hasPretty, stackPreview }
|
|
6363
|
-
)
|
|
6364
|
-
);
|
|
6365
|
-
if (showStacks && stackPreview.length === 0) {
|
|
6366
|
-
const collapsed = collapseStacks(linesBlock.slice(0));
|
|
6367
|
-
out.push(
|
|
6368
|
-
...buildStackSection(collapsed, {
|
|
6369
|
-
projectHint,
|
|
6370
|
-
editorCmd: opts?.editorCmd,
|
|
6371
|
-
showStacks
|
|
6372
|
-
})
|
|
6373
|
-
);
|
|
6439
|
+
seenFiles.add(rel);
|
|
6440
|
+
currentRelFile = rel;
|
|
6441
|
+
if (!(onlyFailures && ch.badge === "PASS")) {
|
|
6442
|
+
out.push(fns.buildFileBadgeLine(rel, ch.badge === "FAIL" ? 1 : 0));
|
|
6374
6443
|
}
|
|
6375
|
-
out.push(drawFailLine());
|
|
6376
|
-
out.push("");
|
|
6377
|
-
lineIndex = scanIndex;
|
|
6378
6444
|
continue;
|
|
6379
6445
|
}
|
|
6380
|
-
|
|
6381
|
-
|
|
6382
|
-
const
|
|
6383
|
-
const
|
|
6384
|
-
const
|
|
6385
|
-
|
|
6386
|
-
|
|
6387
|
-
|
|
6446
|
+
if (ch.tag === "FailureBlock") {
|
|
6447
|
+
out.push(fns.drawFailLine());
|
|
6448
|
+
const location = firstTestLocation(ch.lines, ctx.projectHint);
|
|
6449
|
+
const rel = location ? relPath(location.split(":")[0] ?? "", ctx.cwd) : "";
|
|
6450
|
+
const headerText = rel ? `${rel} > ${ch.title}` : ch.title;
|
|
6451
|
+
out.push(`${Colors.Failure("\xD7")} ${ansi.white(headerText)}`);
|
|
6452
|
+
const codeStart = fns.findCodeFrameStart(ch.lines);
|
|
6453
|
+
const collapsedForSrc = collapseStacks(ch.lines.slice(0));
|
|
6454
|
+
const deepestLoc = fns.deepestProjectLoc(collapsedForSrc, ctx.projectHint);
|
|
6455
|
+
let effectiveLoc = deepestLoc;
|
|
6456
|
+
if (!effectiveLoc && currentRelFile) {
|
|
6457
|
+
try {
|
|
6458
|
+
const abs = path9.resolve(ctx.cwd, currentRelFile);
|
|
6459
|
+
const source = ctx.readSource(abs);
|
|
6460
|
+
const testName = (() => {
|
|
6461
|
+
const parts = ch.title.split(">");
|
|
6462
|
+
return (parts[parts.length - 1] || ch.title).trim();
|
|
6463
|
+
})();
|
|
6464
|
+
const itRe = new RegExp(
|
|
6465
|
+
String.raw`\b(?:it|test)\s*\(\s*['\"]${escapeRegExp(testName)}['\"]`
|
|
6466
|
+
);
|
|
6467
|
+
let index = source.findIndex((line) => itRe.test(line));
|
|
6468
|
+
if (index < 0) {
|
|
6469
|
+
index = source.findIndex((line) => /\bexpect\s*\(/.test(line));
|
|
6470
|
+
} else {
|
|
6471
|
+
const windowEnd = Math.min(source.length, index + 80);
|
|
6472
|
+
for (let i = index; i < windowEnd; i += 1) {
|
|
6473
|
+
if (/\bexpect\s*\(/.test(source[i])) {
|
|
6474
|
+
index = i;
|
|
6475
|
+
break;
|
|
6476
|
+
}
|
|
6477
|
+
}
|
|
6478
|
+
}
|
|
6479
|
+
if (index >= 0) {
|
|
6480
|
+
effectiveLoc = { file: abs.replace(/\\/g, "/"), line: index + 1 };
|
|
6481
|
+
}
|
|
6482
|
+
} catch {
|
|
6483
|
+
}
|
|
6388
6484
|
}
|
|
6389
|
-
|
|
6390
|
-
|
|
6391
|
-
|
|
6392
|
-
out.push(
|
|
6485
|
+
if (codeStart >= 0) {
|
|
6486
|
+
out.push("", ...fns.buildCodeFrameSection(ch.lines, ctx, effectiveLoc), "");
|
|
6487
|
+
} else {
|
|
6488
|
+
out.push("", ...fns.buildCodeFrameSection(ch.lines, ctx, effectiveLoc), "");
|
|
6489
|
+
}
|
|
6490
|
+
const pretty = fns.buildPrettyDiffSection(void 0, ch.lines);
|
|
6491
|
+
out.push(...pretty);
|
|
6492
|
+
const hasPretty = pretty.length > 0;
|
|
6493
|
+
const details = fns.linesFromDetails(void 0);
|
|
6494
|
+
const minimal = (() => {
|
|
6495
|
+
const plain = ch.lines.map((ln) => stripAnsiSimple(ln));
|
|
6496
|
+
const hint = plain.findIndex(
|
|
6497
|
+
(lineText) => /expect\(.+?\)\.(?:to|not\.)/.test(lineText) || /\bError:?\b/.test(lineText)
|
|
6498
|
+
);
|
|
6499
|
+
const acc = [];
|
|
6500
|
+
const start = hint >= 0 ? hint : 0;
|
|
6501
|
+
for (let i = start; i < plain.length; i += 1) {
|
|
6502
|
+
const ln = plain[i];
|
|
6503
|
+
if (!ln.trim()) {
|
|
6504
|
+
break;
|
|
6505
|
+
}
|
|
6506
|
+
if (isStackLine(ln)) {
|
|
6507
|
+
break;
|
|
6508
|
+
}
|
|
6509
|
+
acc.push(ln);
|
|
6510
|
+
}
|
|
6511
|
+
return acc;
|
|
6512
|
+
})();
|
|
6513
|
+
const collapsedForTail = collapseStacks(ch.lines.slice(0));
|
|
6514
|
+
const stackPreview = ctx.showStacks ? collapsedForTail.filter((ln) => isStackLine(stripAnsiSimple(ln))).filter((ln) => ctx.projectHint.test(stripAnsiSimple(ln))).slice(0, 2).map((ln) => ` ${colorStackLine(String(ln), ctx.projectHint)}`) : [];
|
|
6515
|
+
out.push(
|
|
6516
|
+
...fns.buildMessageSection(minimal.length ? minimal : ch.lines, details, ctx, {
|
|
6517
|
+
suppressDiff: hasPretty,
|
|
6518
|
+
stackPreview
|
|
6519
|
+
})
|
|
6520
|
+
);
|
|
6521
|
+
if (minimal.length === 0 && fns.buildFallbackMessageBlock) {
|
|
6522
|
+
out.push(...fns.buildFallbackMessageBlock(ch.lines, { messages: details.messages }));
|
|
6523
|
+
}
|
|
6524
|
+
const consoleInline = (() => {
|
|
6525
|
+
const plain = ch.lines.map((ln) => stripAnsiSimple(ln));
|
|
6526
|
+
const cand = plain.filter((ln) => /\bconsole\.(error|warn)\s*\(/i.test(ln) || /^\s*Error:/.test(ln)).map((ln) => ln.trim()).filter((ln) => ln.length > 0).sort((a, b) => b.length - a.length).slice(0, 3);
|
|
6527
|
+
return cand;
|
|
6528
|
+
})();
|
|
6529
|
+
if (consoleInline.length > 0) {
|
|
6530
|
+
out.push(ansi.dim(" Console errors:"), ...consoleInline.map((ln) => ` ${ln}`), "");
|
|
6531
|
+
}
|
|
6532
|
+
if (ctx.showStacks && fns.buildStackSection && stackPreview.length === 0) {
|
|
6533
|
+
const collapsed = collapseStacks(ch.lines.slice(0));
|
|
6534
|
+
out.push(...fns.buildStackSection(collapsed, ctx));
|
|
6535
|
+
}
|
|
6536
|
+
out.push(fns.drawFailLine(), "");
|
|
6537
|
+
if (rel) {
|
|
6538
|
+
seenFailures.add(`${rel}|${ch.title}`);
|
|
6393
6539
|
}
|
|
6394
|
-
lineIndex += 1;
|
|
6395
6540
|
continue;
|
|
6396
6541
|
}
|
|
6397
|
-
if (
|
|
6398
|
-
out.push(
|
|
6399
|
-
lineIndex += 1;
|
|
6542
|
+
if (ch.tag === "Summary") {
|
|
6543
|
+
out.push(ch.line);
|
|
6400
6544
|
continue;
|
|
6401
6545
|
}
|
|
6402
|
-
if (
|
|
6403
|
-
if (showStacks) {
|
|
6404
|
-
|
|
6405
|
-
out.push(...kept);
|
|
6546
|
+
if (ch.tag === "Stack") {
|
|
6547
|
+
if (ctx.showStacks) {
|
|
6548
|
+
out.push(ch.line);
|
|
6406
6549
|
}
|
|
6407
|
-
lineIndex += 1;
|
|
6408
6550
|
continue;
|
|
6409
6551
|
}
|
|
6410
|
-
|
|
6411
|
-
|
|
6552
|
+
if (!onlyFailures) {
|
|
6553
|
+
out.push(ch.line);
|
|
6554
|
+
}
|
|
6412
6555
|
}
|
|
6413
|
-
const
|
|
6414
|
-
|
|
6415
|
-
|
|
6416
|
-
|
|
6417
|
-
|
|
6418
|
-
|
|
6419
|
-
|
|
6420
|
-
|
|
6421
|
-
|
|
6422
|
-
|
|
6423
|
-
|
|
6424
|
-
|
|
6425
|
-
|
|
6426
|
-
|
|
6556
|
+
const hadParsed = seenFiles.size > 0 || seenFailures.size > 0 || out.some((lineText) => /^(?:\s*)(PASS|FAIL)\b/.test(stripAnsiSimple(lineText)));
|
|
6557
|
+
return { text: out.join("\n"), hadParsed };
|
|
6558
|
+
};
|
|
6559
|
+
|
|
6560
|
+
// src/lib/formatter/context.ts
|
|
6561
|
+
import * as fs5 from "node:fs";
|
|
6562
|
+
var makeCtx = (opts, showStacks = false) => {
|
|
6563
|
+
const cwd = (opts?.cwd ?? process.cwd()).replace(/\\/g, "/");
|
|
6564
|
+
const width = Math.max(
|
|
6565
|
+
40,
|
|
6566
|
+
process.stdout && process.stdout.columns || 80
|
|
6567
|
+
);
|
|
6568
|
+
const projectHint = new RegExp(
|
|
6569
|
+
`(${cwd.replace(/[.*+?^${}()|[\\]\\\\]/g, "\\$&")})|(/gigworx-node/)`
|
|
6570
|
+
);
|
|
6571
|
+
const readSource2 = (file) => {
|
|
6572
|
+
try {
|
|
6573
|
+
return fs5.readFileSync(file.replace(/\\/g, "/"), "utf8").split(/\r?\n/);
|
|
6574
|
+
} catch {
|
|
6575
|
+
return [];
|
|
6576
|
+
}
|
|
6577
|
+
};
|
|
6578
|
+
return { cwd, width, showStacks, projectHint, editorCmd: opts?.editorCmd, readSource: readSource2 };
|
|
6579
|
+
};
|
|
6580
|
+
|
|
6581
|
+
// src/lib/formatter/bridge.ts
|
|
6582
|
+
var import_json52 = __toESM(require_lib(), 1);
|
|
6583
|
+
import * as fs6 from "node:fs";
|
|
6584
|
+
import * as path10 from "node:path";
|
|
6585
|
+
var colorTokens2 = {
|
|
6586
|
+
pass: Colors.Success,
|
|
6587
|
+
fail: Colors.Failure,
|
|
6588
|
+
skip: Colors.Skip,
|
|
6589
|
+
todo: Colors.Todo,
|
|
6590
|
+
passPill: (text) => BackgroundColors.Success(ansi.white(` ${text} `)),
|
|
6591
|
+
failPill: (text) => BackgroundColors.Failure(ansi.white(` ${text} `))
|
|
6592
|
+
};
|
|
6593
|
+
var by = (keySelector) => (left, right) => keySelector(left) - keySelector(right);
|
|
6594
|
+
var isObject = (candidateValue) => !!candidateValue && typeof candidateValue === "object";
|
|
6595
|
+
var asHttpList = (candidateValue) => Array.isArray(candidateValue) ? candidateValue : [];
|
|
6596
|
+
var summarizeUrl = (method, url, route) => {
|
|
6597
|
+
const base = route || url || "";
|
|
6598
|
+
const qs = url && url.includes("?") ? ` ? ${url.split("?")[1]}` : "";
|
|
6599
|
+
return [method || "", base, qs].filter(Boolean).join(" ").trim();
|
|
6600
|
+
};
|
|
6601
|
+
var stripBridgeEventsFromConsole = (maybeConsole) => {
|
|
6602
|
+
if (!Array.isArray(maybeConsole)) {
|
|
6603
|
+
return maybeConsole;
|
|
6604
|
+
}
|
|
6605
|
+
return maybeConsole.filter((entry) => {
|
|
6606
|
+
try {
|
|
6607
|
+
const raw = Array.isArray(entry.message) ? entry.message.map(String).join(" ") : String(entry.message ?? "");
|
|
6608
|
+
return !raw.includes("[JEST-BRIDGE-EVENT]");
|
|
6609
|
+
} catch {
|
|
6610
|
+
return true;
|
|
6427
6611
|
}
|
|
6612
|
+
});
|
|
6613
|
+
};
|
|
6614
|
+
var extractBridgePath2 = (raw, cwd) => {
|
|
6615
|
+
const matches = Array.from(
|
|
6616
|
+
raw.matchAll(/Test results written to:\s+([^\n\r]+jest-bridge-[^\s'"]+\.json)/g)
|
|
6617
|
+
);
|
|
6618
|
+
if (!matches.length) {
|
|
6619
|
+
return null;
|
|
6428
6620
|
}
|
|
6429
|
-
|
|
6430
|
-
|
|
6431
|
-
|
|
6432
|
-
|
|
6433
|
-
|
|
6621
|
+
const jsonPath = (matches[matches.length - 1][1] ?? "").trim().replace(/^["'`]|["'`]$/g, "");
|
|
6622
|
+
return path10.isAbsolute(jsonPath) ? jsonPath : path10.resolve(cwd, jsonPath).replace(/\\/g, "/");
|
|
6623
|
+
};
|
|
6624
|
+
var isTransportError = (msg) => {
|
|
6625
|
+
const lowercaseMessage = (msg ?? "").toLowerCase();
|
|
6626
|
+
return /\bsocket hang up\b|\beconnreset\b|\betimedout\b|\beconnrefused\b|\bwrite epipe\b/.test(
|
|
6627
|
+
lowercaseMessage
|
|
6628
|
+
);
|
|
6629
|
+
};
|
|
6630
|
+
var envNumber = (name, fallback) => {
|
|
6631
|
+
const parsed = Number(process.env[name]);
|
|
6632
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
|
6633
|
+
};
|
|
6634
|
+
var HEADLAMP_HTTP_WINDOW_MS = () => envNumber("HEADLAMP_HTTP_WINDOW_MS", 3e3);
|
|
6635
|
+
var HEADLAMP_HTTP_STRICT_WINDOW_MS = () => envNumber("HEADLAMP_HTTP_STRICT_WINDOW_MS", 600);
|
|
6636
|
+
var HEADLAMP_HTTP_MIN_SCORE = () => envNumber("HEADLAMP_HTTP_MIN_SCORE", 1200);
|
|
6637
|
+
var HEADLAMP_HTTP_DIFF_LIMIT = () => envNumber("HEADLAMP_HTTP_DIFF_LIMIT", 6);
|
|
6638
|
+
var HEADLAMP_HTTP_SHOW_MISS = () => process.env.HEADLAMP_HTTP_MISS === "1";
|
|
6639
|
+
var eventsNear = (http, ts, testPath, windowMs = HEADLAMP_HTTP_WINDOW_MS()) => {
|
|
6640
|
+
if (typeof ts !== "number" || !Number.isFinite(ts)) {
|
|
6641
|
+
return [];
|
|
6642
|
+
}
|
|
6643
|
+
return http.filter((e) => {
|
|
6644
|
+
const timeOk = typeof e.timestampMs === "number" && Math.abs(e.timestampMs - ts) <= windowMs;
|
|
6645
|
+
const pathOk = !testPath || e.testPath === testPath;
|
|
6646
|
+
return timeOk && pathOk;
|
|
6647
|
+
});
|
|
6648
|
+
};
|
|
6649
|
+
var parseMethodPathFromTitle = (title) => {
|
|
6650
|
+
if (!title) {
|
|
6651
|
+
return {};
|
|
6652
|
+
}
|
|
6653
|
+
const matchResult = title.match(/\b(GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS)\s+([^\s)]+)/i);
|
|
6654
|
+
return matchResult ? { method: matchResult[1]?.toUpperCase(), path: matchResult[2] } : {};
|
|
6655
|
+
};
|
|
6656
|
+
var isHttpStatusNumber = (statusNumber) => typeof statusNumber === "number" && statusNumber >= 100 && statusNumber <= 599;
|
|
6657
|
+
var hasStatusSemantics = (assertionLike) => {
|
|
6658
|
+
if (!assertionLike) {
|
|
6659
|
+
return false;
|
|
6660
|
+
}
|
|
6661
|
+
if (isHttpStatusNumber(assertionLike.expectedNumber) || isHttpStatusNumber(assertionLike.receivedNumber)) {
|
|
6662
|
+
return true;
|
|
6663
|
+
}
|
|
6664
|
+
const combinedRaw = `${assertionLike.matcher ?? ""} ${assertionLike.message ?? ""}`;
|
|
6665
|
+
const combinedMessage = combinedRaw.toLowerCase();
|
|
6666
|
+
return /\bstatus(code)?\b|\btohaves(tatus|tatuscode)\b/.test(combinedMessage);
|
|
6667
|
+
};
|
|
6668
|
+
var fileSuggestsHttp = (relPath2) => /(?:^|\/)(routes?|api|controllers?|e2e|integration)(?:\/|\.test\.)/i.test(relPath2);
|
|
6669
|
+
var inferHttpNumbersFromText = (lines) => {
|
|
6670
|
+
const text = lines.join("\n");
|
|
6671
|
+
const match = text.match(/Expected:\s*(\d{3})[\s\S]*?Received:\s*(\d{3})/i);
|
|
6672
|
+
if (match) {
|
|
6673
|
+
return { expectedNumber: Number(match[1]), receivedNumber: Number(match[2]) };
|
|
6674
|
+
}
|
|
6675
|
+
return {};
|
|
6676
|
+
};
|
|
6677
|
+
var titleSuggestsHttp = (title) => {
|
|
6678
|
+
const { method, path: parsedPath } = parseMethodPathFromTitle(title);
|
|
6679
|
+
return Boolean(method || parsedPath && parsedPath.startsWith("/"));
|
|
6680
|
+
};
|
|
6681
|
+
var isHttpRelevant = (ctx) => {
|
|
6682
|
+
const assertionCtx = ctx.assertion;
|
|
6683
|
+
return ctx.hasTransportSignal || ctx.httpCountInSameTest > 0 || titleSuggestsHttp(ctx.title) || hasStatusSemantics(assertionCtx) || fileSuggestsHttp(ctx.relPath);
|
|
6684
|
+
};
|
|
6685
|
+
var routeSimilarityScore = (hint, evt) => {
|
|
6686
|
+
if (!hint.path && !hint.method) {
|
|
6687
|
+
return 0;
|
|
6688
|
+
}
|
|
6689
|
+
const methodOk = hint.method && evt.method ? Number(hint.method === evt.method) : 0;
|
|
6690
|
+
const route = evt.route || evt.url || "";
|
|
6691
|
+
if (!route) {
|
|
6692
|
+
return methodOk * 10;
|
|
6693
|
+
}
|
|
6694
|
+
if (hint.path && route === hint.path) {
|
|
6695
|
+
return 500 + methodOk * 50;
|
|
6696
|
+
}
|
|
6697
|
+
if (hint.path && route.endsWith(hint.path)) {
|
|
6698
|
+
return 300 + methodOk * 50;
|
|
6699
|
+
}
|
|
6700
|
+
if (hint.path && route.includes(hint.path)) {
|
|
6701
|
+
return 200 + methodOk * 50;
|
|
6434
6702
|
}
|
|
6435
|
-
return
|
|
6703
|
+
return methodOk * 10;
|
|
6436
6704
|
};
|
|
6437
|
-
var
|
|
6438
|
-
const
|
|
6439
|
-
|
|
6705
|
+
var scoreHttpForAssertion = (assertion, titleHint) => (candidateEvent) => {
|
|
6706
|
+
const tsA = assertion.timestampMs;
|
|
6707
|
+
const tsH = candidateEvent.timestampMs;
|
|
6708
|
+
const window = isTransportError(assertion.message) ? HEADLAMP_HTTP_STRICT_WINDOW_MS() : HEADLAMP_HTTP_WINDOW_MS();
|
|
6709
|
+
const timeScore = typeof tsA === "number" && typeof tsH === "number" ? Math.max(0, window - Math.abs(tsA - tsH)) : 0;
|
|
6710
|
+
const statusScore = typeof assertion.receivedNumber === "number" && candidateEvent.statusCode === assertion.receivedNumber ? 1500 : typeof assertion.expectedNumber === "number" && candidateEvent.statusCode === assertion.expectedNumber ? 1200 : (candidateEvent.statusCode ?? 0) >= 400 ? 800 : 0;
|
|
6711
|
+
const routeScore = routeSimilarityScore(titleHint, candidateEvent);
|
|
6712
|
+
const specificity = candidateEvent.route ? 80 : candidateEvent.url ? 40 : 0;
|
|
6713
|
+
return timeScore + statusScore + routeScore + specificity;
|
|
6714
|
+
};
|
|
6715
|
+
var pickRelevantHttp = (assertion, http, ctx) => {
|
|
6716
|
+
const hint = parseMethodPathFromTitle(ctx.title);
|
|
6717
|
+
const nameMatches = (leftName, rightName) => !!leftName && !!rightName && (leftName === rightName || leftName.includes(rightName) || rightName.includes(leftName));
|
|
6718
|
+
const sameTest = (leftCtx, rightCtx) => !!leftCtx && !!rightCtx && leftCtx.testPath === rightCtx.testPath && nameMatches(leftCtx.currentTestName, rightCtx.currentTestName);
|
|
6719
|
+
const strictPool = http.filter(
|
|
6720
|
+
(httpEvent) => sameTest(assertion, httpEvent) || sameTest(ctx, httpEvent)
|
|
6721
|
+
);
|
|
6722
|
+
const windowMs = isTransportError(assertion.message) ? HEADLAMP_HTTP_STRICT_WINDOW_MS() : HEADLAMP_HTTP_WINDOW_MS();
|
|
6723
|
+
let pool = strictPool;
|
|
6724
|
+
if (!pool.length) {
|
|
6725
|
+
pool = http.filter(
|
|
6726
|
+
(e) => e.testPath === ctx.testPath && typeof assertion.timestampMs === "number" && typeof e.timestampMs === "number" && Math.abs(e.timestampMs - assertion.timestampMs) <= windowMs
|
|
6727
|
+
);
|
|
6728
|
+
}
|
|
6729
|
+
if (!pool.length) {
|
|
6730
|
+
pool = http.filter(
|
|
6731
|
+
(e) => typeof assertion.timestampMs === "number" && typeof e.timestampMs === "number" && Math.abs(e.timestampMs - assertion.timestampMs) <= windowMs
|
|
6732
|
+
);
|
|
6733
|
+
}
|
|
6734
|
+
if (!pool.length) {
|
|
6735
|
+
return void 0;
|
|
6736
|
+
}
|
|
6737
|
+
const scored = pool.map((httpEvent) => ({ h: httpEvent, s: scoreHttpForAssertion(assertion, hint)(httpEvent) })).sort((leftScore, rightScore) => rightScore.s - leftScore.s);
|
|
6738
|
+
const [best] = scored;
|
|
6739
|
+
const threshold = isTransportError(assertion.message) ? Math.max(HEADLAMP_HTTP_MIN_SCORE(), 1400) : HEADLAMP_HTTP_MIN_SCORE();
|
|
6740
|
+
return best && best.s >= threshold ? best.h : void 0;
|
|
6440
6741
|
};
|
|
6441
|
-
|
|
6742
|
+
var isBridgeJSONLike = (candidateValue) => !!candidateValue && typeof candidateValue === "object" && "aggregated" in candidateValue;
|
|
6743
|
+
var coerceJestJsonToBridge = (raw) => {
|
|
6442
6744
|
if (isBridgeJSONLike(raw)) {
|
|
6443
6745
|
return raw;
|
|
6444
6746
|
}
|
|
@@ -6448,18 +6750,21 @@ function coerceJestJsonToBridge(raw) {
|
|
|
6448
6750
|
}
|
|
6449
6751
|
return {
|
|
6450
6752
|
startTime: Number(j.startTime ?? Date.now()),
|
|
6451
|
-
testResults: j.testResults.map((
|
|
6452
|
-
testFilePath:
|
|
6453
|
-
status:
|
|
6454
|
-
failureMessage:
|
|
6455
|
-
failureDetails:
|
|
6456
|
-
|
|
6753
|
+
testResults: j.testResults.map((tr) => ({
|
|
6754
|
+
testFilePath: tr.testFilePath || tr.name || "",
|
|
6755
|
+
status: tr.status,
|
|
6756
|
+
failureMessage: tr.failureMessage || "",
|
|
6757
|
+
failureDetails: tr.failureDetails ?? [],
|
|
6758
|
+
testExecError: tr.testExecError ?? null,
|
|
6759
|
+
console: tr.console ?? null,
|
|
6760
|
+
testResults: (tr.assertionResults || []).map((assertion) => ({
|
|
6457
6761
|
title: assertion.title,
|
|
6458
6762
|
fullName: assertion.fullName || [...assertion.ancestorTitles || [], assertion.title].join(" "),
|
|
6459
6763
|
status: assertion.status,
|
|
6460
6764
|
duration: assertion.duration || 0,
|
|
6461
6765
|
location: assertion.location ?? null,
|
|
6462
|
-
failureMessages: assertion.failureMessages || []
|
|
6766
|
+
failureMessages: assertion.failureMessages || [],
|
|
6767
|
+
failureDetails: assertion.failureDetails || []
|
|
6463
6768
|
}))
|
|
6464
6769
|
})),
|
|
6465
6770
|
aggregated: {
|
|
@@ -6475,21 +6780,20 @@ function coerceJestJsonToBridge(raw) {
|
|
|
6475
6780
|
success: j.success
|
|
6476
6781
|
}
|
|
6477
6782
|
};
|
|
6478
|
-
}
|
|
6479
|
-
var vitestFooter = (agg,
|
|
6783
|
+
};
|
|
6784
|
+
var vitestFooter = (agg, durationMs) => {
|
|
6480
6785
|
const files = [
|
|
6481
|
-
agg.numFailedTestSuites ?
|
|
6482
|
-
agg.numPassedTestSuites ?
|
|
6483
|
-
agg.numPendingTests ?
|
|
6786
|
+
agg.numFailedTestSuites ? colorTokens2.fail(`${agg.numFailedTestSuites} failed`) : "",
|
|
6787
|
+
agg.numPassedTestSuites ? colorTokens2.pass(`${agg.numPassedTestSuites} passed`) : "",
|
|
6788
|
+
agg.numPendingTests ? colorTokens2.skip(`${agg.numPendingTests} skipped`) : ""
|
|
6484
6789
|
].filter(Boolean).join(ansi.dim(" | "));
|
|
6485
6790
|
const tests = [
|
|
6486
|
-
agg.numFailedTests ?
|
|
6487
|
-
agg.numPassedTests ?
|
|
6488
|
-
agg.numPendingTests ?
|
|
6489
|
-
agg.numTodoTests ?
|
|
6791
|
+
agg.numFailedTests ? colorTokens2.fail(`${agg.numFailedTests} failed`) : "",
|
|
6792
|
+
agg.numPassedTests ? colorTokens2.pass(`${agg.numPassedTests} passed`) : "",
|
|
6793
|
+
agg.numPendingTests ? colorTokens2.skip(`${agg.numPendingTests} skipped`) : "",
|
|
6794
|
+
agg.numTodoTests ? colorTokens2.todo(`${agg.numTodoTests} todo`) : ""
|
|
6490
6795
|
].filter(Boolean).join(ansi.dim(" | "));
|
|
6491
|
-
const
|
|
6492
|
-
const time = durMs != null ? `${Math.max(0, Math.round(durMs))}ms` : "";
|
|
6796
|
+
const time = durationMs != null ? `${Math.max(0, Math.round(durationMs))}ms` : typeof agg.runTimeMs === "number" ? `${Math.max(0, Math.round(agg.runTimeMs))}ms` : "";
|
|
6493
6797
|
const thread = ansi.dim("(in thread 0ms, 0.00%)");
|
|
6494
6798
|
return [
|
|
6495
6799
|
`${ansi.bold("Test Files")} ${files} ${ansi.dim(`(${agg.numTotalTestSuites})`)}`,
|
|
@@ -6497,20 +6801,14 @@ var vitestFooter = (agg, _startedAt, durationMs) => {
|
|
|
6497
6801
|
`${ansi.bold("Time")} ${time} ${thread}`
|
|
6498
6802
|
].join("\n");
|
|
6499
6803
|
};
|
|
6500
|
-
|
|
6501
|
-
const cwd = (opts?.cwd ?? process.cwd()).replace(/\\/g, "/");
|
|
6502
|
-
const projectHint = new RegExp(
|
|
6503
|
-
`(${cwd.replace(/[.*+?^${}()|[\\]\\\\]/g, "\\$&")})|(/gigworx-node/)`
|
|
6504
|
-
);
|
|
6505
|
-
const ctx = { projectHint, editorCmd: opts?.editorCmd, showStacks: true };
|
|
6506
|
-
const onlyFailures = Boolean(opts?.onlyFailures);
|
|
6804
|
+
var renderVitestFromJestJSON = (data, ctx, opts) => {
|
|
6507
6805
|
const out = [];
|
|
6806
|
+
const onlyFailures = Boolean(opts?.onlyFailures);
|
|
6508
6807
|
if (!onlyFailures) {
|
|
6509
|
-
out.push(
|
|
6510
|
-
out.push("");
|
|
6808
|
+
out.push(`${BackgroundColors.Run(ansi.white(" RUN "))} ${ansi.dim(ctx.cwd)}`, "");
|
|
6511
6809
|
}
|
|
6512
6810
|
for (const file of data.testResults) {
|
|
6513
|
-
const rel = file.testFilePath.replace(/\\/g, "/").replace(`${cwd}/`, "");
|
|
6811
|
+
const rel = file.testFilePath.replace(/\\/g, "/").replace(`${ctx.cwd}/`, "");
|
|
6514
6812
|
const failed = file.testResults.filter((assertion) => assertion.status === "failed");
|
|
6515
6813
|
if (!onlyFailures) {
|
|
6516
6814
|
out.push(...buildPerFileOverview(rel, file.testResults));
|
|
@@ -6518,91 +6816,842 @@ function renderVitestFromJestJSON(data, opts) {
|
|
|
6518
6816
|
if (!(onlyFailures && failed.length === 0)) {
|
|
6519
6817
|
out.push(buildFileBadgeLine(rel, failed.length));
|
|
6520
6818
|
}
|
|
6521
|
-
|
|
6819
|
+
let httpSorted = [];
|
|
6820
|
+
let assertionEvents = [];
|
|
6821
|
+
{
|
|
6822
|
+
const parseBridge = (consoleEntries) => {
|
|
6823
|
+
const http = [];
|
|
6824
|
+
const assertions = [];
|
|
6825
|
+
if (!Array.isArray(consoleEntries)) {
|
|
6826
|
+
return { http, assertions };
|
|
6827
|
+
}
|
|
6828
|
+
for (const entry of consoleEntries) {
|
|
6829
|
+
const rec = entry;
|
|
6830
|
+
const rawMsgVal = rec && typeof rec.message !== "undefined" ? rec.message : "";
|
|
6831
|
+
const raw = Array.isArray(rawMsgVal) ? rawMsgVal.map(String).join(" ") : String(rawMsgVal ?? "");
|
|
6832
|
+
if (!raw.includes("[JEST-BRIDGE-EVENT]")) {
|
|
6833
|
+
continue;
|
|
6834
|
+
}
|
|
6835
|
+
const jsonText = raw.split("[JEST-BRIDGE-EVENT]").pop()?.trim() ?? "";
|
|
6836
|
+
try {
|
|
6837
|
+
const evt = import_json52.default.parse(jsonText);
|
|
6838
|
+
const type = evt?.type;
|
|
6839
|
+
if (type === "httpResponse") {
|
|
6840
|
+
const timestampMs = Number(evt.timestampMs ?? Date.now());
|
|
6841
|
+
http.push({
|
|
6842
|
+
kind: "response",
|
|
6843
|
+
timestampMs,
|
|
6844
|
+
method: evt.method,
|
|
6845
|
+
url: evt.url,
|
|
6846
|
+
route: evt.route,
|
|
6847
|
+
statusCode: evt.statusCode,
|
|
6848
|
+
durationMs: evt.durationMs,
|
|
6849
|
+
contentType: evt.contentType,
|
|
6850
|
+
requestId: evt.requestId,
|
|
6851
|
+
json: evt.json,
|
|
6852
|
+
bodyPreview: evt.bodyPreview,
|
|
6853
|
+
testPath: evt.testPath,
|
|
6854
|
+
currentTestName: evt.currentTestName
|
|
6855
|
+
});
|
|
6856
|
+
} else if (type === "httpAbort") {
|
|
6857
|
+
http.push({
|
|
6858
|
+
kind: "abort",
|
|
6859
|
+
timestampMs: Number(evt.timestampMs ?? Date.now()),
|
|
6860
|
+
method: evt.method,
|
|
6861
|
+
url: evt.url,
|
|
6862
|
+
route: evt.route,
|
|
6863
|
+
durationMs: evt.durationMs,
|
|
6864
|
+
testPath: evt.testPath,
|
|
6865
|
+
currentTestName: evt.currentTestName
|
|
6866
|
+
});
|
|
6867
|
+
} else if (type === "httpResponseBatch") {
|
|
6868
|
+
const list = asHttpList(evt?.events);
|
|
6869
|
+
for (const item of list) {
|
|
6870
|
+
const anyItem = item;
|
|
6871
|
+
http.push({
|
|
6872
|
+
timestampMs: Number(anyItem.timestampMs ?? Date.now()),
|
|
6873
|
+
method: anyItem.method,
|
|
6874
|
+
url: anyItem.url,
|
|
6875
|
+
route: anyItem.route,
|
|
6876
|
+
statusCode: anyItem.statusCode,
|
|
6877
|
+
durationMs: anyItem.durationMs,
|
|
6878
|
+
contentType: anyItem.contentType,
|
|
6879
|
+
requestId: anyItem.requestId,
|
|
6880
|
+
json: anyItem.json,
|
|
6881
|
+
bodyPreview: anyItem.bodyPreview,
|
|
6882
|
+
testPath: evt.testPath,
|
|
6883
|
+
currentTestName: evt.currentTestName
|
|
6884
|
+
});
|
|
6885
|
+
}
|
|
6886
|
+
} else if (type === "assertionFailure") {
|
|
6887
|
+
assertions.push({
|
|
6888
|
+
timestampMs: typeof evt.timestampMs === "number" ? evt.timestampMs : void 0,
|
|
6889
|
+
matcher: evt.matcher,
|
|
6890
|
+
expectedNumber: typeof evt.expectedNumber === "number" ? evt.expectedNumber : void 0,
|
|
6891
|
+
receivedNumber: typeof evt.receivedNumber === "number" ? evt.receivedNumber : void 0,
|
|
6892
|
+
message: typeof evt.message === "string" ? evt.message : void 0,
|
|
6893
|
+
stack: typeof evt.stack === "string" ? evt.stack : void 0,
|
|
6894
|
+
testPath: evt.testPath,
|
|
6895
|
+
currentTestName: evt.currentTestName,
|
|
6896
|
+
expectedPreview: typeof evt.expectedPreview === "string" ? evt.expectedPreview : void 0,
|
|
6897
|
+
actualPreview: typeof evt.actualPreview === "string" ? evt.actualPreview : void 0
|
|
6898
|
+
});
|
|
6899
|
+
}
|
|
6900
|
+
} catch {
|
|
6901
|
+
}
|
|
6902
|
+
}
|
|
6903
|
+
return { http, assertions };
|
|
6904
|
+
};
|
|
6905
|
+
const parsed = parseBridge(file.console);
|
|
6906
|
+
httpSorted = [...parsed.http].sort(by((event) => event.timestampMs));
|
|
6907
|
+
assertionEvents = parsed.assertions;
|
|
6908
|
+
}
|
|
6909
|
+
const inSameCtx = (testPath, testName) => httpSorted.filter(
|
|
6910
|
+
(event) => event.testPath === testPath && event.currentTestName === testName
|
|
6911
|
+
);
|
|
6912
|
+
if (file.failureMessage || file.testExecError) {
|
|
6522
6913
|
const lines = file.failureMessage.split(/\r?\n/);
|
|
6523
|
-
const
|
|
6524
|
-
|
|
6525
|
-
|
|
6914
|
+
const combinedDetails = (() => {
|
|
6915
|
+
const base = linesFromDetails(file.failureDetails);
|
|
6916
|
+
const exec = linesFromDetails(
|
|
6917
|
+
Array.isArray(file.testExecError) ? file.testExecError : [file.testExecError]
|
|
6918
|
+
);
|
|
6919
|
+
return {
|
|
6920
|
+
stacks: [...base.stacks, ...exec.stacks],
|
|
6921
|
+
messages: [...base.messages, ...exec.messages]
|
|
6922
|
+
};
|
|
6923
|
+
})();
|
|
6924
|
+
const mergedForStack = collapseStacks([...lines, ...combinedDetails.stacks]);
|
|
6925
|
+
const synthLoc = deepestProjectLoc(mergedForStack, ctx.projectHint);
|
|
6526
6926
|
out.push(...buildCodeFrameSection(lines, ctx, synthLoc));
|
|
6527
|
-
const
|
|
6528
|
-
|
|
6529
|
-
|
|
6530
|
-
const stackPreview = ctx.showStacks ?
|
|
6927
|
+
const payloadPretty = buildPrettyDiffSection(file.failureDetails, lines);
|
|
6928
|
+
out.push(...payloadPretty);
|
|
6929
|
+
const hasPretty = payloadPretty.length > 0;
|
|
6930
|
+
const stackPreview = ctx.showStacks ? mergedForStack.filter((ln) => isStackLine(stripAnsiSimple(ln))).filter((ln) => ctx.projectHint.test(stripAnsiSimple(ln))).slice(0, 2).map((ln) => ` ${colorStackLine(String(ln), ctx.projectHint)}`) : [];
|
|
6531
6931
|
out.push(
|
|
6532
|
-
...buildMessageSection(lines,
|
|
6932
|
+
...buildMessageSection(lines, combinedDetails, ctx, {
|
|
6533
6933
|
suppressDiff: hasPretty,
|
|
6534
6934
|
stackPreview
|
|
6535
6935
|
})
|
|
6536
6936
|
);
|
|
6537
|
-
out.push(...buildConsoleSection(file.console ?? null));
|
|
6937
|
+
out.push(...buildConsoleSection(stripBridgeEventsFromConsole(file.console ?? null)));
|
|
6538
6938
|
if (ctx.showStacks && stackPreview.length === 0) {
|
|
6539
|
-
|
|
6939
|
+
const tail = mergedForStack.filter((ln) => isStackLine(stripAnsiSimple(ln))).slice(-4).map((ln) => ` ${colorStackLine(String(ln), ctx.projectHint)}`);
|
|
6940
|
+
if (tail.length) {
|
|
6941
|
+
out.push(ansi.dim(" Stack:"), ...tail, "");
|
|
6942
|
+
}
|
|
6540
6943
|
}
|
|
6541
6944
|
}
|
|
6542
|
-
for (const
|
|
6945
|
+
for (const assertion of failed) {
|
|
6543
6946
|
out.push(drawFailLine());
|
|
6544
|
-
const header = `${rel} > ${
|
|
6545
|
-
const messagesArray =
|
|
6546
|
-
|
|
6947
|
+
const header = `${rel} > ${assertion.fullName}`;
|
|
6948
|
+
const messagesArray = (() => {
|
|
6949
|
+
if (assertion.failureMessages && assertion.failureMessages.length > 0) {
|
|
6950
|
+
return assertion.failureMessages;
|
|
6951
|
+
}
|
|
6952
|
+
if (file.failureMessage && file.failureMessage.trim().length > 0) {
|
|
6953
|
+
return file.failureMessage.split(/\r?\n/);
|
|
6954
|
+
}
|
|
6955
|
+
const linesFromMatcher = linesFromDetails(
|
|
6956
|
+
assertion.failureDetails || file.failureDetails
|
|
6957
|
+
).messages;
|
|
6958
|
+
if (Array.isArray(linesFromMatcher) && linesFromMatcher.length > 0) {
|
|
6959
|
+
return linesFromMatcher;
|
|
6960
|
+
}
|
|
6961
|
+
return [""];
|
|
6962
|
+
})();
|
|
6963
|
+
const details = linesFromDetails(assertion.failureDetails || file.failureDetails);
|
|
6964
|
+
const matcherMsg = (() => {
|
|
6965
|
+
try {
|
|
6966
|
+
const arr = assertion.failureDetails || file.failureDetails;
|
|
6967
|
+
if (!arr) {
|
|
6968
|
+
return [];
|
|
6969
|
+
}
|
|
6970
|
+
for (const detailEntry of arr) {
|
|
6971
|
+
const obj = detailEntry && typeof detailEntry === "object" ? detailEntry : null;
|
|
6972
|
+
const mr = obj && obj.matcherResult && typeof obj.matcherResult === "object" ? obj.matcherResult : null;
|
|
6973
|
+
if (mr && typeof mr.message === "string" && mr.message.trim()) {
|
|
6974
|
+
const name = typeof mr.matcherName === "string" ? mr.matcherName : "";
|
|
6975
|
+
const matcherHeader = name ? ` ${ansi.bold("Matcher:")} ${ansi.yellow(name)}` : "";
|
|
6976
|
+
const bodyHeader = ` ${ansi.bold("Message:")}`;
|
|
6977
|
+
const body = String(mr.message).split(/\r?\n/).slice(0, 6).map((ln) => ` ${ansi.yellow(ln)}`);
|
|
6978
|
+
return [matcherHeader, bodyHeader, ...body, ""].filter(Boolean);
|
|
6979
|
+
}
|
|
6980
|
+
}
|
|
6981
|
+
} catch {
|
|
6982
|
+
}
|
|
6983
|
+
return [];
|
|
6984
|
+
})();
|
|
6547
6985
|
const mergedForStack = collapseStacks([...messagesArray, ...details.stacks]);
|
|
6548
|
-
const deepestLoc = deepestProjectLoc(mergedForStack, projectHint);
|
|
6549
|
-
const locLink = deepestLoc
|
|
6550
|
-
const href = preferredEditorHref(deepestLoc.file, deepestLoc.line,
|
|
6551
|
-
const base = `${
|
|
6986
|
+
const deepestLoc = deepestProjectLoc(mergedForStack, ctx.projectHint);
|
|
6987
|
+
const locLink = deepestLoc ? (() => {
|
|
6988
|
+
const href = preferredEditorHref(deepestLoc.file, deepestLoc.line, ctx.editorCmd);
|
|
6989
|
+
const base = `${deepestLoc.file.split("/").pop()}:${deepestLoc.line}`;
|
|
6552
6990
|
return osc8(base, href);
|
|
6553
|
-
})();
|
|
6991
|
+
})() : void 0;
|
|
6992
|
+
const bullet = (text) => `${Colors.Failure("\xD7")} ${ansi.white(text)}`;
|
|
6554
6993
|
const headerLine = `${ansi.white(header)}${locLink ? ` ${ansi.dim(`(${locLink})`)}` : ""}`;
|
|
6555
|
-
const bullet = (text) => `${colorTokens.fail("\xD7")} ${ansi.white(text)}`;
|
|
6556
6994
|
out.push(bullet(headerLine));
|
|
6557
6995
|
const msgLines = messagesArray.join("\n").split("\n");
|
|
6558
|
-
const assertFallback = deepestLoc ||
|
|
6559
|
-
|
|
6560
|
-
|
|
6561
|
-
|
|
6562
|
-
|
|
6563
|
-
|
|
6564
|
-
|
|
6565
|
-
|
|
6566
|
-
const stackPreview = ctx.showStacks ?
|
|
6996
|
+
const assertFallback = deepestLoc || assertion.location && { file: file.testFilePath, line: assertion.location.line };
|
|
6997
|
+
out.push("", ...buildCodeFrameSection(msgLines, ctx, assertFallback || void 0), "");
|
|
6998
|
+
const pretty = buildPrettyDiffSection(
|
|
6999
|
+
assertion.failureDetails || file.failureDetails,
|
|
7000
|
+
msgLines
|
|
7001
|
+
);
|
|
7002
|
+
out.push(...pretty);
|
|
7003
|
+
const hasPretty = pretty.length > 0;
|
|
7004
|
+
const stackPreview = ctx.showStacks ? mergedForStack.filter((ln) => isStackLine(stripAnsiSimple(ln))).filter((ln) => ctx.projectHint.test(stripAnsiSimple(ln))).slice(0, 2).map((ln) => ` ${colorStackLine(String(ln), ctx.projectHint)}`) : [];
|
|
7005
|
+
if (matcherMsg.length) {
|
|
7006
|
+
out.push(...matcherMsg);
|
|
7007
|
+
}
|
|
6567
7008
|
out.push(
|
|
6568
7009
|
...buildMessageSection(msgLines, details, ctx, { suppressDiff: hasPretty, stackPreview })
|
|
6569
7010
|
);
|
|
6570
|
-
|
|
6571
|
-
|
|
6572
|
-
|
|
6573
|
-
|
|
6574
|
-
|
|
6575
|
-
|
|
6576
|
-
|
|
7011
|
+
{
|
|
7012
|
+
const HEADLAMP_HTTP_DIFF_LIMIT_LOCAL = () => HEADLAMP_HTTP_DIFF_LIMIT();
|
|
7013
|
+
const safeParseJSON = (text) => {
|
|
7014
|
+
try {
|
|
7015
|
+
return text ? import_json52.default.parse(text) : void 0;
|
|
7016
|
+
} catch {
|
|
7017
|
+
return void 0;
|
|
7018
|
+
}
|
|
7019
|
+
};
|
|
7020
|
+
const jsonDiff = (expected, actual, limit = HEADLAMP_HTTP_DIFF_LIMIT_LOCAL()) => {
|
|
7021
|
+
const outChanges = [];
|
|
7022
|
+
const queue = [];
|
|
7023
|
+
queue.push({
|
|
7024
|
+
pathSoFar: "$",
|
|
7025
|
+
expectedValue: expected,
|
|
7026
|
+
actualValue: actual
|
|
7027
|
+
});
|
|
7028
|
+
while (queue.length && outChanges.length < limit) {
|
|
7029
|
+
const { pathSoFar, expectedValue, actualValue } = queue.shift();
|
|
7030
|
+
const expectedIsObj = expectedValue && typeof expectedValue === "object";
|
|
7031
|
+
const actualIsObj = actualValue && typeof actualValue === "object";
|
|
7032
|
+
if (!expectedIsObj && !actualIsObj) {
|
|
7033
|
+
if (JSON.stringify(expectedValue) !== JSON.stringify(actualValue)) {
|
|
7034
|
+
outChanges.push({
|
|
7035
|
+
kind: "changed",
|
|
7036
|
+
path: pathSoFar,
|
|
7037
|
+
preview: `${String(expectedValue)} \u2192 ${String(actualValue)}`
|
|
7038
|
+
});
|
|
7039
|
+
}
|
|
7040
|
+
} else if (expectedIsObj && !actualIsObj) {
|
|
7041
|
+
outChanges.push({
|
|
7042
|
+
kind: "changed",
|
|
7043
|
+
path: pathSoFar,
|
|
7044
|
+
preview: "[object] \u2192 primitive"
|
|
7045
|
+
});
|
|
7046
|
+
} else if (!expectedIsObj && actualIsObj) {
|
|
7047
|
+
outChanges.push({
|
|
7048
|
+
kind: "changed",
|
|
7049
|
+
path: pathSoFar,
|
|
7050
|
+
preview: "primitive \u2192 [object]"
|
|
7051
|
+
});
|
|
7052
|
+
} else {
|
|
7053
|
+
const expectedKeys = new Set(Object.keys(expectedValue));
|
|
7054
|
+
const actualKeys = new Set(Object.keys(actualValue));
|
|
7055
|
+
for (const key of expectedKeys) {
|
|
7056
|
+
if (!actualKeys.has(key) && outChanges.length < limit) {
|
|
7057
|
+
outChanges.push({ kind: "removed", path: `${pathSoFar}.${key}` });
|
|
7058
|
+
}
|
|
7059
|
+
}
|
|
7060
|
+
for (const key of actualKeys) {
|
|
7061
|
+
if (!expectedKeys.has(key) && outChanges.length < limit) {
|
|
7062
|
+
outChanges.push({ kind: "added", path: `${pathSoFar}.${key}` });
|
|
7063
|
+
}
|
|
7064
|
+
}
|
|
7065
|
+
for (const key of expectedKeys) {
|
|
7066
|
+
if (actualKeys.has(key) && outChanges.length < limit) {
|
|
7067
|
+
queue.push({
|
|
7068
|
+
pathSoFar: `${pathSoFar}.${key}`,
|
|
7069
|
+
expectedValue: expectedValue[key],
|
|
7070
|
+
actualValue: actualValue[key]
|
|
7071
|
+
});
|
|
7072
|
+
}
|
|
7073
|
+
}
|
|
7074
|
+
}
|
|
7075
|
+
}
|
|
7076
|
+
return outChanges;
|
|
7077
|
+
};
|
|
7078
|
+
const importantMessages = (json) => {
|
|
7079
|
+
const msgs = [];
|
|
7080
|
+
try {
|
|
7081
|
+
const obj = isObject(json) ? json : {};
|
|
7082
|
+
const pushMaybe = (candidate) => {
|
|
7083
|
+
if (typeof candidate === "string" && candidate.trim()) {
|
|
7084
|
+
msgs.push(candidate);
|
|
7085
|
+
}
|
|
7086
|
+
};
|
|
7087
|
+
pushMaybe(obj.displayMessage);
|
|
7088
|
+
pushMaybe(obj.message);
|
|
7089
|
+
if (Array.isArray(obj.errors)) {
|
|
7090
|
+
for (const element of obj.errors) {
|
|
7091
|
+
pushMaybe(isObject(element) ? element.message : void 0);
|
|
7092
|
+
}
|
|
7093
|
+
}
|
|
7094
|
+
if (Array.isArray(obj.data)) {
|
|
7095
|
+
for (const element of obj.data) {
|
|
7096
|
+
pushMaybe(isObject(element) ? element.message : void 0);
|
|
7097
|
+
}
|
|
7098
|
+
}
|
|
7099
|
+
} catch {
|
|
7100
|
+
}
|
|
7101
|
+
return msgs.slice(0, 2);
|
|
7102
|
+
};
|
|
7103
|
+
const nameMatches = (leftName, rightName) => !!leftName && !!rightName && (leftName === rightName || leftName.includes(rightName) || rightName.includes(leftName));
|
|
7104
|
+
const corresponding = assertionEvents.find(
|
|
7105
|
+
(aevt) => aevt.testPath === file.testFilePath && nameMatches(aevt.currentTestName, assertion.title)
|
|
7106
|
+
) ?? assertion;
|
|
7107
|
+
const perTestSlice = inSameCtx(file.testFilePath, assertion.title);
|
|
7108
|
+
const nearByTime = eventsNear(
|
|
7109
|
+
httpSorted,
|
|
7110
|
+
corresponding?.timestampMs,
|
|
7111
|
+
file.testFilePath
|
|
6577
7112
|
);
|
|
7113
|
+
const hasAbort = perTestSlice.some((event) => event.kind === "abort");
|
|
7114
|
+
const hasTransport = isTransportError(corresponding?.message) || hasAbort;
|
|
7115
|
+
const httpLikely = isHttpRelevant({
|
|
7116
|
+
assertion: corresponding,
|
|
7117
|
+
title: assertion.fullName,
|
|
7118
|
+
relPath: rel,
|
|
7119
|
+
httpCountInSameTest: perTestSlice.length || nearByTime.length,
|
|
7120
|
+
hasTransportSignal: hasTransport
|
|
7121
|
+
});
|
|
7122
|
+
if (!httpLikely) {
|
|
7123
|
+
} else {
|
|
7124
|
+
const expPreview = corresponding?.expectedPreview;
|
|
7125
|
+
const actPreview = corresponding?.actualPreview;
|
|
7126
|
+
const parsedExpected = safeParseJSON(expPreview);
|
|
7127
|
+
const parsedActual = safeParseJSON(actPreview);
|
|
7128
|
+
let corr = corresponding;
|
|
7129
|
+
if (!isHttpStatusNumber(corr.expectedNumber) && !isHttpStatusNumber(corr.receivedNumber)) {
|
|
7130
|
+
const inferred = inferHttpNumbersFromText(msgLines);
|
|
7131
|
+
if (isHttpStatusNumber(inferred.expectedNumber) || isHttpStatusNumber(inferred.receivedNumber)) {
|
|
7132
|
+
corr = { ...corr, ...inferred };
|
|
7133
|
+
}
|
|
7134
|
+
}
|
|
7135
|
+
const relevant = pickRelevantHttp(
|
|
7136
|
+
{
|
|
7137
|
+
timestampMs: corr?.timestampMs,
|
|
7138
|
+
expectedNumber: corr?.expectedNumber,
|
|
7139
|
+
receivedNumber: corr?.receivedNumber,
|
|
7140
|
+
matcher: corr?.matcher,
|
|
7141
|
+
message: corr?.message,
|
|
7142
|
+
stack: corr?.stack,
|
|
7143
|
+
testPath: file.testFilePath,
|
|
7144
|
+
currentTestName: assertion.title
|
|
7145
|
+
},
|
|
7146
|
+
httpSorted,
|
|
7147
|
+
{
|
|
7148
|
+
testPath: file.testFilePath,
|
|
7149
|
+
currentTestName: assertion.title,
|
|
7150
|
+
title: assertion.fullName
|
|
7151
|
+
}
|
|
7152
|
+
);
|
|
7153
|
+
if (hasTransport) {
|
|
7154
|
+
const tsBase = corresponding?.timestampMs ?? 0;
|
|
7155
|
+
const abortCandidates = perTestSlice.filter((event) => event.kind === "abort").sort((leftEvent, rightEvent) => {
|
|
7156
|
+
const deltaLeft = Math.abs(tsBase - (leftEvent.timestampMs ?? 0));
|
|
7157
|
+
const deltaRight = Math.abs(tsBase - (rightEvent.timestampMs ?? 0));
|
|
7158
|
+
return deltaLeft - deltaRight;
|
|
7159
|
+
});
|
|
7160
|
+
const [nearestAbort] = abortCandidates;
|
|
7161
|
+
if (nearestAbort) {
|
|
7162
|
+
out.push(
|
|
7163
|
+
" HTTP:",
|
|
7164
|
+
`
|
|
7165
|
+
${summarizeUrl(nearestAbort.method, nearestAbort.url, nearestAbort.route)} ${ansi.dim("->")} ${ansi.yellow("connection aborted")}`,
|
|
7166
|
+
(() => {
|
|
7167
|
+
const ms = nearestAbort.durationMs;
|
|
7168
|
+
return ms != null ? ` ${ansi.dim(`(${ms}ms)`)} ` : "";
|
|
7169
|
+
})(),
|
|
7170
|
+
"\n"
|
|
7171
|
+
);
|
|
7172
|
+
} else if (relevant) {
|
|
7173
|
+
} else if (HEADLAMP_HTTP_SHOW_MISS()) {
|
|
7174
|
+
out.push(
|
|
7175
|
+
" HTTP:",
|
|
7176
|
+
`
|
|
7177
|
+
${ansi.dim("Transport error; no matching HTTP exchange in window.")}`,
|
|
7178
|
+
"\n"
|
|
7179
|
+
);
|
|
7180
|
+
}
|
|
7181
|
+
}
|
|
7182
|
+
if (!hasTransport && relevant) {
|
|
7183
|
+
const parts = [];
|
|
7184
|
+
const where = summarizeUrl(relevant.method, relevant.url, relevant.route);
|
|
7185
|
+
const line1 = [
|
|
7186
|
+
" HTTP:",
|
|
7187
|
+
`
|
|
7188
|
+
${where} ${ansi.dim("->")} ${relevant.statusCode ?? "?"}`,
|
|
7189
|
+
(() => {
|
|
7190
|
+
const ms = relevant.durationMs;
|
|
7191
|
+
return typeof ms === "number" ? ` ${ansi.dim(`(${ms}ms)`)} ` : " ";
|
|
7192
|
+
})(),
|
|
7193
|
+
relevant.contentType ? ansi.dim(`(${relevant.contentType})`) : "",
|
|
7194
|
+
relevant.requestId ? ansi.dim(` reqId=${relevant.requestId}`) : ""
|
|
7195
|
+
].join("");
|
|
7196
|
+
const expVsAct = (() => {
|
|
7197
|
+
if (typeof corresponding?.expectedNumber === "number" || typeof corresponding?.receivedNumber === "number") {
|
|
7198
|
+
const exp = corresponding?.expectedNumber != null ? String(corresponding.expectedNumber) : "?";
|
|
7199
|
+
const got = corresponding?.receivedNumber != null ? String(corresponding.receivedNumber) : String(relevant.statusCode ?? "?");
|
|
7200
|
+
return `
|
|
7201
|
+
Expected: ${ansi.yellow(exp)} Received: ${ansi.yellow(got)}`;
|
|
7202
|
+
}
|
|
7203
|
+
return "";
|
|
7204
|
+
})();
|
|
7205
|
+
const whyLines = importantMessages(relevant.json).map((msg) => `
|
|
7206
|
+
Why: ${ansi.white(msg)}`).slice(0, 1).join("");
|
|
7207
|
+
const diffLines = (() => {
|
|
7208
|
+
const rightActual = parsedActual ?? relevant.json;
|
|
7209
|
+
if (!parsedExpected || !rightActual) {
|
|
7210
|
+
return "";
|
|
7211
|
+
}
|
|
7212
|
+
const changes = jsonDiff(parsedExpected, rightActual);
|
|
7213
|
+
if (!changes.length) {
|
|
7214
|
+
return "";
|
|
7215
|
+
}
|
|
7216
|
+
const head = "\n Diff:";
|
|
7217
|
+
const body = changes.map((change) => {
|
|
7218
|
+
const marker = change.kind === "added" ? "+" : change.kind === "removed" ? "-" : "~";
|
|
7219
|
+
const previewText = change.preview ? `: ${ansi.dim(change.preview)}` : "";
|
|
7220
|
+
return `
|
|
7221
|
+
${marker} ${change.path}${previewText}`;
|
|
7222
|
+
}).join("");
|
|
7223
|
+
return head + body;
|
|
7224
|
+
})();
|
|
7225
|
+
parts.push(line1, expVsAct, whyLines, diffLines, "\n");
|
|
7226
|
+
out.push(...parts.filter(Boolean));
|
|
7227
|
+
} else if (!hasTransport && !relevant && HEADLAMP_HTTP_SHOW_MISS()) {
|
|
7228
|
+
out.push(
|
|
7229
|
+
" HTTP:",
|
|
7230
|
+
`
|
|
7231
|
+
${ansi.dim("No relevant HTTP exchange found. (HEADLAMP_HTTP_MISS=0 to hide)")}`,
|
|
7232
|
+
"\n"
|
|
7233
|
+
);
|
|
7234
|
+
}
|
|
7235
|
+
}
|
|
6578
7236
|
}
|
|
6579
|
-
|
|
6580
|
-
|
|
7237
|
+
const minimalInfo = msgLines.every((ln) => !ln.trim());
|
|
7238
|
+
if (minimalInfo) {
|
|
7239
|
+
try {
|
|
7240
|
+
out.push(...buildThrownSection(assertion.failureDetails || []));
|
|
7241
|
+
} catch {
|
|
7242
|
+
}
|
|
7243
|
+
}
|
|
7244
|
+
out.push(...buildConsoleSection(stripBridgeEventsFromConsole(file.console ?? null)));
|
|
7245
|
+
if (ctx.showStacks && stackPreview.length === 0) {
|
|
7246
|
+
const tail = mergedForStack.filter((ln) => isStackLine(stripAnsiSimple(ln))).slice(-4).map((ln) => ` ${colorStackLine(String(ln), ctx.projectHint)}`);
|
|
7247
|
+
if (tail.length) {
|
|
7248
|
+
out.push(ansi.dim(" Stack:"), ...tail, "");
|
|
7249
|
+
}
|
|
7250
|
+
}
|
|
7251
|
+
out.push(drawFailLine(), "");
|
|
6581
7252
|
}
|
|
6582
7253
|
}
|
|
6583
7254
|
const failedCount = data.aggregated.numFailedTests;
|
|
6584
|
-
out.push(drawRule(
|
|
7255
|
+
out.push(drawRule(BackgroundColors.Failure(ansi.white(` Failed Tests ${failedCount} `))));
|
|
6585
7256
|
out.push("");
|
|
6586
|
-
|
|
6587
|
-
|
|
6588
|
-
|
|
6589
|
-
|
|
6590
|
-
);
|
|
6591
|
-
|
|
6592
|
-
|
|
7257
|
+
out.push(vitestFooter(data.aggregated));
|
|
7258
|
+
return out.join("\n");
|
|
7259
|
+
};
|
|
7260
|
+
var tryBridgeFallback = (raw, ctx, opts) => {
|
|
7261
|
+
const bridgeJsonPath = extractBridgePath2(raw, ctx.cwd);
|
|
7262
|
+
if (!bridgeJsonPath || !fs6.existsSync(bridgeJsonPath)) {
|
|
7263
|
+
return null;
|
|
7264
|
+
}
|
|
7265
|
+
try {
|
|
7266
|
+
const json = import_json52.default.parse(fs6.readFileSync(bridgeJsonPath, "utf8"));
|
|
7267
|
+
const bridge = coerceJestJsonToBridge(json);
|
|
7268
|
+
return renderVitestFromJestJSON(bridge, ctx, opts);
|
|
7269
|
+
} catch {
|
|
7270
|
+
return null;
|
|
7271
|
+
}
|
|
7272
|
+
};
|
|
7273
|
+
|
|
7274
|
+
// src/lib/formatJestOutputVitest.ts
|
|
7275
|
+
var formatJestOutputVitest = (raw, opts) => pipe(
|
|
7276
|
+
{ raw, opts },
|
|
7277
|
+
(state) => ({
|
|
7278
|
+
...state,
|
|
7279
|
+
ctx: makeCtx(state.opts, /\bFAIL\b/.test(stripAnsiSimple(state.raw)))
|
|
7280
|
+
}),
|
|
7281
|
+
(state) => ({ ...state, chunks: parseChunks(state.raw) }),
|
|
7282
|
+
(state) => ({
|
|
7283
|
+
...state,
|
|
7284
|
+
rendered: renderChunks(state.chunks, state.ctx, mkPrettyFns(), {
|
|
7285
|
+
onlyFailures: Boolean(state.opts?.onlyFailures)
|
|
7286
|
+
})
|
|
7287
|
+
}),
|
|
7288
|
+
(state) => {
|
|
7289
|
+
if (state.rendered.hadParsed) {
|
|
7290
|
+
return state.rendered.text;
|
|
7291
|
+
}
|
|
7292
|
+
const fallback = tryBridgeFallback(state.raw, state.ctx, {
|
|
7293
|
+
onlyFailures: Boolean(state.opts?.onlyFailures)
|
|
7294
|
+
});
|
|
7295
|
+
if (!fallback) {
|
|
7296
|
+
return state.rendered.text;
|
|
7297
|
+
}
|
|
7298
|
+
const prefix = state.rendered.text;
|
|
7299
|
+
return prefix ? `${prefix}
|
|
7300
|
+
${fallback}` : fallback;
|
|
7301
|
+
}
|
|
7302
|
+
);
|
|
7303
|
+
|
|
7304
|
+
// src/lib/jest-reporter-source.ts
|
|
7305
|
+
var JEST_BRIDGE_REPORTER_SOURCE = `const fs = require('fs');
|
|
7306
|
+
const path = require('path');
|
|
7307
|
+
|
|
7308
|
+
const isObject = (v) => typeof v === 'object' && v !== null;
|
|
7309
|
+
const sanitizeError = (err) => {
|
|
7310
|
+
if (!isObject(err)) return err;
|
|
7311
|
+
const out = {};
|
|
7312
|
+
const name = err.name || (err.constructor && err.constructor.name) || undefined;
|
|
7313
|
+
if (name) out.name = String(name);
|
|
7314
|
+
if (typeof err.message === 'string') out.message = err.message;
|
|
7315
|
+
if (typeof err.stack === 'string') out.stack = err.stack;
|
|
7316
|
+
if (err.code !== undefined) out.code = err.code;
|
|
7317
|
+
if (err.expected !== undefined) out.expected = err.expected;
|
|
7318
|
+
if (err.received !== undefined) out.received = err.received;
|
|
7319
|
+
if (err.matcherResult && isObject(err.matcherResult)) {
|
|
7320
|
+
const mr = err.matcherResult;
|
|
7321
|
+
let messageText;
|
|
7322
|
+
try {
|
|
7323
|
+
messageText = typeof mr.message === 'function' ? String(mr.message()) : (typeof mr.message === 'string' ? mr.message : undefined);
|
|
7324
|
+
} catch {}
|
|
7325
|
+
out.matcherResult = {
|
|
7326
|
+
matcherName: typeof mr.matcherName === 'string' ? mr.matcherName : undefined,
|
|
7327
|
+
message: messageText,
|
|
7328
|
+
stack: typeof mr.stack === 'string' ? mr.stack : undefined,
|
|
7329
|
+
expected: mr.expected,
|
|
7330
|
+
received: mr.received,
|
|
7331
|
+
actual: mr.actual,
|
|
7332
|
+
pass: typeof mr.pass === 'boolean' ? mr.pass : undefined,
|
|
7333
|
+
};
|
|
7334
|
+
}
|
|
7335
|
+
if (err.cause) {
|
|
7336
|
+
out.cause = sanitizeError(err.cause);
|
|
7337
|
+
}
|
|
7338
|
+
// Copy own enumerable props to preserve custom data
|
|
7339
|
+
try {
|
|
7340
|
+
for (const key of Object.keys(err)) {
|
|
7341
|
+
if (!(key in out)) out[key] = err[key];
|
|
7342
|
+
}
|
|
7343
|
+
} catch {}
|
|
7344
|
+
return out;
|
|
7345
|
+
};
|
|
7346
|
+
const sanitizeDetail = (d) => {
|
|
7347
|
+
if (typeof d === 'string') return d;
|
|
7348
|
+
if (!isObject(d)) return d;
|
|
7349
|
+
// Common Jest detail shapes
|
|
7350
|
+
const out = {};
|
|
7351
|
+
if (d.message) out.message = d.message;
|
|
7352
|
+
if (d.stack) out.stack = d.stack;
|
|
7353
|
+
if (d.error) out.error = sanitizeError(d.error);
|
|
7354
|
+
if (d.matcherResult) out.matcherResult = sanitizeError({ matcherResult: d.matcherResult }).matcherResult;
|
|
7355
|
+
if (d.expected !== undefined) out.expected = d.expected;
|
|
7356
|
+
if (d.received !== undefined) out.received = d.received;
|
|
7357
|
+
// Copy the rest
|
|
7358
|
+
try {
|
|
7359
|
+
for (const key of Object.keys(d)) {
|
|
7360
|
+
if (!(key in out)) out[key] = d[key];
|
|
7361
|
+
}
|
|
7362
|
+
} catch {}
|
|
7363
|
+
return out;
|
|
7364
|
+
};
|
|
7365
|
+
|
|
7366
|
+
class BridgeReporter {
|
|
7367
|
+
constructor(globalConfig, options) {
|
|
7368
|
+
this.out = process.env.JEST_BRIDGE_OUT || (options && options.outFile) || path.join(process.cwd(), 'coverage', 'jest-run.json');
|
|
7369
|
+
this.buf = { startTime: Date.now(), testResults: [], aggregated: null };
|
|
7370
|
+
}
|
|
7371
|
+
onRunStart() { this.buf.startTime = Date.now(); }
|
|
7372
|
+
onTestResult(_test, tr) {
|
|
7373
|
+
const mapAssertion = (a) => ({
|
|
7374
|
+
title: a.title,
|
|
7375
|
+
fullName: a.fullName || [...(a.ancestorTitles || []), a.title].join(' '),
|
|
7376
|
+
status: a.status,
|
|
7377
|
+
duration: a.duration || 0,
|
|
7378
|
+
location: a.location || null,
|
|
7379
|
+
failureMessages: (a.failureMessages || []).map(String),
|
|
7380
|
+
failureDetails: (a.failureDetails || []).map(sanitizeDetail),
|
|
7381
|
+
});
|
|
7382
|
+
this.buf.testResults.push({
|
|
7383
|
+
testFilePath: tr.testFilePath,
|
|
7384
|
+
status: tr.numFailingTests ? 'failed' : 'passed',
|
|
7385
|
+
failureMessage: tr.failureMessage || '',
|
|
7386
|
+
failureDetails: (tr.failureDetails || []).map(sanitizeDetail),
|
|
7387
|
+
testExecError: tr.testExecError ? sanitizeError(tr.testExecError) : null,
|
|
7388
|
+
console: tr.console || null,
|
|
7389
|
+
perfStats: tr.perfStats || {},
|
|
7390
|
+
testResults: (tr.testResults || []).map(mapAssertion),
|
|
7391
|
+
});
|
|
7392
|
+
}
|
|
7393
|
+
onRunComplete(_contexts, agg) {
|
|
7394
|
+
this.buf.aggregated = {
|
|
7395
|
+
numTotalTestSuites: agg.numTotalTestSuites,
|
|
7396
|
+
numPassedTestSuites: agg.numPassedTestSuites,
|
|
7397
|
+
numFailedTestSuites: agg.numFailedTestSuites,
|
|
7398
|
+
numTotalTests: agg.numTotalTests,
|
|
7399
|
+
numPassedTests: agg.numPassedTests,
|
|
7400
|
+
numFailedTests: agg.numFailedTests,
|
|
7401
|
+
numPendingTests: agg.numPendingTests,
|
|
7402
|
+
numTodoTests: agg.numTodoTests,
|
|
7403
|
+
startTime: agg.startTime,
|
|
7404
|
+
success: agg.success,
|
|
7405
|
+
runTimeMs: agg.testResults.reduce((t, r) => t + Math.max(0, (r.perfStats?.end || 0) - (r.perfStats?.start || 0)), 0),
|
|
7406
|
+
};
|
|
7407
|
+
fs.mkdirSync(path.dirname(this.out), { recursive: true });
|
|
7408
|
+
fs.writeFileSync(this.out, JSON.stringify(this.buf), 'utf8');
|
|
7409
|
+
}
|
|
6593
7410
|
}
|
|
7411
|
+
module.exports = BridgeReporter;`;
|
|
6594
7412
|
|
|
6595
7413
|
// src/lib/program.ts
|
|
6596
7414
|
init_env_utils();
|
|
6597
7415
|
init_exec();
|
|
6598
7416
|
init_args();
|
|
6599
|
-
import * as
|
|
7417
|
+
import * as path11 from "node:path";
|
|
6600
7418
|
import * as os2 from "node:os";
|
|
6601
7419
|
import * as fsSync3 from "node:fs";
|
|
6602
|
-
import * as
|
|
7420
|
+
import * as fs7 from "node:fs/promises";
|
|
6603
7421
|
import * as LibReport from "istanbul-lib-report";
|
|
6604
7422
|
import * as Reports from "istanbul-reports";
|
|
6605
7423
|
import { createCoverageMap as createCoverageMap2 } from "istanbul-lib-coverage";
|
|
7424
|
+
|
|
7425
|
+
// src/lib/jest-environment-source.ts
|
|
7426
|
+
var JEST_BRIDGE_ENV_SOURCE = `
|
|
7427
|
+
'use strict';
|
|
7428
|
+
|
|
7429
|
+
const NodeEnvironment = require('jest-environment-node').TestEnvironment || require('jest-environment-node');
|
|
7430
|
+
|
|
7431
|
+
module.exports = class BridgeEnv extends NodeEnvironment {
|
|
7432
|
+
constructor(config, context) {
|
|
7433
|
+
super(config, context);
|
|
7434
|
+
const { AsyncLocalStorage } = require('node:async_hooks');
|
|
7435
|
+
this._als = new AsyncLocalStorage();
|
|
7436
|
+
this._cleanup = [];
|
|
7437
|
+
}
|
|
7438
|
+
|
|
7439
|
+
_ctx() {
|
|
7440
|
+
try { const s = this._als.getStore(); if (s) return s; } catch {}
|
|
7441
|
+
try {
|
|
7442
|
+
const st = this.global.expect && typeof this.global.expect.getState === 'function' ? this.global.expect.getState() : {};
|
|
7443
|
+
return { testPath: st.testPath, currentTestName: st.currentTestName };
|
|
7444
|
+
} catch { return {}; }
|
|
7445
|
+
}
|
|
7446
|
+
|
|
7447
|
+
async setup() {
|
|
7448
|
+
await super.setup();
|
|
7449
|
+
|
|
7450
|
+
try { Error.stackTraceLimit = Math.max(Error.stackTraceLimit || 10, 50); } catch {}
|
|
7451
|
+
|
|
7452
|
+
const print = (payload) => { try { this.global.console.error('[JEST-BRIDGE-EVENT]', JSON.stringify(payload)); } catch {} };
|
|
7453
|
+
const toErr = (x) => { try { return x instanceof Error ? x : new Error(String(x)); } catch { return new Error('unknown'); } };
|
|
7454
|
+
|
|
7455
|
+
const onRej = (reason) => {
|
|
7456
|
+
const e = toErr(reason);
|
|
7457
|
+
const c = this._ctx();
|
|
7458
|
+
print({ type: 'unhandledRejection', name: e.name, message: e.message, stack: e.stack, code: e.code ?? undefined, ...c });
|
|
7459
|
+
};
|
|
7460
|
+
const onExc = (error) => {
|
|
7461
|
+
const e = toErr(error);
|
|
7462
|
+
const c = this._ctx();
|
|
7463
|
+
print({ type: 'uncaughtException', name: e.name, message: e.message, stack: e.stack, code: e.code ?? undefined, ...c });
|
|
7464
|
+
};
|
|
7465
|
+
|
|
7466
|
+
this.global.process.on('unhandledRejection', onRej);
|
|
7467
|
+
this.global.process.on('uncaughtException', onExc);
|
|
7468
|
+
this._cleanup.push(() => {
|
|
7469
|
+
this.global.process.off('unhandledRejection', onRej);
|
|
7470
|
+
this.global.process.off('uncaughtException', onExc);
|
|
7471
|
+
});
|
|
7472
|
+
|
|
7473
|
+
// Signal environment readiness so we can confirm the custom env loaded
|
|
7474
|
+
try { const c = this._ctx(); print({ type: 'envReady', ...c }); } catch {}
|
|
7475
|
+
|
|
7476
|
+
try {
|
|
7477
|
+
const http = this.global.require ? this.global.require('http') : require('http');
|
|
7478
|
+
const originalEmit = http && http.Server && http.Server.prototype && http.Server.prototype.emit;
|
|
7479
|
+
if (originalEmit) {
|
|
7480
|
+
const MAX = 64 * 1024;
|
|
7481
|
+
const asString = (x) => { try { if (typeof x === 'string') return x; if (Buffer.isBuffer(x)) return x.toString('utf8'); return String(x); } catch { return ''; } };
|
|
7482
|
+
|
|
7483
|
+
const patched = function(eventName, req, res) {
|
|
7484
|
+
try {
|
|
7485
|
+
if (eventName === 'request' && req && res && typeof res.write === 'function' && typeof res.end === 'function') {
|
|
7486
|
+
const startAt = Date.now();
|
|
7487
|
+
const safeHeader = (k) => { try { return req && req.headers ? String((req.headers[k.toLowerCase()] ?? '')) : ''; } catch { return ''; } };
|
|
7488
|
+
const reqIdHeader = safeHeader('x-request-id');
|
|
7489
|
+
const chunks = [];
|
|
7490
|
+
const write = res.write.bind(res);
|
|
7491
|
+
const end = res.end.bind(res);
|
|
7492
|
+
const method = req.method ? String(req.method) : undefined;
|
|
7493
|
+
const url = (req.originalUrl || req.url) ? String(req.originalUrl || req.url) : undefined;
|
|
7494
|
+
|
|
7495
|
+
res.write = function(chunk, enc, cb) {
|
|
7496
|
+
try { const s = asString(chunk); if (s) chunks.push(s); } catch {}
|
|
7497
|
+
return write(chunk, enc, cb);
|
|
7498
|
+
};
|
|
7499
|
+
res.end = function(chunk, enc, cb) {
|
|
7500
|
+
try { const s = asString(chunk); if (s) chunks.push(s); } catch {}
|
|
7501
|
+
try {
|
|
7502
|
+
const preview = chunks.join('').slice(0, MAX);
|
|
7503
|
+
const statusCode = typeof res.statusCode === 'number' ? res.statusCode : undefined;
|
|
7504
|
+
const ct = (typeof res.getHeader === 'function' && res.getHeader('content-type')) || undefined;
|
|
7505
|
+
const routePath = (req && req.route && req.route.path) || (req && req.baseUrl && req.path ? (req.baseUrl + req.path) : undefined);
|
|
7506
|
+
const jsonParsed = (() => { try { return JSON.parse(preview); } catch { return undefined; } })();
|
|
7507
|
+
const requestId = reqIdHeader || (jsonParsed && typeof jsonParsed === 'object' ? (jsonParsed.requestId || jsonParsed.reqId || '') : '');
|
|
7508
|
+
const ctx = (global.__JEST_BRIDGE_ENV_REF && global.__JEST_BRIDGE_ENV_REF._ctx) ? global.__JEST_BRIDGE_ENV_REF._ctx() : {};
|
|
7509
|
+
const payload = {
|
|
7510
|
+
type: 'httpResponse',
|
|
7511
|
+
timestampMs: Date.now(),
|
|
7512
|
+
durationMs: Math.max(0, Date.now() - startAt),
|
|
7513
|
+
method, url, statusCode,
|
|
7514
|
+
route: routePath ? String(routePath) : undefined,
|
|
7515
|
+
contentType: ct ? String(ct) : undefined,
|
|
7516
|
+
headers: (typeof res.getHeaders === 'function') ? res.getHeaders() : undefined,
|
|
7517
|
+
requestId: requestId ? String(requestId) : undefined,
|
|
7518
|
+
bodyPreview: preview,
|
|
7519
|
+
json: jsonParsed,
|
|
7520
|
+
testPath: ctx.testPath, currentTestName: ctx.currentTestName,
|
|
7521
|
+
};
|
|
7522
|
+
try {
|
|
7523
|
+
if (!global.__JEST_HTTP_EVENTS__) global.__JEST_HTTP_EVENTS__ = [];
|
|
7524
|
+
const arr = global.__JEST_HTTP_EVENTS__;
|
|
7525
|
+
arr.push({ timestampMs: payload.timestampMs, durationMs: payload.durationMs, method: payload.method, url: payload.url, route: payload.route, statusCode: payload.statusCode, contentType: payload.contentType, requestId: payload.requestId, json: payload.json, bodyPreview: payload.bodyPreview });
|
|
7526
|
+
if (arr.length > 25) arr.splice(0, arr.length - 25);
|
|
7527
|
+
} catch {}
|
|
7528
|
+
try { console.error('[JEST-BRIDGE-EVENT]', JSON.stringify(payload)); } catch {}
|
|
7529
|
+
} catch {}
|
|
7530
|
+
return end(chunk, enc, cb);
|
|
7531
|
+
};
|
|
7532
|
+
try {
|
|
7533
|
+
res.on('close', function onClose() {
|
|
7534
|
+
try {
|
|
7535
|
+
const ended = typeof res.writableEnded === 'boolean' ? res.writableEnded : false;
|
|
7536
|
+
if (!ended) {
|
|
7537
|
+
const routePath = (req && req.route && req.route.path) || (req && req.baseUrl && req.path ? (req.baseUrl + req.path) : undefined);
|
|
7538
|
+
const ctx = (global.__JEST_BRIDGE_ENV_REF && global.__JEST_BRIDGE_ENV_REF._ctx) ? global.__JEST_BRIDGE_ENV_REF._ctx() : {};
|
|
7539
|
+
const payload = {
|
|
7540
|
+
type: 'httpAbort',
|
|
7541
|
+
timestampMs: Date.now(),
|
|
7542
|
+
durationMs: Math.max(0, Date.now() - startAt),
|
|
7543
|
+
method, url,
|
|
7544
|
+
route: routePath ? String(routePath) : undefined,
|
|
7545
|
+
testPath: ctx.testPath, currentTestName: ctx.currentTestName,
|
|
7546
|
+
};
|
|
7547
|
+
try { console.error('[JEST-BRIDGE-EVENT]', JSON.stringify(payload)); } catch {}
|
|
7548
|
+
}
|
|
7549
|
+
} catch {}
|
|
7550
|
+
});
|
|
7551
|
+
} catch {}
|
|
7552
|
+
}
|
|
7553
|
+
} catch {}
|
|
7554
|
+
return originalEmit.apply(this, arguments);
|
|
7555
|
+
};
|
|
7556
|
+
|
|
7557
|
+
try { this.global.__JEST_BRIDGE_ENV_REF = this; } catch {}
|
|
7558
|
+
http.Server.prototype.emit = patched;
|
|
7559
|
+
|
|
7560
|
+
this._cleanup.push(() => {
|
|
7561
|
+
try { if (http.Server && http.Server.prototype) http.Server.prototype.emit = originalEmit; } catch {}
|
|
7562
|
+
try { delete this.global.__JEST_BRIDGE_ENV_REF; } catch {}
|
|
7563
|
+
});
|
|
7564
|
+
}
|
|
7565
|
+
} catch {}
|
|
7566
|
+
|
|
7567
|
+
// Wrap test functions to emit rich assertion events on failures
|
|
7568
|
+
try {
|
|
7569
|
+
const g = this.global;
|
|
7570
|
+
const ctxFn = () => {
|
|
7571
|
+
try {
|
|
7572
|
+
const ref = g.__JEST_BRIDGE_ENV_REF;
|
|
7573
|
+
return ref && typeof ref._ctx === 'function' ? ref._ctx() : {};
|
|
7574
|
+
} catch { return {}; }
|
|
7575
|
+
};
|
|
7576
|
+
const emitAssertion = (err) => {
|
|
7577
|
+
try {
|
|
7578
|
+
const e = toErr(err);
|
|
7579
|
+
const mr = e && typeof e === 'object' && e.matcherResult ? e.matcherResult : undefined;
|
|
7580
|
+
const messageText = (() => { try { return mr && typeof mr.message === 'function' ? String(mr.message()) : (e.message || ''); } catch { return e.message || ''; } })();
|
|
7581
|
+
const expectedPreview = (() => { try { return mr && mr.expected !== undefined ? JSON.stringify(mr.expected, null, 2) : undefined; } catch { return undefined; } })();
|
|
7582
|
+
const actualPreview = (() => { try { return mr && mr.received !== undefined ? JSON.stringify(mr.received, null, 2) : undefined; } catch { return undefined; } })();
|
|
7583
|
+
const c = ctxFn();
|
|
7584
|
+
const expectedRaw = (() => { try { return mr?.expected; } catch { return undefined; } })();
|
|
7585
|
+
const receivedRaw = (() => { try { return mr?.received; } catch { return undefined; } })();
|
|
7586
|
+
print({
|
|
7587
|
+
type: 'assertionFailure',
|
|
7588
|
+
timestampMs: Date.now(),
|
|
7589
|
+
matcher: mr && typeof mr.matcherName === 'string' ? mr.matcherName : undefined,
|
|
7590
|
+
expectedPreview,
|
|
7591
|
+
actualPreview,
|
|
7592
|
+
expectedNumber: typeof expectedRaw === 'number' ? expectedRaw : undefined,
|
|
7593
|
+
receivedNumber: typeof receivedRaw === 'number' ? receivedRaw : undefined,
|
|
7594
|
+
message: messageText,
|
|
7595
|
+
stack: e.stack,
|
|
7596
|
+
...c,
|
|
7597
|
+
});
|
|
7598
|
+
} catch {}
|
|
7599
|
+
};
|
|
7600
|
+
const wrap = (orig) => {
|
|
7601
|
+
if (!orig || typeof orig !== 'function') return orig;
|
|
7602
|
+
const wrapped = function(name, fn, timeout) {
|
|
7603
|
+
if (typeof fn !== 'function') return orig.call(this, name, fn, timeout);
|
|
7604
|
+
const run = function() {
|
|
7605
|
+
try {
|
|
7606
|
+
const res = fn.apply(this, arguments);
|
|
7607
|
+
if (res && typeof res.then === 'function') {
|
|
7608
|
+
return res.catch((err) => { emitAssertion(err); throw err; });
|
|
7609
|
+
}
|
|
7610
|
+
return res;
|
|
7611
|
+
} catch (err) {
|
|
7612
|
+
emitAssertion(err);
|
|
7613
|
+
throw err;
|
|
7614
|
+
}
|
|
7615
|
+
};
|
|
7616
|
+
return orig.call(this, name, run, timeout);
|
|
7617
|
+
};
|
|
7618
|
+
try { wrapped.only = orig.only && typeof orig.only === 'function' ? wrap(orig.only) : orig.only; } catch {}
|
|
7619
|
+
try { wrapped.skip = orig.skip && typeof orig.skip === 'function' ? wrap(orig.skip) : orig.skip; } catch {}
|
|
7620
|
+
return wrapped;
|
|
7621
|
+
};
|
|
7622
|
+
try { g.it = wrap(g.it); } catch {}
|
|
7623
|
+
try { g.test = wrap(g.test); } catch {}
|
|
7624
|
+
} catch {}
|
|
7625
|
+
}
|
|
7626
|
+
|
|
7627
|
+
async handleTestEvent(evt, state) {
|
|
7628
|
+
if (evt.name === 'test_start') {
|
|
7629
|
+
const store = { testPath: state.testPath, currentTestName: evt.test.name };
|
|
7630
|
+
try { this._als.enterWith(store); } catch {}
|
|
7631
|
+
} else if (evt.name === 'test_done') {
|
|
7632
|
+
try { this._als.enterWith({}); } catch {}
|
|
7633
|
+
try {
|
|
7634
|
+
const events = Array.isArray(global.__JEST_HTTP_EVENTS__) ? global.__JEST_HTTP_EVENTS__ : [];
|
|
7635
|
+
if (events.length) {
|
|
7636
|
+
const batch = events.slice(-10);
|
|
7637
|
+
const payload = { type: 'httpResponseBatch', events: batch, testPath: state.testPath, currentTestName: evt.test.name };
|
|
7638
|
+
try { this.global.console.error('[JEST-BRIDGE-EVENT]', JSON.stringify(payload)); } catch {}
|
|
7639
|
+
try { global.__JEST_HTTP_EVENTS__ = []; } catch {}
|
|
7640
|
+
}
|
|
7641
|
+
} catch {}
|
|
7642
|
+
}
|
|
7643
|
+
}
|
|
7644
|
+
|
|
7645
|
+
async teardown() {
|
|
7646
|
+
for (let i = this._cleanup.length - 1; i >= 0; i--) {
|
|
7647
|
+
try { this._cleanup[i](); } catch {}
|
|
7648
|
+
}
|
|
7649
|
+
await super.teardown();
|
|
7650
|
+
}
|
|
7651
|
+
};
|
|
7652
|
+
`;
|
|
7653
|
+
|
|
7654
|
+
// src/lib/program.ts
|
|
6606
7655
|
var jestBin = "./node_modules/.bin/jest";
|
|
6607
7656
|
var babelNodeBin = "./node_modules/.bin/babel-node";
|
|
6608
7657
|
var registerSignalHandlersOnce = () => {
|
|
@@ -6645,7 +7694,7 @@ var mergeLcov = async () => {
|
|
|
6645
7694
|
try {
|
|
6646
7695
|
const entries = fsSync3.readdirSync(dir, { withFileTypes: true });
|
|
6647
7696
|
for (const entry of entries) {
|
|
6648
|
-
const full =
|
|
7697
|
+
const full = path11.join(dir, entry.name);
|
|
6649
7698
|
if (entry.isDirectory()) {
|
|
6650
7699
|
out.push(...collectLcovs(full));
|
|
6651
7700
|
} else if (entry.isFile() && entry.name === "lcov.info") {
|
|
@@ -6657,8 +7706,8 @@ var mergeLcov = async () => {
|
|
|
6657
7706
|
return out;
|
|
6658
7707
|
};
|
|
6659
7708
|
try {
|
|
6660
|
-
const jestRoot =
|
|
6661
|
-
const candidates = [
|
|
7709
|
+
const jestRoot = path11.join("coverage", "jest");
|
|
7710
|
+
const candidates = [path11.join(jestRoot, "lcov.info"), ...collectLcovs(jestRoot)].map((candidatePath) => path11.resolve(candidatePath)).filter((absolutePath, index, arr) => arr.indexOf(absolutePath) === index);
|
|
6662
7711
|
for (const filePath of candidates) {
|
|
6663
7712
|
try {
|
|
6664
7713
|
const content = await readOrEmpty(filePath);
|
|
@@ -6698,7 +7747,7 @@ var emitMergedCoverage = async (ui, opts) => {
|
|
|
6698
7747
|
try {
|
|
6699
7748
|
const entries = fsSync3.readdirSync(dir, { withFileTypes: true });
|
|
6700
7749
|
for (const entry of entries) {
|
|
6701
|
-
const full =
|
|
7750
|
+
const full = path11.join(dir, entry.name);
|
|
6702
7751
|
if (entry.isDirectory()) {
|
|
6703
7752
|
out.push(...listJsons(full));
|
|
6704
7753
|
} else if (entry.isFile() && entry.name === "coverage-final.json") {
|
|
@@ -6709,11 +7758,11 @@ var emitMergedCoverage = async (ui, opts) => {
|
|
|
6709
7758
|
}
|
|
6710
7759
|
return out;
|
|
6711
7760
|
};
|
|
6712
|
-
const coverageRoot =
|
|
7761
|
+
const coverageRoot = path11.join("coverage", "jest");
|
|
6713
7762
|
const jsonCandidates = [
|
|
6714
|
-
|
|
7763
|
+
path11.join(coverageRoot, "coverage-final.json"),
|
|
6715
7764
|
...listJsons(coverageRoot)
|
|
6716
|
-
].map((candidatePath) =>
|
|
7765
|
+
].map((candidatePath) => path11.resolve(candidatePath)).filter((absolutePath, index, arr) => {
|
|
6717
7766
|
const isFirst = arr.indexOf(absolutePath) === index;
|
|
6718
7767
|
const exists = fsSync3.existsSync(absolutePath);
|
|
6719
7768
|
return isFirst && exists;
|
|
@@ -6775,7 +7824,7 @@ var emitMergedCoverage = async (ui, opts) => {
|
|
|
6775
7824
|
executedTests: opts.executedTests ?? []
|
|
6776
7825
|
});
|
|
6777
7826
|
const context = LibReport.createContext({
|
|
6778
|
-
dir:
|
|
7827
|
+
dir: path11.resolve("coverage", "merged"),
|
|
6779
7828
|
coverageMap: filteredMap,
|
|
6780
7829
|
defaultSummarizer: "nested"
|
|
6781
7830
|
});
|
|
@@ -6843,8 +7892,8 @@ var emitMergedCoverage = async (ui, opts) => {
|
|
|
6843
7892
|
for (const reporter of reporters) {
|
|
6844
7893
|
reporter.execute(context);
|
|
6845
7894
|
}
|
|
6846
|
-
const textPath =
|
|
6847
|
-
const summaryPath =
|
|
7895
|
+
const textPath = path11.resolve("coverage", "merged", "coverage.txt");
|
|
7896
|
+
const summaryPath = path11.resolve("coverage", "merged", "coverage-summary.txt");
|
|
6848
7897
|
const filesToPrint = [];
|
|
6849
7898
|
if (fsSync3.existsSync(textPath)) {
|
|
6850
7899
|
filesToPrint.push(textPath);
|
|
@@ -6943,11 +7992,57 @@ var program = async () => {
|
|
|
6943
7992
|
return [];
|
|
6944
7993
|
}
|
|
6945
7994
|
};
|
|
7995
|
+
if (mode === "branch") {
|
|
7996
|
+
const resolveDefaultBranch = async () => {
|
|
7997
|
+
const candidates = [];
|
|
7998
|
+
try {
|
|
7999
|
+
const sym = await collect("git", ["symbolic-ref", "refs/remotes/origin/HEAD"]);
|
|
8000
|
+
const headRef = sym.find((ln) => ln.includes("refs/remotes/origin/"));
|
|
8001
|
+
if (headRef) {
|
|
8002
|
+
const m = /refs\/remotes\/(.+)/.exec(headRef);
|
|
8003
|
+
if (m && m[1]) {
|
|
8004
|
+
candidates.push(m[1]);
|
|
8005
|
+
}
|
|
8006
|
+
}
|
|
8007
|
+
} catch {
|
|
8008
|
+
}
|
|
8009
|
+
candidates.push("origin/main", "origin/master");
|
|
8010
|
+
for (const cand of candidates) {
|
|
8011
|
+
const exists = await collect("git", ["rev-parse", "--verify", cand]);
|
|
8012
|
+
if (exists.length > 0) {
|
|
8013
|
+
return cand;
|
|
8014
|
+
}
|
|
8015
|
+
}
|
|
8016
|
+
return void 0;
|
|
8017
|
+
};
|
|
8018
|
+
const defaultBranch = await resolveDefaultBranch();
|
|
8019
|
+
const mergeBase = defaultBranch ? (await collect("git", ["merge-base", "HEAD", defaultBranch]))[0] : void 0;
|
|
8020
|
+
const diffBase = mergeBase ?? "HEAD^";
|
|
8021
|
+
const branchDiff = await collect("git", [
|
|
8022
|
+
"diff",
|
|
8023
|
+
"--name-only",
|
|
8024
|
+
"--diff-filter=ACMRTUXB",
|
|
8025
|
+
diffBase,
|
|
8026
|
+
"HEAD"
|
|
8027
|
+
]);
|
|
8028
|
+
const stagedNow = await collect("git", [
|
|
8029
|
+
"diff",
|
|
8030
|
+
"--name-only",
|
|
8031
|
+
"--diff-filter=ACMRTUXB",
|
|
8032
|
+
"--cached"
|
|
8033
|
+
]);
|
|
8034
|
+
const unstagedNow = await collect("git", ["diff", "--name-only", "--diff-filter=ACMRTUXB"]);
|
|
8035
|
+
const untrackedNow = await collect("git", ["ls-files", "--others", "--exclude-standard"]);
|
|
8036
|
+
const rels2 = Array.from(
|
|
8037
|
+
/* @__PURE__ */ new Set([...branchDiff, ...stagedNow, ...unstagedNow, ...untrackedNow])
|
|
8038
|
+
);
|
|
8039
|
+
return rels2.map((rel) => path11.resolve(cwd, rel).replace(/\\/g, "/")).filter((abs) => !abs.includes("/node_modules/") && !abs.includes("/coverage/"));
|
|
8040
|
+
}
|
|
6946
8041
|
const staged = mode === "staged" || mode === "all" ? await collect("git", ["diff", "--name-only", "--diff-filter=ACMRTUXB", "--cached"]) : [];
|
|
6947
8042
|
const unstagedTracked = mode === "unstaged" || mode === "all" ? await collect("git", ["diff", "--name-only", "--diff-filter=ACMRTUXB"]) : [];
|
|
6948
8043
|
const untracked = mode === "unstaged" || mode === "all" ? await collect("git", ["ls-files", "--others", "--exclude-standard"]) : [];
|
|
6949
8044
|
const rels = Array.from(/* @__PURE__ */ new Set([...staged, ...unstagedTracked, ...untracked]));
|
|
6950
|
-
return rels.map((rel) =>
|
|
8045
|
+
return rels.map((rel) => path11.resolve(cwd, rel).replace(/\\/g, "/")).filter((abs) => !abs.includes("/node_modules/") && !abs.includes("/coverage/"));
|
|
6951
8046
|
};
|
|
6952
8047
|
const repoRootForChanged = workspaceRoot ?? await findRepoRoot();
|
|
6953
8048
|
const changedSelectionAbs = changed ? await getChangedFiles(changed, repoRootForChanged) : [];
|
|
@@ -6972,18 +8067,18 @@ var program = async () => {
|
|
|
6972
8067
|
if (!token) {
|
|
6973
8068
|
continue;
|
|
6974
8069
|
}
|
|
6975
|
-
const isAbs =
|
|
8070
|
+
const isAbs = path11.isAbsolute(token);
|
|
6976
8071
|
const looksLikeRelPath = /[\\/]/.test(token);
|
|
6977
8072
|
let candidateFromRoot;
|
|
6978
8073
|
if (token.startsWith("/")) {
|
|
6979
|
-
candidateFromRoot =
|
|
8074
|
+
candidateFromRoot = path11.join(repoRoot, token.slice(1));
|
|
6980
8075
|
} else if (looksLikeRelPath) {
|
|
6981
|
-
candidateFromRoot =
|
|
8076
|
+
candidateFromRoot = path11.join(repoRoot, token);
|
|
6982
8077
|
} else {
|
|
6983
8078
|
candidateFromRoot = void 0;
|
|
6984
8079
|
}
|
|
6985
8080
|
const tryPushIfProd = (absPath) => {
|
|
6986
|
-
const norm =
|
|
8081
|
+
const norm = path11.resolve(absPath).replace(/\\/g, "/");
|
|
6987
8082
|
const isTest = /(^|\/)tests?\//i.test(norm) || /\.(test|spec)\.[tj]sx?$/i.test(norm);
|
|
6988
8083
|
if (!isTest && fsSync3.existsSync(norm)) {
|
|
6989
8084
|
results.add(norm);
|
|
@@ -7005,7 +8100,7 @@ var program = async () => {
|
|
|
7005
8100
|
}),
|
|
7006
8101
|
timeoutMs: 4e3
|
|
7007
8102
|
});
|
|
7008
|
-
const matches = out.split(/\r?\n/).map((line) => line.trim()).filter(Boolean).map((rel) =>
|
|
8103
|
+
const matches = out.split(/\r?\n/).map((line) => line.trim()).filter(Boolean).map((rel) => path11.resolve(repoRoot, rel).replace(/\\/g, "/")).filter(
|
|
7009
8104
|
(abs) => !abs.includes("/node_modules/") && !abs.includes("/coverage/") && !/(^|\/)tests?\//i.test(abs) && !/\.(test|spec)\.[tj]sx?$/i.test(abs)
|
|
7010
8105
|
);
|
|
7011
8106
|
matches.forEach((abs) => results.add(abs));
|
|
@@ -7026,8 +8121,8 @@ var program = async () => {
|
|
|
7026
8121
|
const jestDiscoveryArgs = selectionIncludesProdPaths ? stripPathTokens(jest) : jest;
|
|
7027
8122
|
const projectConfigs = [];
|
|
7028
8123
|
try {
|
|
7029
|
-
const baseCfg =
|
|
7030
|
-
const tsCfg =
|
|
8124
|
+
const baseCfg = path11.resolve("jest.config.js");
|
|
8125
|
+
const tsCfg = path11.resolve("jest.ts.config.js");
|
|
7031
8126
|
if (fsSync3.existsSync(baseCfg)) {
|
|
7032
8127
|
projectConfigs.push(baseCfg);
|
|
7033
8128
|
}
|
|
@@ -7044,7 +8139,7 @@ var program = async () => {
|
|
|
7044
8139
|
);
|
|
7045
8140
|
const prodSelections2 = expandedProdSelections;
|
|
7046
8141
|
for (const cfg of projectConfigs) {
|
|
7047
|
-
const cfgCwd =
|
|
8142
|
+
const cfgCwd = path11.dirname(cfg);
|
|
7048
8143
|
const allTests = await discoverJestResilient([...jestDiscoveryArgs, "--config", cfg], {
|
|
7049
8144
|
cwd: cfgCwd
|
|
7050
8145
|
});
|
|
@@ -7057,7 +8152,7 @@ var program = async () => {
|
|
|
7057
8152
|
});
|
|
7058
8153
|
} catch (err) {
|
|
7059
8154
|
if (isDebug()) {
|
|
7060
|
-
console.warn(`direct selection failed for project ${
|
|
8155
|
+
console.warn(`direct selection failed for project ${path11.basename(cfg)}: ${String(err)}`);
|
|
7061
8156
|
}
|
|
7062
8157
|
}
|
|
7063
8158
|
perProjectFiles.set(cfg, directPerProject);
|
|
@@ -7069,7 +8164,7 @@ var program = async () => {
|
|
|
7069
8164
|
)} | related=${selectionIncludesProdPaths} | cwd=${repoRootForDiscovery}`
|
|
7070
8165
|
);
|
|
7071
8166
|
for (const cfg of projectConfigs) {
|
|
7072
|
-
const cfgCwd =
|
|
8167
|
+
const cfgCwd = path11.dirname(cfg);
|
|
7073
8168
|
const files = await discoverJestResilient([...jestDiscoveryArgs, "--config", cfg], {
|
|
7074
8169
|
cwd: cfgCwd
|
|
7075
8170
|
});
|
|
@@ -7084,13 +8179,13 @@ var program = async () => {
|
|
|
7084
8179
|
);
|
|
7085
8180
|
const candidates = selectionHasPaths && selectionLooksLikeTest ? selectionTestPaths : files;
|
|
7086
8181
|
const absFiles = candidates.map(
|
|
7087
|
-
(candidatePath) =>
|
|
8182
|
+
(candidatePath) => path11.isAbsolute(candidatePath) ? candidatePath : path11.join(repoRootForDiscovery, candidatePath)
|
|
7088
8183
|
).map((absolutePath) => absolutePath.replace(/\\/g, "/"));
|
|
7089
8184
|
const onlyOwned = await filterCandidatesForProject(
|
|
7090
8185
|
cfg,
|
|
7091
8186
|
jestDiscoveryArgs,
|
|
7092
8187
|
absFiles,
|
|
7093
|
-
|
|
8188
|
+
path11.dirname(cfg)
|
|
7094
8189
|
);
|
|
7095
8190
|
perProjectFiltered.set(cfg, onlyOwned);
|
|
7096
8191
|
}
|
|
@@ -7102,7 +8197,7 @@ var program = async () => {
|
|
|
7102
8197
|
if (selectionHasPaths && prodSelections.length > 0) {
|
|
7103
8198
|
console.info(`rg related \u2192 prodSelections=${prodSelections.length} (starting)`);
|
|
7104
8199
|
const repoRootForRefinement = workspaceRoot ?? await findRepoRoot();
|
|
7105
|
-
const selectionKey = prodSelections.map((absPath) =>
|
|
8200
|
+
const selectionKey = prodSelections.map((absPath) => path11.relative(repoRootForRefinement, absPath).replace(/\\/g, "/")).sort((firstPath, secondPath) => firstPath.localeCompare(secondPath)).join("|");
|
|
7106
8201
|
const { cachedRelated: cachedRelated2, findRelatedTestsFast: findRelatedTestsFast2, DEFAULT_TEST_GLOBS: DEFAULT_TEST_GLOBS2 } = await Promise.resolve().then(() => (init_fast_related(), fast_related_exports));
|
|
7107
8202
|
const { DEFAULT_EXCLUDE: DEFAULT_EXCLUDE2 } = await Promise.resolve().then(() => (init_args(), args_exports));
|
|
7108
8203
|
const rgMatches = await cachedRelated2({
|
|
@@ -7132,7 +8227,7 @@ var program = async () => {
|
|
|
7132
8227
|
cfg,
|
|
7133
8228
|
jestDiscoveryArgs,
|
|
7134
8229
|
rgCandidates,
|
|
7135
|
-
|
|
8230
|
+
path11.dirname(cfg)
|
|
7136
8231
|
);
|
|
7137
8232
|
perProjectFromRg.set(cfg, owned);
|
|
7138
8233
|
}
|
|
@@ -7147,9 +8242,9 @@ var program = async () => {
|
|
|
7147
8242
|
} else {
|
|
7148
8243
|
const repoRootForScan = repoRootForDiscovery;
|
|
7149
8244
|
const toSeeds = (abs) => {
|
|
7150
|
-
const rel =
|
|
8245
|
+
const rel = path11.relative(repoRootForScan, abs).replace(/\\/g, "/");
|
|
7151
8246
|
const withoutExt = rel.replace(/\.(m?[tj]sx?)$/i, "");
|
|
7152
|
-
const base =
|
|
8247
|
+
const base = path11.basename(withoutExt);
|
|
7153
8248
|
const segs = withoutExt.split("/");
|
|
7154
8249
|
const tail2 = segs.slice(-2).join("/");
|
|
7155
8250
|
return Array.from(new Set([withoutExt, base, tail2].filter(Boolean)));
|
|
@@ -7164,8 +8259,8 @@ var program = async () => {
|
|
|
7164
8259
|
}
|
|
7165
8260
|
};
|
|
7166
8261
|
const resolveLocalImport = (fromFile, spec) => {
|
|
7167
|
-
const baseDir =
|
|
7168
|
-
const cand =
|
|
8262
|
+
const baseDir = path11.dirname(fromFile);
|
|
8263
|
+
const cand = path11.resolve(baseDir, spec);
|
|
7169
8264
|
const exts = ["", ".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"];
|
|
7170
8265
|
for (const ext of exts) {
|
|
7171
8266
|
const full = ext ? `${cand}${ext}` : cand;
|
|
@@ -7174,7 +8269,7 @@ var program = async () => {
|
|
|
7174
8269
|
}
|
|
7175
8270
|
}
|
|
7176
8271
|
for (const ext of exts) {
|
|
7177
|
-
const full =
|
|
8272
|
+
const full = path11.join(cand, `index${ext}`);
|
|
7178
8273
|
if (fsSync3.existsSync(full)) {
|
|
7179
8274
|
return full;
|
|
7180
8275
|
}
|
|
@@ -7228,7 +8323,7 @@ var program = async () => {
|
|
|
7228
8323
|
cfg,
|
|
7229
8324
|
jestDiscoveryArgs,
|
|
7230
8325
|
keptCandidates,
|
|
7231
|
-
|
|
8326
|
+
path11.dirname(cfg)
|
|
7232
8327
|
);
|
|
7233
8328
|
perProjectFromScan.set(cfg, owned);
|
|
7234
8329
|
}
|
|
@@ -7254,24 +8349,41 @@ var program = async () => {
|
|
|
7254
8349
|
}
|
|
7255
8350
|
if (effectiveJestFiles.length === 0) {
|
|
7256
8351
|
const repoRoot = repoRootForRefinement;
|
|
8352
|
+
if (jestFiles.length === 0) {
|
|
8353
|
+
try {
|
|
8354
|
+
const allAcross = [];
|
|
8355
|
+
for (const cfg of projectConfigs) {
|
|
8356
|
+
const cfgCwd = path11.dirname(cfg);
|
|
8357
|
+
const listed = await discoverJestResilient([...jestDiscoveryArgs, "--config", cfg], {
|
|
8358
|
+
cwd: cfgCwd
|
|
8359
|
+
});
|
|
8360
|
+
allAcross.push(...listed);
|
|
8361
|
+
}
|
|
8362
|
+
const uniqAll = Array.from(new Set(allAcross.map((p) => p.replace(/\\/g, "/"))));
|
|
8363
|
+
if (uniqAll.length > 0) {
|
|
8364
|
+
jestFiles = uniqAll;
|
|
8365
|
+
}
|
|
8366
|
+
} catch {
|
|
8367
|
+
}
|
|
8368
|
+
}
|
|
7257
8369
|
const seeds = prodSelections.map(
|
|
7258
|
-
(abs) =>
|
|
8370
|
+
(abs) => path11.relative(repoRoot, abs).replace(/\\/g, "/").replace(/\.(m?[tj]sx?)$/i, "")
|
|
7259
8371
|
).flatMap((rel) => {
|
|
7260
|
-
const base =
|
|
8372
|
+
const base = path11.basename(rel);
|
|
7261
8373
|
const segments = rel.split("/");
|
|
7262
8374
|
return Array.from(new Set([rel, base, segments.slice(-2).join("/")].filter(Boolean)));
|
|
7263
8375
|
});
|
|
7264
8376
|
const includesSeed = (text) => seeds.some((seed) => text.includes(seed));
|
|
7265
8377
|
const tryReadFile = async (absPath) => {
|
|
7266
8378
|
try {
|
|
7267
|
-
return await
|
|
8379
|
+
return await fs7.readFile(absPath, "utf8");
|
|
7268
8380
|
} catch {
|
|
7269
8381
|
return "";
|
|
7270
8382
|
}
|
|
7271
8383
|
};
|
|
7272
8384
|
const resolveLocalImport = (fromFile, spec) => {
|
|
7273
|
-
const baseDir =
|
|
7274
|
-
const candidate =
|
|
8385
|
+
const baseDir = path11.dirname(fromFile);
|
|
8386
|
+
const candidate = path11.resolve(baseDir, spec);
|
|
7275
8387
|
const extensions = ["", ".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".mts", ".cts"];
|
|
7276
8388
|
for (const ext of extensions) {
|
|
7277
8389
|
const fullPath = ext ? `${candidate}${ext}` : candidate;
|
|
@@ -7280,7 +8392,7 @@ var program = async () => {
|
|
|
7280
8392
|
}
|
|
7281
8393
|
}
|
|
7282
8394
|
for (const ext of extensions) {
|
|
7283
|
-
const fullPath =
|
|
8395
|
+
const fullPath = path11.join(candidate, `index${ext}`);
|
|
7284
8396
|
if (fsSync3.existsSync(fullPath)) {
|
|
7285
8397
|
return fullPath;
|
|
7286
8398
|
}
|
|
@@ -7437,10 +8549,10 @@ var program = async () => {
|
|
|
7437
8549
|
};
|
|
7438
8550
|
const prodSeedsForRun = (() => {
|
|
7439
8551
|
const changedAbs = (changedSelectionAbs ?? []).map(
|
|
7440
|
-
(absPath) =>
|
|
8552
|
+
(absPath) => path11.resolve(absPath).replace(/\\/g, "/")
|
|
7441
8553
|
);
|
|
7442
8554
|
const selAbs = selectionPathsAugmented.map(
|
|
7443
|
-
(pathToken) =>
|
|
8555
|
+
(pathToken) => path11.resolve(pathToken).replace(/\\/g, "/")
|
|
7444
8556
|
);
|
|
7445
8557
|
return (changedAbs.length ? changedAbs : selAbs).filter(
|
|
7446
8558
|
(abs) => /[\\/]/.test(abs) && !/(^|\/)tests?\//i.test(abs) && !/\.(test|spec)\.[tj]sx?$/i.test(abs)
|
|
@@ -7455,29 +8567,50 @@ var program = async () => {
|
|
|
7455
8567
|
const cfg = projectsToRun[projIndex];
|
|
7456
8568
|
const files = perProjectFiltered.get(cfg) ?? [];
|
|
7457
8569
|
if (files.length === 0) {
|
|
7458
|
-
console.info(`Project ${
|
|
8570
|
+
console.info(`Project ${path11.basename(cfg)}: 0 matching tests after filter; skipping.`);
|
|
7459
8571
|
continue;
|
|
7460
8572
|
}
|
|
7461
8573
|
files.forEach(
|
|
7462
|
-
(absTestPath) => executedTestFilesSet.add(
|
|
8574
|
+
(absTestPath) => executedTestFilesSet.add(path11.resolve(absTestPath).replace(/\\/g, "/"))
|
|
7463
8575
|
);
|
|
7464
|
-
const outJson =
|
|
8576
|
+
const outJson = path11.join(
|
|
7465
8577
|
os2.tmpdir(),
|
|
7466
8578
|
`jest-bridge-${Date.now()}-${Math.random().toString(36).slice(2)}.json`
|
|
7467
8579
|
);
|
|
7468
|
-
const reporterPath =
|
|
8580
|
+
const reporterPath = path11.resolve("scripts/jest-vitest-bridge.cjs");
|
|
7469
8581
|
try {
|
|
7470
|
-
|
|
7471
|
-
|
|
8582
|
+
const needsWrite = (() => {
|
|
8583
|
+
try {
|
|
8584
|
+
const existing = fsSync3.readFileSync(reporterPath, "utf8");
|
|
8585
|
+
return existing !== JEST_BRIDGE_REPORTER_SOURCE;
|
|
8586
|
+
} catch {
|
|
8587
|
+
return true;
|
|
8588
|
+
}
|
|
8589
|
+
})();
|
|
8590
|
+
if (needsWrite) {
|
|
8591
|
+
fsSync3.mkdirSync(path11.dirname(reporterPath), { recursive: true });
|
|
7472
8592
|
fsSync3.writeFileSync(reporterPath, JEST_BRIDGE_REPORTER_SOURCE, "utf8");
|
|
7473
8593
|
}
|
|
8594
|
+
const envPath = path11.resolve("scripts/jest-bridge-env.cjs");
|
|
8595
|
+
try {
|
|
8596
|
+
const existingEnv = fsSync3.readFileSync(envPath, "utf8");
|
|
8597
|
+
if (existingEnv !== JEST_BRIDGE_ENV_SOURCE) {
|
|
8598
|
+
fsSync3.writeFileSync(envPath, JEST_BRIDGE_ENV_SOURCE, "utf8");
|
|
8599
|
+
}
|
|
8600
|
+
} catch {
|
|
8601
|
+
try {
|
|
8602
|
+
fsSync3.mkdirSync(path11.dirname(envPath), { recursive: true });
|
|
8603
|
+
} catch {
|
|
8604
|
+
}
|
|
8605
|
+
fsSync3.writeFileSync(envPath, JEST_BRIDGE_ENV_SOURCE, "utf8");
|
|
8606
|
+
}
|
|
7474
8607
|
} catch (ensureReporterError) {
|
|
7475
8608
|
console.warn(`Unable to ensure jest bridge reporter: ${String(ensureReporterError)}`);
|
|
7476
8609
|
}
|
|
7477
|
-
const selectedFilesForCoverage = selectionPathsAugmented.filter((pathToken) => /[\\/]/.test(pathToken)).filter((pathToken) => !looksLikeTestPath(pathToken)).map((pathToken) =>
|
|
8610
|
+
const selectedFilesForCoverage = selectionPathsAugmented.filter((pathToken) => /[\\/]/.test(pathToken)).filter((pathToken) => !looksLikeTestPath(pathToken)).map((pathToken) => path11.relative(repoRootForDiscovery, pathToken).replace(/\\\\/g, "/")).filter((rel) => rel && !/^\.+\//.test(rel)).map((rel) => rel.startsWith("./") ? rel : `./${rel}`);
|
|
7478
8611
|
const coverageFromArgs = [];
|
|
7479
|
-
for (const
|
|
7480
|
-
coverageFromArgs.push("--collectCoverageFrom",
|
|
8612
|
+
for (const relPath2 of selectedFilesForCoverage) {
|
|
8613
|
+
coverageFromArgs.push("--collectCoverageFrom", relPath2);
|
|
7481
8614
|
}
|
|
7482
8615
|
const { code, output } = await runWithCapture(
|
|
7483
8616
|
babelNodeBin,
|
|
@@ -7487,17 +8620,16 @@ var program = async () => {
|
|
|
7487
8620
|
jestBin,
|
|
7488
8621
|
"--config",
|
|
7489
8622
|
cfg,
|
|
8623
|
+
"--testLocationInResults",
|
|
7490
8624
|
"--runTestsByPath",
|
|
7491
8625
|
`--reporters=${reporterPath}`,
|
|
7492
|
-
"--silent",
|
|
7493
8626
|
"--colors",
|
|
7494
|
-
"--
|
|
7495
|
-
"
|
|
7496
|
-
outJson,
|
|
8627
|
+
"--env",
|
|
8628
|
+
path11.resolve("scripts/jest-bridge-env.cjs"),
|
|
7497
8629
|
...sanitizedJestRunArgs,
|
|
7498
8630
|
...collectCoverage ? [
|
|
7499
8631
|
"--coverageDirectory",
|
|
7500
|
-
|
|
8632
|
+
path11.join("coverage", "jest", path11.basename(cfg).replace(/[^a-zA-Z0-9_.-]+/g, "_"))
|
|
7501
8633
|
] : [],
|
|
7502
8634
|
...coverageFromArgs,
|
|
7503
8635
|
"--passWithNoTests",
|
|
@@ -7530,17 +8662,23 @@ var program = async () => {
|
|
|
7530
8662
|
...bridge,
|
|
7531
8663
|
testResults: sortTestResultsWithRank(fileRank, bridge.testResults).reverse()
|
|
7532
8664
|
};
|
|
7533
|
-
pretty = renderVitestFromJestJSON(
|
|
7534
|
-
|
|
7535
|
-
|
|
7536
|
-
|
|
7537
|
-
|
|
8665
|
+
pretty = renderVitestFromJestJSON(
|
|
8666
|
+
reordered,
|
|
8667
|
+
makeCtx(
|
|
8668
|
+
{ cwd: repoRootForDiscovery, ...editorCmd !== void 0 ? { editorCmd } : {} },
|
|
8669
|
+
/\bFAIL\b/.test(stripAnsiSimple(output))
|
|
8670
|
+
),
|
|
8671
|
+
{ onlyFailures }
|
|
8672
|
+
);
|
|
7538
8673
|
} catch {
|
|
7539
|
-
pretty = renderVitestFromJestJSON(
|
|
7540
|
-
|
|
7541
|
-
|
|
7542
|
-
|
|
7543
|
-
|
|
8674
|
+
pretty = renderVitestFromJestJSON(
|
|
8675
|
+
bridge,
|
|
8676
|
+
makeCtx(
|
|
8677
|
+
{ cwd: repoRootForDiscovery, ...editorCmd !== void 0 ? { editorCmd } : {} },
|
|
8678
|
+
/\bFAIL\b/.test(stripAnsiSimple(output))
|
|
8679
|
+
),
|
|
8680
|
+
{ onlyFailures }
|
|
8681
|
+
);
|
|
7544
8682
|
}
|
|
7545
8683
|
if (debug) {
|
|
7546
8684
|
const preview = pretty.split("\n").slice(0, 3).join("\n");
|
|
@@ -7554,18 +8692,31 @@ ${preview}${pretty.includes("\n") ? "\n\u2026" : ""}`);
|
|
|
7554
8692
|
console.info(String(jsonErr));
|
|
7555
8693
|
console.info(`fallback: raw output lines=${output.split(/\r?\n/).length}`);
|
|
7556
8694
|
}
|
|
7557
|
-
|
|
8695
|
+
pretty = formatJestOutputVitest(output, {
|
|
7558
8696
|
cwd: repoRootForDiscovery,
|
|
7559
8697
|
...editorCmd !== void 0 ? { editorCmd } : {},
|
|
7560
8698
|
onlyFailures
|
|
7561
|
-
};
|
|
7562
|
-
pretty = formatJestOutputVitest(output, renderOpts);
|
|
8699
|
+
});
|
|
7563
8700
|
if (debug) {
|
|
7564
8701
|
const preview = pretty.split("\n").slice(0, 3).join("\n");
|
|
7565
8702
|
console.info(`pretty preview (text):
|
|
7566
8703
|
${preview}${pretty.includes("\n") ? "\n\u2026" : ""}`);
|
|
7567
8704
|
}
|
|
7568
8705
|
}
|
|
8706
|
+
try {
|
|
8707
|
+
const looksSparse = /\n\s*Error:\s*\n/.test(pretty) && !/(Message:|Thrown:|Events:|Console errors:)/.test(pretty);
|
|
8708
|
+
if (looksSparse) {
|
|
8709
|
+
const rawAlso = formatJestOutputVitest(output, {
|
|
8710
|
+
cwd: repoRootForDiscovery,
|
|
8711
|
+
...editorCmd !== void 0 ? { editorCmd } : {},
|
|
8712
|
+
onlyFailures
|
|
8713
|
+
});
|
|
8714
|
+
const merged = `${stripFooter(pretty)}
|
|
8715
|
+
${stripFooter(rawAlso)}`.trimEnd();
|
|
8716
|
+
pretty = merged;
|
|
8717
|
+
}
|
|
8718
|
+
} catch {
|
|
8719
|
+
}
|
|
7569
8720
|
pretty = stripFooter(pretty);
|
|
7570
8721
|
if (pretty.trim().length > 0) {
|
|
7571
8722
|
process.stdout.write(pretty.endsWith("\n") ? pretty : `${pretty}
|
|
@@ -7604,10 +8755,10 @@ ${preview}${pretty.includes("\n") ? "\n\u2026" : ""}`);
|
|
|
7604
8755
|
try {
|
|
7605
8756
|
const prodSeeds = (() => {
|
|
7606
8757
|
const changedAbs = (changedSelectionAbs ?? []).map(
|
|
7607
|
-
(absPath) =>
|
|
8758
|
+
(absPath) => path11.resolve(absPath).replace(/\\/g, "/")
|
|
7608
8759
|
);
|
|
7609
8760
|
const selAbs = selectionPathsAugmented.map(
|
|
7610
|
-
(pathToken) =>
|
|
8761
|
+
(pathToken) => path11.resolve(pathToken).replace(/\\/g, "/")
|
|
7611
8762
|
);
|
|
7612
8763
|
return (changedAbs.length ? changedAbs : selAbs).filter(
|
|
7613
8764
|
(abs) => /[\\/]/.test(abs) && !/(^|\/)tests?\//i.test(abs) && !/\.(test|spec)\.[tj]sx?$/i.test(abs)
|
|
@@ -7621,11 +8772,18 @@ ${preview}${pretty.includes("\n") ? "\n\u2026" : ""}`);
|
|
|
7621
8772
|
unified.testResults = ordered;
|
|
7622
8773
|
} catch {
|
|
7623
8774
|
}
|
|
7624
|
-
const
|
|
7625
|
-
|
|
7626
|
-
|
|
7627
|
-
|
|
7628
|
-
|
|
8775
|
+
const showStacks = Boolean(unified.aggregated?.numFailedTests > 0);
|
|
8776
|
+
let text = renderVitestFromJestJSON(
|
|
8777
|
+
unified,
|
|
8778
|
+
makeCtx(
|
|
8779
|
+
{ cwd: repoRootForDiscovery, ...editorCmd !== void 0 ? { editorCmd } : {} },
|
|
8780
|
+
showStacks
|
|
8781
|
+
),
|
|
8782
|
+
{ onlyFailures }
|
|
8783
|
+
);
|
|
8784
|
+
if (onlyFailures) {
|
|
8785
|
+
text = text.split(/\r?\n/).filter((line) => !/^\s*PASS\b/.test(stripAnsiSimple(line))).join("\n");
|
|
8786
|
+
}
|
|
7629
8787
|
if (text.trim().length > 0) {
|
|
7630
8788
|
process.stdout.write(text.endsWith("\n") ? text : `${text}
|
|
7631
8789
|
`);
|
|
@@ -7670,11 +8828,13 @@ export {
|
|
|
7670
8828
|
barCell,
|
|
7671
8829
|
buildCodeFrameSection,
|
|
7672
8830
|
buildConsoleSection,
|
|
8831
|
+
buildFallbackMessageBlock,
|
|
7673
8832
|
buildFileBadgeLine,
|
|
7674
8833
|
buildMessageSection,
|
|
7675
8834
|
buildPerFileOverview,
|
|
7676
8835
|
buildPrettyDiffSection,
|
|
7677
8836
|
buildStackSection,
|
|
8837
|
+
buildThrownSection,
|
|
7678
8838
|
cell,
|
|
7679
8839
|
coerceJestJsonToBridge,
|
|
7680
8840
|
collapseStacks,
|
|
@@ -7689,32 +8849,30 @@ export {
|
|
|
7689
8849
|
discoverJestResilient,
|
|
7690
8850
|
discoverTargets,
|
|
7691
8851
|
drawFailLine,
|
|
7692
|
-
drawFailRule,
|
|
7693
8852
|
drawRule,
|
|
7694
8853
|
emitMergedCoverage,
|
|
7695
|
-
extractAssertionMessage,
|
|
7696
|
-
extractExpectedReceived,
|
|
7697
|
-
extractFromUnifiedDiff,
|
|
7698
8854
|
filterCandidatesForProject,
|
|
7699
8855
|
filterCoverageMap,
|
|
7700
8856
|
findCodeFrameStart,
|
|
7701
8857
|
findRepoRoot,
|
|
7702
8858
|
firstTestLocation,
|
|
7703
8859
|
formatJestOutputVitest,
|
|
7704
|
-
indentBlock,
|
|
7705
8860
|
isFilesObject,
|
|
7706
8861
|
isStackLine,
|
|
8862
|
+
isTransportError,
|
|
7707
8863
|
isTruthy,
|
|
7708
|
-
labelForMessage,
|
|
7709
8864
|
linesFromDetails,
|
|
7710
8865
|
linkifyPadded,
|
|
8866
|
+
makeCtx,
|
|
7711
8867
|
mergeLcov,
|
|
7712
8868
|
missedBranches,
|
|
7713
8869
|
missedFunctions,
|
|
8870
|
+
mkPrettyFns,
|
|
7714
8871
|
osc8,
|
|
7715
8872
|
parseActionsFromTokens,
|
|
8873
|
+
parseMethodPathFromTitle,
|
|
8874
|
+
pickRelevantHttp,
|
|
7716
8875
|
preferredEditorHref,
|
|
7717
|
-
prettifyPrettyFormatBlock,
|
|
7718
8876
|
printCompactCoverage,
|
|
7719
8877
|
printDetailedCoverage,
|
|
7720
8878
|
printPerFileCompositeTables,
|
|
@@ -7722,19 +8880,21 @@ export {
|
|
|
7722
8880
|
readCoverageJson,
|
|
7723
8881
|
registerSignalHandlersOnce,
|
|
7724
8882
|
relativizeForMatch,
|
|
8883
|
+
renderCodeFrame,
|
|
7725
8884
|
renderPerFileCompositeTable,
|
|
7726
8885
|
renderRunLine,
|
|
7727
|
-
renderSourceCodeFrame,
|
|
7728
8886
|
renderTable,
|
|
7729
8887
|
renderVitestFromJestJSON,
|
|
7730
8888
|
resolveImportWithRoot,
|
|
8889
|
+
routeSimilarityScore,
|
|
7731
8890
|
rule,
|
|
7732
8891
|
runJestBootstrap,
|
|
7733
|
-
|
|
8892
|
+
scoreHttpForAssertion,
|
|
7734
8893
|
stripAnsiSimple,
|
|
7735
8894
|
supportsUnicode,
|
|
7736
8895
|
tintPct,
|
|
7737
8896
|
toPosix,
|
|
8897
|
+
tryBridgeFallback,
|
|
7738
8898
|
tryResolveFile
|
|
7739
8899
|
};
|
|
7740
8900
|
//# sourceMappingURL=index.js.map
|