headlamp 0.1.10 → 0.1.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.cjs +1855 -735
- package/dist/cli.cjs.map +4 -4
- package/dist/index.js +1913 -823
- 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(
|
|
@@ -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
|
});
|
|
@@ -735,7 +735,9 @@ __export(fast_related_exports, {
|
|
|
735
735
|
findRelatedTestsFast: () => findRelatedTestsFast
|
|
736
736
|
});
|
|
737
737
|
import * as path3 from "node:path";
|
|
738
|
+
import * as os2 from "node:os";
|
|
738
739
|
import * as fs from "node:fs/promises";
|
|
740
|
+
import { createHash } from "node:crypto";
|
|
739
741
|
var TailSegmentCount, EmptyCount, JsonIndentSpaces, DEFAULT_TEST_GLOBS, findRelatedTestsFast, cachedRelated;
|
|
740
742
|
var init_fast_related = __esm({
|
|
741
743
|
"src/lib/fast-related.ts"() {
|
|
@@ -788,9 +790,7 @@ var init_fast_related = __esm({
|
|
|
788
790
|
}
|
|
789
791
|
const args = ["-n", "-l", "-S", "-F"];
|
|
790
792
|
testGlobs.forEach((globPattern) => args.push("-g", globPattern));
|
|
791
|
-
excludeGlobs.forEach(
|
|
792
|
-
(excludeGlobPattern) => args.push("-g", `!${excludeGlobPattern}`)
|
|
793
|
-
);
|
|
793
|
+
excludeGlobs.forEach((excludeGlobPattern) => args.push("-g", `!${excludeGlobPattern}`));
|
|
794
794
|
seeds.forEach((seedToken) => args.push("-e", seedToken));
|
|
795
795
|
let raw = "";
|
|
796
796
|
try {
|
|
@@ -802,9 +802,7 @@ var init_fast_related = __esm({
|
|
|
802
802
|
}
|
|
803
803
|
const lines = raw.split(/\r?\n/).map((lineText) => lineText.trim()).filter(Boolean);
|
|
804
804
|
const looksLikeTest = (pathText) => /\.(test|spec)\.[tj]sx?$/i.test(pathText) || /(^|\/)tests?\//i.test(pathText);
|
|
805
|
-
const absolute = lines.map(
|
|
806
|
-
(relativePath) => path3.resolve(repoRoot, relativePath).replace(/\\/g, "/")
|
|
807
|
-
).filter(looksLikeTest);
|
|
805
|
+
const absolute = lines.map((relativePath) => path3.resolve(repoRoot, relativePath).replace(/\\/g, "/")).filter(looksLikeTest);
|
|
808
806
|
const uniq = Array.from(new Set(absolute));
|
|
809
807
|
const results = [];
|
|
810
808
|
await Promise.all(
|
|
@@ -819,17 +817,15 @@ var init_fast_related = __esm({
|
|
|
819
817
|
return results;
|
|
820
818
|
};
|
|
821
819
|
cachedRelated = async (opts) => {
|
|
822
|
-
const
|
|
820
|
+
const cacheRoot = process.env.HEADLAMP_CACHE_DIR || path3.join(os2.tmpdir(), "headlamp-cache");
|
|
821
|
+
const repoKey = createHash("sha1").update(path3.resolve(opts.repoRoot)).digest("hex").slice(0, 12);
|
|
822
|
+
const cacheDir = path3.join(cacheRoot, repoKey);
|
|
823
823
|
const cacheFile = path3.join(cacheDir, "relevant-tests.json");
|
|
824
824
|
let head = "nogit";
|
|
825
825
|
try {
|
|
826
|
-
const raw = await runText(
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
{
|
|
830
|
-
env: safeEnv(process.env, {})
|
|
831
|
-
}
|
|
832
|
-
);
|
|
826
|
+
const raw = await runText("git", ["-C", opts.repoRoot, "rev-parse", "--short", "HEAD"], {
|
|
827
|
+
env: safeEnv(process.env, {})
|
|
828
|
+
});
|
|
833
829
|
head = raw.trim() || "nogit";
|
|
834
830
|
} catch {
|
|
835
831
|
head = "nogit";
|
|
@@ -861,10 +857,7 @@ var init_fast_related = __esm({
|
|
|
861
857
|
try {
|
|
862
858
|
const next = { ...bag, [key]: Array.from(new Set(recomputed)) };
|
|
863
859
|
await fs.mkdir(cacheDir, { recursive: true });
|
|
864
|
-
await fs.writeFile(
|
|
865
|
-
cacheFile,
|
|
866
|
-
JSON.stringify(next, null, JsonIndentSpaces)
|
|
867
|
-
);
|
|
860
|
+
await fs.writeFile(cacheFile, JSON.stringify(next, null, JsonIndentSpaces));
|
|
868
861
|
} catch {
|
|
869
862
|
}
|
|
870
863
|
return recomputed;
|
|
@@ -1133,8 +1126,8 @@ var require_utils = __commonJS({
|
|
|
1133
1126
|
}
|
|
1134
1127
|
return output;
|
|
1135
1128
|
};
|
|
1136
|
-
exports.basename = (
|
|
1137
|
-
const segs =
|
|
1129
|
+
exports.basename = (path12, { windows } = {}) => {
|
|
1130
|
+
const segs = path12.split(windows ? /[\\/]/ : "/");
|
|
1138
1131
|
const last = segs[segs.length - 1];
|
|
1139
1132
|
if (last === "") {
|
|
1140
1133
|
return segs[segs.length - 2];
|
|
@@ -2250,7 +2243,7 @@ var require_picomatch = __commonJS({
|
|
|
2250
2243
|
var parse = require_parse();
|
|
2251
2244
|
var utils = require_utils();
|
|
2252
2245
|
var constants = require_constants();
|
|
2253
|
-
var
|
|
2246
|
+
var isObject2 = (val) => val && typeof val === "object" && !Array.isArray(val);
|
|
2254
2247
|
var picomatch2 = (glob, options, returnState = false) => {
|
|
2255
2248
|
if (Array.isArray(glob)) {
|
|
2256
2249
|
const fns = glob.map((input) => picomatch2(input, options, returnState));
|
|
@@ -2263,7 +2256,7 @@ var require_picomatch = __commonJS({
|
|
|
2263
2256
|
};
|
|
2264
2257
|
return arrayMatcher;
|
|
2265
2258
|
}
|
|
2266
|
-
const isState =
|
|
2259
|
+
const isState = isObject2(glob) && glob.tokens && glob.input;
|
|
2267
2260
|
if (glob === "" || typeof glob !== "string" && !isState) {
|
|
2268
2261
|
throw new TypeError("Expected pattern to be a non-empty string");
|
|
2269
2262
|
}
|
|
@@ -3503,11 +3496,11 @@ var require_lib = __commonJS({
|
|
|
3503
3496
|
"node_modules/json5/lib/index.js"(exports, module) {
|
|
3504
3497
|
var parse = require_parse2();
|
|
3505
3498
|
var stringify = require_stringify();
|
|
3506
|
-
var
|
|
3499
|
+
var JSON53 = {
|
|
3507
3500
|
parse,
|
|
3508
3501
|
stringify
|
|
3509
3502
|
};
|
|
3510
|
-
module.exports =
|
|
3503
|
+
module.exports = JSON53;
|
|
3511
3504
|
}
|
|
3512
3505
|
});
|
|
3513
3506
|
|
|
@@ -4705,6 +4698,19 @@ var missedFunctions = (file) => {
|
|
|
4705
4698
|
}
|
|
4706
4699
|
return out.sort((firstFunction, secondFunction) => firstFunction.line - secondFunction.line);
|
|
4707
4700
|
};
|
|
4701
|
+
var clamp = (value, lowerBound, upperBound) => Math.max(lowerBound, Math.min(upperBound, value));
|
|
4702
|
+
var renderCodeFrame = (source, miss, context = 3) => {
|
|
4703
|
+
const lines = source.split(/\r?\n/);
|
|
4704
|
+
const from = clamp(miss.start - context, 1, lines.length);
|
|
4705
|
+
const to = clamp(miss.end + context, 1, lines.length);
|
|
4706
|
+
const out = [];
|
|
4707
|
+
for (let ln = from; ln <= to; ln += 1) {
|
|
4708
|
+
const body = lines[ln - 1] ?? "";
|
|
4709
|
+
const tag = ln >= miss.start && ln <= miss.end ? `> ${ln.toString().padStart(4)}|` : ` ${ln.toString().padStart(4)}|`;
|
|
4710
|
+
out.push(`${tag} ${body}`);
|
|
4711
|
+
}
|
|
4712
|
+
return out.join("\n");
|
|
4713
|
+
};
|
|
4708
4714
|
var compositeBarPct = (summary, hotspots) => {
|
|
4709
4715
|
const base = Math.min(
|
|
4710
4716
|
Number.isFinite(summary.lines.pct) ? summary.lines.pct : 0,
|
|
@@ -4970,7 +4976,7 @@ var printCompactCoverage = async (opts) => {
|
|
|
4970
4976
|
);
|
|
4971
4977
|
}
|
|
4972
4978
|
};
|
|
4973
|
-
var shortenPathPreservingFilename = (
|
|
4979
|
+
var shortenPathPreservingFilename = (relPath2, maxWidth, opts) => {
|
|
4974
4980
|
const ellipsis = opts?.ellipsis ?? "\u2026";
|
|
4975
4981
|
const START_HEAD = Math.max(0, opts?.keepHead ?? 1);
|
|
4976
4982
|
const START_TAIL = Math.max(0, opts?.keepTail ?? 1);
|
|
@@ -5142,7 +5148,7 @@ var shortenPathPreservingFilename = (relPath, maxWidth, opts) => {
|
|
|
5142
5148
|
}
|
|
5143
5149
|
return null;
|
|
5144
5150
|
};
|
|
5145
|
-
const normalized =
|
|
5151
|
+
const normalized = relPath2.replace(/\\/g, "/");
|
|
5146
5152
|
if (visibleWidth(normalized) <= maxWidth) {
|
|
5147
5153
|
return normalized;
|
|
5148
5154
|
}
|
|
@@ -5598,34 +5604,81 @@ var printPerFileCompositeTables = async (opts) => {
|
|
|
5598
5604
|
}
|
|
5599
5605
|
};
|
|
5600
5606
|
|
|
5601
|
-
// src/lib/
|
|
5602
|
-
|
|
5603
|
-
|
|
5604
|
-
|
|
5605
|
-
|
|
5606
|
-
var
|
|
5607
|
-
|
|
5608
|
-
|
|
5609
|
-
)
|
|
5610
|
-
|
|
5611
|
-
|
|
5607
|
+
// src/lib/fp.ts
|
|
5608
|
+
function pipe(initial, ...fns) {
|
|
5609
|
+
return fns.reduce((acc, fn) => fn(acc), initial);
|
|
5610
|
+
}
|
|
5611
|
+
var some = (value) => ({ tag: "Some", value });
|
|
5612
|
+
var none = { tag: "None" };
|
|
5613
|
+
var unfoldr = (initial, step2) => {
|
|
5614
|
+
const out = [];
|
|
5615
|
+
for (let state = initial; ; ) {
|
|
5616
|
+
const result = step2(state);
|
|
5617
|
+
if (result.tag === "None") {
|
|
5618
|
+
break;
|
|
5619
|
+
}
|
|
5620
|
+
const [element, next] = result.value;
|
|
5621
|
+
out.push(element);
|
|
5622
|
+
state = next;
|
|
5612
5623
|
}
|
|
5613
|
-
|
|
5614
|
-
return path9.isAbsolute(jsonPath) ? jsonPath : path9.resolve(cwd, jsonPath);
|
|
5624
|
+
return out;
|
|
5615
5625
|
};
|
|
5616
|
-
|
|
5617
|
-
|
|
5618
|
-
|
|
5619
|
-
|
|
5620
|
-
|
|
5621
|
-
|
|
5622
|
-
|
|
5626
|
+
|
|
5627
|
+
// src/lib/formatter/parse.ts
|
|
5628
|
+
var isFailureStart = (lineText) => /^\s*●\s+/.test(lineText);
|
|
5629
|
+
var isSuiteLine = (lineText) => /^\s*(PASS|FAIL)\s+/.test(lineText);
|
|
5630
|
+
var isSummaryLine = (lineText) => /^\s*(Test Suites:|Tests:|Snapshots:|Time:|Ran all)/.test(lineText);
|
|
5631
|
+
var collectFailure = (allLines, startIndex) => {
|
|
5632
|
+
const title = stripAnsiSimple(allLines[startIndex]).replace(/^\s*●\s+/, "").trim();
|
|
5633
|
+
const buf = [allLines[startIndex]];
|
|
5634
|
+
let i = startIndex + 1;
|
|
5635
|
+
for (; i < allLines.length; i += 1) {
|
|
5636
|
+
const simple = stripAnsiSimple(allLines[i]);
|
|
5637
|
+
const nextIsStart = isFailureStart(simple) || isSuiteLine(simple) || isSummaryLine(simple);
|
|
5638
|
+
const prevBlank = stripAnsiSimple(allLines[i - 1] ?? "").trim() === "";
|
|
5639
|
+
if (nextIsStart && prevBlank) {
|
|
5640
|
+
break;
|
|
5641
|
+
}
|
|
5642
|
+
buf.push(allLines[i]);
|
|
5623
5643
|
}
|
|
5624
|
-
|
|
5625
|
-
|
|
5626
|
-
|
|
5644
|
+
return [{ tag: "FailureBlock", title, lines: buf }, i];
|
|
5645
|
+
};
|
|
5646
|
+
var parseSuite = (lineText) => {
|
|
5647
|
+
const match = lineText.match(/^\s*(PASS|FAIL)\s+(.+)$/);
|
|
5648
|
+
return { tag: "PassFail", badge: match[1], rel: match[2] };
|
|
5649
|
+
};
|
|
5650
|
+
var parseChunks = (raw) => {
|
|
5651
|
+
const lines = raw.split(/\r?\n/);
|
|
5652
|
+
return unfoldr({ index: 0 }, (state) => {
|
|
5653
|
+
if (state.index >= lines.length) {
|
|
5654
|
+
return none;
|
|
5655
|
+
}
|
|
5656
|
+
const line = lines[state.index];
|
|
5657
|
+
const simple = stripAnsiSimple(line);
|
|
5658
|
+
if (isFailureStart(simple)) {
|
|
5659
|
+
const [chunk, next] = collectFailure(lines, state.index);
|
|
5660
|
+
return some([chunk, { index: next }]);
|
|
5661
|
+
}
|
|
5662
|
+
if (isSuiteLine(simple)) {
|
|
5663
|
+
return some([parseSuite(simple), { index: state.index + 1 }]);
|
|
5664
|
+
}
|
|
5665
|
+
if (isSummaryLine(simple)) {
|
|
5666
|
+
return some([{ tag: "Summary", line }, { index: state.index + 1 }]);
|
|
5667
|
+
}
|
|
5668
|
+
if (isStackLine(simple)) {
|
|
5669
|
+
return some([{ tag: "Stack", line }, { index: state.index + 1 }]);
|
|
5670
|
+
}
|
|
5671
|
+
return some([{ tag: "Other", line }, { index: state.index + 1 }]);
|
|
5672
|
+
});
|
|
5627
5673
|
};
|
|
5628
|
-
|
|
5674
|
+
|
|
5675
|
+
// src/lib/formatter/render.ts
|
|
5676
|
+
import * as path9 from "node:path";
|
|
5677
|
+
|
|
5678
|
+
// src/lib/formatter/fns.ts
|
|
5679
|
+
var import_json5 = __toESM(require_lib(), 1);
|
|
5680
|
+
import * as fs4 from "node:fs";
|
|
5681
|
+
import * as util from "node:util";
|
|
5629
5682
|
var colorTokens = {
|
|
5630
5683
|
pass: Colors.Success,
|
|
5631
5684
|
fail: Colors.Failure,
|
|
@@ -5636,80 +5689,18 @@ var colorTokens = {
|
|
|
5636
5689
|
failPill: (text) => BackgroundColors.Failure(ansi.white(` ${text} `)),
|
|
5637
5690
|
runPill: (text) => BackgroundColors.Run(ansi.white(` ${text} `))
|
|
5638
5691
|
};
|
|
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
|
-
}
|
|
5692
|
+
var drawRule = (label) => {
|
|
5693
|
+
const width = Math.max(
|
|
5694
|
+
40,
|
|
5695
|
+
process.stdout && process.stdout.columns || 80
|
|
5696
|
+
);
|
|
5697
|
+
if (!label) {
|
|
5698
|
+
return ansi.dim("\u2500".repeat(width));
|
|
5709
5699
|
}
|
|
5710
|
-
|
|
5700
|
+
const plain = stripAnsiSimple(label);
|
|
5701
|
+
const pad = Math.max(1, width - plain.length - 1);
|
|
5702
|
+
return `${ansi.dim("\u2500".repeat(pad))} ${label}`;
|
|
5711
5703
|
};
|
|
5712
|
-
var drawFailRule = (label = " FAIL ") => drawRule(colorTokens.failPill(label));
|
|
5713
5704
|
var drawFailLine = () => {
|
|
5714
5705
|
const width = Math.max(
|
|
5715
5706
|
40,
|
|
@@ -5718,9 +5709,29 @@ var drawFailLine = () => {
|
|
|
5718
5709
|
return colorTokens.fail("\u2500".repeat(width));
|
|
5719
5710
|
};
|
|
5720
5711
|
var renderRunLine = (cwd) => `${colorTokens.runPill("RUN")} ${ansi.dim(cwd.replace(/\\/g, "/"))}`;
|
|
5712
|
+
var buildFileBadgeLine = (rel, failedCount) => failedCount > 0 ? `${colorTokens.failPill("FAIL")} ${ansi.white(rel)}` : `${colorTokens.passPill("PASS")} ${ansi.white(rel)}`;
|
|
5713
|
+
var buildPerFileOverview = (rel, assertions) => {
|
|
5714
|
+
const out = [];
|
|
5715
|
+
out.push(`${ansi.magenta(rel)} ${ansi.dim(`(${assertions.length})`)}`);
|
|
5716
|
+
for (const assertion of assertions) {
|
|
5717
|
+
const name = assertion.fullName;
|
|
5718
|
+
if (assertion.status === "passed") {
|
|
5719
|
+
out.push(` ${colorTokens.pass("\u2713")} ${ansi.dim(name)}`);
|
|
5720
|
+
} else if (assertion.status === "todo") {
|
|
5721
|
+
out.push(` ${colorTokens.todo("\u2610")} ${ansi.dim(name)} ${colorTokens.todo("[todo]")}`);
|
|
5722
|
+
} else if (assertion.status === "pending") {
|
|
5723
|
+
out.push(` ${colorTokens.skip("\u2193")} ${ansi.dim(name)} ${colorTokens.skip("[skipped]")}`);
|
|
5724
|
+
} else {
|
|
5725
|
+
out.push(` ${colorTokens.fail("\xD7")} ${ansi.white(name)}`);
|
|
5726
|
+
}
|
|
5727
|
+
}
|
|
5728
|
+
out.push("");
|
|
5729
|
+
return out;
|
|
5730
|
+
};
|
|
5731
|
+
var isObjectRecord = (value) => typeof value === "object" && value !== null;
|
|
5721
5732
|
var colorStackLine = (line, projectHint) => {
|
|
5722
5733
|
const plainLine = stripAnsiSimple(line);
|
|
5723
|
-
if (
|
|
5734
|
+
if (!/\s+at\s+/.test(plainLine)) {
|
|
5724
5735
|
return plainLine;
|
|
5725
5736
|
}
|
|
5726
5737
|
const match = plainLine.match(/\(?([^\s()]+):(\d+):(\d+)\)?$/);
|
|
@@ -5736,255 +5747,257 @@ var colorStackLine = (line, projectHint) => {
|
|
|
5736
5747
|
`(${coloredPath}${ansi.dim(":")}${ansi.white(`${lineNumber}:${columnNumber}`)})`
|
|
5737
5748
|
);
|
|
5738
5749
|
};
|
|
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}`);
|
|
5750
|
+
var extractBridgePath = (raw, cwd) => {
|
|
5751
|
+
const re = /Test results written to:\s+([^\n\r]+jest-bridge-[^\s'"]+\.json)/g;
|
|
5752
|
+
const matches = Array.from(raw.matchAll(re));
|
|
5753
|
+
if (matches.length === 0) {
|
|
5754
|
+
return null;
|
|
5765
5755
|
}
|
|
5766
|
-
|
|
5756
|
+
const jsonPath = (matches[matches.length - 1][1] ?? "").trim().replace(/^["'`]|["'`]$/g, "");
|
|
5757
|
+
return /^\//.test(jsonPath) ? jsonPath : `${cwd.replace(/\\/g, "/")}/${jsonPath}`;
|
|
5767
5758
|
};
|
|
5759
|
+
var findCodeFrameStart = (lines) => lines.findIndex((line) => /^\s*(>?\s*\d+\s*\|)/.test(stripAnsiSimple(line)));
|
|
5768
5760
|
var _sourceCache = /* @__PURE__ */ new Map();
|
|
5769
5761
|
var readSource = (file) => {
|
|
5770
|
-
const
|
|
5771
|
-
const
|
|
5772
|
-
if (
|
|
5773
|
-
return
|
|
5762
|
+
const normalized = file.replace(/\\/g, "/");
|
|
5763
|
+
const hit = _sourceCache.get(normalized);
|
|
5764
|
+
if (hit) {
|
|
5765
|
+
return hit;
|
|
5774
5766
|
}
|
|
5775
5767
|
try {
|
|
5776
|
-
const
|
|
5777
|
-
|
|
5778
|
-
_sourceCache.set(normalizedFile, arr);
|
|
5768
|
+
const arr = fs4.readFileSync(normalized, "utf8").split(/\r?\n/);
|
|
5769
|
+
_sourceCache.set(normalized, arr);
|
|
5779
5770
|
return arr;
|
|
5780
5771
|
} catch {
|
|
5781
5772
|
return [];
|
|
5782
5773
|
}
|
|
5783
5774
|
};
|
|
5775
|
+
var renderInlineCodeFrame = (lines, start) => {
|
|
5776
|
+
const out = [];
|
|
5777
|
+
for (let i = start; i < lines.length; i += 1) {
|
|
5778
|
+
const raw = stripAnsiSimple(lines[i]);
|
|
5779
|
+
if (!raw.trim()) {
|
|
5780
|
+
break;
|
|
5781
|
+
}
|
|
5782
|
+
if (/^\s*\^+\s*$/.test(raw)) {
|
|
5783
|
+
out.push(` ${Colors.Failure(raw.trimEnd())}`);
|
|
5784
|
+
} else {
|
|
5785
|
+
const ptr = raw.match(/^\s*>(\s*\d+)\s*\|\s?(.*)$/);
|
|
5786
|
+
if (ptr) {
|
|
5787
|
+
const num = ansi.dim(ptr[1].trim());
|
|
5788
|
+
const code = ansi.yellow(ptr[2] ?? "");
|
|
5789
|
+
out.push(` ${Colors.Failure(">")} ${num} ${ansi.dim("|")} ${code}`);
|
|
5790
|
+
} else {
|
|
5791
|
+
const nor = raw.match(/^\s*(\d+)\s*\|\s?(.*)$/);
|
|
5792
|
+
if (nor) {
|
|
5793
|
+
const num = ansi.dim(nor[1]);
|
|
5794
|
+
const code = ansi.dim(nor[2] ?? "");
|
|
5795
|
+
out.push(` ${num} ${ansi.dim("|")} ${code}`);
|
|
5796
|
+
} else {
|
|
5797
|
+
out.push(` ${raw}`);
|
|
5798
|
+
}
|
|
5799
|
+
}
|
|
5800
|
+
}
|
|
5801
|
+
}
|
|
5802
|
+
return out;
|
|
5803
|
+
};
|
|
5784
5804
|
var renderSourceCodeFrame = (file, line, context = 3) => {
|
|
5785
5805
|
const lines = readSource(file);
|
|
5786
|
-
if (
|
|
5806
|
+
if (lines.length === 0 || !Number.isFinite(line)) {
|
|
5787
5807
|
return [];
|
|
5788
5808
|
}
|
|
5789
5809
|
const idx = Math.max(1, Math.min(line, lines.length));
|
|
5790
5810
|
const start = Math.max(1, idx - context);
|
|
5791
5811
|
const end = Math.min(lines.length, idx + context);
|
|
5792
5812
|
const out = [];
|
|
5793
|
-
for (let
|
|
5794
|
-
const num = ansi.dim(String(
|
|
5795
|
-
const code =
|
|
5796
|
-
if (
|
|
5797
|
-
out.push(` ${
|
|
5813
|
+
for (let current = start; current <= end; current += 1) {
|
|
5814
|
+
const num = ansi.dim(String(current));
|
|
5815
|
+
const code = current === idx ? ansi.yellow(lines[current - 1] ?? "") : ansi.dim(lines[current - 1] ?? "");
|
|
5816
|
+
if (current === idx) {
|
|
5817
|
+
out.push(` ${Colors.Failure(">")} ${num} ${ansi.dim("|")} ${code}`);
|
|
5798
5818
|
} else {
|
|
5799
5819
|
out.push(` ${num} ${ansi.dim("|")} ${code}`);
|
|
5800
5820
|
}
|
|
5801
5821
|
}
|
|
5802
|
-
out.push(` ${
|
|
5822
|
+
out.push(` ${Colors.Failure("^")}`);
|
|
5803
5823
|
return out;
|
|
5804
5824
|
};
|
|
5805
|
-
var
|
|
5806
|
-
|
|
5807
|
-
|
|
5825
|
+
var stackLocation = (line) => {
|
|
5826
|
+
const match = stripAnsiSimple(line).match(/\(?([^\s()]+):(\d+):\d+\)?$/);
|
|
5827
|
+
if (!match) {
|
|
5828
|
+
return null;
|
|
5829
|
+
}
|
|
5830
|
+
return { file: match[1].replace(/\\/g, "/"), line: Number(match[2]) };
|
|
5831
|
+
};
|
|
5832
|
+
var deepestProjectLoc = (stackLines, projectHint) => {
|
|
5833
|
+
for (let i = stackLines.length - 1; i >= 0; i -= 1) {
|
|
5834
|
+
const simple = stripAnsiSimple(stackLines[i]);
|
|
5808
5835
|
if (isStackLine(simple) && projectHint.test(simple) && !/node_modules|vitest|jest/.test(simple)) {
|
|
5809
|
-
return i;
|
|
5836
|
+
return stackLocation(stackLines[i]);
|
|
5810
5837
|
}
|
|
5811
5838
|
}
|
|
5812
|
-
return
|
|
5839
|
+
return null;
|
|
5813
5840
|
};
|
|
5814
|
-
var
|
|
5815
|
-
const
|
|
5816
|
-
|
|
5817
|
-
|
|
5841
|
+
var buildCodeFrameSection = (messageLines, ctx, synthLoc) => {
|
|
5842
|
+
const out = [];
|
|
5843
|
+
const start = findCodeFrameStart(messageLines);
|
|
5844
|
+
if (start >= 0) {
|
|
5845
|
+
out.push(...renderInlineCodeFrame(messageLines, start), "");
|
|
5846
|
+
return out;
|
|
5818
5847
|
}
|
|
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)}`);
|
|
5848
|
+
if (ctx.showStacks && synthLoc) {
|
|
5849
|
+
out.push(...renderSourceCodeFrame(synthLoc.file, synthLoc.line), "");
|
|
5850
|
+
}
|
|
5851
|
+
return out;
|
|
5826
5852
|
};
|
|
5827
|
-
var
|
|
5828
|
-
var
|
|
5829
|
-
|
|
5830
|
-
|
|
5831
|
-
|
|
5832
|
-
|
|
5833
|
-
|
|
5834
|
-
|
|
5835
|
-
|
|
5853
|
+
var normalizeBlock = (raw) => raw.replace(/^\s*Array\s*\[/, "[").replace(/^\s*Object\s*\{/, "{").replace(/,(\s*[\]}])/g, "$1");
|
|
5854
|
+
var stringifyPrettierish = (value) => {
|
|
5855
|
+
if (typeof value === "string") {
|
|
5856
|
+
const text = normalizeBlock(value.trim());
|
|
5857
|
+
try {
|
|
5858
|
+
const parsed = import_json5.default.parse(text);
|
|
5859
|
+
return JSON.stringify(parsed, null, 2);
|
|
5860
|
+
} catch {
|
|
5861
|
+
return value;
|
|
5862
|
+
}
|
|
5836
5863
|
}
|
|
5837
|
-
|
|
5838
|
-
|
|
5839
|
-
|
|
5840
|
-
|
|
5841
|
-
|
|
5842
|
-
|
|
5843
|
-
|
|
5844
|
-
|
|
5864
|
+
if (Array.isArray(value) || isObjectRecord(value)) {
|
|
5865
|
+
try {
|
|
5866
|
+
return JSON.stringify(value, null, 2);
|
|
5867
|
+
} catch {
|
|
5868
|
+
return util.inspect(value, {
|
|
5869
|
+
depth: 10,
|
|
5870
|
+
breakLength: Infinity,
|
|
5871
|
+
compact: false,
|
|
5872
|
+
sorted: true
|
|
5873
|
+
});
|
|
5845
5874
|
}
|
|
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
5875
|
}
|
|
5859
|
-
|
|
5860
|
-
|
|
5876
|
+
return util.inspect(value, {
|
|
5877
|
+
depth: 10,
|
|
5878
|
+
breakLength: Infinity,
|
|
5879
|
+
compact: false,
|
|
5880
|
+
sorted: true
|
|
5881
|
+
});
|
|
5882
|
+
};
|
|
5883
|
+
var isArrayOfPrimitives = (value) => Array.isArray(value) && value.every(
|
|
5884
|
+
(element) => ["string", "number", "boolean"].includes(typeof element) || element === null
|
|
5885
|
+
);
|
|
5886
|
+
var extractFromUnifiedDiff = (rawLines) => {
|
|
5887
|
+
const lines = (rawLines ?? []).map((lineText) => stripAnsiSimple(lineText));
|
|
5888
|
+
let startIndex = -1;
|
|
5889
|
+
for (let i = 0; i < lines.length; i += 1) {
|
|
5890
|
+
const lt = lines[i];
|
|
5891
|
+
if (/^\s*(?:[-+]\s*)?(Array\s*\[|Object\s*\{)/.test(lt)) {
|
|
5892
|
+
startIndex = i;
|
|
5893
|
+
break;
|
|
5894
|
+
}
|
|
5861
5895
|
}
|
|
5862
|
-
if (
|
|
5863
|
-
|
|
5896
|
+
if (startIndex < 0) {
|
|
5897
|
+
return {};
|
|
5864
5898
|
}
|
|
5865
|
-
|
|
5866
|
-
|
|
5867
|
-
|
|
5868
|
-
|
|
5869
|
-
|
|
5899
|
+
const expectedParts = [];
|
|
5900
|
+
const receivedParts = [];
|
|
5901
|
+
let opened = false;
|
|
5902
|
+
let expDone = false;
|
|
5903
|
+
let recDone = false;
|
|
5904
|
+
const canParseJsonish = (input) => {
|
|
5905
|
+
const text = normalizeBlock(input).trim();
|
|
5906
|
+
try {
|
|
5907
|
+
import_json5.default.parse(text);
|
|
5908
|
+
return true;
|
|
5909
|
+
} catch {
|
|
5910
|
+
return false;
|
|
5911
|
+
}
|
|
5912
|
+
};
|
|
5913
|
+
for (let i = startIndex; i < lines.length; i += 1) {
|
|
5914
|
+
const lineText = lines[i];
|
|
5915
|
+
const unsigned = lineText.replace(/^\s*[-+]\s?/, "");
|
|
5916
|
+
const isMinus = /^\s*-\s/.test(lineText);
|
|
5917
|
+
const isPlus = /^\s*\+\s/.test(lineText);
|
|
5918
|
+
if (!opened) {
|
|
5919
|
+
const looksLikeStart = /^\s*(Array\s*\[|Object\s*\{)/.test(unsigned);
|
|
5920
|
+
if (!looksLikeStart) {
|
|
5921
|
+
continue;
|
|
5870
5922
|
}
|
|
5871
|
-
|
|
5923
|
+
opened = true;
|
|
5924
|
+
}
|
|
5925
|
+
if (isMinus) {
|
|
5926
|
+
expectedParts.push(unsigned);
|
|
5927
|
+
} else if (isPlus) {
|
|
5928
|
+
receivedParts.push(unsigned);
|
|
5929
|
+
} else {
|
|
5930
|
+
expectedParts.push(unsigned);
|
|
5931
|
+
receivedParts.push(unsigned);
|
|
5932
|
+
}
|
|
5933
|
+
if (!expDone && expectedParts.length > 0) {
|
|
5934
|
+
expDone = canParseJsonish(expectedParts.join("\n"));
|
|
5935
|
+
}
|
|
5936
|
+
if (!recDone && receivedParts.length > 0) {
|
|
5937
|
+
recDone = canParseJsonish(receivedParts.join("\n"));
|
|
5938
|
+
}
|
|
5939
|
+
if (opened && expDone && recDone) {
|
|
5940
|
+
break;
|
|
5872
5941
|
}
|
|
5873
5942
|
}
|
|
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;
|
|
5943
|
+
const toJsonLikeString = (joined) => {
|
|
5944
|
+
if (!joined) {
|
|
5945
|
+
return void 0;
|
|
5952
5946
|
}
|
|
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);
|
|
5947
|
+
const text = normalizeBlock(joined).trim();
|
|
5948
|
+
try {
|
|
5949
|
+
const parsed = import_json5.default.parse(text);
|
|
5950
|
+
return JSON.stringify(parsed, null, 2);
|
|
5951
|
+
} catch {
|
|
5952
|
+
return text;
|
|
5965
5953
|
}
|
|
5954
|
+
};
|
|
5955
|
+
const expected = expectedParts.length ? expectedParts.join("\n") : void 0;
|
|
5956
|
+
const received = receivedParts.length ? receivedParts.join("\n") : void 0;
|
|
5957
|
+
const result = {};
|
|
5958
|
+
const expStr = toJsonLikeString(expected);
|
|
5959
|
+
const recStr = toJsonLikeString(received);
|
|
5960
|
+
if (expStr !== void 0) {
|
|
5961
|
+
result.expected = expStr;
|
|
5966
5962
|
}
|
|
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) {
|
|
5963
|
+
if (recStr !== void 0) {
|
|
5964
|
+
result.received = recStr;
|
|
5965
|
+
}
|
|
5966
|
+
return result;
|
|
5967
|
+
};
|
|
5968
|
+
var extractExpectedReceived = (details, lines) => {
|
|
5979
5969
|
if (details) {
|
|
5980
5970
|
for (const detail of details) {
|
|
5981
|
-
const dict =
|
|
5982
|
-
const
|
|
5983
|
-
if (
|
|
5984
|
-
const
|
|
5985
|
-
const
|
|
5986
|
-
|
|
5987
|
-
|
|
5971
|
+
const dict = isObjectRecord(detail) ? detail : void 0;
|
|
5972
|
+
const matcher = dict && isObjectRecord(dict.matcherResult) ? dict.matcherResult : void 0;
|
|
5973
|
+
if (matcher) {
|
|
5974
|
+
const expectedValue = matcher.expected;
|
|
5975
|
+
const receivedValue = matcher.received;
|
|
5976
|
+
const matcherName = String(
|
|
5977
|
+
matcher.matcherName || ""
|
|
5978
|
+
);
|
|
5979
|
+
if (matcherName === "toHaveBeenCalledTimes" || matcherName === "toBeCalledTimes") {
|
|
5980
|
+
const getCallsCount = (actual) => {
|
|
5981
|
+
if (isObjectRecord(actual) && Array.isArray(actual.calls)) {
|
|
5982
|
+
return actual.calls.length;
|
|
5983
|
+
}
|
|
5984
|
+
if (typeof actual === "number") {
|
|
5985
|
+
return actual;
|
|
5986
|
+
}
|
|
5987
|
+
if (Array.isArray(actual)) {
|
|
5988
|
+
return actual.length;
|
|
5989
|
+
}
|
|
5990
|
+
return void 0;
|
|
5991
|
+
};
|
|
5992
|
+
const expectedNumber = getCallsCount(expectedValue);
|
|
5993
|
+
const actualValue = matcher.actual ?? receivedValue;
|
|
5994
|
+
const receivedNumber = getCallsCount(actualValue);
|
|
5995
|
+
if (expectedNumber !== void 0 || receivedNumber !== void 0) {
|
|
5996
|
+
return { expected: expectedNumber, received: receivedNumber };
|
|
5997
|
+
}
|
|
5998
|
+
}
|
|
5999
|
+
if (expectedValue !== void 0 && receivedValue !== void 0) {
|
|
6000
|
+
return { expected: expectedValue, received: receivedValue };
|
|
5988
6001
|
}
|
|
5989
6002
|
}
|
|
5990
6003
|
}
|
|
@@ -6025,116 +6038,34 @@ function extractExpectedReceived(details, lines) {
|
|
|
6025
6038
|
}
|
|
6026
6039
|
}
|
|
6027
6040
|
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;
|
|
6041
|
+
};
|
|
6042
|
+
var buildPrettyDiffSection = (details, messageLines) => {
|
|
6043
|
+
const payload = extractExpectedReceived(details, messageLines);
|
|
6044
|
+
if (payload.expected === void 0 && payload.received === void 0) {
|
|
6045
|
+
return [];
|
|
6110
6046
|
}
|
|
6111
|
-
|
|
6112
|
-
|
|
6113
|
-
function renderPrettyDiff(payload) {
|
|
6047
|
+
const expectedString = stringifyPrettierish(payload.expected);
|
|
6048
|
+
const receivedString = stringifyPrettierish(payload.received);
|
|
6114
6049
|
const out = [];
|
|
6115
|
-
const
|
|
6116
|
-
|
|
6117
|
-
return out;
|
|
6118
|
-
}
|
|
6119
|
-
const expectedString = stringifyPrettierish(expected);
|
|
6120
|
-
const receivedString = stringifyPrettierish(received);
|
|
6050
|
+
const expectedLenLabel = Array.isArray(payload.expected) ? ansi.dim(` (len ${payload.expected.length})`) : "";
|
|
6051
|
+
out.push(` ${ansi.bold("Expected")}${expectedLenLabel}`);
|
|
6121
6052
|
out.push(
|
|
6122
|
-
|
|
6123
|
-
expected && Array.isArray(expected) ? `(len ${expected.length})` : ""
|
|
6124
|
-
)}`
|
|
6053
|
+
expectedString.split("\n").map((expectedLine) => ` ${Colors.Success(expectedLine)}`).join("\n")
|
|
6125
6054
|
);
|
|
6126
|
-
|
|
6055
|
+
const receivedLenLabel = Array.isArray(payload.received) ? ansi.dim(` (len ${payload.received.length})`) : "";
|
|
6056
|
+
out.push(` ${ansi.bold("Received")}${receivedLenLabel}`);
|
|
6127
6057
|
out.push(
|
|
6128
|
-
|
|
6129
|
-
received && Array.isArray(received) ? `(len ${received.length})` : ""
|
|
6130
|
-
)}`
|
|
6058
|
+
receivedString.split("\n").map((receivedLine) => ` ${Colors.Failure(receivedLine)}`).join("\n")
|
|
6131
6059
|
);
|
|
6132
|
-
|
|
6133
|
-
|
|
6134
|
-
|
|
6135
|
-
|
|
6136
|
-
const
|
|
6137
|
-
|
|
6060
|
+
if (isArrayOfPrimitives(payload.expected) && isArrayOfPrimitives(payload.received)) {
|
|
6061
|
+
const expectedSet = new Set(
|
|
6062
|
+
payload.expected.map((element) => String(element))
|
|
6063
|
+
);
|
|
6064
|
+
const receivedSet = new Set(
|
|
6065
|
+
payload.received.map((element) => String(element))
|
|
6066
|
+
);
|
|
6067
|
+
const missing = Array.from(expectedSet).filter((element) => !receivedSet.has(element));
|
|
6068
|
+
const unexpected = Array.from(receivedSet).filter((element) => !expectedSet.has(element));
|
|
6138
6069
|
const parts = [];
|
|
6139
6070
|
if (missing.length) {
|
|
6140
6071
|
parts.push(
|
|
@@ -6147,68 +6078,85 @@ function renderPrettyDiff(payload) {
|
|
|
6147
6078
|
);
|
|
6148
6079
|
}
|
|
6149
6080
|
if (parts.length) {
|
|
6150
|
-
out.push(` ${ansi.dim("Difference:")} ${
|
|
6081
|
+
out.push(` ${ansi.dim("Difference:")} ${Colors.Failure(parts.join(ansi.dim(" | ")))}`);
|
|
6151
6082
|
}
|
|
6152
6083
|
}
|
|
6153
6084
|
out.push("");
|
|
6154
6085
|
return out;
|
|
6155
|
-
}
|
|
6156
|
-
|
|
6157
|
-
const
|
|
6158
|
-
|
|
6159
|
-
|
|
6160
|
-
|
|
6161
|
-
const errorLine = details.messages.find(
|
|
6162
|
-
(lineText) => /\b(?:Error|TypeError|ReferenceError|SyntaxError|RangeError|AssertionError)\b/.test(lineText)
|
|
6086
|
+
};
|
|
6087
|
+
var buildMessageSection = (messageLines, details, _ctx, opts) => {
|
|
6088
|
+
const out = [];
|
|
6089
|
+
const lines = messageLines.map((lineText) => stripAnsiSimple(lineText));
|
|
6090
|
+
const hintIdx = lines.findIndex(
|
|
6091
|
+
(candidate) => /expect\(.+?\)\.(?:to|not\.)/.test(candidate) || /\b(?:AssertionError|Error):/.test(candidate)
|
|
6163
6092
|
);
|
|
6164
|
-
|
|
6165
|
-
|
|
6166
|
-
|
|
6167
|
-
const firstNonEmpty = details.messages.find((lineText) => lineText.trim().length);
|
|
6168
|
-
if (firstNonEmpty) {
|
|
6169
|
-
return [firstNonEmpty];
|
|
6170
|
-
}
|
|
6171
|
-
return [];
|
|
6172
|
-
}
|
|
6173
|
-
function colorUnifiedDiffLine(simple) {
|
|
6174
|
-
if (/^\s*-\s/.test(simple)) {
|
|
6175
|
-
return colorTokens.fail(simple);
|
|
6093
|
+
const acc = [];
|
|
6094
|
+
if (hintIdx >= 0) {
|
|
6095
|
+
acc.push(lines[hintIdx]);
|
|
6176
6096
|
}
|
|
6177
|
-
|
|
6178
|
-
|
|
6097
|
+
const pushBlock = (start) => {
|
|
6098
|
+
acc.push(lines[start]);
|
|
6099
|
+
for (let i = start + 1; i < lines.length; i += 1) {
|
|
6100
|
+
const candidate = lines[i];
|
|
6101
|
+
if (!candidate.trim() || isStackLine(candidate)) {
|
|
6102
|
+
break;
|
|
6103
|
+
}
|
|
6104
|
+
acc.push(candidate);
|
|
6105
|
+
}
|
|
6106
|
+
};
|
|
6107
|
+
const expectedIdx = lines.findIndex((lineText) => /^\s*Expected:/.test(lineText));
|
|
6108
|
+
const receivedIdx = lines.findIndex((lineText) => /^\s*Received:/.test(lineText));
|
|
6109
|
+
const diffIdx = lines.findIndex(
|
|
6110
|
+
(lineText) => /^\s*(?:- Expected|\+ Received|Difference:)/.test(lineText)
|
|
6111
|
+
);
|
|
6112
|
+
if (expectedIdx >= 0) {
|
|
6113
|
+
pushBlock(expectedIdx);
|
|
6179
6114
|
}
|
|
6180
|
-
|
|
6181
|
-
|
|
6182
|
-
var findCodeFrameStart = (lines) => lines.findIndex((line) => /^\s*(>?\s*\d+\s*\|)/.test(stripAnsiSimple(line)));
|
|
6183
|
-
var deepestProjectLoc = (stackLines, projectHint) => {
|
|
6184
|
-
const idx = findLastProjectFrameIndex(stackLines, projectHint);
|
|
6185
|
-
return idx >= 0 ? stackLocation(stackLines[idx]) : null;
|
|
6186
|
-
};
|
|
6187
|
-
var buildCodeFrameSection = (messageLines, ctx, synthLoc) => {
|
|
6188
|
-
const lines = [];
|
|
6189
|
-
const start = findCodeFrameStart(messageLines);
|
|
6190
|
-
if (start >= 0) {
|
|
6191
|
-
lines.push(...renderCodeFrame(messageLines, start), "");
|
|
6192
|
-
return lines;
|
|
6115
|
+
if (receivedIdx >= 0) {
|
|
6116
|
+
pushBlock(receivedIdx);
|
|
6193
6117
|
}
|
|
6194
|
-
if (
|
|
6195
|
-
|
|
6118
|
+
if (diffIdx >= 0) {
|
|
6119
|
+
pushBlock(diffIdx);
|
|
6196
6120
|
}
|
|
6197
|
-
|
|
6198
|
-
};
|
|
6199
|
-
var buildPrettyDiffSection = (details, messageLines) => renderPrettyDiff(extractExpectedReceived(details, messageLines));
|
|
6200
|
-
var buildMessageSection = (messageLines, details, ctx, opts) => {
|
|
6201
|
-
const out = [];
|
|
6202
|
-
const primary = pickPrimaryMessage(messageLines, details);
|
|
6203
|
-
const filtered = opts?.suppressDiff ? primary.filter((raw) => {
|
|
6121
|
+
const filtered = opts?.suppressDiff ? acc.filter((raw) => {
|
|
6204
6122
|
const simple = stripAnsiSimple(raw);
|
|
6205
6123
|
return !/^\s*(Expected:|Received:|Difference:)\b/.test(simple) && !/^\s*[-+]\s/.test(simple) && !/^\s*(Array\s*\[|Object\s*\{)/.test(simple);
|
|
6206
|
-
}) :
|
|
6207
|
-
|
|
6208
|
-
|
|
6124
|
+
}) : acc;
|
|
6125
|
+
const hasOnlyBareError = filtered.length === 0 || filtered.length === 1 && /^\s*(?:Error|AssertionError):?\s*$/.test(filtered[0] ?? "");
|
|
6126
|
+
const fallbackLines = [];
|
|
6127
|
+
if (hasOnlyBareError) {
|
|
6128
|
+
const startFrom = hintIdx >= 0 ? hintIdx + 1 : 0;
|
|
6129
|
+
for (let i = startFrom; i < lines.length; i += 1) {
|
|
6130
|
+
const candidate = lines[i];
|
|
6131
|
+
if (!candidate.trim()) {
|
|
6132
|
+
break;
|
|
6133
|
+
}
|
|
6134
|
+
if (isStackLine(candidate)) {
|
|
6135
|
+
break;
|
|
6136
|
+
}
|
|
6137
|
+
fallbackLines.push(candidate);
|
|
6138
|
+
}
|
|
6139
|
+
if (fallbackLines.length === 0 && details && details.messages && details.messages.length) {
|
|
6140
|
+
fallbackLines.push(
|
|
6141
|
+
...details.messages.map((messageText) => stripAnsiSimple(messageText)).filter((messageText) => messageText.trim().length > 0).slice(0, 6)
|
|
6142
|
+
);
|
|
6143
|
+
}
|
|
6144
|
+
}
|
|
6145
|
+
if (filtered.length > 0) {
|
|
6146
|
+
const label = (() => {
|
|
6147
|
+
const joined = filtered.join(" ");
|
|
6148
|
+
const matchResult = joined.match(/\b(TypeError|ReferenceError|SyntaxError|RangeError|AssertionError)\b/) || joined.match(/\bError\b/);
|
|
6149
|
+
if (matchResult) {
|
|
6150
|
+
const typeName = matchResult[1] ?? "Error";
|
|
6151
|
+
return `${typeName}:`;
|
|
6152
|
+
}
|
|
6153
|
+
return /expect\(.+?\)\.(?:to|not\.)/.test(joined) ? "Assertion:" : "Message:";
|
|
6154
|
+
})();
|
|
6209
6155
|
out.push(` ${ansi.bold(label)}`);
|
|
6210
|
-
|
|
6211
|
-
|
|
6156
|
+
const body = hasOnlyBareError ? fallbackLines : filtered;
|
|
6157
|
+
for (const lineText of body) {
|
|
6158
|
+
const colored = /^\s*-\s/.test(lineText) ? Colors.Failure(lineText) : /^\s*\+\s/.test(lineText) ? Colors.Success(lineText) : lineText;
|
|
6159
|
+
out.push(` ${ansi.yellow(colored)}`);
|
|
6212
6160
|
}
|
|
6213
6161
|
if (opts?.stackPreview && opts.stackPreview.length) {
|
|
6214
6162
|
for (const frame of opts.stackPreview) {
|
|
@@ -6219,32 +6167,85 @@ var buildMessageSection = (messageLines, details, ctx, opts) => {
|
|
|
6219
6167
|
}
|
|
6220
6168
|
return out;
|
|
6221
6169
|
};
|
|
6222
|
-
|
|
6223
|
-
|
|
6224
|
-
|
|
6225
|
-
|
|
6226
|
-
|
|
6227
|
-
if (!Array.isArray(maybeConsole)) {
|
|
6228
|
-
return out;
|
|
6170
|
+
var linesFromDetails = (details) => {
|
|
6171
|
+
const stacks = [];
|
|
6172
|
+
const messages = [];
|
|
6173
|
+
if (!details) {
|
|
6174
|
+
return { stacks, messages };
|
|
6229
6175
|
}
|
|
6230
|
-
const
|
|
6231
|
-
|
|
6232
|
-
|
|
6233
|
-
|
|
6234
|
-
}
|
|
6235
|
-
const
|
|
6236
|
-
|
|
6237
|
-
|
|
6238
|
-
|
|
6239
|
-
|
|
6240
|
-
|
|
6241
|
-
|
|
6242
|
-
|
|
6243
|
-
|
|
6176
|
+
const pushMaybe = (value, bucket) => {
|
|
6177
|
+
if (typeof value === "string" && value.trim()) {
|
|
6178
|
+
bucket.push(...value.split(/\r?\n/));
|
|
6179
|
+
}
|
|
6180
|
+
};
|
|
6181
|
+
const visitDeep = (value, depth) => {
|
|
6182
|
+
if (depth > 3 || value == null) {
|
|
6183
|
+
return;
|
|
6184
|
+
}
|
|
6185
|
+
if (typeof value === "string") {
|
|
6186
|
+
pushMaybe(value, messages);
|
|
6187
|
+
return;
|
|
6188
|
+
}
|
|
6189
|
+
if (typeof value !== "object") {
|
|
6190
|
+
return;
|
|
6191
|
+
}
|
|
6192
|
+
const obj = value;
|
|
6193
|
+
if (typeof obj.message === "string") {
|
|
6194
|
+
pushMaybe(obj.message, messages);
|
|
6195
|
+
}
|
|
6196
|
+
if (typeof obj.stack === "string") {
|
|
6197
|
+
pushMaybe(obj.stack, stacks);
|
|
6198
|
+
}
|
|
6199
|
+
if (typeof obj.expected === "string") {
|
|
6200
|
+
pushMaybe(obj.expected, messages);
|
|
6201
|
+
}
|
|
6202
|
+
if (typeof obj.received === "string") {
|
|
6203
|
+
pushMaybe(obj.received, messages);
|
|
6204
|
+
}
|
|
6205
|
+
const arrays = ["errors", "causes", "aggregatedErrors"];
|
|
6206
|
+
for (const key of arrays) {
|
|
6207
|
+
const arr = obj[key];
|
|
6208
|
+
if (Array.isArray(arr)) {
|
|
6209
|
+
for (const element of arr) {
|
|
6210
|
+
visitDeep(element, depth + 1);
|
|
6211
|
+
}
|
|
6212
|
+
}
|
|
6213
|
+
}
|
|
6214
|
+
const nestedCandidates = ["error", "cause", "matcherResult"];
|
|
6215
|
+
for (const key of nestedCandidates) {
|
|
6216
|
+
if (obj[key] && typeof obj[key] === "object") {
|
|
6217
|
+
visitDeep(obj[key], depth + 1);
|
|
6218
|
+
}
|
|
6219
|
+
}
|
|
6220
|
+
};
|
|
6221
|
+
for (const detail of details) {
|
|
6222
|
+
if (typeof detail === "string") {
|
|
6223
|
+
if (/\s+at\s.+\(.+:\d+:\d+\)/.test(detail)) {
|
|
6224
|
+
pushMaybe(detail, stacks);
|
|
6225
|
+
} else {
|
|
6226
|
+
pushMaybe(detail, messages);
|
|
6227
|
+
}
|
|
6228
|
+
} else if (isObjectRecord(detail)) {
|
|
6229
|
+
pushMaybe(detail.stack, stacks);
|
|
6230
|
+
pushMaybe(detail.message, messages);
|
|
6231
|
+
const err = isObjectRecord(detail.error) ? detail.error : void 0;
|
|
6232
|
+
if (err) {
|
|
6233
|
+
pushMaybe(err.stack, stacks);
|
|
6234
|
+
pushMaybe(err.message, messages);
|
|
6235
|
+
}
|
|
6236
|
+
const matcher = isObjectRecord(detail.matcherResult) ? detail.matcherResult : void 0;
|
|
6237
|
+
if (matcher) {
|
|
6238
|
+
pushMaybe(matcher.stack, stacks);
|
|
6239
|
+
pushMaybe(matcher.message, messages);
|
|
6240
|
+
pushMaybe(matcher.expected, messages);
|
|
6241
|
+
pushMaybe(matcher.received, messages);
|
|
6242
|
+
}
|
|
6243
|
+
if (messages.length === 0 && stacks.length === 0) {
|
|
6244
|
+
visitDeep(detail, 0);
|
|
6245
|
+
}
|
|
6244
6246
|
}
|
|
6245
|
-
out.push("");
|
|
6246
6247
|
}
|
|
6247
|
-
return
|
|
6248
|
+
return { stacks, messages };
|
|
6248
6249
|
};
|
|
6249
6250
|
var buildStackSection = (mergedForStack, ctx, fallbackLoc) => {
|
|
6250
6251
|
const out = [];
|
|
@@ -6253,13 +6254,18 @@ var buildStackSection = (mergedForStack, ctx, fallbackLoc) => {
|
|
|
6253
6254
|
out.push(` ${ansi.dim("(hidden by TEST_CLI_STACKS=)")}`, "");
|
|
6254
6255
|
return out;
|
|
6255
6256
|
}
|
|
6256
|
-
const
|
|
6257
|
+
const onlyStack = mergedForStack.filter(
|
|
6258
|
+
(lineText) => isStackLine(stripAnsiSimple(lineText))
|
|
6259
|
+
);
|
|
6260
|
+
const tail = onlyStack.slice(-4);
|
|
6257
6261
|
if (tail.length) {
|
|
6258
|
-
|
|
6262
|
+
for (const frameLine of tail) {
|
|
6263
|
+
out.push(` ${colorStackLine(String(frameLine), ctx.projectHint)}`);
|
|
6264
|
+
}
|
|
6259
6265
|
const loc = deepestProjectLoc(mergedForStack, ctx.projectHint);
|
|
6260
6266
|
if (loc) {
|
|
6261
6267
|
const href = preferredEditorHref(loc.file, loc.line, ctx.editorCmd);
|
|
6262
|
-
out.push(` ${ansi.dim("at")} ${osc8(`${
|
|
6268
|
+
out.push(` ${ansi.dim("at")} ${osc8(`${loc.file.split("/").pop()}:${loc.line}`, href)}`);
|
|
6263
6269
|
}
|
|
6264
6270
|
out.push("");
|
|
6265
6271
|
return out;
|
|
@@ -6274,171 +6280,460 @@ var buildStackSection = (mergedForStack, ctx, fallbackLoc) => {
|
|
|
6274
6280
|
out.push(` ${ansi.dim("(no stack provided)")}`, "");
|
|
6275
6281
|
return out;
|
|
6276
6282
|
};
|
|
6277
|
-
var
|
|
6278
|
-
var
|
|
6283
|
+
var MAX_CONSOLE_ERRORS_TO_SHOW = 3;
|
|
6284
|
+
var isConsoleEntry = (candidate) => typeof candidate === "object" && candidate !== null;
|
|
6285
|
+
var buildConsoleSection = (maybeConsole) => {
|
|
6279
6286
|
const out = [];
|
|
6280
|
-
|
|
6281
|
-
|
|
6282
|
-
|
|
6283
|
-
|
|
6284
|
-
|
|
6285
|
-
|
|
6286
|
-
|
|
6287
|
-
|
|
6288
|
-
|
|
6289
|
-
|
|
6290
|
-
|
|
6287
|
+
if (!Array.isArray(maybeConsole)) {
|
|
6288
|
+
return out;
|
|
6289
|
+
}
|
|
6290
|
+
const entries = maybeConsole.filter(isConsoleEntry);
|
|
6291
|
+
const errorsOnly = entries.filter((entry) => String(entry?.type ?? "").toLowerCase() === "error");
|
|
6292
|
+
const scored = errorsOnly.map((entry) => {
|
|
6293
|
+
const raw = entry?.message;
|
|
6294
|
+
const msg = Array.isArray(raw) ? raw.map(String).join(" ") : typeof raw === "string" ? raw : String(raw ?? "");
|
|
6295
|
+
return { msg, score: msg.length };
|
|
6296
|
+
}).filter((item) => item.msg.trim().length > 0).sort((left, right) => right.score - left.score).slice(0, MAX_CONSOLE_ERRORS_TO_SHOW);
|
|
6297
|
+
if (scored.length) {
|
|
6298
|
+
out.push(ansi.dim(" Console errors:"));
|
|
6299
|
+
for (const item of scored) {
|
|
6300
|
+
out.push(` ${ansi.dim("\u2022")} ${item.msg}`);
|
|
6291
6301
|
}
|
|
6302
|
+
out.push("");
|
|
6292
6303
|
}
|
|
6293
|
-
out.push("");
|
|
6294
6304
|
return out;
|
|
6295
6305
|
};
|
|
6296
|
-
var
|
|
6297
|
-
const
|
|
6298
|
-
const
|
|
6299
|
-
const
|
|
6300
|
-
|
|
6306
|
+
var buildFallbackMessageBlock = (messageLines, details) => {
|
|
6307
|
+
const normalize2 = (arr) => arr.map((lineText) => stripAnsiSimple(lineText)).filter((line) => line.trim().length > 0);
|
|
6308
|
+
const normalized = normalize2(messageLines);
|
|
6309
|
+
const informative = normalized.filter((line) => !/^\s*(?:Error|AssertionError):?\s*$/.test(line));
|
|
6310
|
+
if (informative.length > 0) {
|
|
6311
|
+
return [];
|
|
6312
|
+
}
|
|
6313
|
+
const errorIdx = normalized.findIndex(
|
|
6314
|
+
(line) => /(TypeError|ReferenceError|SyntaxError|RangeError|AssertionError|Error):?/.test(line)
|
|
6301
6315
|
);
|
|
6302
|
-
const
|
|
6303
|
-
|
|
6316
|
+
const collected = [];
|
|
6317
|
+
if (errorIdx >= 0) {
|
|
6318
|
+
for (let i = errorIdx; i < normalized.length && collected.length < 8; i += 1) {
|
|
6319
|
+
const ln = normalized[i];
|
|
6320
|
+
if (!ln.trim()) {
|
|
6321
|
+
break;
|
|
6322
|
+
}
|
|
6323
|
+
if (isStackLine(ln)) {
|
|
6324
|
+
break;
|
|
6325
|
+
}
|
|
6326
|
+
collected.push(ln);
|
|
6327
|
+
}
|
|
6328
|
+
}
|
|
6329
|
+
const fromDetails = collected.length > 0 ? [] : normalize2(details.messages).slice(0, 6);
|
|
6330
|
+
const linesToShow = collected.length > 0 ? collected : fromDetails;
|
|
6331
|
+
if (linesToShow.length === 0) {
|
|
6332
|
+
return [];
|
|
6333
|
+
}
|
|
6304
6334
|
const out = [];
|
|
6305
|
-
|
|
6306
|
-
const
|
|
6307
|
-
|
|
6308
|
-
|
|
6309
|
-
|
|
6310
|
-
|
|
6311
|
-
|
|
6312
|
-
|
|
6313
|
-
|
|
6314
|
-
|
|
6315
|
-
|
|
6316
|
-
|
|
6317
|
-
|
|
6318
|
-
|
|
6319
|
-
|
|
6320
|
-
|
|
6335
|
+
out.push(` ${ansi.bold("Message:")}`);
|
|
6336
|
+
for (const lineText of linesToShow) {
|
|
6337
|
+
out.push(` ${ansi.yellow(lineText)}`);
|
|
6338
|
+
}
|
|
6339
|
+
out.push("");
|
|
6340
|
+
return out;
|
|
6341
|
+
};
|
|
6342
|
+
var buildThrownSection = (details) => {
|
|
6343
|
+
const toLines = (value) => {
|
|
6344
|
+
if (value == null) {
|
|
6345
|
+
return [];
|
|
6346
|
+
}
|
|
6347
|
+
if (typeof value === "string") {
|
|
6348
|
+
return value.split(/\r?\n/);
|
|
6349
|
+
}
|
|
6350
|
+
try {
|
|
6351
|
+
return JSON.stringify(value, null, 2).split(/\r?\n/);
|
|
6352
|
+
} catch {
|
|
6353
|
+
return [String(value)];
|
|
6354
|
+
}
|
|
6355
|
+
};
|
|
6356
|
+
const candidates = [];
|
|
6357
|
+
for (const d of details) {
|
|
6358
|
+
const obj = d && typeof d === "object" ? d : null;
|
|
6359
|
+
if (obj && obj.error && typeof obj.error === "object") {
|
|
6360
|
+
const err = obj.error;
|
|
6361
|
+
if (typeof err.name === "string") {
|
|
6362
|
+
candidates.push(`name: ${err.name}`);
|
|
6321
6363
|
}
|
|
6322
|
-
|
|
6323
|
-
(
|
|
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;
|
|
6330
|
-
continue;
|
|
6364
|
+
if (typeof err.message === "string") {
|
|
6365
|
+
candidates.push(`message: ${err.message}`);
|
|
6331
6366
|
}
|
|
6332
|
-
|
|
6333
|
-
|
|
6334
|
-
|
|
6335
|
-
|
|
6336
|
-
|
|
6337
|
-
|
|
6338
|
-
if (
|
|
6339
|
-
|
|
6340
|
-
|
|
6341
|
-
|
|
6342
|
-
|
|
6343
|
-
|
|
6344
|
-
|
|
6345
|
-
|
|
6346
|
-
|
|
6347
|
-
|
|
6348
|
-
|
|
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
|
-
);
|
|
6367
|
+
if (typeof err.code === "string" || typeof err.code === "number") {
|
|
6368
|
+
candidates.push(`code: ${String(err.code)}`);
|
|
6369
|
+
}
|
|
6370
|
+
if (typeof err.cause === "string") {
|
|
6371
|
+
candidates.push(`cause: ${err.cause}`);
|
|
6372
|
+
}
|
|
6373
|
+
if (err.cause && typeof err.cause === "object") {
|
|
6374
|
+
candidates.push("cause:");
|
|
6375
|
+
candidates.push(...toLines(err.cause));
|
|
6376
|
+
}
|
|
6377
|
+
const rest = { ...err };
|
|
6378
|
+
delete rest.name;
|
|
6379
|
+
delete rest.message;
|
|
6380
|
+
delete rest.code;
|
|
6381
|
+
delete rest.stack;
|
|
6382
|
+
if (Object.keys(rest).length > 0) {
|
|
6383
|
+
candidates.push("details:");
|
|
6384
|
+
candidates.push(...toLines(rest));
|
|
6374
6385
|
}
|
|
6375
|
-
out.push(drawFailLine());
|
|
6376
|
-
out.push("");
|
|
6377
|
-
lineIndex = scanIndex;
|
|
6378
|
-
continue;
|
|
6379
6386
|
}
|
|
6380
|
-
|
|
6381
|
-
|
|
6382
|
-
|
|
6383
|
-
|
|
6384
|
-
|
|
6387
|
+
}
|
|
6388
|
+
if (!candidates.length) {
|
|
6389
|
+
return [];
|
|
6390
|
+
}
|
|
6391
|
+
const out = [];
|
|
6392
|
+
out.push(` ${ansi.bold("Thrown:")}`);
|
|
6393
|
+
for (const line of candidates.slice(0, 50)) {
|
|
6394
|
+
out.push(` ${ansi.yellow(line)}`);
|
|
6395
|
+
}
|
|
6396
|
+
out.push("");
|
|
6397
|
+
return out;
|
|
6398
|
+
};
|
|
6399
|
+
var mkPrettyFns = () => ({
|
|
6400
|
+
drawRule,
|
|
6401
|
+
drawFailLine,
|
|
6402
|
+
renderRunLine,
|
|
6403
|
+
buildPerFileOverview,
|
|
6404
|
+
buildFileBadgeLine,
|
|
6405
|
+
extractBridgePath,
|
|
6406
|
+
buildCodeFrameSection,
|
|
6407
|
+
buildMessageSection,
|
|
6408
|
+
buildPrettyDiffSection,
|
|
6409
|
+
buildStackSection,
|
|
6410
|
+
deepestProjectLoc,
|
|
6411
|
+
findCodeFrameStart,
|
|
6412
|
+
linesFromDetails,
|
|
6413
|
+
buildFallbackMessageBlock,
|
|
6414
|
+
buildThrownSection
|
|
6415
|
+
});
|
|
6416
|
+
|
|
6417
|
+
// src/lib/formatter/render.ts
|
|
6418
|
+
var relPath = (abs, cwd) => abs.replace(/\\/g, "/").replace(`${cwd}/`, "");
|
|
6419
|
+
var renderChunks = (chunks, ctx, fns, opts) => {
|
|
6420
|
+
const out = [];
|
|
6421
|
+
const seenFiles = /* @__PURE__ */ new Set();
|
|
6422
|
+
const seenFailures = /* @__PURE__ */ new Set();
|
|
6423
|
+
const onlyFailures = Boolean(opts?.onlyFailures);
|
|
6424
|
+
let currentRelFile = null;
|
|
6425
|
+
const escapeRegExp = (text) => text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
6426
|
+
for (const ch of chunks) {
|
|
6427
|
+
if (ch.tag === "PassFail") {
|
|
6428
|
+
const rel = relPath(ch.rel, ctx.cwd);
|
|
6385
6429
|
if (seenFiles.has(rel)) {
|
|
6386
|
-
lineIndex += 1;
|
|
6387
6430
|
continue;
|
|
6388
6431
|
}
|
|
6389
6432
|
seenFiles.add(rel);
|
|
6390
|
-
|
|
6391
|
-
|
|
6392
|
-
out.push(
|
|
6433
|
+
currentRelFile = rel;
|
|
6434
|
+
if (!(onlyFailures && ch.badge === "PASS")) {
|
|
6435
|
+
out.push(fns.buildFileBadgeLine(rel, ch.badge === "FAIL" ? 1 : 0));
|
|
6393
6436
|
}
|
|
6394
|
-
lineIndex += 1;
|
|
6395
6437
|
continue;
|
|
6396
6438
|
}
|
|
6397
|
-
if (
|
|
6398
|
-
out.push(
|
|
6399
|
-
|
|
6439
|
+
if (ch.tag === "FailureBlock") {
|
|
6440
|
+
out.push(fns.drawFailLine());
|
|
6441
|
+
const location = firstTestLocation(ch.lines, ctx.projectHint);
|
|
6442
|
+
const rel = location ? relPath(location.split(":")[0] ?? "", ctx.cwd) : "";
|
|
6443
|
+
const headerText = rel ? `${rel} > ${ch.title}` : ch.title;
|
|
6444
|
+
out.push(`${Colors.Failure("\xD7")} ${ansi.white(headerText)}`);
|
|
6445
|
+
const codeStart = fns.findCodeFrameStart(ch.lines);
|
|
6446
|
+
const collapsedForSrc = collapseStacks(ch.lines.slice(0));
|
|
6447
|
+
const deepestLoc = fns.deepestProjectLoc(collapsedForSrc, ctx.projectHint);
|
|
6448
|
+
let effectiveLoc = deepestLoc;
|
|
6449
|
+
if (!effectiveLoc && currentRelFile) {
|
|
6450
|
+
try {
|
|
6451
|
+
const abs = path9.resolve(ctx.cwd, currentRelFile);
|
|
6452
|
+
const source = ctx.readSource(abs);
|
|
6453
|
+
const testName = (() => {
|
|
6454
|
+
const parts = ch.title.split(">");
|
|
6455
|
+
return (parts[parts.length - 1] || ch.title).trim();
|
|
6456
|
+
})();
|
|
6457
|
+
const itRe = new RegExp(
|
|
6458
|
+
String.raw`\b(?:it|test)\s*\(\s*['\"]${escapeRegExp(testName)}['\"]`
|
|
6459
|
+
);
|
|
6460
|
+
let index = source.findIndex((line) => itRe.test(line));
|
|
6461
|
+
if (index < 0) {
|
|
6462
|
+
index = source.findIndex((line) => /\bexpect\s*\(/.test(line));
|
|
6463
|
+
} else {
|
|
6464
|
+
const windowEnd = Math.min(source.length, index + 80);
|
|
6465
|
+
for (let i = index; i < windowEnd; i += 1) {
|
|
6466
|
+
if (/\bexpect\s*\(/.test(source[i])) {
|
|
6467
|
+
index = i;
|
|
6468
|
+
break;
|
|
6469
|
+
}
|
|
6470
|
+
}
|
|
6471
|
+
}
|
|
6472
|
+
if (index >= 0) {
|
|
6473
|
+
effectiveLoc = { file: abs.replace(/\\/g, "/"), line: index + 1 };
|
|
6474
|
+
}
|
|
6475
|
+
} catch {
|
|
6476
|
+
}
|
|
6477
|
+
}
|
|
6478
|
+
if (codeStart >= 0) {
|
|
6479
|
+
out.push("", ...fns.buildCodeFrameSection(ch.lines, ctx, effectiveLoc), "");
|
|
6480
|
+
} else {
|
|
6481
|
+
out.push("", ...fns.buildCodeFrameSection(ch.lines, ctx, effectiveLoc), "");
|
|
6482
|
+
}
|
|
6483
|
+
const pretty = fns.buildPrettyDiffSection(void 0, ch.lines);
|
|
6484
|
+
out.push(...pretty);
|
|
6485
|
+
const hasPretty = pretty.length > 0;
|
|
6486
|
+
const details = fns.linesFromDetails(void 0);
|
|
6487
|
+
const minimal = (() => {
|
|
6488
|
+
const plain = ch.lines.map((ln) => stripAnsiSimple(ln));
|
|
6489
|
+
const hint = plain.findIndex(
|
|
6490
|
+
(lineText) => /expect\(.+?\)\.(?:to|not\.)/.test(lineText) || /\bError:?\b/.test(lineText)
|
|
6491
|
+
);
|
|
6492
|
+
const acc = [];
|
|
6493
|
+
const start = hint >= 0 ? hint : 0;
|
|
6494
|
+
for (let i = start; i < plain.length; i += 1) {
|
|
6495
|
+
const ln = plain[i];
|
|
6496
|
+
if (!ln.trim()) {
|
|
6497
|
+
break;
|
|
6498
|
+
}
|
|
6499
|
+
if (isStackLine(ln)) {
|
|
6500
|
+
break;
|
|
6501
|
+
}
|
|
6502
|
+
acc.push(ln);
|
|
6503
|
+
}
|
|
6504
|
+
return acc;
|
|
6505
|
+
})();
|
|
6506
|
+
const collapsedForTail = collapseStacks(ch.lines.slice(0));
|
|
6507
|
+
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)}`) : [];
|
|
6508
|
+
out.push(
|
|
6509
|
+
...fns.buildMessageSection(minimal.length ? minimal : ch.lines, details, ctx, {
|
|
6510
|
+
suppressDiff: hasPretty,
|
|
6511
|
+
stackPreview
|
|
6512
|
+
})
|
|
6513
|
+
);
|
|
6514
|
+
if (minimal.length === 0 && fns.buildFallbackMessageBlock) {
|
|
6515
|
+
out.push(...fns.buildFallbackMessageBlock(ch.lines, { messages: details.messages }));
|
|
6516
|
+
}
|
|
6517
|
+
const consoleInline = (() => {
|
|
6518
|
+
const plain = ch.lines.map((ln) => stripAnsiSimple(ln));
|
|
6519
|
+
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);
|
|
6520
|
+
return cand;
|
|
6521
|
+
})();
|
|
6522
|
+
if (consoleInline.length > 0) {
|
|
6523
|
+
out.push(ansi.dim(" Console errors:"), ...consoleInline.map((ln) => ` ${ln}`), "");
|
|
6524
|
+
}
|
|
6525
|
+
if (ctx.showStacks && fns.buildStackSection && stackPreview.length === 0) {
|
|
6526
|
+
const collapsed = collapseStacks(ch.lines.slice(0));
|
|
6527
|
+
out.push(...fns.buildStackSection(collapsed, ctx));
|
|
6528
|
+
}
|
|
6529
|
+
out.push(fns.drawFailLine(), "");
|
|
6530
|
+
if (rel) {
|
|
6531
|
+
seenFailures.add(`${rel}|${ch.title}`);
|
|
6532
|
+
}
|
|
6400
6533
|
continue;
|
|
6401
6534
|
}
|
|
6402
|
-
if (
|
|
6403
|
-
|
|
6404
|
-
|
|
6405
|
-
|
|
6535
|
+
if (ch.tag === "Summary") {
|
|
6536
|
+
out.push(ch.line);
|
|
6537
|
+
continue;
|
|
6538
|
+
}
|
|
6539
|
+
if (ch.tag === "Stack") {
|
|
6540
|
+
if (ctx.showStacks) {
|
|
6541
|
+
out.push(ch.line);
|
|
6406
6542
|
}
|
|
6407
|
-
lineIndex += 1;
|
|
6408
6543
|
continue;
|
|
6409
6544
|
}
|
|
6410
|
-
|
|
6411
|
-
|
|
6545
|
+
if (!onlyFailures) {
|
|
6546
|
+
out.push(ch.line);
|
|
6547
|
+
}
|
|
6412
6548
|
}
|
|
6413
|
-
const
|
|
6414
|
-
|
|
6415
|
-
|
|
6416
|
-
|
|
6417
|
-
|
|
6418
|
-
|
|
6419
|
-
|
|
6420
|
-
|
|
6421
|
-
|
|
6422
|
-
|
|
6423
|
-
|
|
6424
|
-
|
|
6425
|
-
|
|
6426
|
-
|
|
6549
|
+
const hadParsed = seenFiles.size > 0 || seenFailures.size > 0 || out.some((lineText) => /^(?:\s*)(PASS|FAIL)\b/.test(stripAnsiSimple(lineText)));
|
|
6550
|
+
return { text: out.join("\n"), hadParsed };
|
|
6551
|
+
};
|
|
6552
|
+
|
|
6553
|
+
// src/lib/formatter/context.ts
|
|
6554
|
+
import * as fs5 from "node:fs";
|
|
6555
|
+
var makeCtx = (opts, showStacks = false) => {
|
|
6556
|
+
const cwd = (opts?.cwd ?? process.cwd()).replace(/\\/g, "/");
|
|
6557
|
+
const width = Math.max(
|
|
6558
|
+
40,
|
|
6559
|
+
process.stdout && process.stdout.columns || 80
|
|
6560
|
+
);
|
|
6561
|
+
const projectHint = new RegExp(
|
|
6562
|
+
`(${cwd.replace(/[.*+?^${}()|[\\]\\\\]/g, "\\$&")})|(/gigworx-node/)`
|
|
6563
|
+
);
|
|
6564
|
+
const readSource2 = (file) => {
|
|
6565
|
+
try {
|
|
6566
|
+
return fs5.readFileSync(file.replace(/\\/g, "/"), "utf8").split(/\r?\n/);
|
|
6567
|
+
} catch {
|
|
6568
|
+
return [];
|
|
6427
6569
|
}
|
|
6570
|
+
};
|
|
6571
|
+
return { cwd, width, showStacks, projectHint, editorCmd: opts?.editorCmd, readSource: readSource2 };
|
|
6572
|
+
};
|
|
6573
|
+
|
|
6574
|
+
// src/lib/formatter/bridge.ts
|
|
6575
|
+
var import_json52 = __toESM(require_lib(), 1);
|
|
6576
|
+
import * as fs6 from "node:fs";
|
|
6577
|
+
import * as path10 from "node:path";
|
|
6578
|
+
var colorTokens2 = {
|
|
6579
|
+
pass: Colors.Success,
|
|
6580
|
+
fail: Colors.Failure,
|
|
6581
|
+
skip: Colors.Skip,
|
|
6582
|
+
todo: Colors.Todo,
|
|
6583
|
+
passPill: (text) => BackgroundColors.Success(ansi.white(` ${text} `)),
|
|
6584
|
+
failPill: (text) => BackgroundColors.Failure(ansi.white(` ${text} `))
|
|
6585
|
+
};
|
|
6586
|
+
var by = (keySelector) => (left, right) => keySelector(left) - keySelector(right);
|
|
6587
|
+
var isObject = (candidateValue) => !!candidateValue && typeof candidateValue === "object";
|
|
6588
|
+
var asHttpList = (candidateValue) => Array.isArray(candidateValue) ? candidateValue : [];
|
|
6589
|
+
var summarizeUrl = (method, url, route) => {
|
|
6590
|
+
const base = route || url || "";
|
|
6591
|
+
const qs = url && url.includes("?") ? ` ? ${url.split("?")[1]}` : "";
|
|
6592
|
+
return [method || "", base, qs].filter(Boolean).join(" ").trim();
|
|
6593
|
+
};
|
|
6594
|
+
var stripBridgeEventsFromConsole = (maybeConsole) => {
|
|
6595
|
+
if (!Array.isArray(maybeConsole)) {
|
|
6596
|
+
return maybeConsole;
|
|
6428
6597
|
}
|
|
6429
|
-
|
|
6430
|
-
|
|
6431
|
-
|
|
6432
|
-
|
|
6433
|
-
|
|
6598
|
+
return maybeConsole.filter((entry) => {
|
|
6599
|
+
try {
|
|
6600
|
+
const raw = Array.isArray(entry.message) ? entry.message.map(String).join(" ") : String(entry.message ?? "");
|
|
6601
|
+
return !raw.includes("[JEST-BRIDGE-EVENT]");
|
|
6602
|
+
} catch {
|
|
6603
|
+
return true;
|
|
6604
|
+
}
|
|
6605
|
+
});
|
|
6606
|
+
};
|
|
6607
|
+
var extractBridgePath2 = (raw, cwd) => {
|
|
6608
|
+
const matches = Array.from(
|
|
6609
|
+
raw.matchAll(/Test results written to:\s+([^\n\r]+jest-bridge-[^\s'"]+\.json)/g)
|
|
6610
|
+
);
|
|
6611
|
+
if (!matches.length) {
|
|
6612
|
+
return null;
|
|
6613
|
+
}
|
|
6614
|
+
const jsonPath = (matches[matches.length - 1][1] ?? "").trim().replace(/^["'`]|["'`]$/g, "");
|
|
6615
|
+
return path10.isAbsolute(jsonPath) ? jsonPath : path10.resolve(cwd, jsonPath).replace(/\\/g, "/");
|
|
6616
|
+
};
|
|
6617
|
+
var isTransportError = (msg) => {
|
|
6618
|
+
const lowercaseMessage = (msg ?? "").toLowerCase();
|
|
6619
|
+
return /\bsocket hang up\b|\beconnreset\b|\betimedout\b|\beconnrefused\b|\bwrite epipe\b/.test(
|
|
6620
|
+
lowercaseMessage
|
|
6621
|
+
);
|
|
6622
|
+
};
|
|
6623
|
+
var envNumber = (name, fallback) => {
|
|
6624
|
+
const parsed = Number(process.env[name]);
|
|
6625
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
|
6626
|
+
};
|
|
6627
|
+
var HEADLAMP_HTTP_WINDOW_MS = () => envNumber("HEADLAMP_HTTP_WINDOW_MS", 3e3);
|
|
6628
|
+
var HEADLAMP_HTTP_STRICT_WINDOW_MS = () => envNumber("HEADLAMP_HTTP_STRICT_WINDOW_MS", 600);
|
|
6629
|
+
var HEADLAMP_HTTP_MIN_SCORE = () => envNumber("HEADLAMP_HTTP_MIN_SCORE", 1200);
|
|
6630
|
+
var HEADLAMP_HTTP_DIFF_LIMIT = () => envNumber("HEADLAMP_HTTP_DIFF_LIMIT", 6);
|
|
6631
|
+
var HEADLAMP_HTTP_SHOW_MISS = () => process.env.HEADLAMP_HTTP_MISS === "1";
|
|
6632
|
+
var eventsNear = (http, ts, testPath, windowMs = HEADLAMP_HTTP_WINDOW_MS()) => {
|
|
6633
|
+
if (typeof ts !== "number" || !Number.isFinite(ts)) {
|
|
6634
|
+
return [];
|
|
6635
|
+
}
|
|
6636
|
+
return http.filter((e) => {
|
|
6637
|
+
const timeOk = typeof e.timestampMs === "number" && Math.abs(e.timestampMs - ts) <= windowMs;
|
|
6638
|
+
const pathOk = !testPath || e.testPath === testPath;
|
|
6639
|
+
return timeOk && pathOk;
|
|
6640
|
+
});
|
|
6641
|
+
};
|
|
6642
|
+
var parseMethodPathFromTitle = (title) => {
|
|
6643
|
+
if (!title) {
|
|
6644
|
+
return {};
|
|
6645
|
+
}
|
|
6646
|
+
const matchResult = title.match(/\b(GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS)\s+([^\s)]+)/i);
|
|
6647
|
+
return matchResult ? { method: matchResult[1]?.toUpperCase(), path: matchResult[2] } : {};
|
|
6648
|
+
};
|
|
6649
|
+
var isHttpStatusNumber = (statusNumber) => typeof statusNumber === "number" && statusNumber >= 100 && statusNumber <= 599;
|
|
6650
|
+
var hasStatusSemantics = (assertionLike) => {
|
|
6651
|
+
if (!assertionLike) {
|
|
6652
|
+
return false;
|
|
6653
|
+
}
|
|
6654
|
+
if (isHttpStatusNumber(assertionLike.expectedNumber) || isHttpStatusNumber(assertionLike.receivedNumber)) {
|
|
6655
|
+
return true;
|
|
6656
|
+
}
|
|
6657
|
+
const combinedRaw = `${assertionLike.matcher ?? ""} ${assertionLike.message ?? ""}`;
|
|
6658
|
+
const combinedMessage = combinedRaw.toLowerCase();
|
|
6659
|
+
return /\bstatus(code)?\b|\btohaves(tatus|tatuscode)\b/.test(combinedMessage);
|
|
6660
|
+
};
|
|
6661
|
+
var fileSuggestsHttp = (relPath2) => /(?:^|\/)(routes?|api|controllers?|e2e|integration)(?:\/|\.test\.)/i.test(relPath2);
|
|
6662
|
+
var inferHttpNumbersFromText = (lines) => {
|
|
6663
|
+
const text = lines.join("\n");
|
|
6664
|
+
const match = text.match(/Expected:\s*(\d{3})[\s\S]*?Received:\s*(\d{3})/i);
|
|
6665
|
+
if (match) {
|
|
6666
|
+
return { expectedNumber: Number(match[1]), receivedNumber: Number(match[2]) };
|
|
6434
6667
|
}
|
|
6435
|
-
return
|
|
6668
|
+
return {};
|
|
6669
|
+
};
|
|
6670
|
+
var titleSuggestsHttp = (title) => {
|
|
6671
|
+
const { method, path: parsedPath } = parseMethodPathFromTitle(title);
|
|
6672
|
+
return Boolean(method || parsedPath && parsedPath.startsWith("/"));
|
|
6673
|
+
};
|
|
6674
|
+
var isHttpRelevant = (ctx) => {
|
|
6675
|
+
const assertionCtx = ctx.assertion;
|
|
6676
|
+
return ctx.hasTransportSignal || ctx.httpCountInSameTest > 0 || titleSuggestsHttp(ctx.title) || hasStatusSemantics(assertionCtx) || fileSuggestsHttp(ctx.relPath);
|
|
6677
|
+
};
|
|
6678
|
+
var routeSimilarityScore = (hint, evt) => {
|
|
6679
|
+
if (!hint.path && !hint.method) {
|
|
6680
|
+
return 0;
|
|
6681
|
+
}
|
|
6682
|
+
const methodOk = hint.method && evt.method ? Number(hint.method === evt.method) : 0;
|
|
6683
|
+
const route = evt.route || evt.url || "";
|
|
6684
|
+
if (!route) {
|
|
6685
|
+
return methodOk * 10;
|
|
6686
|
+
}
|
|
6687
|
+
if (hint.path && route === hint.path) {
|
|
6688
|
+
return 500 + methodOk * 50;
|
|
6689
|
+
}
|
|
6690
|
+
if (hint.path && route.endsWith(hint.path)) {
|
|
6691
|
+
return 300 + methodOk * 50;
|
|
6692
|
+
}
|
|
6693
|
+
if (hint.path && route.includes(hint.path)) {
|
|
6694
|
+
return 200 + methodOk * 50;
|
|
6695
|
+
}
|
|
6696
|
+
return methodOk * 10;
|
|
6436
6697
|
};
|
|
6437
|
-
var
|
|
6438
|
-
const
|
|
6439
|
-
|
|
6698
|
+
var scoreHttpForAssertion = (assertion, titleHint) => (candidateEvent) => {
|
|
6699
|
+
const tsA = assertion.timestampMs;
|
|
6700
|
+
const tsH = candidateEvent.timestampMs;
|
|
6701
|
+
const window = isTransportError(assertion.message) ? HEADLAMP_HTTP_STRICT_WINDOW_MS() : HEADLAMP_HTTP_WINDOW_MS();
|
|
6702
|
+
const timeScore = typeof tsA === "number" && typeof tsH === "number" ? Math.max(0, window - Math.abs(tsA - tsH)) : 0;
|
|
6703
|
+
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;
|
|
6704
|
+
const routeScore = routeSimilarityScore(titleHint, candidateEvent);
|
|
6705
|
+
const specificity = candidateEvent.route ? 80 : candidateEvent.url ? 40 : 0;
|
|
6706
|
+
return timeScore + statusScore + routeScore + specificity;
|
|
6440
6707
|
};
|
|
6441
|
-
|
|
6708
|
+
var pickRelevantHttp = (assertion, http, ctx) => {
|
|
6709
|
+
const hint = parseMethodPathFromTitle(ctx.title);
|
|
6710
|
+
const nameMatches = (leftName, rightName) => !!leftName && !!rightName && (leftName === rightName || leftName.includes(rightName) || rightName.includes(leftName));
|
|
6711
|
+
const sameTest = (leftCtx, rightCtx) => !!leftCtx && !!rightCtx && leftCtx.testPath === rightCtx.testPath && nameMatches(leftCtx.currentTestName, rightCtx.currentTestName);
|
|
6712
|
+
const strictPool = http.filter(
|
|
6713
|
+
(httpEvent) => sameTest(assertion, httpEvent) || sameTest(ctx, httpEvent)
|
|
6714
|
+
);
|
|
6715
|
+
const windowMs = isTransportError(assertion.message) ? HEADLAMP_HTTP_STRICT_WINDOW_MS() : HEADLAMP_HTTP_WINDOW_MS();
|
|
6716
|
+
let pool = strictPool;
|
|
6717
|
+
if (!pool.length) {
|
|
6718
|
+
pool = http.filter(
|
|
6719
|
+
(e) => e.testPath === ctx.testPath && typeof assertion.timestampMs === "number" && typeof e.timestampMs === "number" && Math.abs(e.timestampMs - assertion.timestampMs) <= windowMs
|
|
6720
|
+
);
|
|
6721
|
+
}
|
|
6722
|
+
if (!pool.length) {
|
|
6723
|
+
pool = http.filter(
|
|
6724
|
+
(e) => typeof assertion.timestampMs === "number" && typeof e.timestampMs === "number" && Math.abs(e.timestampMs - assertion.timestampMs) <= windowMs
|
|
6725
|
+
);
|
|
6726
|
+
}
|
|
6727
|
+
if (!pool.length) {
|
|
6728
|
+
return void 0;
|
|
6729
|
+
}
|
|
6730
|
+
const scored = pool.map((httpEvent) => ({ h: httpEvent, s: scoreHttpForAssertion(assertion, hint)(httpEvent) })).sort((leftScore, rightScore) => rightScore.s - leftScore.s);
|
|
6731
|
+
const [best] = scored;
|
|
6732
|
+
const threshold = isTransportError(assertion.message) ? Math.max(HEADLAMP_HTTP_MIN_SCORE(), 1400) : HEADLAMP_HTTP_MIN_SCORE();
|
|
6733
|
+
return best && best.s >= threshold ? best.h : void 0;
|
|
6734
|
+
};
|
|
6735
|
+
var isBridgeJSONLike = (candidateValue) => !!candidateValue && typeof candidateValue === "object" && "aggregated" in candidateValue;
|
|
6736
|
+
var coerceJestJsonToBridge = (raw) => {
|
|
6442
6737
|
if (isBridgeJSONLike(raw)) {
|
|
6443
6738
|
return raw;
|
|
6444
6739
|
}
|
|
@@ -6448,18 +6743,21 @@ function coerceJestJsonToBridge(raw) {
|
|
|
6448
6743
|
}
|
|
6449
6744
|
return {
|
|
6450
6745
|
startTime: Number(j.startTime ?? Date.now()),
|
|
6451
|
-
testResults: j.testResults.map((
|
|
6452
|
-
testFilePath:
|
|
6453
|
-
status:
|
|
6454
|
-
failureMessage:
|
|
6455
|
-
failureDetails:
|
|
6456
|
-
|
|
6746
|
+
testResults: j.testResults.map((tr) => ({
|
|
6747
|
+
testFilePath: tr.testFilePath || tr.name || "",
|
|
6748
|
+
status: tr.status,
|
|
6749
|
+
failureMessage: tr.failureMessage || "",
|
|
6750
|
+
failureDetails: tr.failureDetails ?? [],
|
|
6751
|
+
testExecError: tr.testExecError ?? null,
|
|
6752
|
+
console: tr.console ?? null,
|
|
6753
|
+
testResults: (tr.assertionResults || []).map((assertion) => ({
|
|
6457
6754
|
title: assertion.title,
|
|
6458
6755
|
fullName: assertion.fullName || [...assertion.ancestorTitles || [], assertion.title].join(" "),
|
|
6459
6756
|
status: assertion.status,
|
|
6460
6757
|
duration: assertion.duration || 0,
|
|
6461
6758
|
location: assertion.location ?? null,
|
|
6462
|
-
failureMessages: assertion.failureMessages || []
|
|
6759
|
+
failureMessages: assertion.failureMessages || [],
|
|
6760
|
+
failureDetails: assertion.failureDetails || []
|
|
6463
6761
|
}))
|
|
6464
6762
|
})),
|
|
6465
6763
|
aggregated: {
|
|
@@ -6475,21 +6773,20 @@ function coerceJestJsonToBridge(raw) {
|
|
|
6475
6773
|
success: j.success
|
|
6476
6774
|
}
|
|
6477
6775
|
};
|
|
6478
|
-
}
|
|
6479
|
-
var vitestFooter = (agg,
|
|
6776
|
+
};
|
|
6777
|
+
var vitestFooter = (agg, durationMs) => {
|
|
6480
6778
|
const files = [
|
|
6481
|
-
agg.numFailedTestSuites ?
|
|
6482
|
-
agg.numPassedTestSuites ?
|
|
6483
|
-
agg.numPendingTests ?
|
|
6779
|
+
agg.numFailedTestSuites ? colorTokens2.fail(`${agg.numFailedTestSuites} failed`) : "",
|
|
6780
|
+
agg.numPassedTestSuites ? colorTokens2.pass(`${agg.numPassedTestSuites} passed`) : "",
|
|
6781
|
+
agg.numPendingTests ? colorTokens2.skip(`${agg.numPendingTests} skipped`) : ""
|
|
6484
6782
|
].filter(Boolean).join(ansi.dim(" | "));
|
|
6485
6783
|
const tests = [
|
|
6486
|
-
agg.numFailedTests ?
|
|
6487
|
-
agg.numPassedTests ?
|
|
6488
|
-
agg.numPendingTests ?
|
|
6489
|
-
agg.numTodoTests ?
|
|
6784
|
+
agg.numFailedTests ? colorTokens2.fail(`${agg.numFailedTests} failed`) : "",
|
|
6785
|
+
agg.numPassedTests ? colorTokens2.pass(`${agg.numPassedTests} passed`) : "",
|
|
6786
|
+
agg.numPendingTests ? colorTokens2.skip(`${agg.numPendingTests} skipped`) : "",
|
|
6787
|
+
agg.numTodoTests ? colorTokens2.todo(`${agg.numTodoTests} todo`) : ""
|
|
6490
6788
|
].filter(Boolean).join(ansi.dim(" | "));
|
|
6491
|
-
const
|
|
6492
|
-
const time = durMs != null ? `${Math.max(0, Math.round(durMs))}ms` : "";
|
|
6789
|
+
const time = durationMs != null ? `${Math.max(0, Math.round(durationMs))}ms` : typeof agg.runTimeMs === "number" ? `${Math.max(0, Math.round(agg.runTimeMs))}ms` : "";
|
|
6493
6790
|
const thread = ansi.dim("(in thread 0ms, 0.00%)");
|
|
6494
6791
|
return [
|
|
6495
6792
|
`${ansi.bold("Test Files")} ${files} ${ansi.dim(`(${agg.numTotalTestSuites})`)}`,
|
|
@@ -6497,20 +6794,14 @@ var vitestFooter = (agg, _startedAt, durationMs) => {
|
|
|
6497
6794
|
`${ansi.bold("Time")} ${time} ${thread}`
|
|
6498
6795
|
].join("\n");
|
|
6499
6796
|
};
|
|
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);
|
|
6797
|
+
var renderVitestFromJestJSON = (data, ctx, opts) => {
|
|
6507
6798
|
const out = [];
|
|
6799
|
+
const onlyFailures = Boolean(opts?.onlyFailures);
|
|
6508
6800
|
if (!onlyFailures) {
|
|
6509
|
-
out.push(
|
|
6510
|
-
out.push("");
|
|
6801
|
+
out.push(`${BackgroundColors.Run(ansi.white(" RUN "))} ${ansi.dim(ctx.cwd)}`, "");
|
|
6511
6802
|
}
|
|
6512
6803
|
for (const file of data.testResults) {
|
|
6513
|
-
const rel = file.testFilePath.replace(/\\/g, "/").replace(`${cwd}/`, "");
|
|
6804
|
+
const rel = file.testFilePath.replace(/\\/g, "/").replace(`${ctx.cwd}/`, "");
|
|
6514
6805
|
const failed = file.testResults.filter((assertion) => assertion.status === "failed");
|
|
6515
6806
|
if (!onlyFailures) {
|
|
6516
6807
|
out.push(...buildPerFileOverview(rel, file.testResults));
|
|
@@ -6518,91 +6809,842 @@ function renderVitestFromJestJSON(data, opts) {
|
|
|
6518
6809
|
if (!(onlyFailures && failed.length === 0)) {
|
|
6519
6810
|
out.push(buildFileBadgeLine(rel, failed.length));
|
|
6520
6811
|
}
|
|
6521
|
-
|
|
6812
|
+
let httpSorted = [];
|
|
6813
|
+
let assertionEvents = [];
|
|
6814
|
+
{
|
|
6815
|
+
const parseBridge = (consoleEntries) => {
|
|
6816
|
+
const http = [];
|
|
6817
|
+
const assertions = [];
|
|
6818
|
+
if (!Array.isArray(consoleEntries)) {
|
|
6819
|
+
return { http, assertions };
|
|
6820
|
+
}
|
|
6821
|
+
for (const entry of consoleEntries) {
|
|
6822
|
+
const rec = entry;
|
|
6823
|
+
const rawMsgVal = rec && typeof rec.message !== "undefined" ? rec.message : "";
|
|
6824
|
+
const raw = Array.isArray(rawMsgVal) ? rawMsgVal.map(String).join(" ") : String(rawMsgVal ?? "");
|
|
6825
|
+
if (!raw.includes("[JEST-BRIDGE-EVENT]")) {
|
|
6826
|
+
continue;
|
|
6827
|
+
}
|
|
6828
|
+
const jsonText = raw.split("[JEST-BRIDGE-EVENT]").pop()?.trim() ?? "";
|
|
6829
|
+
try {
|
|
6830
|
+
const evt = import_json52.default.parse(jsonText);
|
|
6831
|
+
const type = evt?.type;
|
|
6832
|
+
if (type === "httpResponse") {
|
|
6833
|
+
const timestampMs = Number(evt.timestampMs ?? Date.now());
|
|
6834
|
+
http.push({
|
|
6835
|
+
kind: "response",
|
|
6836
|
+
timestampMs,
|
|
6837
|
+
method: evt.method,
|
|
6838
|
+
url: evt.url,
|
|
6839
|
+
route: evt.route,
|
|
6840
|
+
statusCode: evt.statusCode,
|
|
6841
|
+
durationMs: evt.durationMs,
|
|
6842
|
+
contentType: evt.contentType,
|
|
6843
|
+
requestId: evt.requestId,
|
|
6844
|
+
json: evt.json,
|
|
6845
|
+
bodyPreview: evt.bodyPreview,
|
|
6846
|
+
testPath: evt.testPath,
|
|
6847
|
+
currentTestName: evt.currentTestName
|
|
6848
|
+
});
|
|
6849
|
+
} else if (type === "httpAbort") {
|
|
6850
|
+
http.push({
|
|
6851
|
+
kind: "abort",
|
|
6852
|
+
timestampMs: Number(evt.timestampMs ?? Date.now()),
|
|
6853
|
+
method: evt.method,
|
|
6854
|
+
url: evt.url,
|
|
6855
|
+
route: evt.route,
|
|
6856
|
+
durationMs: evt.durationMs,
|
|
6857
|
+
testPath: evt.testPath,
|
|
6858
|
+
currentTestName: evt.currentTestName
|
|
6859
|
+
});
|
|
6860
|
+
} else if (type === "httpResponseBatch") {
|
|
6861
|
+
const list = asHttpList(evt?.events);
|
|
6862
|
+
for (const item of list) {
|
|
6863
|
+
const anyItem = item;
|
|
6864
|
+
http.push({
|
|
6865
|
+
timestampMs: Number(anyItem.timestampMs ?? Date.now()),
|
|
6866
|
+
method: anyItem.method,
|
|
6867
|
+
url: anyItem.url,
|
|
6868
|
+
route: anyItem.route,
|
|
6869
|
+
statusCode: anyItem.statusCode,
|
|
6870
|
+
durationMs: anyItem.durationMs,
|
|
6871
|
+
contentType: anyItem.contentType,
|
|
6872
|
+
requestId: anyItem.requestId,
|
|
6873
|
+
json: anyItem.json,
|
|
6874
|
+
bodyPreview: anyItem.bodyPreview,
|
|
6875
|
+
testPath: evt.testPath,
|
|
6876
|
+
currentTestName: evt.currentTestName
|
|
6877
|
+
});
|
|
6878
|
+
}
|
|
6879
|
+
} else if (type === "assertionFailure") {
|
|
6880
|
+
assertions.push({
|
|
6881
|
+
timestampMs: typeof evt.timestampMs === "number" ? evt.timestampMs : void 0,
|
|
6882
|
+
matcher: evt.matcher,
|
|
6883
|
+
expectedNumber: typeof evt.expectedNumber === "number" ? evt.expectedNumber : void 0,
|
|
6884
|
+
receivedNumber: typeof evt.receivedNumber === "number" ? evt.receivedNumber : void 0,
|
|
6885
|
+
message: typeof evt.message === "string" ? evt.message : void 0,
|
|
6886
|
+
stack: typeof evt.stack === "string" ? evt.stack : void 0,
|
|
6887
|
+
testPath: evt.testPath,
|
|
6888
|
+
currentTestName: evt.currentTestName,
|
|
6889
|
+
expectedPreview: typeof evt.expectedPreview === "string" ? evt.expectedPreview : void 0,
|
|
6890
|
+
actualPreview: typeof evt.actualPreview === "string" ? evt.actualPreview : void 0
|
|
6891
|
+
});
|
|
6892
|
+
}
|
|
6893
|
+
} catch {
|
|
6894
|
+
}
|
|
6895
|
+
}
|
|
6896
|
+
return { http, assertions };
|
|
6897
|
+
};
|
|
6898
|
+
const parsed = parseBridge(file.console);
|
|
6899
|
+
httpSorted = [...parsed.http].sort(by((event) => event.timestampMs));
|
|
6900
|
+
assertionEvents = parsed.assertions;
|
|
6901
|
+
}
|
|
6902
|
+
const inSameCtx = (testPath, testName) => httpSorted.filter(
|
|
6903
|
+
(event) => event.testPath === testPath && event.currentTestName === testName
|
|
6904
|
+
);
|
|
6905
|
+
if (file.failureMessage || file.testExecError) {
|
|
6522
6906
|
const lines = file.failureMessage.split(/\r?\n/);
|
|
6523
|
-
const
|
|
6524
|
-
|
|
6525
|
-
|
|
6907
|
+
const combinedDetails = (() => {
|
|
6908
|
+
const base = linesFromDetails(file.failureDetails);
|
|
6909
|
+
const exec = linesFromDetails(
|
|
6910
|
+
Array.isArray(file.testExecError) ? file.testExecError : [file.testExecError]
|
|
6911
|
+
);
|
|
6912
|
+
return {
|
|
6913
|
+
stacks: [...base.stacks, ...exec.stacks],
|
|
6914
|
+
messages: [...base.messages, ...exec.messages]
|
|
6915
|
+
};
|
|
6916
|
+
})();
|
|
6917
|
+
const mergedForStack = collapseStacks([...lines, ...combinedDetails.stacks]);
|
|
6918
|
+
const synthLoc = deepestProjectLoc(mergedForStack, ctx.projectHint);
|
|
6526
6919
|
out.push(...buildCodeFrameSection(lines, ctx, synthLoc));
|
|
6527
|
-
const
|
|
6528
|
-
|
|
6529
|
-
|
|
6530
|
-
const stackPreview = ctx.showStacks ?
|
|
6920
|
+
const payloadPretty = buildPrettyDiffSection(file.failureDetails, lines);
|
|
6921
|
+
out.push(...payloadPretty);
|
|
6922
|
+
const hasPretty = payloadPretty.length > 0;
|
|
6923
|
+
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
6924
|
out.push(
|
|
6532
|
-
...buildMessageSection(lines,
|
|
6925
|
+
...buildMessageSection(lines, combinedDetails, ctx, {
|
|
6533
6926
|
suppressDiff: hasPretty,
|
|
6534
6927
|
stackPreview
|
|
6535
6928
|
})
|
|
6536
6929
|
);
|
|
6537
|
-
out.push(...buildConsoleSection(file.console ?? null));
|
|
6930
|
+
out.push(...buildConsoleSection(stripBridgeEventsFromConsole(file.console ?? null)));
|
|
6538
6931
|
if (ctx.showStacks && stackPreview.length === 0) {
|
|
6539
|
-
|
|
6932
|
+
const tail = mergedForStack.filter((ln) => isStackLine(stripAnsiSimple(ln))).slice(-4).map((ln) => ` ${colorStackLine(String(ln), ctx.projectHint)}`);
|
|
6933
|
+
if (tail.length) {
|
|
6934
|
+
out.push(ansi.dim(" Stack:"), ...tail, "");
|
|
6935
|
+
}
|
|
6540
6936
|
}
|
|
6541
6937
|
}
|
|
6542
|
-
for (const
|
|
6938
|
+
for (const assertion of failed) {
|
|
6543
6939
|
out.push(drawFailLine());
|
|
6544
|
-
const header = `${rel} > ${
|
|
6545
|
-
const messagesArray =
|
|
6546
|
-
|
|
6940
|
+
const header = `${rel} > ${assertion.fullName}`;
|
|
6941
|
+
const messagesArray = (() => {
|
|
6942
|
+
if (assertion.failureMessages && assertion.failureMessages.length > 0) {
|
|
6943
|
+
return assertion.failureMessages;
|
|
6944
|
+
}
|
|
6945
|
+
if (file.failureMessage && file.failureMessage.trim().length > 0) {
|
|
6946
|
+
return file.failureMessage.split(/\r?\n/);
|
|
6947
|
+
}
|
|
6948
|
+
const linesFromMatcher = linesFromDetails(
|
|
6949
|
+
assertion.failureDetails || file.failureDetails
|
|
6950
|
+
).messages;
|
|
6951
|
+
if (Array.isArray(linesFromMatcher) && linesFromMatcher.length > 0) {
|
|
6952
|
+
return linesFromMatcher;
|
|
6953
|
+
}
|
|
6954
|
+
return [""];
|
|
6955
|
+
})();
|
|
6956
|
+
const details = linesFromDetails(assertion.failureDetails || file.failureDetails);
|
|
6957
|
+
const matcherMsg = (() => {
|
|
6958
|
+
try {
|
|
6959
|
+
const arr = assertion.failureDetails || file.failureDetails;
|
|
6960
|
+
if (!arr) {
|
|
6961
|
+
return [];
|
|
6962
|
+
}
|
|
6963
|
+
for (const detailEntry of arr) {
|
|
6964
|
+
const obj = detailEntry && typeof detailEntry === "object" ? detailEntry : null;
|
|
6965
|
+
const mr = obj && obj.matcherResult && typeof obj.matcherResult === "object" ? obj.matcherResult : null;
|
|
6966
|
+
if (mr && typeof mr.message === "string" && mr.message.trim()) {
|
|
6967
|
+
const name = typeof mr.matcherName === "string" ? mr.matcherName : "";
|
|
6968
|
+
const matcherHeader = name ? ` ${ansi.bold("Matcher:")} ${ansi.yellow(name)}` : "";
|
|
6969
|
+
const bodyHeader = ` ${ansi.bold("Message:")}`;
|
|
6970
|
+
const body = String(mr.message).split(/\r?\n/).slice(0, 6).map((ln) => ` ${ansi.yellow(ln)}`);
|
|
6971
|
+
return [matcherHeader, bodyHeader, ...body, ""].filter(Boolean);
|
|
6972
|
+
}
|
|
6973
|
+
}
|
|
6974
|
+
} catch {
|
|
6975
|
+
}
|
|
6976
|
+
return [];
|
|
6977
|
+
})();
|
|
6547
6978
|
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 = `${
|
|
6979
|
+
const deepestLoc = deepestProjectLoc(mergedForStack, ctx.projectHint);
|
|
6980
|
+
const locLink = deepestLoc ? (() => {
|
|
6981
|
+
const href = preferredEditorHref(deepestLoc.file, deepestLoc.line, ctx.editorCmd);
|
|
6982
|
+
const base = `${deepestLoc.file.split("/").pop()}:${deepestLoc.line}`;
|
|
6552
6983
|
return osc8(base, href);
|
|
6553
|
-
})();
|
|
6984
|
+
})() : void 0;
|
|
6985
|
+
const bullet = (text) => `${Colors.Failure("\xD7")} ${ansi.white(text)}`;
|
|
6554
6986
|
const headerLine = `${ansi.white(header)}${locLink ? ` ${ansi.dim(`(${locLink})`)}` : ""}`;
|
|
6555
|
-
const bullet = (text) => `${colorTokens.fail("\xD7")} ${ansi.white(text)}`;
|
|
6556
6987
|
out.push(bullet(headerLine));
|
|
6557
6988
|
const msgLines = messagesArray.join("\n").split("\n");
|
|
6558
|
-
const assertFallback = deepestLoc ||
|
|
6559
|
-
|
|
6560
|
-
|
|
6561
|
-
|
|
6562
|
-
|
|
6563
|
-
|
|
6564
|
-
|
|
6565
|
-
|
|
6566
|
-
const stackPreview = ctx.showStacks ?
|
|
6989
|
+
const assertFallback = deepestLoc || assertion.location && { file: file.testFilePath, line: assertion.location.line };
|
|
6990
|
+
out.push("", ...buildCodeFrameSection(msgLines, ctx, assertFallback || void 0), "");
|
|
6991
|
+
const pretty = buildPrettyDiffSection(
|
|
6992
|
+
assertion.failureDetails || file.failureDetails,
|
|
6993
|
+
msgLines
|
|
6994
|
+
);
|
|
6995
|
+
out.push(...pretty);
|
|
6996
|
+
const hasPretty = pretty.length > 0;
|
|
6997
|
+
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)}`) : [];
|
|
6998
|
+
if (matcherMsg.length) {
|
|
6999
|
+
out.push(...matcherMsg);
|
|
7000
|
+
}
|
|
6567
7001
|
out.push(
|
|
6568
7002
|
...buildMessageSection(msgLines, details, ctx, { suppressDiff: hasPretty, stackPreview })
|
|
6569
7003
|
);
|
|
6570
|
-
|
|
6571
|
-
|
|
6572
|
-
|
|
6573
|
-
|
|
6574
|
-
|
|
6575
|
-
|
|
6576
|
-
|
|
7004
|
+
{
|
|
7005
|
+
const HEADLAMP_HTTP_DIFF_LIMIT_LOCAL = () => HEADLAMP_HTTP_DIFF_LIMIT();
|
|
7006
|
+
const safeParseJSON = (text) => {
|
|
7007
|
+
try {
|
|
7008
|
+
return text ? import_json52.default.parse(text) : void 0;
|
|
7009
|
+
} catch {
|
|
7010
|
+
return void 0;
|
|
7011
|
+
}
|
|
7012
|
+
};
|
|
7013
|
+
const jsonDiff = (expected, actual, limit = HEADLAMP_HTTP_DIFF_LIMIT_LOCAL()) => {
|
|
7014
|
+
const outChanges = [];
|
|
7015
|
+
const queue = [];
|
|
7016
|
+
queue.push({
|
|
7017
|
+
pathSoFar: "$",
|
|
7018
|
+
expectedValue: expected,
|
|
7019
|
+
actualValue: actual
|
|
7020
|
+
});
|
|
7021
|
+
while (queue.length && outChanges.length < limit) {
|
|
7022
|
+
const { pathSoFar, expectedValue, actualValue } = queue.shift();
|
|
7023
|
+
const expectedIsObj = expectedValue && typeof expectedValue === "object";
|
|
7024
|
+
const actualIsObj = actualValue && typeof actualValue === "object";
|
|
7025
|
+
if (!expectedIsObj && !actualIsObj) {
|
|
7026
|
+
if (JSON.stringify(expectedValue) !== JSON.stringify(actualValue)) {
|
|
7027
|
+
outChanges.push({
|
|
7028
|
+
kind: "changed",
|
|
7029
|
+
path: pathSoFar,
|
|
7030
|
+
preview: `${String(expectedValue)} \u2192 ${String(actualValue)}`
|
|
7031
|
+
});
|
|
7032
|
+
}
|
|
7033
|
+
} else if (expectedIsObj && !actualIsObj) {
|
|
7034
|
+
outChanges.push({
|
|
7035
|
+
kind: "changed",
|
|
7036
|
+
path: pathSoFar,
|
|
7037
|
+
preview: "[object] \u2192 primitive"
|
|
7038
|
+
});
|
|
7039
|
+
} else if (!expectedIsObj && actualIsObj) {
|
|
7040
|
+
outChanges.push({
|
|
7041
|
+
kind: "changed",
|
|
7042
|
+
path: pathSoFar,
|
|
7043
|
+
preview: "primitive \u2192 [object]"
|
|
7044
|
+
});
|
|
7045
|
+
} else {
|
|
7046
|
+
const expectedKeys = new Set(Object.keys(expectedValue));
|
|
7047
|
+
const actualKeys = new Set(Object.keys(actualValue));
|
|
7048
|
+
for (const key of expectedKeys) {
|
|
7049
|
+
if (!actualKeys.has(key) && outChanges.length < limit) {
|
|
7050
|
+
outChanges.push({ kind: "removed", path: `${pathSoFar}.${key}` });
|
|
7051
|
+
}
|
|
7052
|
+
}
|
|
7053
|
+
for (const key of actualKeys) {
|
|
7054
|
+
if (!expectedKeys.has(key) && outChanges.length < limit) {
|
|
7055
|
+
outChanges.push({ kind: "added", path: `${pathSoFar}.${key}` });
|
|
7056
|
+
}
|
|
7057
|
+
}
|
|
7058
|
+
for (const key of expectedKeys) {
|
|
7059
|
+
if (actualKeys.has(key) && outChanges.length < limit) {
|
|
7060
|
+
queue.push({
|
|
7061
|
+
pathSoFar: `${pathSoFar}.${key}`,
|
|
7062
|
+
expectedValue: expectedValue[key],
|
|
7063
|
+
actualValue: actualValue[key]
|
|
7064
|
+
});
|
|
7065
|
+
}
|
|
7066
|
+
}
|
|
7067
|
+
}
|
|
7068
|
+
}
|
|
7069
|
+
return outChanges;
|
|
7070
|
+
};
|
|
7071
|
+
const importantMessages = (json) => {
|
|
7072
|
+
const msgs = [];
|
|
7073
|
+
try {
|
|
7074
|
+
const obj = isObject(json) ? json : {};
|
|
7075
|
+
const pushMaybe = (candidate) => {
|
|
7076
|
+
if (typeof candidate === "string" && candidate.trim()) {
|
|
7077
|
+
msgs.push(candidate);
|
|
7078
|
+
}
|
|
7079
|
+
};
|
|
7080
|
+
pushMaybe(obj.displayMessage);
|
|
7081
|
+
pushMaybe(obj.message);
|
|
7082
|
+
if (Array.isArray(obj.errors)) {
|
|
7083
|
+
for (const element of obj.errors) {
|
|
7084
|
+
pushMaybe(isObject(element) ? element.message : void 0);
|
|
7085
|
+
}
|
|
7086
|
+
}
|
|
7087
|
+
if (Array.isArray(obj.data)) {
|
|
7088
|
+
for (const element of obj.data) {
|
|
7089
|
+
pushMaybe(isObject(element) ? element.message : void 0);
|
|
7090
|
+
}
|
|
7091
|
+
}
|
|
7092
|
+
} catch {
|
|
7093
|
+
}
|
|
7094
|
+
return msgs.slice(0, 2);
|
|
7095
|
+
};
|
|
7096
|
+
const nameMatches = (leftName, rightName) => !!leftName && !!rightName && (leftName === rightName || leftName.includes(rightName) || rightName.includes(leftName));
|
|
7097
|
+
const corresponding = assertionEvents.find(
|
|
7098
|
+
(aevt) => aevt.testPath === file.testFilePath && nameMatches(aevt.currentTestName, assertion.title)
|
|
7099
|
+
) ?? assertion;
|
|
7100
|
+
const perTestSlice = inSameCtx(file.testFilePath, assertion.title);
|
|
7101
|
+
const nearByTime = eventsNear(
|
|
7102
|
+
httpSorted,
|
|
7103
|
+
corresponding?.timestampMs,
|
|
7104
|
+
file.testFilePath
|
|
6577
7105
|
);
|
|
7106
|
+
const hasAbort = perTestSlice.some((event) => event.kind === "abort");
|
|
7107
|
+
const hasTransport = isTransportError(corresponding?.message) || hasAbort;
|
|
7108
|
+
const httpLikely = isHttpRelevant({
|
|
7109
|
+
assertion: corresponding,
|
|
7110
|
+
title: assertion.fullName,
|
|
7111
|
+
relPath: rel,
|
|
7112
|
+
httpCountInSameTest: perTestSlice.length || nearByTime.length,
|
|
7113
|
+
hasTransportSignal: hasTransport
|
|
7114
|
+
});
|
|
7115
|
+
if (!httpLikely) {
|
|
7116
|
+
} else {
|
|
7117
|
+
const expPreview = corresponding?.expectedPreview;
|
|
7118
|
+
const actPreview = corresponding?.actualPreview;
|
|
7119
|
+
const parsedExpected = safeParseJSON(expPreview);
|
|
7120
|
+
const parsedActual = safeParseJSON(actPreview);
|
|
7121
|
+
let corr = corresponding;
|
|
7122
|
+
if (!isHttpStatusNumber(corr.expectedNumber) && !isHttpStatusNumber(corr.receivedNumber)) {
|
|
7123
|
+
const inferred = inferHttpNumbersFromText(msgLines);
|
|
7124
|
+
if (isHttpStatusNumber(inferred.expectedNumber) || isHttpStatusNumber(inferred.receivedNumber)) {
|
|
7125
|
+
corr = { ...corr, ...inferred };
|
|
7126
|
+
}
|
|
7127
|
+
}
|
|
7128
|
+
const relevant = pickRelevantHttp(
|
|
7129
|
+
{
|
|
7130
|
+
timestampMs: corr?.timestampMs,
|
|
7131
|
+
expectedNumber: corr?.expectedNumber,
|
|
7132
|
+
receivedNumber: corr?.receivedNumber,
|
|
7133
|
+
matcher: corr?.matcher,
|
|
7134
|
+
message: corr?.message,
|
|
7135
|
+
stack: corr?.stack,
|
|
7136
|
+
testPath: file.testFilePath,
|
|
7137
|
+
currentTestName: assertion.title
|
|
7138
|
+
},
|
|
7139
|
+
httpSorted,
|
|
7140
|
+
{
|
|
7141
|
+
testPath: file.testFilePath,
|
|
7142
|
+
currentTestName: assertion.title,
|
|
7143
|
+
title: assertion.fullName
|
|
7144
|
+
}
|
|
7145
|
+
);
|
|
7146
|
+
if (hasTransport) {
|
|
7147
|
+
const tsBase = corresponding?.timestampMs ?? 0;
|
|
7148
|
+
const abortCandidates = perTestSlice.filter((event) => event.kind === "abort").sort((leftEvent, rightEvent) => {
|
|
7149
|
+
const deltaLeft = Math.abs(tsBase - (leftEvent.timestampMs ?? 0));
|
|
7150
|
+
const deltaRight = Math.abs(tsBase - (rightEvent.timestampMs ?? 0));
|
|
7151
|
+
return deltaLeft - deltaRight;
|
|
7152
|
+
});
|
|
7153
|
+
const [nearestAbort] = abortCandidates;
|
|
7154
|
+
if (nearestAbort) {
|
|
7155
|
+
out.push(
|
|
7156
|
+
" HTTP:",
|
|
7157
|
+
`
|
|
7158
|
+
${summarizeUrl(nearestAbort.method, nearestAbort.url, nearestAbort.route)} ${ansi.dim("->")} ${ansi.yellow("connection aborted")}`,
|
|
7159
|
+
(() => {
|
|
7160
|
+
const ms = nearestAbort.durationMs;
|
|
7161
|
+
return ms != null ? ` ${ansi.dim(`(${ms}ms)`)} ` : "";
|
|
7162
|
+
})(),
|
|
7163
|
+
"\n"
|
|
7164
|
+
);
|
|
7165
|
+
} else if (relevant) {
|
|
7166
|
+
} else if (HEADLAMP_HTTP_SHOW_MISS()) {
|
|
7167
|
+
out.push(
|
|
7168
|
+
" HTTP:",
|
|
7169
|
+
`
|
|
7170
|
+
${ansi.dim("Transport error; no matching HTTP exchange in window.")}`,
|
|
7171
|
+
"\n"
|
|
7172
|
+
);
|
|
7173
|
+
}
|
|
7174
|
+
}
|
|
7175
|
+
if (!hasTransport && relevant) {
|
|
7176
|
+
const parts = [];
|
|
7177
|
+
const where = summarizeUrl(relevant.method, relevant.url, relevant.route);
|
|
7178
|
+
const line1 = [
|
|
7179
|
+
" HTTP:",
|
|
7180
|
+
`
|
|
7181
|
+
${where} ${ansi.dim("->")} ${relevant.statusCode ?? "?"}`,
|
|
7182
|
+
(() => {
|
|
7183
|
+
const ms = relevant.durationMs;
|
|
7184
|
+
return typeof ms === "number" ? ` ${ansi.dim(`(${ms}ms)`)} ` : " ";
|
|
7185
|
+
})(),
|
|
7186
|
+
relevant.contentType ? ansi.dim(`(${relevant.contentType})`) : "",
|
|
7187
|
+
relevant.requestId ? ansi.dim(` reqId=${relevant.requestId}`) : ""
|
|
7188
|
+
].join("");
|
|
7189
|
+
const expVsAct = (() => {
|
|
7190
|
+
if (typeof corresponding?.expectedNumber === "number" || typeof corresponding?.receivedNumber === "number") {
|
|
7191
|
+
const exp = corresponding?.expectedNumber != null ? String(corresponding.expectedNumber) : "?";
|
|
7192
|
+
const got = corresponding?.receivedNumber != null ? String(corresponding.receivedNumber) : String(relevant.statusCode ?? "?");
|
|
7193
|
+
return `
|
|
7194
|
+
Expected: ${ansi.yellow(exp)} Received: ${ansi.yellow(got)}`;
|
|
7195
|
+
}
|
|
7196
|
+
return "";
|
|
7197
|
+
})();
|
|
7198
|
+
const whyLines = importantMessages(relevant.json).map((msg) => `
|
|
7199
|
+
Why: ${ansi.white(msg)}`).slice(0, 1).join("");
|
|
7200
|
+
const diffLines = (() => {
|
|
7201
|
+
const rightActual = parsedActual ?? relevant.json;
|
|
7202
|
+
if (!parsedExpected || !rightActual) {
|
|
7203
|
+
return "";
|
|
7204
|
+
}
|
|
7205
|
+
const changes = jsonDiff(parsedExpected, rightActual);
|
|
7206
|
+
if (!changes.length) {
|
|
7207
|
+
return "";
|
|
7208
|
+
}
|
|
7209
|
+
const head = "\n Diff:";
|
|
7210
|
+
const body = changes.map((change) => {
|
|
7211
|
+
const marker = change.kind === "added" ? "+" : change.kind === "removed" ? "-" : "~";
|
|
7212
|
+
const previewText = change.preview ? `: ${ansi.dim(change.preview)}` : "";
|
|
7213
|
+
return `
|
|
7214
|
+
${marker} ${change.path}${previewText}`;
|
|
7215
|
+
}).join("");
|
|
7216
|
+
return head + body;
|
|
7217
|
+
})();
|
|
7218
|
+
parts.push(line1, expVsAct, whyLines, diffLines, "\n");
|
|
7219
|
+
out.push(...parts.filter(Boolean));
|
|
7220
|
+
} else if (!hasTransport && !relevant && HEADLAMP_HTTP_SHOW_MISS()) {
|
|
7221
|
+
out.push(
|
|
7222
|
+
" HTTP:",
|
|
7223
|
+
`
|
|
7224
|
+
${ansi.dim("No relevant HTTP exchange found. (HEADLAMP_HTTP_MISS=0 to hide)")}`,
|
|
7225
|
+
"\n"
|
|
7226
|
+
);
|
|
7227
|
+
}
|
|
7228
|
+
}
|
|
6578
7229
|
}
|
|
6579
|
-
|
|
6580
|
-
|
|
7230
|
+
const minimalInfo = msgLines.every((ln) => !ln.trim());
|
|
7231
|
+
if (minimalInfo) {
|
|
7232
|
+
try {
|
|
7233
|
+
out.push(...buildThrownSection(assertion.failureDetails || []));
|
|
7234
|
+
} catch {
|
|
7235
|
+
}
|
|
7236
|
+
}
|
|
7237
|
+
out.push(...buildConsoleSection(stripBridgeEventsFromConsole(file.console ?? null)));
|
|
7238
|
+
if (ctx.showStacks && stackPreview.length === 0) {
|
|
7239
|
+
const tail = mergedForStack.filter((ln) => isStackLine(stripAnsiSimple(ln))).slice(-4).map((ln) => ` ${colorStackLine(String(ln), ctx.projectHint)}`);
|
|
7240
|
+
if (tail.length) {
|
|
7241
|
+
out.push(ansi.dim(" Stack:"), ...tail, "");
|
|
7242
|
+
}
|
|
7243
|
+
}
|
|
7244
|
+
out.push(drawFailLine(), "");
|
|
6581
7245
|
}
|
|
6582
7246
|
}
|
|
6583
7247
|
const failedCount = data.aggregated.numFailedTests;
|
|
6584
|
-
out.push(drawRule(
|
|
7248
|
+
out.push(drawRule(BackgroundColors.Failure(ansi.white(` Failed Tests ${failedCount} `))));
|
|
6585
7249
|
out.push("");
|
|
6586
|
-
|
|
6587
|
-
|
|
6588
|
-
|
|
6589
|
-
|
|
6590
|
-
);
|
|
6591
|
-
|
|
6592
|
-
|
|
7250
|
+
out.push(vitestFooter(data.aggregated));
|
|
7251
|
+
return out.join("\n");
|
|
7252
|
+
};
|
|
7253
|
+
var tryBridgeFallback = (raw, ctx, opts) => {
|
|
7254
|
+
const bridgeJsonPath = extractBridgePath2(raw, ctx.cwd);
|
|
7255
|
+
if (!bridgeJsonPath || !fs6.existsSync(bridgeJsonPath)) {
|
|
7256
|
+
return null;
|
|
7257
|
+
}
|
|
7258
|
+
try {
|
|
7259
|
+
const json = import_json52.default.parse(fs6.readFileSync(bridgeJsonPath, "utf8"));
|
|
7260
|
+
const bridge = coerceJestJsonToBridge(json);
|
|
7261
|
+
return renderVitestFromJestJSON(bridge, ctx, opts);
|
|
7262
|
+
} catch {
|
|
7263
|
+
return null;
|
|
7264
|
+
}
|
|
7265
|
+
};
|
|
7266
|
+
|
|
7267
|
+
// src/lib/formatJestOutputVitest.ts
|
|
7268
|
+
var formatJestOutputVitest = (raw, opts) => pipe(
|
|
7269
|
+
{ raw, opts },
|
|
7270
|
+
(state) => ({
|
|
7271
|
+
...state,
|
|
7272
|
+
ctx: makeCtx(state.opts, /\bFAIL\b/.test(stripAnsiSimple(state.raw)))
|
|
7273
|
+
}),
|
|
7274
|
+
(state) => ({ ...state, chunks: parseChunks(state.raw) }),
|
|
7275
|
+
(state) => ({
|
|
7276
|
+
...state,
|
|
7277
|
+
rendered: renderChunks(state.chunks, state.ctx, mkPrettyFns(), {
|
|
7278
|
+
onlyFailures: Boolean(state.opts?.onlyFailures)
|
|
7279
|
+
})
|
|
7280
|
+
}),
|
|
7281
|
+
(state) => {
|
|
7282
|
+
if (state.rendered.hadParsed) {
|
|
7283
|
+
return state.rendered.text;
|
|
7284
|
+
}
|
|
7285
|
+
const fallback = tryBridgeFallback(state.raw, state.ctx, {
|
|
7286
|
+
onlyFailures: Boolean(state.opts?.onlyFailures)
|
|
7287
|
+
});
|
|
7288
|
+
if (!fallback) {
|
|
7289
|
+
return state.rendered.text;
|
|
7290
|
+
}
|
|
7291
|
+
const prefix = state.rendered.text;
|
|
7292
|
+
return prefix ? `${prefix}
|
|
7293
|
+
${fallback}` : fallback;
|
|
7294
|
+
}
|
|
7295
|
+
);
|
|
7296
|
+
|
|
7297
|
+
// src/lib/jest-reporter-source.ts
|
|
7298
|
+
var JEST_BRIDGE_REPORTER_SOURCE = `const fs = require('fs');
|
|
7299
|
+
const path = require('path');
|
|
7300
|
+
|
|
7301
|
+
const isObject = (v) => typeof v === 'object' && v !== null;
|
|
7302
|
+
const sanitizeError = (err) => {
|
|
7303
|
+
if (!isObject(err)) return err;
|
|
7304
|
+
const out = {};
|
|
7305
|
+
const name = err.name || (err.constructor && err.constructor.name) || undefined;
|
|
7306
|
+
if (name) out.name = String(name);
|
|
7307
|
+
if (typeof err.message === 'string') out.message = err.message;
|
|
7308
|
+
if (typeof err.stack === 'string') out.stack = err.stack;
|
|
7309
|
+
if (err.code !== undefined) out.code = err.code;
|
|
7310
|
+
if (err.expected !== undefined) out.expected = err.expected;
|
|
7311
|
+
if (err.received !== undefined) out.received = err.received;
|
|
7312
|
+
if (err.matcherResult && isObject(err.matcherResult)) {
|
|
7313
|
+
const mr = err.matcherResult;
|
|
7314
|
+
let messageText;
|
|
7315
|
+
try {
|
|
7316
|
+
messageText = typeof mr.message === 'function' ? String(mr.message()) : (typeof mr.message === 'string' ? mr.message : undefined);
|
|
7317
|
+
} catch {}
|
|
7318
|
+
out.matcherResult = {
|
|
7319
|
+
matcherName: typeof mr.matcherName === 'string' ? mr.matcherName : undefined,
|
|
7320
|
+
message: messageText,
|
|
7321
|
+
stack: typeof mr.stack === 'string' ? mr.stack : undefined,
|
|
7322
|
+
expected: mr.expected,
|
|
7323
|
+
received: mr.received,
|
|
7324
|
+
actual: mr.actual,
|
|
7325
|
+
pass: typeof mr.pass === 'boolean' ? mr.pass : undefined,
|
|
7326
|
+
};
|
|
7327
|
+
}
|
|
7328
|
+
if (err.cause) {
|
|
7329
|
+
out.cause = sanitizeError(err.cause);
|
|
7330
|
+
}
|
|
7331
|
+
// Copy own enumerable props to preserve custom data
|
|
7332
|
+
try {
|
|
7333
|
+
for (const key of Object.keys(err)) {
|
|
7334
|
+
if (!(key in out)) out[key] = err[key];
|
|
7335
|
+
}
|
|
7336
|
+
} catch {}
|
|
7337
|
+
return out;
|
|
7338
|
+
};
|
|
7339
|
+
const sanitizeDetail = (d) => {
|
|
7340
|
+
if (typeof d === 'string') return d;
|
|
7341
|
+
if (!isObject(d)) return d;
|
|
7342
|
+
// Common Jest detail shapes
|
|
7343
|
+
const out = {};
|
|
7344
|
+
if (d.message) out.message = d.message;
|
|
7345
|
+
if (d.stack) out.stack = d.stack;
|
|
7346
|
+
if (d.error) out.error = sanitizeError(d.error);
|
|
7347
|
+
if (d.matcherResult) out.matcherResult = sanitizeError({ matcherResult: d.matcherResult }).matcherResult;
|
|
7348
|
+
if (d.expected !== undefined) out.expected = d.expected;
|
|
7349
|
+
if (d.received !== undefined) out.received = d.received;
|
|
7350
|
+
// Copy the rest
|
|
7351
|
+
try {
|
|
7352
|
+
for (const key of Object.keys(d)) {
|
|
7353
|
+
if (!(key in out)) out[key] = d[key];
|
|
7354
|
+
}
|
|
7355
|
+
} catch {}
|
|
7356
|
+
return out;
|
|
7357
|
+
};
|
|
7358
|
+
|
|
7359
|
+
class BridgeReporter {
|
|
7360
|
+
constructor(globalConfig, options) {
|
|
7361
|
+
this.out = process.env.JEST_BRIDGE_OUT || (options && options.outFile) || path.join(process.cwd(), 'coverage', 'jest-run.json');
|
|
7362
|
+
this.buf = { startTime: Date.now(), testResults: [], aggregated: null };
|
|
7363
|
+
}
|
|
7364
|
+
onRunStart() { this.buf.startTime = Date.now(); }
|
|
7365
|
+
onTestResult(_test, tr) {
|
|
7366
|
+
const mapAssertion = (a) => ({
|
|
7367
|
+
title: a.title,
|
|
7368
|
+
fullName: a.fullName || [...(a.ancestorTitles || []), a.title].join(' '),
|
|
7369
|
+
status: a.status,
|
|
7370
|
+
duration: a.duration || 0,
|
|
7371
|
+
location: a.location || null,
|
|
7372
|
+
failureMessages: (a.failureMessages || []).map(String),
|
|
7373
|
+
failureDetails: (a.failureDetails || []).map(sanitizeDetail),
|
|
7374
|
+
});
|
|
7375
|
+
this.buf.testResults.push({
|
|
7376
|
+
testFilePath: tr.testFilePath,
|
|
7377
|
+
status: tr.numFailingTests ? 'failed' : 'passed',
|
|
7378
|
+
failureMessage: tr.failureMessage || '',
|
|
7379
|
+
failureDetails: (tr.failureDetails || []).map(sanitizeDetail),
|
|
7380
|
+
testExecError: tr.testExecError ? sanitizeError(tr.testExecError) : null,
|
|
7381
|
+
console: tr.console || null,
|
|
7382
|
+
perfStats: tr.perfStats || {},
|
|
7383
|
+
testResults: (tr.testResults || []).map(mapAssertion),
|
|
7384
|
+
});
|
|
7385
|
+
}
|
|
7386
|
+
onRunComplete(_contexts, agg) {
|
|
7387
|
+
this.buf.aggregated = {
|
|
7388
|
+
numTotalTestSuites: agg.numTotalTestSuites,
|
|
7389
|
+
numPassedTestSuites: agg.numPassedTestSuites,
|
|
7390
|
+
numFailedTestSuites: agg.numFailedTestSuites,
|
|
7391
|
+
numTotalTests: agg.numTotalTests,
|
|
7392
|
+
numPassedTests: agg.numPassedTests,
|
|
7393
|
+
numFailedTests: agg.numFailedTests,
|
|
7394
|
+
numPendingTests: agg.numPendingTests,
|
|
7395
|
+
numTodoTests: agg.numTodoTests,
|
|
7396
|
+
startTime: agg.startTime,
|
|
7397
|
+
success: agg.success,
|
|
7398
|
+
runTimeMs: agg.testResults.reduce((t, r) => t + Math.max(0, (r.perfStats?.end || 0) - (r.perfStats?.start || 0)), 0),
|
|
7399
|
+
};
|
|
7400
|
+
fs.mkdirSync(path.dirname(this.out), { recursive: true });
|
|
7401
|
+
fs.writeFileSync(this.out, JSON.stringify(this.buf), 'utf8');
|
|
7402
|
+
}
|
|
6593
7403
|
}
|
|
7404
|
+
module.exports = BridgeReporter;`;
|
|
6594
7405
|
|
|
6595
7406
|
// src/lib/program.ts
|
|
6596
7407
|
init_env_utils();
|
|
6597
7408
|
init_exec();
|
|
6598
7409
|
init_args();
|
|
6599
|
-
import * as
|
|
6600
|
-
import * as
|
|
7410
|
+
import * as path11 from "node:path";
|
|
7411
|
+
import * as os3 from "node:os";
|
|
6601
7412
|
import * as fsSync3 from "node:fs";
|
|
6602
|
-
import * as
|
|
7413
|
+
import * as fs7 from "node:fs/promises";
|
|
6603
7414
|
import * as LibReport from "istanbul-lib-report";
|
|
6604
7415
|
import * as Reports from "istanbul-reports";
|
|
6605
7416
|
import { createCoverageMap as createCoverageMap2 } from "istanbul-lib-coverage";
|
|
7417
|
+
|
|
7418
|
+
// src/lib/jest-environment-source.ts
|
|
7419
|
+
var JEST_BRIDGE_ENV_SOURCE = `
|
|
7420
|
+
'use strict';
|
|
7421
|
+
|
|
7422
|
+
const NodeEnvironment = require('jest-environment-node').TestEnvironment || require('jest-environment-node');
|
|
7423
|
+
|
|
7424
|
+
module.exports = class BridgeEnv extends NodeEnvironment {
|
|
7425
|
+
constructor(config, context) {
|
|
7426
|
+
super(config, context);
|
|
7427
|
+
const { AsyncLocalStorage } = require('node:async_hooks');
|
|
7428
|
+
this._als = new AsyncLocalStorage();
|
|
7429
|
+
this._cleanup = [];
|
|
7430
|
+
}
|
|
7431
|
+
|
|
7432
|
+
_ctx() {
|
|
7433
|
+
try { const s = this._als.getStore(); if (s) return s; } catch {}
|
|
7434
|
+
try {
|
|
7435
|
+
const st = this.global.expect && typeof this.global.expect.getState === 'function' ? this.global.expect.getState() : {};
|
|
7436
|
+
return { testPath: st.testPath, currentTestName: st.currentTestName };
|
|
7437
|
+
} catch { return {}; }
|
|
7438
|
+
}
|
|
7439
|
+
|
|
7440
|
+
async setup() {
|
|
7441
|
+
await super.setup();
|
|
7442
|
+
|
|
7443
|
+
try { Error.stackTraceLimit = Math.max(Error.stackTraceLimit || 10, 50); } catch {}
|
|
7444
|
+
|
|
7445
|
+
const print = (payload) => { try { this.global.console.error('[JEST-BRIDGE-EVENT]', JSON.stringify(payload)); } catch {} };
|
|
7446
|
+
const toErr = (x) => { try { return x instanceof Error ? x : new Error(String(x)); } catch { return new Error('unknown'); } };
|
|
7447
|
+
|
|
7448
|
+
const onRej = (reason) => {
|
|
7449
|
+
const e = toErr(reason);
|
|
7450
|
+
const c = this._ctx();
|
|
7451
|
+
print({ type: 'unhandledRejection', name: e.name, message: e.message, stack: e.stack, code: e.code ?? undefined, ...c });
|
|
7452
|
+
};
|
|
7453
|
+
const onExc = (error) => {
|
|
7454
|
+
const e = toErr(error);
|
|
7455
|
+
const c = this._ctx();
|
|
7456
|
+
print({ type: 'uncaughtException', name: e.name, message: e.message, stack: e.stack, code: e.code ?? undefined, ...c });
|
|
7457
|
+
};
|
|
7458
|
+
|
|
7459
|
+
this.global.process.on('unhandledRejection', onRej);
|
|
7460
|
+
this.global.process.on('uncaughtException', onExc);
|
|
7461
|
+
this._cleanup.push(() => {
|
|
7462
|
+
this.global.process.off('unhandledRejection', onRej);
|
|
7463
|
+
this.global.process.off('uncaughtException', onExc);
|
|
7464
|
+
});
|
|
7465
|
+
|
|
7466
|
+
// Signal environment readiness so we can confirm the custom env loaded
|
|
7467
|
+
try { const c = this._ctx(); print({ type: 'envReady', ...c }); } catch {}
|
|
7468
|
+
|
|
7469
|
+
try {
|
|
7470
|
+
const http = this.global.require ? this.global.require('http') : require('http');
|
|
7471
|
+
const originalEmit = http && http.Server && http.Server.prototype && http.Server.prototype.emit;
|
|
7472
|
+
if (originalEmit) {
|
|
7473
|
+
const MAX = 64 * 1024;
|
|
7474
|
+
const asString = (x) => { try { if (typeof x === 'string') return x; if (Buffer.isBuffer(x)) return x.toString('utf8'); return String(x); } catch { return ''; } };
|
|
7475
|
+
|
|
7476
|
+
const patched = function(eventName, req, res) {
|
|
7477
|
+
try {
|
|
7478
|
+
if (eventName === 'request' && req && res && typeof res.write === 'function' && typeof res.end === 'function') {
|
|
7479
|
+
const startAt = Date.now();
|
|
7480
|
+
const safeHeader = (k) => { try { return req && req.headers ? String((req.headers[k.toLowerCase()] ?? '')) : ''; } catch { return ''; } };
|
|
7481
|
+
const reqIdHeader = safeHeader('x-request-id');
|
|
7482
|
+
const chunks = [];
|
|
7483
|
+
const write = res.write.bind(res);
|
|
7484
|
+
const end = res.end.bind(res);
|
|
7485
|
+
const method = req.method ? String(req.method) : undefined;
|
|
7486
|
+
const url = (req.originalUrl || req.url) ? String(req.originalUrl || req.url) : undefined;
|
|
7487
|
+
|
|
7488
|
+
res.write = function(chunk, enc, cb) {
|
|
7489
|
+
try { const s = asString(chunk); if (s) chunks.push(s); } catch {}
|
|
7490
|
+
return write(chunk, enc, cb);
|
|
7491
|
+
};
|
|
7492
|
+
res.end = function(chunk, enc, cb) {
|
|
7493
|
+
try { const s = asString(chunk); if (s) chunks.push(s); } catch {}
|
|
7494
|
+
try {
|
|
7495
|
+
const preview = chunks.join('').slice(0, MAX);
|
|
7496
|
+
const statusCode = typeof res.statusCode === 'number' ? res.statusCode : undefined;
|
|
7497
|
+
const ct = (typeof res.getHeader === 'function' && res.getHeader('content-type')) || undefined;
|
|
7498
|
+
const routePath = (req && req.route && req.route.path) || (req && req.baseUrl && req.path ? (req.baseUrl + req.path) : undefined);
|
|
7499
|
+
const jsonParsed = (() => { try { return JSON.parse(preview); } catch { return undefined; } })();
|
|
7500
|
+
const requestId = reqIdHeader || (jsonParsed && typeof jsonParsed === 'object' ? (jsonParsed.requestId || jsonParsed.reqId || '') : '');
|
|
7501
|
+
const ctx = (global.__JEST_BRIDGE_ENV_REF && global.__JEST_BRIDGE_ENV_REF._ctx) ? global.__JEST_BRIDGE_ENV_REF._ctx() : {};
|
|
7502
|
+
const payload = {
|
|
7503
|
+
type: 'httpResponse',
|
|
7504
|
+
timestampMs: Date.now(),
|
|
7505
|
+
durationMs: Math.max(0, Date.now() - startAt),
|
|
7506
|
+
method, url, statusCode,
|
|
7507
|
+
route: routePath ? String(routePath) : undefined,
|
|
7508
|
+
contentType: ct ? String(ct) : undefined,
|
|
7509
|
+
headers: (typeof res.getHeaders === 'function') ? res.getHeaders() : undefined,
|
|
7510
|
+
requestId: requestId ? String(requestId) : undefined,
|
|
7511
|
+
bodyPreview: preview,
|
|
7512
|
+
json: jsonParsed,
|
|
7513
|
+
testPath: ctx.testPath, currentTestName: ctx.currentTestName,
|
|
7514
|
+
};
|
|
7515
|
+
try {
|
|
7516
|
+
if (!global.__JEST_HTTP_EVENTS__) global.__JEST_HTTP_EVENTS__ = [];
|
|
7517
|
+
const arr = global.__JEST_HTTP_EVENTS__;
|
|
7518
|
+
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 });
|
|
7519
|
+
if (arr.length > 25) arr.splice(0, arr.length - 25);
|
|
7520
|
+
} catch {}
|
|
7521
|
+
try { console.error('[JEST-BRIDGE-EVENT]', JSON.stringify(payload)); } catch {}
|
|
7522
|
+
} catch {}
|
|
7523
|
+
return end(chunk, enc, cb);
|
|
7524
|
+
};
|
|
7525
|
+
try {
|
|
7526
|
+
res.on('close', function onClose() {
|
|
7527
|
+
try {
|
|
7528
|
+
const ended = typeof res.writableEnded === 'boolean' ? res.writableEnded : false;
|
|
7529
|
+
if (!ended) {
|
|
7530
|
+
const routePath = (req && req.route && req.route.path) || (req && req.baseUrl && req.path ? (req.baseUrl + req.path) : undefined);
|
|
7531
|
+
const ctx = (global.__JEST_BRIDGE_ENV_REF && global.__JEST_BRIDGE_ENV_REF._ctx) ? global.__JEST_BRIDGE_ENV_REF._ctx() : {};
|
|
7532
|
+
const payload = {
|
|
7533
|
+
type: 'httpAbort',
|
|
7534
|
+
timestampMs: Date.now(),
|
|
7535
|
+
durationMs: Math.max(0, Date.now() - startAt),
|
|
7536
|
+
method, url,
|
|
7537
|
+
route: routePath ? String(routePath) : undefined,
|
|
7538
|
+
testPath: ctx.testPath, currentTestName: ctx.currentTestName,
|
|
7539
|
+
};
|
|
7540
|
+
try { console.error('[JEST-BRIDGE-EVENT]', JSON.stringify(payload)); } catch {}
|
|
7541
|
+
}
|
|
7542
|
+
} catch {}
|
|
7543
|
+
});
|
|
7544
|
+
} catch {}
|
|
7545
|
+
}
|
|
7546
|
+
} catch {}
|
|
7547
|
+
return originalEmit.apply(this, arguments);
|
|
7548
|
+
};
|
|
7549
|
+
|
|
7550
|
+
try { this.global.__JEST_BRIDGE_ENV_REF = this; } catch {}
|
|
7551
|
+
http.Server.prototype.emit = patched;
|
|
7552
|
+
|
|
7553
|
+
this._cleanup.push(() => {
|
|
7554
|
+
try { if (http.Server && http.Server.prototype) http.Server.prototype.emit = originalEmit; } catch {}
|
|
7555
|
+
try { delete this.global.__JEST_BRIDGE_ENV_REF; } catch {}
|
|
7556
|
+
});
|
|
7557
|
+
}
|
|
7558
|
+
} catch {}
|
|
7559
|
+
|
|
7560
|
+
// Wrap test functions to emit rich assertion events on failures
|
|
7561
|
+
try {
|
|
7562
|
+
const g = this.global;
|
|
7563
|
+
const ctxFn = () => {
|
|
7564
|
+
try {
|
|
7565
|
+
const ref = g.__JEST_BRIDGE_ENV_REF;
|
|
7566
|
+
return ref && typeof ref._ctx === 'function' ? ref._ctx() : {};
|
|
7567
|
+
} catch { return {}; }
|
|
7568
|
+
};
|
|
7569
|
+
const emitAssertion = (err) => {
|
|
7570
|
+
try {
|
|
7571
|
+
const e = toErr(err);
|
|
7572
|
+
const mr = e && typeof e === 'object' && e.matcherResult ? e.matcherResult : undefined;
|
|
7573
|
+
const messageText = (() => { try { return mr && typeof mr.message === 'function' ? String(mr.message()) : (e.message || ''); } catch { return e.message || ''; } })();
|
|
7574
|
+
const expectedPreview = (() => { try { return mr && mr.expected !== undefined ? JSON.stringify(mr.expected, null, 2) : undefined; } catch { return undefined; } })();
|
|
7575
|
+
const actualPreview = (() => { try { return mr && mr.received !== undefined ? JSON.stringify(mr.received, null, 2) : undefined; } catch { return undefined; } })();
|
|
7576
|
+
const c = ctxFn();
|
|
7577
|
+
const expectedRaw = (() => { try { return mr?.expected; } catch { return undefined; } })();
|
|
7578
|
+
const receivedRaw = (() => { try { return mr?.received; } catch { return undefined; } })();
|
|
7579
|
+
print({
|
|
7580
|
+
type: 'assertionFailure',
|
|
7581
|
+
timestampMs: Date.now(),
|
|
7582
|
+
matcher: mr && typeof mr.matcherName === 'string' ? mr.matcherName : undefined,
|
|
7583
|
+
expectedPreview,
|
|
7584
|
+
actualPreview,
|
|
7585
|
+
expectedNumber: typeof expectedRaw === 'number' ? expectedRaw : undefined,
|
|
7586
|
+
receivedNumber: typeof receivedRaw === 'number' ? receivedRaw : undefined,
|
|
7587
|
+
message: messageText,
|
|
7588
|
+
stack: e.stack,
|
|
7589
|
+
...c,
|
|
7590
|
+
});
|
|
7591
|
+
} catch {}
|
|
7592
|
+
};
|
|
7593
|
+
const wrap = (orig) => {
|
|
7594
|
+
if (!orig || typeof orig !== 'function') return orig;
|
|
7595
|
+
const wrapped = function(name, fn, timeout) {
|
|
7596
|
+
if (typeof fn !== 'function') return orig.call(this, name, fn, timeout);
|
|
7597
|
+
const run = function() {
|
|
7598
|
+
try {
|
|
7599
|
+
const res = fn.apply(this, arguments);
|
|
7600
|
+
if (res && typeof res.then === 'function') {
|
|
7601
|
+
return res.catch((err) => { emitAssertion(err); throw err; });
|
|
7602
|
+
}
|
|
7603
|
+
return res;
|
|
7604
|
+
} catch (err) {
|
|
7605
|
+
emitAssertion(err);
|
|
7606
|
+
throw err;
|
|
7607
|
+
}
|
|
7608
|
+
};
|
|
7609
|
+
return orig.call(this, name, run, timeout);
|
|
7610
|
+
};
|
|
7611
|
+
try { wrapped.only = orig.only && typeof orig.only === 'function' ? wrap(orig.only) : orig.only; } catch {}
|
|
7612
|
+
try { wrapped.skip = orig.skip && typeof orig.skip === 'function' ? wrap(orig.skip) : orig.skip; } catch {}
|
|
7613
|
+
return wrapped;
|
|
7614
|
+
};
|
|
7615
|
+
try { g.it = wrap(g.it); } catch {}
|
|
7616
|
+
try { g.test = wrap(g.test); } catch {}
|
|
7617
|
+
} catch {}
|
|
7618
|
+
}
|
|
7619
|
+
|
|
7620
|
+
async handleTestEvent(evt, state) {
|
|
7621
|
+
if (evt.name === 'test_start') {
|
|
7622
|
+
const store = { testPath: state.testPath, currentTestName: evt.test.name };
|
|
7623
|
+
try { this._als.enterWith(store); } catch {}
|
|
7624
|
+
} else if (evt.name === 'test_done') {
|
|
7625
|
+
try { this._als.enterWith({}); } catch {}
|
|
7626
|
+
try {
|
|
7627
|
+
const events = Array.isArray(global.__JEST_HTTP_EVENTS__) ? global.__JEST_HTTP_EVENTS__ : [];
|
|
7628
|
+
if (events.length) {
|
|
7629
|
+
const batch = events.slice(-10);
|
|
7630
|
+
const payload = { type: 'httpResponseBatch', events: batch, testPath: state.testPath, currentTestName: evt.test.name };
|
|
7631
|
+
try { this.global.console.error('[JEST-BRIDGE-EVENT]', JSON.stringify(payload)); } catch {}
|
|
7632
|
+
try { global.__JEST_HTTP_EVENTS__ = []; } catch {}
|
|
7633
|
+
}
|
|
7634
|
+
} catch {}
|
|
7635
|
+
}
|
|
7636
|
+
}
|
|
7637
|
+
|
|
7638
|
+
async teardown() {
|
|
7639
|
+
for (let i = this._cleanup.length - 1; i >= 0; i--) {
|
|
7640
|
+
try { this._cleanup[i](); } catch {}
|
|
7641
|
+
}
|
|
7642
|
+
await super.teardown();
|
|
7643
|
+
}
|
|
7644
|
+
};
|
|
7645
|
+
`;
|
|
7646
|
+
|
|
7647
|
+
// src/lib/program.ts
|
|
6606
7648
|
var jestBin = "./node_modules/.bin/jest";
|
|
6607
7649
|
var babelNodeBin = "./node_modules/.bin/babel-node";
|
|
6608
7650
|
var registerSignalHandlersOnce = () => {
|
|
@@ -6645,7 +7687,7 @@ var mergeLcov = async () => {
|
|
|
6645
7687
|
try {
|
|
6646
7688
|
const entries = fsSync3.readdirSync(dir, { withFileTypes: true });
|
|
6647
7689
|
for (const entry of entries) {
|
|
6648
|
-
const full =
|
|
7690
|
+
const full = path11.join(dir, entry.name);
|
|
6649
7691
|
if (entry.isDirectory()) {
|
|
6650
7692
|
out.push(...collectLcovs(full));
|
|
6651
7693
|
} else if (entry.isFile() && entry.name === "lcov.info") {
|
|
@@ -6657,8 +7699,8 @@ var mergeLcov = async () => {
|
|
|
6657
7699
|
return out;
|
|
6658
7700
|
};
|
|
6659
7701
|
try {
|
|
6660
|
-
const jestRoot =
|
|
6661
|
-
const candidates = [
|
|
7702
|
+
const jestRoot = path11.join("coverage", "jest");
|
|
7703
|
+
const candidates = [path11.join(jestRoot, "lcov.info"), ...collectLcovs(jestRoot)].map((candidatePath) => path11.resolve(candidatePath)).filter((absolutePath, index, arr) => arr.indexOf(absolutePath) === index);
|
|
6662
7704
|
for (const filePath of candidates) {
|
|
6663
7705
|
try {
|
|
6664
7706
|
const content = await readOrEmpty(filePath);
|
|
@@ -6698,7 +7740,7 @@ var emitMergedCoverage = async (ui, opts) => {
|
|
|
6698
7740
|
try {
|
|
6699
7741
|
const entries = fsSync3.readdirSync(dir, { withFileTypes: true });
|
|
6700
7742
|
for (const entry of entries) {
|
|
6701
|
-
const full =
|
|
7743
|
+
const full = path11.join(dir, entry.name);
|
|
6702
7744
|
if (entry.isDirectory()) {
|
|
6703
7745
|
out.push(...listJsons(full));
|
|
6704
7746
|
} else if (entry.isFile() && entry.name === "coverage-final.json") {
|
|
@@ -6709,11 +7751,11 @@ var emitMergedCoverage = async (ui, opts) => {
|
|
|
6709
7751
|
}
|
|
6710
7752
|
return out;
|
|
6711
7753
|
};
|
|
6712
|
-
const coverageRoot =
|
|
7754
|
+
const coverageRoot = path11.join("coverage", "jest");
|
|
6713
7755
|
const jsonCandidates = [
|
|
6714
|
-
|
|
7756
|
+
path11.join(coverageRoot, "coverage-final.json"),
|
|
6715
7757
|
...listJsons(coverageRoot)
|
|
6716
|
-
].map((candidatePath) =>
|
|
7758
|
+
].map((candidatePath) => path11.resolve(candidatePath)).filter((absolutePath, index, arr) => {
|
|
6717
7759
|
const isFirst = arr.indexOf(absolutePath) === index;
|
|
6718
7760
|
const exists = fsSync3.existsSync(absolutePath);
|
|
6719
7761
|
return isFirst && exists;
|
|
@@ -6775,7 +7817,7 @@ var emitMergedCoverage = async (ui, opts) => {
|
|
|
6775
7817
|
executedTests: opts.executedTests ?? []
|
|
6776
7818
|
});
|
|
6777
7819
|
const context = LibReport.createContext({
|
|
6778
|
-
dir:
|
|
7820
|
+
dir: path11.resolve("coverage", "merged"),
|
|
6779
7821
|
coverageMap: filteredMap,
|
|
6780
7822
|
defaultSummarizer: "nested"
|
|
6781
7823
|
});
|
|
@@ -6843,8 +7885,8 @@ var emitMergedCoverage = async (ui, opts) => {
|
|
|
6843
7885
|
for (const reporter of reporters) {
|
|
6844
7886
|
reporter.execute(context);
|
|
6845
7887
|
}
|
|
6846
|
-
const textPath =
|
|
6847
|
-
const summaryPath =
|
|
7888
|
+
const textPath = path11.resolve("coverage", "merged", "coverage.txt");
|
|
7889
|
+
const summaryPath = path11.resolve("coverage", "merged", "coverage-summary.txt");
|
|
6848
7890
|
const filesToPrint = [];
|
|
6849
7891
|
if (fsSync3.existsSync(textPath)) {
|
|
6850
7892
|
filesToPrint.push(textPath);
|
|
@@ -6987,13 +8029,13 @@ var program = async () => {
|
|
|
6987
8029
|
const rels2 = Array.from(
|
|
6988
8030
|
/* @__PURE__ */ new Set([...branchDiff, ...stagedNow, ...unstagedNow, ...untrackedNow])
|
|
6989
8031
|
);
|
|
6990
|
-
return rels2.map((rel) =>
|
|
8032
|
+
return rels2.map((rel) => path11.resolve(cwd, rel).replace(/\\/g, "/")).filter((abs) => !abs.includes("/node_modules/") && !abs.includes("/coverage/"));
|
|
6991
8033
|
}
|
|
6992
8034
|
const staged = mode === "staged" || mode === "all" ? await collect("git", ["diff", "--name-only", "--diff-filter=ACMRTUXB", "--cached"]) : [];
|
|
6993
8035
|
const unstagedTracked = mode === "unstaged" || mode === "all" ? await collect("git", ["diff", "--name-only", "--diff-filter=ACMRTUXB"]) : [];
|
|
6994
8036
|
const untracked = mode === "unstaged" || mode === "all" ? await collect("git", ["ls-files", "--others", "--exclude-standard"]) : [];
|
|
6995
8037
|
const rels = Array.from(/* @__PURE__ */ new Set([...staged, ...unstagedTracked, ...untracked]));
|
|
6996
|
-
return rels.map((rel) =>
|
|
8038
|
+
return rels.map((rel) => path11.resolve(cwd, rel).replace(/\\/g, "/")).filter((abs) => !abs.includes("/node_modules/") && !abs.includes("/coverage/"));
|
|
6997
8039
|
};
|
|
6998
8040
|
const repoRootForChanged = workspaceRoot ?? await findRepoRoot();
|
|
6999
8041
|
const changedSelectionAbs = changed ? await getChangedFiles(changed, repoRootForChanged) : [];
|
|
@@ -7018,18 +8060,18 @@ var program = async () => {
|
|
|
7018
8060
|
if (!token) {
|
|
7019
8061
|
continue;
|
|
7020
8062
|
}
|
|
7021
|
-
const isAbs =
|
|
8063
|
+
const isAbs = path11.isAbsolute(token);
|
|
7022
8064
|
const looksLikeRelPath = /[\\/]/.test(token);
|
|
7023
8065
|
let candidateFromRoot;
|
|
7024
8066
|
if (token.startsWith("/")) {
|
|
7025
|
-
candidateFromRoot =
|
|
8067
|
+
candidateFromRoot = path11.join(repoRoot, token.slice(1));
|
|
7026
8068
|
} else if (looksLikeRelPath) {
|
|
7027
|
-
candidateFromRoot =
|
|
8069
|
+
candidateFromRoot = path11.join(repoRoot, token);
|
|
7028
8070
|
} else {
|
|
7029
8071
|
candidateFromRoot = void 0;
|
|
7030
8072
|
}
|
|
7031
8073
|
const tryPushIfProd = (absPath) => {
|
|
7032
|
-
const norm =
|
|
8074
|
+
const norm = path11.resolve(absPath).replace(/\\/g, "/");
|
|
7033
8075
|
const isTest = /(^|\/)tests?\//i.test(norm) || /\.(test|spec)\.[tj]sx?$/i.test(norm);
|
|
7034
8076
|
if (!isTest && fsSync3.existsSync(norm)) {
|
|
7035
8077
|
results.add(norm);
|
|
@@ -7051,7 +8093,7 @@ var program = async () => {
|
|
|
7051
8093
|
}),
|
|
7052
8094
|
timeoutMs: 4e3
|
|
7053
8095
|
});
|
|
7054
|
-
const matches = out.split(/\r?\n/).map((line) => line.trim()).filter(Boolean).map((rel) =>
|
|
8096
|
+
const matches = out.split(/\r?\n/).map((line) => line.trim()).filter(Boolean).map((rel) => path11.resolve(repoRoot, rel).replace(/\\/g, "/")).filter(
|
|
7055
8097
|
(abs) => !abs.includes("/node_modules/") && !abs.includes("/coverage/") && !/(^|\/)tests?\//i.test(abs) && !/\.(test|spec)\.[tj]sx?$/i.test(abs)
|
|
7056
8098
|
);
|
|
7057
8099
|
matches.forEach((abs) => results.add(abs));
|
|
@@ -7072,8 +8114,8 @@ var program = async () => {
|
|
|
7072
8114
|
const jestDiscoveryArgs = selectionIncludesProdPaths ? stripPathTokens(jest) : jest;
|
|
7073
8115
|
const projectConfigs = [];
|
|
7074
8116
|
try {
|
|
7075
|
-
const baseCfg =
|
|
7076
|
-
const tsCfg =
|
|
8117
|
+
const baseCfg = path11.resolve("jest.config.js");
|
|
8118
|
+
const tsCfg = path11.resolve("jest.ts.config.js");
|
|
7077
8119
|
if (fsSync3.existsSync(baseCfg)) {
|
|
7078
8120
|
projectConfigs.push(baseCfg);
|
|
7079
8121
|
}
|
|
@@ -7090,7 +8132,7 @@ var program = async () => {
|
|
|
7090
8132
|
);
|
|
7091
8133
|
const prodSelections2 = expandedProdSelections;
|
|
7092
8134
|
for (const cfg of projectConfigs) {
|
|
7093
|
-
const cfgCwd =
|
|
8135
|
+
const cfgCwd = path11.dirname(cfg);
|
|
7094
8136
|
const allTests = await discoverJestResilient([...jestDiscoveryArgs, "--config", cfg], {
|
|
7095
8137
|
cwd: cfgCwd
|
|
7096
8138
|
});
|
|
@@ -7103,7 +8145,7 @@ var program = async () => {
|
|
|
7103
8145
|
});
|
|
7104
8146
|
} catch (err) {
|
|
7105
8147
|
if (isDebug()) {
|
|
7106
|
-
console.warn(`direct selection failed for project ${
|
|
8148
|
+
console.warn(`direct selection failed for project ${path11.basename(cfg)}: ${String(err)}`);
|
|
7107
8149
|
}
|
|
7108
8150
|
}
|
|
7109
8151
|
perProjectFiles.set(cfg, directPerProject);
|
|
@@ -7115,7 +8157,7 @@ var program = async () => {
|
|
|
7115
8157
|
)} | related=${selectionIncludesProdPaths} | cwd=${repoRootForDiscovery}`
|
|
7116
8158
|
);
|
|
7117
8159
|
for (const cfg of projectConfigs) {
|
|
7118
|
-
const cfgCwd =
|
|
8160
|
+
const cfgCwd = path11.dirname(cfg);
|
|
7119
8161
|
const files = await discoverJestResilient([...jestDiscoveryArgs, "--config", cfg], {
|
|
7120
8162
|
cwd: cfgCwd
|
|
7121
8163
|
});
|
|
@@ -7130,13 +8172,13 @@ var program = async () => {
|
|
|
7130
8172
|
);
|
|
7131
8173
|
const candidates = selectionHasPaths && selectionLooksLikeTest ? selectionTestPaths : files;
|
|
7132
8174
|
const absFiles = candidates.map(
|
|
7133
|
-
(candidatePath) =>
|
|
8175
|
+
(candidatePath) => path11.isAbsolute(candidatePath) ? candidatePath : path11.join(repoRootForDiscovery, candidatePath)
|
|
7134
8176
|
).map((absolutePath) => absolutePath.replace(/\\/g, "/"));
|
|
7135
8177
|
const onlyOwned = await filterCandidatesForProject(
|
|
7136
8178
|
cfg,
|
|
7137
8179
|
jestDiscoveryArgs,
|
|
7138
8180
|
absFiles,
|
|
7139
|
-
|
|
8181
|
+
path11.dirname(cfg)
|
|
7140
8182
|
);
|
|
7141
8183
|
perProjectFiltered.set(cfg, onlyOwned);
|
|
7142
8184
|
}
|
|
@@ -7148,7 +8190,7 @@ var program = async () => {
|
|
|
7148
8190
|
if (selectionHasPaths && prodSelections.length > 0) {
|
|
7149
8191
|
console.info(`rg related \u2192 prodSelections=${prodSelections.length} (starting)`);
|
|
7150
8192
|
const repoRootForRefinement = workspaceRoot ?? await findRepoRoot();
|
|
7151
|
-
const selectionKey = prodSelections.map((absPath) =>
|
|
8193
|
+
const selectionKey = prodSelections.map((absPath) => path11.relative(repoRootForRefinement, absPath).replace(/\\/g, "/")).sort((firstPath, secondPath) => firstPath.localeCompare(secondPath)).join("|");
|
|
7152
8194
|
const { cachedRelated: cachedRelated2, findRelatedTestsFast: findRelatedTestsFast2, DEFAULT_TEST_GLOBS: DEFAULT_TEST_GLOBS2 } = await Promise.resolve().then(() => (init_fast_related(), fast_related_exports));
|
|
7153
8195
|
const { DEFAULT_EXCLUDE: DEFAULT_EXCLUDE2 } = await Promise.resolve().then(() => (init_args(), args_exports));
|
|
7154
8196
|
const rgMatches = await cachedRelated2({
|
|
@@ -7178,7 +8220,7 @@ var program = async () => {
|
|
|
7178
8220
|
cfg,
|
|
7179
8221
|
jestDiscoveryArgs,
|
|
7180
8222
|
rgCandidates,
|
|
7181
|
-
|
|
8223
|
+
path11.dirname(cfg)
|
|
7182
8224
|
);
|
|
7183
8225
|
perProjectFromRg.set(cfg, owned);
|
|
7184
8226
|
}
|
|
@@ -7193,9 +8235,9 @@ var program = async () => {
|
|
|
7193
8235
|
} else {
|
|
7194
8236
|
const repoRootForScan = repoRootForDiscovery;
|
|
7195
8237
|
const toSeeds = (abs) => {
|
|
7196
|
-
const rel =
|
|
8238
|
+
const rel = path11.relative(repoRootForScan, abs).replace(/\\/g, "/");
|
|
7197
8239
|
const withoutExt = rel.replace(/\.(m?[tj]sx?)$/i, "");
|
|
7198
|
-
const base =
|
|
8240
|
+
const base = path11.basename(withoutExt);
|
|
7199
8241
|
const segs = withoutExt.split("/");
|
|
7200
8242
|
const tail2 = segs.slice(-2).join("/");
|
|
7201
8243
|
return Array.from(new Set([withoutExt, base, tail2].filter(Boolean)));
|
|
@@ -7210,8 +8252,8 @@ var program = async () => {
|
|
|
7210
8252
|
}
|
|
7211
8253
|
};
|
|
7212
8254
|
const resolveLocalImport = (fromFile, spec) => {
|
|
7213
|
-
const baseDir =
|
|
7214
|
-
const cand =
|
|
8255
|
+
const baseDir = path11.dirname(fromFile);
|
|
8256
|
+
const cand = path11.resolve(baseDir, spec);
|
|
7215
8257
|
const exts = ["", ".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"];
|
|
7216
8258
|
for (const ext of exts) {
|
|
7217
8259
|
const full = ext ? `${cand}${ext}` : cand;
|
|
@@ -7220,7 +8262,7 @@ var program = async () => {
|
|
|
7220
8262
|
}
|
|
7221
8263
|
}
|
|
7222
8264
|
for (const ext of exts) {
|
|
7223
|
-
const full =
|
|
8265
|
+
const full = path11.join(cand, `index${ext}`);
|
|
7224
8266
|
if (fsSync3.existsSync(full)) {
|
|
7225
8267
|
return full;
|
|
7226
8268
|
}
|
|
@@ -7274,7 +8316,7 @@ var program = async () => {
|
|
|
7274
8316
|
cfg,
|
|
7275
8317
|
jestDiscoveryArgs,
|
|
7276
8318
|
keptCandidates,
|
|
7277
|
-
|
|
8319
|
+
path11.dirname(cfg)
|
|
7278
8320
|
);
|
|
7279
8321
|
perProjectFromScan.set(cfg, owned);
|
|
7280
8322
|
}
|
|
@@ -7304,7 +8346,7 @@ var program = async () => {
|
|
|
7304
8346
|
try {
|
|
7305
8347
|
const allAcross = [];
|
|
7306
8348
|
for (const cfg of projectConfigs) {
|
|
7307
|
-
const cfgCwd =
|
|
8349
|
+
const cfgCwd = path11.dirname(cfg);
|
|
7308
8350
|
const listed = await discoverJestResilient([...jestDiscoveryArgs, "--config", cfg], {
|
|
7309
8351
|
cwd: cfgCwd
|
|
7310
8352
|
});
|
|
@@ -7318,23 +8360,23 @@ var program = async () => {
|
|
|
7318
8360
|
}
|
|
7319
8361
|
}
|
|
7320
8362
|
const seeds = prodSelections.map(
|
|
7321
|
-
(abs) =>
|
|
8363
|
+
(abs) => path11.relative(repoRoot, abs).replace(/\\/g, "/").replace(/\.(m?[tj]sx?)$/i, "")
|
|
7322
8364
|
).flatMap((rel) => {
|
|
7323
|
-
const base =
|
|
8365
|
+
const base = path11.basename(rel);
|
|
7324
8366
|
const segments = rel.split("/");
|
|
7325
8367
|
return Array.from(new Set([rel, base, segments.slice(-2).join("/")].filter(Boolean)));
|
|
7326
8368
|
});
|
|
7327
8369
|
const includesSeed = (text) => seeds.some((seed) => text.includes(seed));
|
|
7328
8370
|
const tryReadFile = async (absPath) => {
|
|
7329
8371
|
try {
|
|
7330
|
-
return await
|
|
8372
|
+
return await fs7.readFile(absPath, "utf8");
|
|
7331
8373
|
} catch {
|
|
7332
8374
|
return "";
|
|
7333
8375
|
}
|
|
7334
8376
|
};
|
|
7335
8377
|
const resolveLocalImport = (fromFile, spec) => {
|
|
7336
|
-
const baseDir =
|
|
7337
|
-
const candidate =
|
|
8378
|
+
const baseDir = path11.dirname(fromFile);
|
|
8379
|
+
const candidate = path11.resolve(baseDir, spec);
|
|
7338
8380
|
const extensions = ["", ".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".mts", ".cts"];
|
|
7339
8381
|
for (const ext of extensions) {
|
|
7340
8382
|
const fullPath = ext ? `${candidate}${ext}` : candidate;
|
|
@@ -7343,7 +8385,7 @@ var program = async () => {
|
|
|
7343
8385
|
}
|
|
7344
8386
|
}
|
|
7345
8387
|
for (const ext of extensions) {
|
|
7346
|
-
const fullPath =
|
|
8388
|
+
const fullPath = path11.join(candidate, `index${ext}`);
|
|
7347
8389
|
if (fsSync3.existsSync(fullPath)) {
|
|
7348
8390
|
return fullPath;
|
|
7349
8391
|
}
|
|
@@ -7500,10 +8542,10 @@ var program = async () => {
|
|
|
7500
8542
|
};
|
|
7501
8543
|
const prodSeedsForRun = (() => {
|
|
7502
8544
|
const changedAbs = (changedSelectionAbs ?? []).map(
|
|
7503
|
-
(absPath) =>
|
|
8545
|
+
(absPath) => path11.resolve(absPath).replace(/\\/g, "/")
|
|
7504
8546
|
);
|
|
7505
8547
|
const selAbs = selectionPathsAugmented.map(
|
|
7506
|
-
(pathToken) =>
|
|
8548
|
+
(pathToken) => path11.resolve(pathToken).replace(/\\/g, "/")
|
|
7507
8549
|
);
|
|
7508
8550
|
return (changedAbs.length ? changedAbs : selAbs).filter(
|
|
7509
8551
|
(abs) => /[\\/]/.test(abs) && !/(^|\/)tests?\//i.test(abs) && !/\.(test|spec)\.[tj]sx?$/i.test(abs)
|
|
@@ -7518,29 +8560,50 @@ var program = async () => {
|
|
|
7518
8560
|
const cfg = projectsToRun[projIndex];
|
|
7519
8561
|
const files = perProjectFiltered.get(cfg) ?? [];
|
|
7520
8562
|
if (files.length === 0) {
|
|
7521
|
-
console.info(`Project ${
|
|
8563
|
+
console.info(`Project ${path11.basename(cfg)}: 0 matching tests after filter; skipping.`);
|
|
7522
8564
|
continue;
|
|
7523
8565
|
}
|
|
7524
8566
|
files.forEach(
|
|
7525
|
-
(absTestPath) => executedTestFilesSet.add(
|
|
8567
|
+
(absTestPath) => executedTestFilesSet.add(path11.resolve(absTestPath).replace(/\\/g, "/"))
|
|
7526
8568
|
);
|
|
7527
|
-
const outJson =
|
|
7528
|
-
|
|
8569
|
+
const outJson = path11.join(
|
|
8570
|
+
os3.tmpdir(),
|
|
7529
8571
|
`jest-bridge-${Date.now()}-${Math.random().toString(36).slice(2)}.json`
|
|
7530
8572
|
);
|
|
7531
|
-
const reporterPath =
|
|
8573
|
+
const reporterPath = path11.resolve("scripts/jest-vitest-bridge.cjs");
|
|
7532
8574
|
try {
|
|
7533
|
-
|
|
7534
|
-
|
|
8575
|
+
const needsWrite = (() => {
|
|
8576
|
+
try {
|
|
8577
|
+
const existing = fsSync3.readFileSync(reporterPath, "utf8");
|
|
8578
|
+
return existing !== JEST_BRIDGE_REPORTER_SOURCE;
|
|
8579
|
+
} catch {
|
|
8580
|
+
return true;
|
|
8581
|
+
}
|
|
8582
|
+
})();
|
|
8583
|
+
if (needsWrite) {
|
|
8584
|
+
fsSync3.mkdirSync(path11.dirname(reporterPath), { recursive: true });
|
|
7535
8585
|
fsSync3.writeFileSync(reporterPath, JEST_BRIDGE_REPORTER_SOURCE, "utf8");
|
|
7536
8586
|
}
|
|
8587
|
+
const envPath = path11.resolve("scripts/jest-bridge-env.cjs");
|
|
8588
|
+
try {
|
|
8589
|
+
const existingEnv = fsSync3.readFileSync(envPath, "utf8");
|
|
8590
|
+
if (existingEnv !== JEST_BRIDGE_ENV_SOURCE) {
|
|
8591
|
+
fsSync3.writeFileSync(envPath, JEST_BRIDGE_ENV_SOURCE, "utf8");
|
|
8592
|
+
}
|
|
8593
|
+
} catch {
|
|
8594
|
+
try {
|
|
8595
|
+
fsSync3.mkdirSync(path11.dirname(envPath), { recursive: true });
|
|
8596
|
+
} catch {
|
|
8597
|
+
}
|
|
8598
|
+
fsSync3.writeFileSync(envPath, JEST_BRIDGE_ENV_SOURCE, "utf8");
|
|
8599
|
+
}
|
|
7537
8600
|
} catch (ensureReporterError) {
|
|
7538
8601
|
console.warn(`Unable to ensure jest bridge reporter: ${String(ensureReporterError)}`);
|
|
7539
8602
|
}
|
|
7540
|
-
const selectedFilesForCoverage = selectionPathsAugmented.filter((pathToken) => /[\\/]/.test(pathToken)).filter((pathToken) => !looksLikeTestPath(pathToken)).map((pathToken) =>
|
|
8603
|
+
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}`);
|
|
7541
8604
|
const coverageFromArgs = [];
|
|
7542
|
-
for (const
|
|
7543
|
-
coverageFromArgs.push("--collectCoverageFrom",
|
|
8605
|
+
for (const relPath2 of selectedFilesForCoverage) {
|
|
8606
|
+
coverageFromArgs.push("--collectCoverageFrom", relPath2);
|
|
7544
8607
|
}
|
|
7545
8608
|
const { code, output } = await runWithCapture(
|
|
7546
8609
|
babelNodeBin,
|
|
@@ -7550,17 +8613,16 @@ var program = async () => {
|
|
|
7550
8613
|
jestBin,
|
|
7551
8614
|
"--config",
|
|
7552
8615
|
cfg,
|
|
8616
|
+
"--testLocationInResults",
|
|
7553
8617
|
"--runTestsByPath",
|
|
7554
8618
|
`--reporters=${reporterPath}`,
|
|
7555
|
-
"--silent",
|
|
7556
8619
|
"--colors",
|
|
7557
|
-
"--
|
|
7558
|
-
"
|
|
7559
|
-
outJson,
|
|
8620
|
+
"--env",
|
|
8621
|
+
path11.resolve("scripts/jest-bridge-env.cjs"),
|
|
7560
8622
|
...sanitizedJestRunArgs,
|
|
7561
8623
|
...collectCoverage ? [
|
|
7562
8624
|
"--coverageDirectory",
|
|
7563
|
-
|
|
8625
|
+
path11.join("coverage", "jest", path11.basename(cfg).replace(/[^a-zA-Z0-9_.-]+/g, "_"))
|
|
7564
8626
|
] : [],
|
|
7565
8627
|
...coverageFromArgs,
|
|
7566
8628
|
"--passWithNoTests",
|
|
@@ -7593,17 +8655,23 @@ var program = async () => {
|
|
|
7593
8655
|
...bridge,
|
|
7594
8656
|
testResults: sortTestResultsWithRank(fileRank, bridge.testResults).reverse()
|
|
7595
8657
|
};
|
|
7596
|
-
pretty = renderVitestFromJestJSON(
|
|
7597
|
-
|
|
7598
|
-
|
|
7599
|
-
|
|
7600
|
-
|
|
8658
|
+
pretty = renderVitestFromJestJSON(
|
|
8659
|
+
reordered,
|
|
8660
|
+
makeCtx(
|
|
8661
|
+
{ cwd: repoRootForDiscovery, ...editorCmd !== void 0 ? { editorCmd } : {} },
|
|
8662
|
+
/\bFAIL\b/.test(stripAnsiSimple(output))
|
|
8663
|
+
),
|
|
8664
|
+
{ onlyFailures }
|
|
8665
|
+
);
|
|
7601
8666
|
} catch {
|
|
7602
|
-
pretty = renderVitestFromJestJSON(
|
|
7603
|
-
|
|
7604
|
-
|
|
7605
|
-
|
|
7606
|
-
|
|
8667
|
+
pretty = renderVitestFromJestJSON(
|
|
8668
|
+
bridge,
|
|
8669
|
+
makeCtx(
|
|
8670
|
+
{ cwd: repoRootForDiscovery, ...editorCmd !== void 0 ? { editorCmd } : {} },
|
|
8671
|
+
/\bFAIL\b/.test(stripAnsiSimple(output))
|
|
8672
|
+
),
|
|
8673
|
+
{ onlyFailures }
|
|
8674
|
+
);
|
|
7607
8675
|
}
|
|
7608
8676
|
if (debug) {
|
|
7609
8677
|
const preview = pretty.split("\n").slice(0, 3).join("\n");
|
|
@@ -7617,18 +8685,31 @@ ${preview}${pretty.includes("\n") ? "\n\u2026" : ""}`);
|
|
|
7617
8685
|
console.info(String(jsonErr));
|
|
7618
8686
|
console.info(`fallback: raw output lines=${output.split(/\r?\n/).length}`);
|
|
7619
8687
|
}
|
|
7620
|
-
|
|
8688
|
+
pretty = formatJestOutputVitest(output, {
|
|
7621
8689
|
cwd: repoRootForDiscovery,
|
|
7622
8690
|
...editorCmd !== void 0 ? { editorCmd } : {},
|
|
7623
8691
|
onlyFailures
|
|
7624
|
-
};
|
|
7625
|
-
pretty = formatJestOutputVitest(output, renderOpts);
|
|
8692
|
+
});
|
|
7626
8693
|
if (debug) {
|
|
7627
8694
|
const preview = pretty.split("\n").slice(0, 3).join("\n");
|
|
7628
8695
|
console.info(`pretty preview (text):
|
|
7629
8696
|
${preview}${pretty.includes("\n") ? "\n\u2026" : ""}`);
|
|
7630
8697
|
}
|
|
7631
8698
|
}
|
|
8699
|
+
try {
|
|
8700
|
+
const looksSparse = /\n\s*Error:\s*\n/.test(pretty) && !/(Message:|Thrown:|Events:|Console errors:)/.test(pretty);
|
|
8701
|
+
if (looksSparse) {
|
|
8702
|
+
const rawAlso = formatJestOutputVitest(output, {
|
|
8703
|
+
cwd: repoRootForDiscovery,
|
|
8704
|
+
...editorCmd !== void 0 ? { editorCmd } : {},
|
|
8705
|
+
onlyFailures
|
|
8706
|
+
});
|
|
8707
|
+
const merged = `${stripFooter(pretty)}
|
|
8708
|
+
${stripFooter(rawAlso)}`.trimEnd();
|
|
8709
|
+
pretty = merged;
|
|
8710
|
+
}
|
|
8711
|
+
} catch {
|
|
8712
|
+
}
|
|
7632
8713
|
pretty = stripFooter(pretty);
|
|
7633
8714
|
if (pretty.trim().length > 0) {
|
|
7634
8715
|
process.stdout.write(pretty.endsWith("\n") ? pretty : `${pretty}
|
|
@@ -7667,10 +8748,10 @@ ${preview}${pretty.includes("\n") ? "\n\u2026" : ""}`);
|
|
|
7667
8748
|
try {
|
|
7668
8749
|
const prodSeeds = (() => {
|
|
7669
8750
|
const changedAbs = (changedSelectionAbs ?? []).map(
|
|
7670
|
-
(absPath) =>
|
|
8751
|
+
(absPath) => path11.resolve(absPath).replace(/\\/g, "/")
|
|
7671
8752
|
);
|
|
7672
8753
|
const selAbs = selectionPathsAugmented.map(
|
|
7673
|
-
(pathToken) =>
|
|
8754
|
+
(pathToken) => path11.resolve(pathToken).replace(/\\/g, "/")
|
|
7674
8755
|
);
|
|
7675
8756
|
return (changedAbs.length ? changedAbs : selAbs).filter(
|
|
7676
8757
|
(abs) => /[\\/]/.test(abs) && !/(^|\/)tests?\//i.test(abs) && !/\.(test|spec)\.[tj]sx?$/i.test(abs)
|
|
@@ -7684,11 +8765,18 @@ ${preview}${pretty.includes("\n") ? "\n\u2026" : ""}`);
|
|
|
7684
8765
|
unified.testResults = ordered;
|
|
7685
8766
|
} catch {
|
|
7686
8767
|
}
|
|
7687
|
-
const
|
|
7688
|
-
|
|
7689
|
-
|
|
7690
|
-
|
|
7691
|
-
|
|
8768
|
+
const showStacks = Boolean(unified.aggregated?.numFailedTests > 0);
|
|
8769
|
+
let text = renderVitestFromJestJSON(
|
|
8770
|
+
unified,
|
|
8771
|
+
makeCtx(
|
|
8772
|
+
{ cwd: repoRootForDiscovery, ...editorCmd !== void 0 ? { editorCmd } : {} },
|
|
8773
|
+
showStacks
|
|
8774
|
+
),
|
|
8775
|
+
{ onlyFailures }
|
|
8776
|
+
);
|
|
8777
|
+
if (onlyFailures) {
|
|
8778
|
+
text = text.split(/\r?\n/).filter((line) => !/^\s*PASS\b/.test(stripAnsiSimple(line))).join("\n");
|
|
8779
|
+
}
|
|
7692
8780
|
if (text.trim().length > 0) {
|
|
7693
8781
|
process.stdout.write(text.endsWith("\n") ? text : `${text}
|
|
7694
8782
|
`);
|
|
@@ -7733,11 +8821,13 @@ export {
|
|
|
7733
8821
|
barCell,
|
|
7734
8822
|
buildCodeFrameSection,
|
|
7735
8823
|
buildConsoleSection,
|
|
8824
|
+
buildFallbackMessageBlock,
|
|
7736
8825
|
buildFileBadgeLine,
|
|
7737
8826
|
buildMessageSection,
|
|
7738
8827
|
buildPerFileOverview,
|
|
7739
8828
|
buildPrettyDiffSection,
|
|
7740
8829
|
buildStackSection,
|
|
8830
|
+
buildThrownSection,
|
|
7741
8831
|
cell,
|
|
7742
8832
|
coerceJestJsonToBridge,
|
|
7743
8833
|
collapseStacks,
|
|
@@ -7752,32 +8842,30 @@ export {
|
|
|
7752
8842
|
discoverJestResilient,
|
|
7753
8843
|
discoverTargets,
|
|
7754
8844
|
drawFailLine,
|
|
7755
|
-
drawFailRule,
|
|
7756
8845
|
drawRule,
|
|
7757
8846
|
emitMergedCoverage,
|
|
7758
|
-
extractAssertionMessage,
|
|
7759
|
-
extractExpectedReceived,
|
|
7760
|
-
extractFromUnifiedDiff,
|
|
7761
8847
|
filterCandidatesForProject,
|
|
7762
8848
|
filterCoverageMap,
|
|
7763
8849
|
findCodeFrameStart,
|
|
7764
8850
|
findRepoRoot,
|
|
7765
8851
|
firstTestLocation,
|
|
7766
8852
|
formatJestOutputVitest,
|
|
7767
|
-
indentBlock,
|
|
7768
8853
|
isFilesObject,
|
|
7769
8854
|
isStackLine,
|
|
8855
|
+
isTransportError,
|
|
7770
8856
|
isTruthy,
|
|
7771
|
-
labelForMessage,
|
|
7772
8857
|
linesFromDetails,
|
|
7773
8858
|
linkifyPadded,
|
|
8859
|
+
makeCtx,
|
|
7774
8860
|
mergeLcov,
|
|
7775
8861
|
missedBranches,
|
|
7776
8862
|
missedFunctions,
|
|
8863
|
+
mkPrettyFns,
|
|
7777
8864
|
osc8,
|
|
7778
8865
|
parseActionsFromTokens,
|
|
8866
|
+
parseMethodPathFromTitle,
|
|
8867
|
+
pickRelevantHttp,
|
|
7779
8868
|
preferredEditorHref,
|
|
7780
|
-
prettifyPrettyFormatBlock,
|
|
7781
8869
|
printCompactCoverage,
|
|
7782
8870
|
printDetailedCoverage,
|
|
7783
8871
|
printPerFileCompositeTables,
|
|
@@ -7785,19 +8873,21 @@ export {
|
|
|
7785
8873
|
readCoverageJson,
|
|
7786
8874
|
registerSignalHandlersOnce,
|
|
7787
8875
|
relativizeForMatch,
|
|
8876
|
+
renderCodeFrame,
|
|
7788
8877
|
renderPerFileCompositeTable,
|
|
7789
8878
|
renderRunLine,
|
|
7790
|
-
renderSourceCodeFrame,
|
|
7791
8879
|
renderTable,
|
|
7792
8880
|
renderVitestFromJestJSON,
|
|
7793
8881
|
resolveImportWithRoot,
|
|
8882
|
+
routeSimilarityScore,
|
|
7794
8883
|
rule,
|
|
7795
8884
|
runJestBootstrap,
|
|
7796
|
-
|
|
8885
|
+
scoreHttpForAssertion,
|
|
7797
8886
|
stripAnsiSimple,
|
|
7798
8887
|
supportsUnicode,
|
|
7799
8888
|
tintPct,
|
|
7800
8889
|
toPosix,
|
|
8890
|
+
tryBridgeFallback,
|
|
7801
8891
|
tryResolveFile
|
|
7802
8892
|
};
|
|
7803
8893
|
//# sourceMappingURL=index.js.map
|