headlamp 0.1.13 → 0.1.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -0
- package/dist/cli.cjs +565 -533
- package/dist/cli.cjs.map +4 -4
- package/dist/index.js +565 -537
- package/dist/index.js.map +4 -4
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -69,7 +69,8 @@ var init_args = __esm({
|
|
|
69
69
|
coverageMaxFiles: (value) => ({ type: "coverageMaxFiles", value }),
|
|
70
70
|
coverageMaxHotspots: (value) => ({ type: "coverageMaxHotspots", value }),
|
|
71
71
|
coveragePageFit: (value) => ({ type: "coveragePageFit", value }),
|
|
72
|
-
changed: (value) => ({ type: "changed", value })
|
|
72
|
+
changed: (value) => ({ type: "changed", value }),
|
|
73
|
+
changedDepth: (value) => ({ type: "changedDepth", value })
|
|
73
74
|
};
|
|
74
75
|
Some = (value) => ({ _tag: "some", value });
|
|
75
76
|
None = { _tag: "none" };
|
|
@@ -290,6 +291,16 @@ var init_args = __esm({
|
|
|
290
291
|
const mode = raw === "staged" ? "staged" : raw === "unstaged" ? "unstaged" : raw === "branch" ? "branch" : "all";
|
|
291
292
|
return step([ActionBuilders.changed(mode)], true);
|
|
292
293
|
}),
|
|
294
|
+
// --changed.depth flag: maximum transitive import depth for changed selection refinement
|
|
295
|
+
rule.startsWith("--changed.depth=", (value) => {
|
|
296
|
+
const raw = (value.split("=")[1] ?? "").trim();
|
|
297
|
+
const num = Number(raw);
|
|
298
|
+
return step(Number.isFinite(num) && num > 0 ? [ActionBuilders.changedDepth(num)] : []);
|
|
299
|
+
}),
|
|
300
|
+
rule.withLookahead("--changed.depth", (_flag, lookahead) => {
|
|
301
|
+
const num = Number(String(lookahead).trim());
|
|
302
|
+
return step(Number.isFinite(num) && num > 0 ? [ActionBuilders.changedDepth(num)] : [], true);
|
|
303
|
+
}),
|
|
293
304
|
rule.withLookahead(
|
|
294
305
|
"-t",
|
|
295
306
|
(flag, lookahead) => step(
|
|
@@ -396,6 +407,8 @@ var init_args = __esm({
|
|
|
396
407
|
return { vitest: [], jest: [], coverage: false, coveragePageFit: action.value };
|
|
397
408
|
case "changed":
|
|
398
409
|
return { vitest: [], jest: [], coverage: false, changed: action.value };
|
|
410
|
+
case "changedDepth":
|
|
411
|
+
return { vitest: [], jest: [], coverage: false, changedDepth: action.value };
|
|
399
412
|
case "jestArg":
|
|
400
413
|
return { vitest: [], jest: [action.value], coverage: false };
|
|
401
414
|
case "vitestArg":
|
|
@@ -436,6 +449,7 @@ var init_args = __esm({
|
|
|
436
449
|
return {
|
|
437
450
|
...next,
|
|
438
451
|
...right.changed !== void 0 || left.changed !== void 0 ? { changed: right.changed ?? left.changed } : {},
|
|
452
|
+
...right.changedDepth !== void 0 || left.changedDepth !== void 0 ? { changedDepth: right.changedDepth ?? left.changedDepth } : {},
|
|
439
453
|
...right.coverageAbortOnFailure !== void 0 || left.coverageAbortOnFailure !== void 0 ? { coverageAbortOnFailure: right.coverageAbortOnFailure ?? left.coverageAbortOnFailure } : {},
|
|
440
454
|
...right.onlyFailures !== void 0 || left.onlyFailures !== void 0 ? { onlyFailures: right.onlyFailures ?? left.onlyFailures } : {},
|
|
441
455
|
...right.coverageDetail !== void 0 || left.coverageDetail !== void 0 ? { coverageDetail: right.coverageDetail ?? left.coverageDetail } : {},
|
|
@@ -526,7 +540,8 @@ var init_args = __esm({
|
|
|
526
540
|
coveragePageFit,
|
|
527
541
|
...contrib.editorCmd !== void 0 ? { editorCmd: contrib.editorCmd } : {},
|
|
528
542
|
...contrib.workspaceRoot !== void 0 ? { workspaceRoot: contrib.workspaceRoot } : {},
|
|
529
|
-
...contrib.changed !== void 0 ? { changed: contrib.changed } : {}
|
|
543
|
+
...contrib.changed !== void 0 ? { changed: contrib.changed } : {},
|
|
544
|
+
...contrib.changedDepth !== void 0 ? { changedDepth: contrib.changedDepth } : {}
|
|
530
545
|
};
|
|
531
546
|
return out;
|
|
532
547
|
};
|
|
@@ -3675,11 +3690,11 @@ var tintPct = (pct) => {
|
|
|
3675
3690
|
var bar = (pct, width = DEFAULT_BAR_WIDTH) => {
|
|
3676
3691
|
const filled = Math.round(pct / PERCENT_MAX * width);
|
|
3677
3692
|
const solid = supportsUnicode() ? "\u2588" : "#";
|
|
3678
|
-
const
|
|
3693
|
+
const empty2 = supportsUnicode() ? "\u2591" : "-";
|
|
3679
3694
|
const good = tintPct(pct);
|
|
3680
3695
|
const MIN_REMAINING = 0;
|
|
3681
3696
|
return `${good(solid.repeat(filled))}${ansi.gray(
|
|
3682
|
-
|
|
3697
|
+
empty2.repeat(Math.max(MIN_REMAINING, width - filled))
|
|
3683
3698
|
)}`;
|
|
3684
3699
|
};
|
|
3685
3700
|
|
|
@@ -6601,11 +6616,25 @@ var extractBridgePath2 = (raw, cwd) => {
|
|
|
6601
6616
|
const jsonPath = (matches[matches.length - 1][1] ?? "").trim().replace(/^['"`]|['"`]$/g, "");
|
|
6602
6617
|
return path10.isAbsolute(jsonPath) ? jsonPath : path10.resolve(cwd, jsonPath).replace(/\\/g, "/");
|
|
6603
6618
|
};
|
|
6604
|
-
|
|
6605
|
-
|
|
6606
|
-
|
|
6607
|
-
|
|
6608
|
-
|
|
6619
|
+
|
|
6620
|
+
// src/lib/formatter/bridge/utils.ts
|
|
6621
|
+
var import_json52 = __toESM(require_lib(), 1);
|
|
6622
|
+
|
|
6623
|
+
// src/lib/formatter/bridge/http.ts
|
|
6624
|
+
var envNumber = (name, fallback) => {
|
|
6625
|
+
const parsed = Number(process.env[name]);
|
|
6626
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
|
6627
|
+
};
|
|
6628
|
+
var HEADLAMP_HTTP_WINDOW_MS = () => envNumber("HEADLAMP_HTTP_WINDOW_MS", 3e3);
|
|
6629
|
+
var HEADLAMP_HTTP_STRICT_WINDOW_MS = () => envNumber("HEADLAMP_HTTP_STRICT_WINDOW_MS", 600);
|
|
6630
|
+
var HEADLAMP_HTTP_MIN_SCORE = () => envNumber("HEADLAMP_HTTP_MIN_SCORE", 1200);
|
|
6631
|
+
var HEADLAMP_HTTP_DIFF_LIMIT = () => envNumber("HEADLAMP_HTTP_DIFF_LIMIT", 6);
|
|
6632
|
+
var HEADLAMP_HTTP_SHOW_MISS = () => process.env.HEADLAMP_HTTP_MISS === "1";
|
|
6633
|
+
var asHttpList = (candidateValue) => Array.isArray(candidateValue) ? candidateValue : [];
|
|
6634
|
+
var summarizeUrl = (method, url, route) => {
|
|
6635
|
+
const base = route || url || "";
|
|
6636
|
+
const qs = url && url.includes("?") ? ` ? ${url.split("?")[1]}` : "";
|
|
6637
|
+
return [method || "", base, qs].filter(Boolean).join(" ").trim();
|
|
6609
6638
|
};
|
|
6610
6639
|
var parseMethodPathFromTitle = (title) => {
|
|
6611
6640
|
if (!title) {
|
|
@@ -6614,6 +6643,42 @@ var parseMethodPathFromTitle = (title) => {
|
|
|
6614
6643
|
const matchResult = title.match(/\b(GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS)\s+([^\s)]+)/i);
|
|
6615
6644
|
return matchResult ? { method: matchResult[1]?.toUpperCase(), path: matchResult[2] } : {};
|
|
6616
6645
|
};
|
|
6646
|
+
var eventsNear = (http, ts, testPath, windowMs = HEADLAMP_HTTP_WINDOW_MS()) => {
|
|
6647
|
+
if (typeof ts !== "number" || !Number.isFinite(ts)) {
|
|
6648
|
+
return [];
|
|
6649
|
+
}
|
|
6650
|
+
return http.filter((event) => {
|
|
6651
|
+
const timeOk = typeof event.timestampMs === "number" && Math.abs(event.timestampMs - ts) <= windowMs;
|
|
6652
|
+
const pathOk = !testPath || event.testPath === testPath;
|
|
6653
|
+
return timeOk && pathOk;
|
|
6654
|
+
});
|
|
6655
|
+
};
|
|
6656
|
+
var isHttpStatusNumber = (statusNumber) => typeof statusNumber === "number" && statusNumber >= 100 && statusNumber <= 599;
|
|
6657
|
+
var inferHttpNumbersFromText = (lines) => {
|
|
6658
|
+
const text = lines.join("\n");
|
|
6659
|
+
const match = text.match(/Expected:\s*(\d{3})[\s\S]*?Received:\s*(\d{3})/i);
|
|
6660
|
+
if (match) {
|
|
6661
|
+
return { expectedNumber: Number(match[1]), receivedNumber: Number(match[2]) };
|
|
6662
|
+
}
|
|
6663
|
+
return {};
|
|
6664
|
+
};
|
|
6665
|
+
var titleSuggestsHttp = (title) => {
|
|
6666
|
+
const { method, path: parsedPath } = parseMethodPathFromTitle(title);
|
|
6667
|
+
return Boolean(method || parsedPath && parsedPath.startsWith("/"));
|
|
6668
|
+
};
|
|
6669
|
+
var hasStatusSemantics = (assertionLike) => {
|
|
6670
|
+
if (!assertionLike) {
|
|
6671
|
+
return false;
|
|
6672
|
+
}
|
|
6673
|
+
if (isHttpStatusNumber(assertionLike.expectedNumber) || isHttpStatusNumber(assertionLike.receivedNumber)) {
|
|
6674
|
+
return true;
|
|
6675
|
+
}
|
|
6676
|
+
const combinedRaw = `${assertionLike.matcher ?? ""} ${assertionLike.message ?? ""}`;
|
|
6677
|
+
const combinedMessage = combinedRaw.toLowerCase();
|
|
6678
|
+
return /\bstatus(code)?\b|\btohaves(tatus|tatuscode)\b/.test(combinedMessage);
|
|
6679
|
+
};
|
|
6680
|
+
var fileSuggestsHttp = (relPath2) => /(?:^|\/)(routes?|api|controllers?|e2e|integration)(?:\/|\.test\.)/i.test(relPath2);
|
|
6681
|
+
var isHttpRelevant = (ctx) => ctx.hasTransportSignal || ctx.httpCountInSameTest > 0 || titleSuggestsHttp(ctx.title) || hasStatusSemantics(ctx.assertion) || fileSuggestsHttp(ctx.relPath);
|
|
6617
6682
|
var routeSimilarityScore = (hint, evt) => {
|
|
6618
6683
|
if (!hint.path && !hint.method) {
|
|
6619
6684
|
return 0;
|
|
@@ -6634,15 +6699,12 @@ var routeSimilarityScore = (hint, evt) => {
|
|
|
6634
6699
|
}
|
|
6635
6700
|
return methodOk * 10;
|
|
6636
6701
|
};
|
|
6637
|
-
var
|
|
6638
|
-
const
|
|
6639
|
-
return
|
|
6702
|
+
var isTransportError = (msg) => {
|
|
6703
|
+
const lowercaseMessage = (msg ?? "").toLowerCase();
|
|
6704
|
+
return /\bsocket hang up\b|\beconnreset\b|\betimedout\b|\beconnrefused\b|\bwrite epipe\b/.test(
|
|
6705
|
+
lowercaseMessage
|
|
6706
|
+
);
|
|
6640
6707
|
};
|
|
6641
|
-
var HEADLAMP_HTTP_WINDOW_MS = () => envNumber("HEADLAMP_HTTP_WINDOW_MS", 3e3);
|
|
6642
|
-
var HEADLAMP_HTTP_STRICT_WINDOW_MS = () => envNumber("HEADLAMP_HTTP_STRICT_WINDOW_MS", 600);
|
|
6643
|
-
var HEADLAMP_HTTP_MIN_SCORE = () => envNumber("HEADLAMP_HTTP_MIN_SCORE", 1200);
|
|
6644
|
-
var HEADLAMP_HTTP_DIFF_LIMIT = () => envNumber("HEADLAMP_HTTP_DIFF_LIMIT", 6);
|
|
6645
|
-
var HEADLAMP_HTTP_SHOW_MISS = () => process.env.HEADLAMP_HTTP_MISS === "1";
|
|
6646
6708
|
var scoreHttpForAssertion = (assertion, titleHint) => (candidateEvent) => {
|
|
6647
6709
|
const tsA = assertion.timestampMs;
|
|
6648
6710
|
const tsH = candidateEvent.timestampMs;
|
|
@@ -6688,7 +6750,386 @@ var pickRelevantHttp = (assertion, http, ctx) => {
|
|
|
6688
6750
|
};
|
|
6689
6751
|
|
|
6690
6752
|
// src/lib/formatter/bridge/utils.ts
|
|
6691
|
-
var
|
|
6753
|
+
var colorTokens2 = {
|
|
6754
|
+
pass: Colors.Success,
|
|
6755
|
+
fail: Colors.Failure,
|
|
6756
|
+
skip: Colors.Skip,
|
|
6757
|
+
todo: Colors.Todo,
|
|
6758
|
+
passPill: (text) => BackgroundColors.Success(ansi.white(` ${text} `)),
|
|
6759
|
+
failPill: (text) => BackgroundColors.Failure(ansi.white(` ${text} `))
|
|
6760
|
+
};
|
|
6761
|
+
var joinLines = (chunks) => chunks.join("\n");
|
|
6762
|
+
var empty = [];
|
|
6763
|
+
var concat = (...xs) => xs.flat();
|
|
6764
|
+
var by = (keySelector) => (left, right) => keySelector(left) - keySelector(right);
|
|
6765
|
+
var isObject = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
6766
|
+
var stripBridgeEventsFromConsole = (maybeConsole) => {
|
|
6767
|
+
if (!Array.isArray(maybeConsole)) {
|
|
6768
|
+
return maybeConsole;
|
|
6769
|
+
}
|
|
6770
|
+
return maybeConsole.filter((entry) => {
|
|
6771
|
+
try {
|
|
6772
|
+
const raw = Array.isArray(entry.message) ? entry.message.map(String).join(" ") : String(entry.message ?? "");
|
|
6773
|
+
return !raw.includes("[JEST-BRIDGE-EVENT]");
|
|
6774
|
+
} catch {
|
|
6775
|
+
return true;
|
|
6776
|
+
}
|
|
6777
|
+
});
|
|
6778
|
+
};
|
|
6779
|
+
var parseBridgeConsole = (consoleEntries) => {
|
|
6780
|
+
const http = [];
|
|
6781
|
+
const assertions = [];
|
|
6782
|
+
if (!Array.isArray(consoleEntries)) {
|
|
6783
|
+
return { http, assertions };
|
|
6784
|
+
}
|
|
6785
|
+
for (const entry of consoleEntries) {
|
|
6786
|
+
const rec = entry;
|
|
6787
|
+
const rawMsgVal = rec && typeof rec.message !== "undefined" ? rec.message : "";
|
|
6788
|
+
const raw = Array.isArray(rawMsgVal) ? rawMsgVal.map(String).join(" ") : String(rawMsgVal ?? "");
|
|
6789
|
+
if (!raw.includes("[JEST-BRIDGE-EVENT]")) {
|
|
6790
|
+
continue;
|
|
6791
|
+
}
|
|
6792
|
+
const jsonText = raw.split("[JEST-BRIDGE-EVENT]").pop()?.trim() ?? "";
|
|
6793
|
+
try {
|
|
6794
|
+
const evt = import_json52.default.parse(jsonText);
|
|
6795
|
+
const type = evt?.type;
|
|
6796
|
+
if (type === "httpResponse") {
|
|
6797
|
+
const timestampMs = Number(evt.timestampMs ?? Date.now());
|
|
6798
|
+
http.push({
|
|
6799
|
+
kind: "response",
|
|
6800
|
+
timestampMs,
|
|
6801
|
+
method: evt.method,
|
|
6802
|
+
url: evt.url,
|
|
6803
|
+
route: evt.route,
|
|
6804
|
+
statusCode: evt.statusCode,
|
|
6805
|
+
durationMs: evt.durationMs,
|
|
6806
|
+
contentType: evt.contentType,
|
|
6807
|
+
requestId: evt.requestId,
|
|
6808
|
+
json: evt.json,
|
|
6809
|
+
bodyPreview: evt.bodyPreview,
|
|
6810
|
+
testPath: evt.testPath,
|
|
6811
|
+
currentTestName: evt.currentTestName
|
|
6812
|
+
});
|
|
6813
|
+
} else if (type === "httpAbort") {
|
|
6814
|
+
http.push({
|
|
6815
|
+
kind: "abort",
|
|
6816
|
+
timestampMs: Number(evt.timestampMs ?? Date.now()),
|
|
6817
|
+
method: evt.method,
|
|
6818
|
+
url: evt.url,
|
|
6819
|
+
route: evt.route,
|
|
6820
|
+
durationMs: evt.durationMs,
|
|
6821
|
+
testPath: evt.testPath,
|
|
6822
|
+
currentTestName: evt.currentTestName
|
|
6823
|
+
});
|
|
6824
|
+
} else if (type === "httpResponseBatch") {
|
|
6825
|
+
const list = asHttpList(evt?.events);
|
|
6826
|
+
for (const item of list) {
|
|
6827
|
+
const anyItem = item;
|
|
6828
|
+
http.push({
|
|
6829
|
+
timestampMs: Number(anyItem.timestampMs ?? Date.now()),
|
|
6830
|
+
method: anyItem.method,
|
|
6831
|
+
url: anyItem.url,
|
|
6832
|
+
route: anyItem.route,
|
|
6833
|
+
statusCode: anyItem.statusCode,
|
|
6834
|
+
durationMs: anyItem.durationMs,
|
|
6835
|
+
contentType: anyItem.contentType,
|
|
6836
|
+
requestId: anyItem.requestId,
|
|
6837
|
+
json: anyItem.json,
|
|
6838
|
+
bodyPreview: anyItem.bodyPreview,
|
|
6839
|
+
testPath: evt.testPath,
|
|
6840
|
+
currentTestName: evt.currentTestName
|
|
6841
|
+
});
|
|
6842
|
+
}
|
|
6843
|
+
} else if (type === "assertionFailure") {
|
|
6844
|
+
assertions.push({
|
|
6845
|
+
timestampMs: typeof evt.timestampMs === "number" ? evt.timestampMs : void 0,
|
|
6846
|
+
matcher: evt.matcher,
|
|
6847
|
+
expectedNumber: typeof evt.expectedNumber === "number" ? evt.expectedNumber : void 0,
|
|
6848
|
+
receivedNumber: typeof evt.receivedNumber === "number" ? evt.receivedNumber : void 0,
|
|
6849
|
+
message: typeof evt.message === "string" ? evt.message : void 0,
|
|
6850
|
+
stack: typeof evt.stack === "string" ? evt.stack : void 0,
|
|
6851
|
+
testPath: evt.testPath,
|
|
6852
|
+
currentTestName: evt.currentTestName,
|
|
6853
|
+
expectedPreview: typeof evt.expectedPreview === "string" ? evt.expectedPreview : void 0,
|
|
6854
|
+
actualPreview: typeof evt.actualPreview === "string" ? evt.actualPreview : void 0
|
|
6855
|
+
});
|
|
6856
|
+
}
|
|
6857
|
+
} catch {
|
|
6858
|
+
}
|
|
6859
|
+
}
|
|
6860
|
+
return { http, assertions };
|
|
6861
|
+
};
|
|
6862
|
+
var renderRunHeader = ({ ctx, onlyFailures }) => onlyFailures ? empty : [`${BackgroundColors.Run(ansi.white(" RUN "))} ${ansi.dim(ctx.cwd)}`, ""];
|
|
6863
|
+
var renderPerFileOverviewBlock = (rel, testResults, onlyFailures) => onlyFailures ? empty : buildPerFileOverview(rel, testResults);
|
|
6864
|
+
var renderFileBadge = (rel, failedCount, onlyFailures) => onlyFailures && failedCount === 0 ? empty : [buildFileBadgeLine(rel, failedCount)];
|
|
6865
|
+
var condenseBlankRuns = (lines) => {
|
|
6866
|
+
const out = [];
|
|
6867
|
+
let lastBlank = false;
|
|
6868
|
+
for (const ln of lines) {
|
|
6869
|
+
const isBlank = !stripAnsiSimple(String(ln ?? "")).trim();
|
|
6870
|
+
if (isBlank) {
|
|
6871
|
+
if (!lastBlank) {
|
|
6872
|
+
out.push("");
|
|
6873
|
+
}
|
|
6874
|
+
lastBlank = true;
|
|
6875
|
+
} else {
|
|
6876
|
+
out.push(String(ln));
|
|
6877
|
+
lastBlank = false;
|
|
6878
|
+
}
|
|
6879
|
+
}
|
|
6880
|
+
return out;
|
|
6881
|
+
};
|
|
6882
|
+
var mergeMsgLines = (primaryRaw, detailMsgs) => {
|
|
6883
|
+
const primary = primaryRaw.trim() ? primaryRaw.split(/\r?\n/) : [];
|
|
6884
|
+
const key = (line) => stripAnsiSimple(line).trim();
|
|
6885
|
+
const seen = new Set(primary.map(key));
|
|
6886
|
+
const merged = [...primary];
|
|
6887
|
+
for (const msg of detailMsgs) {
|
|
6888
|
+
const msgKey = key(String(msg ?? ""));
|
|
6889
|
+
if (!msgKey) {
|
|
6890
|
+
continue;
|
|
6891
|
+
}
|
|
6892
|
+
if (!seen.has(msgKey)) {
|
|
6893
|
+
merged.push(msg);
|
|
6894
|
+
seen.add(msgKey);
|
|
6895
|
+
}
|
|
6896
|
+
}
|
|
6897
|
+
return condenseBlankRuns(merged);
|
|
6898
|
+
};
|
|
6899
|
+
var renderFileLevelFailure = (file, ctx) => {
|
|
6900
|
+
if (!(file.failureMessage || file.testExecError)) {
|
|
6901
|
+
return empty;
|
|
6902
|
+
}
|
|
6903
|
+
const base = linesFromDetails(file.failureDetails);
|
|
6904
|
+
const exec = linesFromDetails(
|
|
6905
|
+
Array.isArray(file.testExecError) ? file.testExecError : [file.testExecError]
|
|
6906
|
+
);
|
|
6907
|
+
const combinedDetails = {
|
|
6908
|
+
stacks: [...base.stacks, ...exec.stacks],
|
|
6909
|
+
messages: [...base.messages, ...exec.messages]
|
|
6910
|
+
};
|
|
6911
|
+
const msgLines = mergeMsgLines(file.failureMessage || "", combinedDetails.messages);
|
|
6912
|
+
const mergedForStack = collapseStacks([...msgLines, ...combinedDetails.stacks]);
|
|
6913
|
+
const synthLoc = deepestProjectLoc(mergedForStack, ctx.projectHint);
|
|
6914
|
+
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)}`) : [];
|
|
6915
|
+
const code = buildCodeFrameSection(msgLines, ctx, synthLoc);
|
|
6916
|
+
const pretty = buildPrettyDiffSection(file.failureDetails, msgLines);
|
|
6917
|
+
const message = buildMessageSection(msgLines, combinedDetails, ctx, {
|
|
6918
|
+
suppressDiff: pretty.length > 0,
|
|
6919
|
+
stackPreview
|
|
6920
|
+
});
|
|
6921
|
+
const consoleBlock = buildConsoleSection(stripBridgeEventsFromConsole(file.console ?? null));
|
|
6922
|
+
const stackTail = ctx.showStacks && stackPreview.length === 0 ? (() => {
|
|
6923
|
+
const tail = mergedForStack.filter((ln) => isStackLine(stripAnsiSimple(ln))).slice(-4).map((ln) => ` ${colorStackLine(String(ln), ctx.projectHint)}`);
|
|
6924
|
+
return tail.length ? [ansi.dim(" Stack:"), ...tail, ""] : empty;
|
|
6925
|
+
})() : empty;
|
|
6926
|
+
return concat(code, pretty, message, consoleBlock, stackTail);
|
|
6927
|
+
};
|
|
6928
|
+
var renderHttpCard = (args) => {
|
|
6929
|
+
const { file, relPath: rel, assertion, assertionEvents, httpSorted } = args;
|
|
6930
|
+
const nameMatches = (left, right) => !!left && !!right && (left === right || left.includes(right) || right.includes(left));
|
|
6931
|
+
const inSameCtx = (testPath, testName) => httpSorted.filter(
|
|
6932
|
+
(event) => event.testPath === testPath && nameMatches(event.currentTestName, testName)
|
|
6933
|
+
);
|
|
6934
|
+
const perTestSlice = inSameCtx(file.testFilePath, assertion.fullName);
|
|
6935
|
+
const corresponding = assertionEvents.find(
|
|
6936
|
+
(event) => event.testPath === file.testFilePath && nameMatches(event.currentTestName, assertion.fullName)
|
|
6937
|
+
) ?? assertion;
|
|
6938
|
+
const nearByTime = eventsNear(
|
|
6939
|
+
httpSorted,
|
|
6940
|
+
corresponding?.timestampMs,
|
|
6941
|
+
file.testFilePath
|
|
6942
|
+
);
|
|
6943
|
+
const hasAbort = perTestSlice.some((event) => event.kind === "abort");
|
|
6944
|
+
const hasTransport = isTransportError(corresponding?.message) || hasAbort;
|
|
6945
|
+
const httpLikely = isHttpRelevant({
|
|
6946
|
+
assertion: corresponding,
|
|
6947
|
+
title: assertion.fullName,
|
|
6948
|
+
relPath: rel,
|
|
6949
|
+
httpCountInSameTest: perTestSlice.length || nearByTime.length,
|
|
6950
|
+
hasTransportSignal: hasTransport
|
|
6951
|
+
});
|
|
6952
|
+
if (!httpLikely) {
|
|
6953
|
+
return empty;
|
|
6954
|
+
}
|
|
6955
|
+
const HEADLAMP_HTTP_DIFF_LIMIT_LOCAL = () => HEADLAMP_HTTP_DIFF_LIMIT();
|
|
6956
|
+
const safeParseJSON = (text) => {
|
|
6957
|
+
try {
|
|
6958
|
+
return text ? import_json52.default.parse(text) : void 0;
|
|
6959
|
+
} catch {
|
|
6960
|
+
return void 0;
|
|
6961
|
+
}
|
|
6962
|
+
};
|
|
6963
|
+
const expPreview = corresponding?.expectedPreview;
|
|
6964
|
+
const actPreview = corresponding?.actualPreview;
|
|
6965
|
+
const parsedExpected = safeParseJSON(expPreview);
|
|
6966
|
+
const parsedActual = safeParseJSON(actPreview);
|
|
6967
|
+
let corr = corresponding;
|
|
6968
|
+
if (!isHttpStatusNumber(corr.expectedNumber) && !isHttpStatusNumber(corr.receivedNumber)) {
|
|
6969
|
+
const inferred = inferHttpNumbersFromText(
|
|
6970
|
+
(assertion.failureMessages?.join("\n") || file.failureMessage || "").split("\n")
|
|
6971
|
+
);
|
|
6972
|
+
if (isHttpStatusNumber(inferred.expectedNumber) || isHttpStatusNumber(inferred.receivedNumber)) {
|
|
6973
|
+
corr = { ...corr, ...inferred };
|
|
6974
|
+
}
|
|
6975
|
+
}
|
|
6976
|
+
const relevant = pickRelevantHttp(
|
|
6977
|
+
{
|
|
6978
|
+
timestampMs: corr?.timestampMs,
|
|
6979
|
+
expectedNumber: corr?.expectedNumber,
|
|
6980
|
+
receivedNumber: corr?.receivedNumber,
|
|
6981
|
+
matcher: corr?.matcher,
|
|
6982
|
+
message: corr?.message,
|
|
6983
|
+
stack: corr?.stack,
|
|
6984
|
+
testPath: file.testFilePath,
|
|
6985
|
+
currentTestName: assertion.title
|
|
6986
|
+
},
|
|
6987
|
+
httpSorted,
|
|
6988
|
+
{
|
|
6989
|
+
testPath: file.testFilePath,
|
|
6990
|
+
currentTestName: assertion.fullName,
|
|
6991
|
+
title: assertion.fullName
|
|
6992
|
+
}
|
|
6993
|
+
);
|
|
6994
|
+
if (hasTransport) {
|
|
6995
|
+
const tsBase = corr?.timestampMs ?? 0;
|
|
6996
|
+
const [nearestAbort] = perTestSlice.filter((event) => event.kind === "abort").sort(
|
|
6997
|
+
(left, right) => Math.abs(tsBase - (left.timestampMs ?? 0)) - Math.abs(tsBase - (right.timestampMs ?? 0))
|
|
6998
|
+
);
|
|
6999
|
+
if (nearestAbort) {
|
|
7000
|
+
const ms = nearestAbort.durationMs;
|
|
7001
|
+
return [
|
|
7002
|
+
" HTTP:",
|
|
7003
|
+
`
|
|
7004
|
+
${summarizeUrl(nearestAbort.method, nearestAbort.url, nearestAbort.route)} ${ansi.dim("->")} ${ansi.yellow("connection aborted")}`,
|
|
7005
|
+
ms != null ? ` ${ansi.dim(`(${ms}ms)`)} ` : "",
|
|
7006
|
+
"\n"
|
|
7007
|
+
];
|
|
7008
|
+
}
|
|
7009
|
+
return HEADLAMP_HTTP_SHOW_MISS() ? [
|
|
7010
|
+
" HTTP:",
|
|
7011
|
+
`
|
|
7012
|
+
${ansi.dim("Transport error; no matching HTTP exchange in window.")}`,
|
|
7013
|
+
"\n"
|
|
7014
|
+
] : empty;
|
|
7015
|
+
}
|
|
7016
|
+
if (!relevant) {
|
|
7017
|
+
return HEADLAMP_HTTP_SHOW_MISS() ? [
|
|
7018
|
+
" HTTP:",
|
|
7019
|
+
`
|
|
7020
|
+
${ansi.dim("No relevant HTTP exchange found. (HEADLAMP_HTTP_MISS=0 to hide)")}`,
|
|
7021
|
+
"\n"
|
|
7022
|
+
] : empty;
|
|
7023
|
+
}
|
|
7024
|
+
const jsonDiff = (expected, actual, limit = HEADLAMP_HTTP_DIFF_LIMIT_LOCAL()) => {
|
|
7025
|
+
const out = [];
|
|
7026
|
+
const queue = [
|
|
7027
|
+
{ pathSoFar: "$", expectedValue: expected, actualValue: actual }
|
|
7028
|
+
];
|
|
7029
|
+
while (queue.length && out.length < limit) {
|
|
7030
|
+
const { pathSoFar, expectedValue, actualValue } = queue.shift();
|
|
7031
|
+
const expectedIsObject = expectedValue && typeof expectedValue === "object";
|
|
7032
|
+
const actualIsObject = actualValue && typeof actualValue === "object";
|
|
7033
|
+
if (!expectedIsObject && !actualIsObject) {
|
|
7034
|
+
if (JSON.stringify(expectedValue) !== JSON.stringify(actualValue)) {
|
|
7035
|
+
out.push({
|
|
7036
|
+
kind: "changed",
|
|
7037
|
+
path: pathSoFar,
|
|
7038
|
+
preview: `${String(expectedValue)} \u2192 ${String(actualValue)}`
|
|
7039
|
+
});
|
|
7040
|
+
}
|
|
7041
|
+
} else if (expectedIsObject && !actualIsObject) {
|
|
7042
|
+
out.push({ kind: "changed", path: pathSoFar, preview: "[object] \u2192 primitive" });
|
|
7043
|
+
} else if (!expectedIsObject && actualIsObject) {
|
|
7044
|
+
out.push({ kind: "changed", path: pathSoFar, preview: "primitive \u2192 [object]" });
|
|
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) && out.length < limit) {
|
|
7050
|
+
out.push({ kind: "removed", path: `${pathSoFar}.${key}` });
|
|
7051
|
+
}
|
|
7052
|
+
}
|
|
7053
|
+
for (const key of actualKeys) {
|
|
7054
|
+
if (!expectedKeys.has(key) && out.length < limit) {
|
|
7055
|
+
out.push({ kind: "added", path: `${pathSoFar}.${key}` });
|
|
7056
|
+
}
|
|
7057
|
+
}
|
|
7058
|
+
for (const key of expectedKeys) {
|
|
7059
|
+
if (actualKeys.has(key) && out.length < limit) {
|
|
7060
|
+
queue.push({
|
|
7061
|
+
pathSoFar: `${pathSoFar}.${key}`,
|
|
7062
|
+
expectedValue: expectedValue[key],
|
|
7063
|
+
actualValue: actualValue[key]
|
|
7064
|
+
});
|
|
7065
|
+
}
|
|
7066
|
+
}
|
|
7067
|
+
}
|
|
7068
|
+
}
|
|
7069
|
+
return out;
|
|
7070
|
+
};
|
|
7071
|
+
const importantMessages = (json) => {
|
|
7072
|
+
const msgs = [];
|
|
7073
|
+
try {
|
|
7074
|
+
const obj = isObject(json) ? json : {};
|
|
7075
|
+
const push = (msg) => {
|
|
7076
|
+
if (typeof msg === "string" && msg.trim()) {
|
|
7077
|
+
msgs.push(msg);
|
|
7078
|
+
}
|
|
7079
|
+
};
|
|
7080
|
+
push(obj.displayMessage);
|
|
7081
|
+
push(obj.message);
|
|
7082
|
+
if (Array.isArray(obj.errors)) {
|
|
7083
|
+
for (const event of obj.errors) {
|
|
7084
|
+
push(isObject(event) ? event.message : void 0);
|
|
7085
|
+
}
|
|
7086
|
+
}
|
|
7087
|
+
if (Array.isArray(obj.data)) {
|
|
7088
|
+
for (const event of obj.data) {
|
|
7089
|
+
push(isObject(event) ? event.message : void 0);
|
|
7090
|
+
}
|
|
7091
|
+
}
|
|
7092
|
+
} catch {
|
|
7093
|
+
}
|
|
7094
|
+
return msgs.slice(0, 2);
|
|
7095
|
+
};
|
|
7096
|
+
const where = summarizeUrl(relevant.method, relevant.url, relevant.route);
|
|
7097
|
+
const header = [
|
|
7098
|
+
" HTTP:",
|
|
7099
|
+
`
|
|
7100
|
+
${where} ${ansi.dim("->")} ${relevant.statusCode ?? "?"}`,
|
|
7101
|
+
typeof relevant.durationMs === "number" ? ` ${ansi.dim(`(${relevant.durationMs}ms)`)} ` : " ",
|
|
7102
|
+
relevant.contentType ? ansi.dim(`(${relevant.contentType})`) : "",
|
|
7103
|
+
relevant.requestId ? ansi.dim(` reqId=${relevant.requestId}`) : ""
|
|
7104
|
+
].join("");
|
|
7105
|
+
const expVsAct = typeof corr?.expectedNumber === "number" || typeof corr?.receivedNumber === "number" ? (() => {
|
|
7106
|
+
const exp = corr?.expectedNumber != null ? String(corr.expectedNumber) : "?";
|
|
7107
|
+
const got = corr?.receivedNumber != null ? String(corr.receivedNumber) : String(relevant.statusCode ?? "?");
|
|
7108
|
+
return `
|
|
7109
|
+
Expected: ${ansi.yellow(exp)} Received: ${ansi.yellow(got)}`;
|
|
7110
|
+
})() : "";
|
|
7111
|
+
const why = importantMessages(parsedActual ?? relevant.json).map((msg) => `
|
|
7112
|
+
Why: ${ansi.white(msg)}`).slice(0, 1).join("") || "";
|
|
7113
|
+
const diff = (() => {
|
|
7114
|
+
const rightActual = parsedActual ?? relevant.json;
|
|
7115
|
+
if (!parsedExpected || !rightActual) {
|
|
7116
|
+
return "";
|
|
7117
|
+
}
|
|
7118
|
+
const changes = jsonDiff(parsedExpected, rightActual);
|
|
7119
|
+
if (!changes.length) {
|
|
7120
|
+
return "";
|
|
7121
|
+
}
|
|
7122
|
+
const body = changes.map((change) => {
|
|
7123
|
+
const marker = change.kind === "added" ? "+" : change.kind === "removed" ? "-" : "~";
|
|
7124
|
+
const preview = change.preview ? `: ${ansi.dim(change.preview)}` : "";
|
|
7125
|
+
return `
|
|
7126
|
+
${marker} ${change.path}${preview}`;
|
|
7127
|
+
}).join("");
|
|
7128
|
+
return `
|
|
7129
|
+
Diff:${body}`;
|
|
7130
|
+
})();
|
|
7131
|
+
return [header, expVsAct, why, diff, "\n"].filter(Boolean);
|
|
7132
|
+
};
|
|
6692
7133
|
var coerceJestJsonToBridge = (raw) => {
|
|
6693
7134
|
if (raw && typeof raw === "object" && "aggregated" in raw) {
|
|
6694
7135
|
return raw;
|
|
@@ -6731,71 +7172,99 @@ var coerceJestJsonToBridge = (raw) => {
|
|
|
6731
7172
|
}
|
|
6732
7173
|
};
|
|
6733
7174
|
};
|
|
6734
|
-
var
|
|
6735
|
-
|
|
6736
|
-
|
|
6737
|
-
|
|
6738
|
-
|
|
6739
|
-
|
|
6740
|
-
|
|
6741
|
-
|
|
6742
|
-
|
|
6743
|
-
|
|
6744
|
-
|
|
6745
|
-
|
|
6746
|
-
|
|
6747
|
-
|
|
6748
|
-
|
|
6749
|
-
};
|
|
6750
|
-
|
|
6751
|
-
|
|
6752
|
-
|
|
6753
|
-
|
|
6754
|
-
return maybeConsole.filter((entry) => {
|
|
7175
|
+
var renderFailedAssertion = (args) => {
|
|
7176
|
+
const { file, relPath: rel, assertion, ctx, assertionEvents, httpSorted } = args;
|
|
7177
|
+
const header = `${rel} > ${assertion.fullName}`;
|
|
7178
|
+
const bullet = (text) => `${Colors.Failure("\xD7")} ${ansi.white(text)}`;
|
|
7179
|
+
const failureMessage = file.failureMessage || "";
|
|
7180
|
+
const detailMsgs = linesFromDetails(assertion.failureDetails || file.failureDetails).messages;
|
|
7181
|
+
const primaryBlock = assertion.failureMessages?.length ? assertion.failureMessages.join("\n") : failureMessage;
|
|
7182
|
+
const messagesArray = mergeMsgLines(primaryBlock, detailMsgs);
|
|
7183
|
+
const details = linesFromDetails(assertion.failureDetails || file.failureDetails);
|
|
7184
|
+
const mergedForStack = collapseStacks([...messagesArray, ...details.stacks]);
|
|
7185
|
+
const deepestLoc = deepestProjectLoc(mergedForStack, ctx.projectHint);
|
|
7186
|
+
const locLink = deepestLoc ? (() => {
|
|
7187
|
+
const href = preferredEditorHref(deepestLoc.file, deepestLoc.line, ctx.editorCmd);
|
|
7188
|
+
const base = `${deepestLoc.file.split("/").pop()}:${deepestLoc.line}`;
|
|
7189
|
+
return osc8(base, href);
|
|
7190
|
+
})() : void 0;
|
|
7191
|
+
const headerLine = `${ansi.white(header)}${locLink ? ` ${ansi.dim(`(${locLink})`)}` : ""}`;
|
|
7192
|
+
const msgLines = messagesArray.join("\n").split("\n");
|
|
7193
|
+
const assertFallback = deepestLoc || assertion.location && { file: file.testFilePath, line: assertion.location.line };
|
|
7194
|
+
const matcherMsg = (() => {
|
|
6755
7195
|
try {
|
|
6756
|
-
const
|
|
6757
|
-
|
|
7196
|
+
const arr = assertion.failureDetails || file.failureDetails;
|
|
7197
|
+
if (!arr) {
|
|
7198
|
+
return empty;
|
|
7199
|
+
}
|
|
7200
|
+
for (const detailEntry of arr) {
|
|
7201
|
+
const obj = detailEntry && typeof detailEntry === "object" ? detailEntry : null;
|
|
7202
|
+
const mr = obj && obj.matcherResult && typeof obj.matcherResult === "object" ? obj.matcherResult : null;
|
|
7203
|
+
if (mr && typeof mr.message === "string" && mr.message.trim()) {
|
|
7204
|
+
const name = typeof mr.matcherName === "string" ? mr.matcherName : "";
|
|
7205
|
+
const matcherHeader = name ? ` ${ansi.bold("Matcher:")} ${ansi.yellow(name)}` : "";
|
|
7206
|
+
const bodyHeader = ` ${ansi.bold("Message:")}`;
|
|
7207
|
+
const body = String(mr.message).split(/\r?\n/).slice(0, 6).map((ln) => ` ${ansi.yellow(ln)}`);
|
|
7208
|
+
return [matcherHeader, bodyHeader, ...body, ""].filter(Boolean);
|
|
7209
|
+
}
|
|
7210
|
+
}
|
|
6758
7211
|
} catch {
|
|
6759
|
-
return true;
|
|
6760
7212
|
}
|
|
7213
|
+
return empty;
|
|
7214
|
+
})();
|
|
7215
|
+
const code = concat(
|
|
7216
|
+
["", drawFailLine(), bullet(headerLine), ""],
|
|
7217
|
+
buildCodeFrameSection(msgLines, ctx, assertFallback || void 0),
|
|
7218
|
+
[""]
|
|
7219
|
+
);
|
|
7220
|
+
const pretty = buildPrettyDiffSection(assertion.failureDetails || file.failureDetails, msgLines);
|
|
7221
|
+
const hasPretty = pretty.length > 0;
|
|
7222
|
+
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)}`) : [];
|
|
7223
|
+
const message = buildMessageSection(msgLines, details, ctx, {
|
|
7224
|
+
suppressDiff: hasPretty,
|
|
7225
|
+
stackPreview
|
|
6761
7226
|
});
|
|
7227
|
+
const httpCard = renderHttpCard({ file, relPath: rel, assertion, assertionEvents, httpSorted });
|
|
7228
|
+
const minimalInfo = msgLines.every((ln) => !ln.trim());
|
|
7229
|
+
const thrown = minimalInfo ? (() => {
|
|
7230
|
+
try {
|
|
7231
|
+
return buildThrownSection(assertion.failureDetails || []);
|
|
7232
|
+
} catch {
|
|
7233
|
+
return empty;
|
|
7234
|
+
}
|
|
7235
|
+
})() : empty;
|
|
7236
|
+
const consoleBlock = buildConsoleSection(stripBridgeEventsFromConsole(file.console ?? null));
|
|
7237
|
+
const stackTail = ctx.showStacks && stackPreview.length === 0 ? (() => {
|
|
7238
|
+
const merged = collapseStacks([...msgLines, ...details.stacks]);
|
|
7239
|
+
const tail = collapseStacks(merged).filter((ln) => isStackLine(stripAnsiSimple(ln))).slice(-4).map((ln) => ` ${colorStackLine(String(ln), ctx.projectHint)}`);
|
|
7240
|
+
return tail.length ? [ansi.dim(" Stack:"), ...tail, ""] : empty;
|
|
7241
|
+
})() : empty;
|
|
7242
|
+
return concat(code, pretty, matcherMsg, message, httpCard, thrown, consoleBlock, stackTail, [
|
|
7243
|
+
drawFailLine(),
|
|
7244
|
+
""
|
|
7245
|
+
]);
|
|
6762
7246
|
};
|
|
6763
|
-
var
|
|
6764
|
-
|
|
6765
|
-
|
|
6766
|
-
}
|
|
6767
|
-
|
|
6768
|
-
|
|
6769
|
-
|
|
6770
|
-
|
|
6771
|
-
|
|
6772
|
-
|
|
6773
|
-
|
|
6774
|
-
|
|
6775
|
-
|
|
6776
|
-
|
|
6777
|
-
|
|
6778
|
-
|
|
6779
|
-
|
|
6780
|
-
|
|
6781
|
-
|
|
6782
|
-
|
|
6783
|
-
const { method, path: parsedPath } = parseMethodPathFromTitle(title);
|
|
6784
|
-
return Boolean(method || parsedPath && parsedPath.startsWith("/"));
|
|
6785
|
-
};
|
|
6786
|
-
var hasStatusSemantics = (assertionLike) => {
|
|
6787
|
-
if (!assertionLike) {
|
|
6788
|
-
return false;
|
|
6789
|
-
}
|
|
6790
|
-
if (isHttpStatusNumber(assertionLike.expectedNumber) || isHttpStatusNumber(assertionLike.receivedNumber)) {
|
|
6791
|
-
return true;
|
|
6792
|
-
}
|
|
6793
|
-
const combinedRaw = `${assertionLike.matcher ?? ""} ${assertionLike.message ?? ""}`;
|
|
6794
|
-
const combinedMessage = combinedRaw.toLowerCase();
|
|
6795
|
-
return /\bstatus(code)?\b|\btohaves(tatus|tatuscode)\b/.test(combinedMessage);
|
|
7247
|
+
var renderFileBlock = (file, env) => {
|
|
7248
|
+
const rel = file.testFilePath.replace(/\\/g, "/").replace(`${env.ctx.cwd}/`, "");
|
|
7249
|
+
const failed = file.testResults.filter((assertion) => assertion.status === "failed");
|
|
7250
|
+
const { http, assertions } = parseBridgeConsole(file.console);
|
|
7251
|
+
const httpSorted = [...http].sort(by((event) => event.timestampMs));
|
|
7252
|
+
return concat(
|
|
7253
|
+
renderPerFileOverviewBlock(rel, file.testResults, env.onlyFailures),
|
|
7254
|
+
renderFileBadge(rel, failed.length, env.onlyFailures),
|
|
7255
|
+
renderFileLevelFailure(file, env.ctx),
|
|
7256
|
+
...failed.map(
|
|
7257
|
+
(assertion) => renderFailedAssertion({
|
|
7258
|
+
file,
|
|
7259
|
+
relPath: rel,
|
|
7260
|
+
assertion,
|
|
7261
|
+
ctx: env.ctx,
|
|
7262
|
+
assertionEvents: assertions,
|
|
7263
|
+
httpSorted
|
|
7264
|
+
})
|
|
7265
|
+
)
|
|
7266
|
+
);
|
|
6796
7267
|
};
|
|
6797
|
-
var fileSuggestsHttp = (relPath2) => /(?:^|\/)(routes?|api|controllers?|e2e|integration)(?:\/|\.test\.)/i.test(relPath2);
|
|
6798
|
-
var isHttpRelevant = (ctx) => ctx.hasTransportSignal || ctx.httpCountInSameTest > 0 || titleSuggestsHttp(ctx.title) || hasStatusSemantics(ctx.assertion) || fileSuggestsHttp(ctx.relPath);
|
|
6799
7268
|
var vitestFooter = (agg, durationMs) => {
|
|
6800
7269
|
const files = [
|
|
6801
7270
|
agg.numFailedTestSuites ? colorTokens2.fail(`${agg.numFailedTestSuites} failed`) : "",
|
|
@@ -6816,462 +7285,24 @@ var vitestFooter = (agg, durationMs) => {
|
|
|
6816
7285
|
`${ansi.bold("Time")} ${time} ${thread}`
|
|
6817
7286
|
].join("\n");
|
|
6818
7287
|
};
|
|
6819
|
-
var
|
|
6820
|
-
const out = [];
|
|
6821
|
-
const onlyFailures = Boolean(opts?.onlyFailures);
|
|
6822
|
-
if (!onlyFailures) {
|
|
6823
|
-
out.push(`${BackgroundColors.Run(ansi.white(" RUN "))} ${ansi.dim(ctx.cwd)}`, "");
|
|
6824
|
-
}
|
|
6825
|
-
for (const file of data.testResults) {
|
|
6826
|
-
const rel = file.testFilePath.replace(/\\/g, "/").replace(`${ctx.cwd}/`, "");
|
|
6827
|
-
const failed = file.testResults.filter((assertion) => assertion.status === "failed");
|
|
6828
|
-
if (!onlyFailures) {
|
|
6829
|
-
out.push(...buildPerFileOverview(rel, file.testResults));
|
|
6830
|
-
}
|
|
6831
|
-
if (!(onlyFailures && failed.length === 0)) {
|
|
6832
|
-
out.push(buildFileBadgeLine(rel, failed.length));
|
|
6833
|
-
}
|
|
6834
|
-
let httpSorted = [];
|
|
6835
|
-
let assertionEvents = [];
|
|
6836
|
-
{
|
|
6837
|
-
const parseBridge = (consoleEntries) => {
|
|
6838
|
-
const http = [];
|
|
6839
|
-
const assertions = [];
|
|
6840
|
-
if (!Array.isArray(consoleEntries)) {
|
|
6841
|
-
return { http, assertions };
|
|
6842
|
-
}
|
|
6843
|
-
for (const entry of consoleEntries) {
|
|
6844
|
-
const rec = entry;
|
|
6845
|
-
const rawMsgVal = rec && typeof rec.message !== "undefined" ? rec.message : "";
|
|
6846
|
-
const raw = Array.isArray(rawMsgVal) ? rawMsgVal.map(String).join(" ") : String(rawMsgVal ?? "");
|
|
6847
|
-
if (!raw.includes("[JEST-BRIDGE-EVENT]")) {
|
|
6848
|
-
continue;
|
|
6849
|
-
}
|
|
6850
|
-
const jsonText = raw.split("[JEST-BRIDGE-EVENT]").pop()?.trim() ?? "";
|
|
6851
|
-
try {
|
|
6852
|
-
const evt = import_json52.default.parse(jsonText);
|
|
6853
|
-
const type = evt?.type;
|
|
6854
|
-
if (type === "httpResponse") {
|
|
6855
|
-
const timestampMs = Number(evt.timestampMs ?? Date.now());
|
|
6856
|
-
http.push({
|
|
6857
|
-
kind: "response",
|
|
6858
|
-
timestampMs,
|
|
6859
|
-
method: evt.method,
|
|
6860
|
-
url: evt.url,
|
|
6861
|
-
route: evt.route,
|
|
6862
|
-
statusCode: evt.statusCode,
|
|
6863
|
-
durationMs: evt.durationMs,
|
|
6864
|
-
contentType: evt.contentType,
|
|
6865
|
-
requestId: evt.requestId,
|
|
6866
|
-
json: evt.json,
|
|
6867
|
-
bodyPreview: evt.bodyPreview,
|
|
6868
|
-
testPath: evt.testPath,
|
|
6869
|
-
currentTestName: evt.currentTestName
|
|
6870
|
-
});
|
|
6871
|
-
} else if (type === "httpAbort") {
|
|
6872
|
-
http.push({
|
|
6873
|
-
kind: "abort",
|
|
6874
|
-
timestampMs: Number(evt.timestampMs ?? Date.now()),
|
|
6875
|
-
method: evt.method,
|
|
6876
|
-
url: evt.url,
|
|
6877
|
-
route: evt.route,
|
|
6878
|
-
durationMs: evt.durationMs,
|
|
6879
|
-
testPath: evt.testPath,
|
|
6880
|
-
currentTestName: evt.currentTestName
|
|
6881
|
-
});
|
|
6882
|
-
} else if (type === "httpResponseBatch") {
|
|
6883
|
-
const list = asHttpList(evt?.events);
|
|
6884
|
-
for (const item of list) {
|
|
6885
|
-
const anyItem = item;
|
|
6886
|
-
http.push({
|
|
6887
|
-
timestampMs: Number(anyItem.timestampMs ?? Date.now()),
|
|
6888
|
-
method: anyItem.method,
|
|
6889
|
-
url: anyItem.url,
|
|
6890
|
-
route: anyItem.route,
|
|
6891
|
-
statusCode: anyItem.statusCode,
|
|
6892
|
-
durationMs: anyItem.durationMs,
|
|
6893
|
-
contentType: anyItem.contentType,
|
|
6894
|
-
requestId: anyItem.requestId,
|
|
6895
|
-
json: anyItem.json,
|
|
6896
|
-
bodyPreview: anyItem.bodyPreview,
|
|
6897
|
-
testPath: evt.testPath,
|
|
6898
|
-
currentTestName: evt.currentTestName
|
|
6899
|
-
});
|
|
6900
|
-
}
|
|
6901
|
-
} else if (type === "assertionFailure") {
|
|
6902
|
-
assertions.push({
|
|
6903
|
-
timestampMs: typeof evt.timestampMs === "number" ? evt.timestampMs : void 0,
|
|
6904
|
-
matcher: evt.matcher,
|
|
6905
|
-
expectedNumber: typeof evt.expectedNumber === "number" ? evt.expectedNumber : void 0,
|
|
6906
|
-
receivedNumber: typeof evt.receivedNumber === "number" ? evt.receivedNumber : void 0,
|
|
6907
|
-
message: typeof evt.message === "string" ? evt.message : void 0,
|
|
6908
|
-
stack: typeof evt.stack === "string" ? evt.stack : void 0,
|
|
6909
|
-
testPath: evt.testPath,
|
|
6910
|
-
currentTestName: evt.currentTestName,
|
|
6911
|
-
expectedPreview: typeof evt.expectedPreview === "string" ? evt.expectedPreview : void 0,
|
|
6912
|
-
actualPreview: typeof evt.actualPreview === "string" ? evt.actualPreview : void 0
|
|
6913
|
-
});
|
|
6914
|
-
}
|
|
6915
|
-
} catch {
|
|
6916
|
-
}
|
|
6917
|
-
}
|
|
6918
|
-
return { http, assertions };
|
|
6919
|
-
};
|
|
6920
|
-
const parsed = parseBridge(file.console);
|
|
6921
|
-
httpSorted = [...parsed.http].sort(by((event) => event.timestampMs));
|
|
6922
|
-
assertionEvents = parsed.assertions;
|
|
6923
|
-
}
|
|
6924
|
-
const inSameCtx = (testPath, testName) => httpSorted.filter(
|
|
6925
|
-
(event) => event.testPath === testPath && event.currentTestName === testName
|
|
6926
|
-
);
|
|
6927
|
-
if (file.failureMessage || file.testExecError) {
|
|
6928
|
-
const lines = file.failureMessage.split(/\r?\n/);
|
|
6929
|
-
const combinedDetails = (() => {
|
|
6930
|
-
const base = linesFromDetails(file.failureDetails);
|
|
6931
|
-
const exec = linesFromDetails(
|
|
6932
|
-
Array.isArray(file.testExecError) ? file.testExecError : [file.testExecError]
|
|
6933
|
-
);
|
|
6934
|
-
return {
|
|
6935
|
-
stacks: [...base.stacks, ...exec.stacks],
|
|
6936
|
-
messages: [...base.messages, ...exec.messages]
|
|
6937
|
-
};
|
|
6938
|
-
})();
|
|
6939
|
-
const mergedForStack = collapseStacks([...lines, ...combinedDetails.stacks]);
|
|
6940
|
-
const synthLoc = deepestProjectLoc(mergedForStack, ctx.projectHint);
|
|
6941
|
-
out.push(...buildCodeFrameSection(lines, ctx, synthLoc));
|
|
6942
|
-
const payloadPretty = buildPrettyDiffSection(file.failureDetails, lines);
|
|
6943
|
-
out.push(...payloadPretty);
|
|
6944
|
-
const hasPretty = payloadPretty.length > 0;
|
|
6945
|
-
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)}`) : [];
|
|
6946
|
-
out.push(
|
|
6947
|
-
...buildMessageSection(lines, combinedDetails, ctx, {
|
|
6948
|
-
suppressDiff: hasPretty,
|
|
6949
|
-
stackPreview
|
|
6950
|
-
})
|
|
6951
|
-
);
|
|
6952
|
-
out.push(...buildConsoleSection(stripBridgeEventsFromConsole(file.console ?? null)));
|
|
6953
|
-
if (ctx.showStacks && stackPreview.length === 0) {
|
|
6954
|
-
const tail = mergedForStack.filter((ln) => isStackLine(stripAnsiSimple(ln))).slice(-4).map((ln) => ` ${colorStackLine(String(ln), ctx.projectHint)}`);
|
|
6955
|
-
if (tail.length) {
|
|
6956
|
-
out.push(ansi.dim(" Stack:"), ...tail, "");
|
|
6957
|
-
}
|
|
6958
|
-
}
|
|
6959
|
-
}
|
|
6960
|
-
for (const assertion of failed) {
|
|
6961
|
-
out.push(drawFailLine());
|
|
6962
|
-
const header = `${rel} > ${assertion.fullName}`;
|
|
6963
|
-
const messagesArray = (() => {
|
|
6964
|
-
if (assertion.failureMessages && assertion.failureMessages.length > 0) {
|
|
6965
|
-
return assertion.failureMessages;
|
|
6966
|
-
}
|
|
6967
|
-
if (file.failureMessage && file.failureMessage.trim().length > 0) {
|
|
6968
|
-
return file.failureMessage.split(/\r?\n/);
|
|
6969
|
-
}
|
|
6970
|
-
const linesFromMatcher = linesFromDetails(
|
|
6971
|
-
assertion.failureDetails || file.failureDetails
|
|
6972
|
-
).messages;
|
|
6973
|
-
if (Array.isArray(linesFromMatcher) && linesFromMatcher.length > 0) {
|
|
6974
|
-
return linesFromMatcher;
|
|
6975
|
-
}
|
|
6976
|
-
return [""];
|
|
6977
|
-
})();
|
|
6978
|
-
const details = linesFromDetails(assertion.failureDetails || file.failureDetails);
|
|
6979
|
-
const matcherMsg = (() => {
|
|
6980
|
-
try {
|
|
6981
|
-
const arr = assertion.failureDetails || file.failureDetails;
|
|
6982
|
-
if (!arr) {
|
|
6983
|
-
return [];
|
|
6984
|
-
}
|
|
6985
|
-
for (const detailEntry of arr) {
|
|
6986
|
-
const obj = detailEntry && typeof detailEntry === "object" ? detailEntry : null;
|
|
6987
|
-
const mr = obj && obj.matcherResult && typeof obj.matcherResult === "object" ? obj.matcherResult : null;
|
|
6988
|
-
if (mr && typeof mr.message === "string" && mr.message.trim()) {
|
|
6989
|
-
const name = typeof mr.matcherName === "string" ? mr.matcherName : "";
|
|
6990
|
-
const matcherHeader = name ? ` ${ansi.bold("Matcher:")} ${ansi.yellow(name)}` : "";
|
|
6991
|
-
const bodyHeader = ` ${ansi.bold("Message:")}`;
|
|
6992
|
-
const body = String(mr.message).split(/\r?\n/).slice(0, 6).map((ln) => ` ${ansi.yellow(ln)}`);
|
|
6993
|
-
return [matcherHeader, bodyHeader, ...body, ""].filter(Boolean);
|
|
6994
|
-
}
|
|
6995
|
-
}
|
|
6996
|
-
} catch {
|
|
6997
|
-
}
|
|
6998
|
-
return [];
|
|
6999
|
-
})();
|
|
7000
|
-
const mergedForStack = collapseStacks([...messagesArray, ...details.stacks]);
|
|
7001
|
-
const deepestLoc = deepestProjectLoc(mergedForStack, ctx.projectHint);
|
|
7002
|
-
const locLink = deepestLoc ? (() => {
|
|
7003
|
-
const href = preferredEditorHref(deepestLoc.file, deepestLoc.line, ctx.editorCmd);
|
|
7004
|
-
const base = `${deepestLoc.file.split("/").pop()}:${deepestLoc.line}`;
|
|
7005
|
-
return osc8(base, href);
|
|
7006
|
-
})() : void 0;
|
|
7007
|
-
const bullet = (text) => `${Colors.Failure("\xD7")} ${ansi.white(text)}`;
|
|
7008
|
-
const headerLine = `${ansi.white(header)}${locLink ? ` ${ansi.dim(`(${locLink})`)}` : ""}`;
|
|
7009
|
-
out.push(bullet(headerLine));
|
|
7010
|
-
const msgLines = messagesArray.join("\n").split("\n");
|
|
7011
|
-
const assertFallback = deepestLoc || assertion.location && { file: file.testFilePath, line: assertion.location.line };
|
|
7012
|
-
out.push("", ...buildCodeFrameSection(msgLines, ctx, assertFallback || void 0), "");
|
|
7013
|
-
const pretty = buildPrettyDiffSection(
|
|
7014
|
-
assertion.failureDetails || file.failureDetails,
|
|
7015
|
-
msgLines
|
|
7016
|
-
);
|
|
7017
|
-
out.push(...pretty);
|
|
7018
|
-
const hasPretty = pretty.length > 0;
|
|
7019
|
-
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)}`) : [];
|
|
7020
|
-
if (matcherMsg.length) {
|
|
7021
|
-
out.push(...matcherMsg);
|
|
7022
|
-
}
|
|
7023
|
-
out.push(
|
|
7024
|
-
...buildMessageSection(msgLines, details, ctx, { suppressDiff: hasPretty, stackPreview })
|
|
7025
|
-
);
|
|
7026
|
-
{
|
|
7027
|
-
const HEADLAMP_HTTP_DIFF_LIMIT_LOCAL = () => HEADLAMP_HTTP_DIFF_LIMIT();
|
|
7028
|
-
const safeParseJSON = (text) => {
|
|
7029
|
-
try {
|
|
7030
|
-
return text ? import_json52.default.parse(text) : void 0;
|
|
7031
|
-
} catch {
|
|
7032
|
-
return void 0;
|
|
7033
|
-
}
|
|
7034
|
-
};
|
|
7035
|
-
const jsonDiff = (expected, actual, limit = HEADLAMP_HTTP_DIFF_LIMIT_LOCAL()) => {
|
|
7036
|
-
const outChanges = [];
|
|
7037
|
-
const queue = [];
|
|
7038
|
-
queue.push({
|
|
7039
|
-
pathSoFar: "$",
|
|
7040
|
-
expectedValue: expected,
|
|
7041
|
-
actualValue: actual
|
|
7042
|
-
});
|
|
7043
|
-
while (queue.length && outChanges.length < limit) {
|
|
7044
|
-
const { pathSoFar, expectedValue, actualValue } = queue.shift();
|
|
7045
|
-
const expectedIsObj = expectedValue && typeof expectedValue === "object";
|
|
7046
|
-
const actualIsObj = actualValue && typeof actualValue === "object";
|
|
7047
|
-
if (!expectedIsObj && !actualIsObj) {
|
|
7048
|
-
if (JSON.stringify(expectedValue) !== JSON.stringify(actualValue)) {
|
|
7049
|
-
outChanges.push({
|
|
7050
|
-
kind: "changed",
|
|
7051
|
-
path: pathSoFar,
|
|
7052
|
-
preview: `${String(expectedValue)} \u2192 ${String(actualValue)}`
|
|
7053
|
-
});
|
|
7054
|
-
}
|
|
7055
|
-
} else if (expectedIsObj && !actualIsObj) {
|
|
7056
|
-
outChanges.push({
|
|
7057
|
-
kind: "changed",
|
|
7058
|
-
path: pathSoFar,
|
|
7059
|
-
preview: "[object] \u2192 primitive"
|
|
7060
|
-
});
|
|
7061
|
-
} else if (!expectedIsObj && actualIsObj) {
|
|
7062
|
-
outChanges.push({
|
|
7063
|
-
kind: "changed",
|
|
7064
|
-
path: pathSoFar,
|
|
7065
|
-
preview: "primitive \u2192 [object]"
|
|
7066
|
-
});
|
|
7067
|
-
} else {
|
|
7068
|
-
const expectedKeys = new Set(Object.keys(expectedValue));
|
|
7069
|
-
const actualKeys = new Set(Object.keys(actualValue));
|
|
7070
|
-
for (const key of expectedKeys) {
|
|
7071
|
-
if (!actualKeys.has(key) && outChanges.length < limit) {
|
|
7072
|
-
outChanges.push({ kind: "removed", path: `${pathSoFar}.${key}` });
|
|
7073
|
-
}
|
|
7074
|
-
}
|
|
7075
|
-
for (const key of actualKeys) {
|
|
7076
|
-
if (!expectedKeys.has(key) && outChanges.length < limit) {
|
|
7077
|
-
outChanges.push({ kind: "added", path: `${pathSoFar}.${key}` });
|
|
7078
|
-
}
|
|
7079
|
-
}
|
|
7080
|
-
for (const key of expectedKeys) {
|
|
7081
|
-
if (actualKeys.has(key) && outChanges.length < limit) {
|
|
7082
|
-
queue.push({
|
|
7083
|
-
pathSoFar: `${pathSoFar}.${key}`,
|
|
7084
|
-
expectedValue: expectedValue[key],
|
|
7085
|
-
actualValue: actualValue[key]
|
|
7086
|
-
});
|
|
7087
|
-
}
|
|
7088
|
-
}
|
|
7089
|
-
}
|
|
7090
|
-
}
|
|
7091
|
-
return outChanges;
|
|
7092
|
-
};
|
|
7093
|
-
const importantMessages = (json) => {
|
|
7094
|
-
const msgs = [];
|
|
7095
|
-
try {
|
|
7096
|
-
const obj = isObject(json) ? json : {};
|
|
7097
|
-
const pushMaybe = (candidate) => {
|
|
7098
|
-
if (typeof candidate === "string" && candidate.trim()) {
|
|
7099
|
-
msgs.push(candidate);
|
|
7100
|
-
}
|
|
7101
|
-
};
|
|
7102
|
-
pushMaybe(obj.displayMessage);
|
|
7103
|
-
pushMaybe(obj.message);
|
|
7104
|
-
if (Array.isArray(obj.errors)) {
|
|
7105
|
-
for (const element of obj.errors) {
|
|
7106
|
-
pushMaybe(isObject(element) ? element.message : void 0);
|
|
7107
|
-
}
|
|
7108
|
-
}
|
|
7109
|
-
if (Array.isArray(obj.data)) {
|
|
7110
|
-
for (const element of obj.data) {
|
|
7111
|
-
pushMaybe(isObject(element) ? element.message : void 0);
|
|
7112
|
-
}
|
|
7113
|
-
}
|
|
7114
|
-
} catch {
|
|
7115
|
-
}
|
|
7116
|
-
return msgs.slice(0, 2);
|
|
7117
|
-
};
|
|
7118
|
-
const nameMatches = (leftName, rightName) => !!leftName && !!rightName && (leftName === rightName || leftName.includes(rightName) || rightName.includes(leftName));
|
|
7119
|
-
const corresponding = assertionEvents.find(
|
|
7120
|
-
(aevt) => aevt.testPath === file.testFilePath && nameMatches(aevt.currentTestName, assertion.title)
|
|
7121
|
-
) ?? assertion;
|
|
7122
|
-
const perTestSlice = inSameCtx(file.testFilePath, assertion.title);
|
|
7123
|
-
const nearByTime = eventsNear(
|
|
7124
|
-
httpSorted,
|
|
7125
|
-
corresponding?.timestampMs,
|
|
7126
|
-
file.testFilePath
|
|
7127
|
-
);
|
|
7128
|
-
const hasAbort = perTestSlice.some((event) => event.kind === "abort");
|
|
7129
|
-
const hasTransport = isTransportError(corresponding?.message) || hasAbort;
|
|
7130
|
-
const httpLikely = isHttpRelevant({
|
|
7131
|
-
assertion: corresponding,
|
|
7132
|
-
title: assertion.fullName,
|
|
7133
|
-
relPath: rel,
|
|
7134
|
-
httpCountInSameTest: perTestSlice.length || nearByTime.length,
|
|
7135
|
-
hasTransportSignal: hasTransport
|
|
7136
|
-
});
|
|
7137
|
-
if (!httpLikely) {
|
|
7138
|
-
} else {
|
|
7139
|
-
const expPreview = corresponding?.expectedPreview;
|
|
7140
|
-
const actPreview = corresponding?.actualPreview;
|
|
7141
|
-
const parsedExpected = safeParseJSON(expPreview);
|
|
7142
|
-
const parsedActual = safeParseJSON(actPreview);
|
|
7143
|
-
let corr = corresponding;
|
|
7144
|
-
if (!isHttpStatusNumber(corr.expectedNumber) && !isHttpStatusNumber(corr.receivedNumber)) {
|
|
7145
|
-
const inferred = inferHttpNumbersFromText(msgLines);
|
|
7146
|
-
if (isHttpStatusNumber(inferred.expectedNumber) || isHttpStatusNumber(inferred.receivedNumber)) {
|
|
7147
|
-
corr = { ...corr, ...inferred };
|
|
7148
|
-
}
|
|
7149
|
-
}
|
|
7150
|
-
const relevant = pickRelevantHttp(
|
|
7151
|
-
{
|
|
7152
|
-
timestampMs: corr?.timestampMs,
|
|
7153
|
-
expectedNumber: corr?.expectedNumber,
|
|
7154
|
-
receivedNumber: corr?.receivedNumber,
|
|
7155
|
-
matcher: corr?.matcher,
|
|
7156
|
-
message: corr?.message,
|
|
7157
|
-
stack: corr?.stack,
|
|
7158
|
-
testPath: file.testFilePath,
|
|
7159
|
-
currentTestName: assertion.title
|
|
7160
|
-
},
|
|
7161
|
-
httpSorted,
|
|
7162
|
-
{
|
|
7163
|
-
testPath: file.testFilePath,
|
|
7164
|
-
currentTestName: assertion.title,
|
|
7165
|
-
title: assertion.fullName
|
|
7166
|
-
}
|
|
7167
|
-
);
|
|
7168
|
-
if (hasTransport) {
|
|
7169
|
-
const tsBase = corresponding?.timestampMs ?? 0;
|
|
7170
|
-
const abortCandidates = perTestSlice.filter((event) => event.kind === "abort").sort((leftEvent, rightEvent) => {
|
|
7171
|
-
const deltaLeft = Math.abs(tsBase - (leftEvent.timestampMs ?? 0));
|
|
7172
|
-
const deltaRight = Math.abs(tsBase - (rightEvent.timestampMs ?? 0));
|
|
7173
|
-
return deltaLeft - deltaRight;
|
|
7174
|
-
});
|
|
7175
|
-
const [nearestAbort] = abortCandidates;
|
|
7176
|
-
if (nearestAbort) {
|
|
7177
|
-
out.push(
|
|
7178
|
-
" HTTP:",
|
|
7179
|
-
`
|
|
7180
|
-
${summarizeUrl(nearestAbort.method, nearestAbort.url, nearestAbort.route)} ${ansi.dim("->")} ${ansi.yellow("connection aborted")}`,
|
|
7181
|
-
(() => {
|
|
7182
|
-
const ms = nearestAbort.durationMs;
|
|
7183
|
-
return ms != null ? ` ${ansi.dim(`(${ms}ms)`)} ` : "";
|
|
7184
|
-
})(),
|
|
7185
|
-
"\n"
|
|
7186
|
-
);
|
|
7187
|
-
} else if (relevant) {
|
|
7188
|
-
} else if (HEADLAMP_HTTP_SHOW_MISS()) {
|
|
7189
|
-
out.push(
|
|
7190
|
-
" HTTP:",
|
|
7191
|
-
`
|
|
7192
|
-
${ansi.dim("Transport error; no matching HTTP exchange in window.")}`,
|
|
7193
|
-
"\n"
|
|
7194
|
-
);
|
|
7195
|
-
}
|
|
7196
|
-
}
|
|
7197
|
-
if (!hasTransport && relevant) {
|
|
7198
|
-
const parts = [];
|
|
7199
|
-
const where = summarizeUrl(relevant.method, relevant.url, relevant.route);
|
|
7200
|
-
const line1 = [
|
|
7201
|
-
" HTTP:",
|
|
7202
|
-
`
|
|
7203
|
-
${where} ${ansi.dim("->")} ${relevant.statusCode ?? "?"}`,
|
|
7204
|
-
(() => {
|
|
7205
|
-
const ms = relevant.durationMs;
|
|
7206
|
-
return typeof ms === "number" ? ` ${ansi.dim(`(${ms}ms)`)} ` : " ";
|
|
7207
|
-
})(),
|
|
7208
|
-
relevant.contentType ? ansi.dim(`(${relevant.contentType})`) : "",
|
|
7209
|
-
relevant.requestId ? ansi.dim(` reqId=${relevant.requestId}`) : ""
|
|
7210
|
-
].join("");
|
|
7211
|
-
const expVsAct = (() => {
|
|
7212
|
-
if (typeof corresponding?.expectedNumber === "number" || typeof corresponding?.receivedNumber === "number") {
|
|
7213
|
-
const exp = corresponding?.expectedNumber != null ? String(corresponding.expectedNumber) : "?";
|
|
7214
|
-
const got = corresponding?.receivedNumber != null ? String(corresponding.receivedNumber) : String(relevant.statusCode ?? "?");
|
|
7215
|
-
return `
|
|
7216
|
-
Expected: ${ansi.yellow(exp)} Received: ${ansi.yellow(got)}`;
|
|
7217
|
-
}
|
|
7218
|
-
return "";
|
|
7219
|
-
})();
|
|
7220
|
-
const whyLines = importantMessages(relevant.json).map((msg) => `
|
|
7221
|
-
Why: ${ansi.white(msg)}`).slice(0, 1).join("");
|
|
7222
|
-
const diffLines = (() => {
|
|
7223
|
-
const rightActual = parsedActual ?? relevant.json;
|
|
7224
|
-
if (!parsedExpected || !rightActual) {
|
|
7225
|
-
return "";
|
|
7226
|
-
}
|
|
7227
|
-
const changes = jsonDiff(parsedExpected, rightActual);
|
|
7228
|
-
if (!changes.length) {
|
|
7229
|
-
return "";
|
|
7230
|
-
}
|
|
7231
|
-
const head = "\n Diff:";
|
|
7232
|
-
const body = changes.map((change) => {
|
|
7233
|
-
const marker = change.kind === "added" ? "+" : change.kind === "removed" ? "-" : "~";
|
|
7234
|
-
const previewText = change.preview ? `: ${ansi.dim(change.preview)}` : "";
|
|
7235
|
-
return `
|
|
7236
|
-
${marker} ${change.path}${previewText}`;
|
|
7237
|
-
}).join("");
|
|
7238
|
-
return head + body;
|
|
7239
|
-
})();
|
|
7240
|
-
parts.push(line1, expVsAct, whyLines, diffLines, "\n");
|
|
7241
|
-
out.push(...parts.filter(Boolean));
|
|
7242
|
-
} else if (!hasTransport && !relevant && HEADLAMP_HTTP_SHOW_MISS()) {
|
|
7243
|
-
out.push(
|
|
7244
|
-
" HTTP:",
|
|
7245
|
-
`
|
|
7246
|
-
${ansi.dim("No relevant HTTP exchange found. (HEADLAMP_HTTP_MISS=0 to hide)")}`,
|
|
7247
|
-
"\n"
|
|
7248
|
-
);
|
|
7249
|
-
}
|
|
7250
|
-
}
|
|
7251
|
-
}
|
|
7252
|
-
const minimalInfo = msgLines.every((ln) => !ln.trim());
|
|
7253
|
-
if (minimalInfo) {
|
|
7254
|
-
try {
|
|
7255
|
-
out.push(...buildThrownSection(assertion.failureDetails || []));
|
|
7256
|
-
} catch {
|
|
7257
|
-
}
|
|
7258
|
-
}
|
|
7259
|
-
out.push(...buildConsoleSection(stripBridgeEventsFromConsole(file.console ?? null)));
|
|
7260
|
-
if (ctx.showStacks && stackPreview.length === 0) {
|
|
7261
|
-
const tail = mergedForStack.filter((ln) => isStackLine(stripAnsiSimple(ln))).slice(-4).map((ln) => ` ${colorStackLine(String(ln), ctx.projectHint)}`);
|
|
7262
|
-
if (tail.length) {
|
|
7263
|
-
out.push(ansi.dim(" Stack:"), ...tail, "");
|
|
7264
|
-
}
|
|
7265
|
-
}
|
|
7266
|
-
out.push(drawFailLine(), "");
|
|
7267
|
-
}
|
|
7268
|
-
}
|
|
7288
|
+
var renderFooter = (data) => {
|
|
7269
7289
|
const failedCount = data.aggregated.numFailedTests;
|
|
7270
|
-
|
|
7271
|
-
|
|
7272
|
-
|
|
7273
|
-
|
|
7290
|
+
return [
|
|
7291
|
+
drawRule(BackgroundColors.Failure(ansi.white(` Failed Tests ${failedCount} `))),
|
|
7292
|
+
"",
|
|
7293
|
+
vitestFooter(data.aggregated)
|
|
7294
|
+
];
|
|
7274
7295
|
};
|
|
7296
|
+
var renderVitestFromJestJSON = (data, ctx, opts) => pipe(
|
|
7297
|
+
concat(
|
|
7298
|
+
renderRunHeader({ ctx, onlyFailures: Boolean(opts?.onlyFailures) }),
|
|
7299
|
+
...data.testResults.map(
|
|
7300
|
+
(file) => renderFileBlock(file, { ctx, onlyFailures: Boolean(opts?.onlyFailures) })
|
|
7301
|
+
),
|
|
7302
|
+
renderFooter(data)
|
|
7303
|
+
),
|
|
7304
|
+
joinLines
|
|
7305
|
+
);
|
|
7275
7306
|
|
|
7276
7307
|
// src/lib/formatter/bridge/tryBridgeFallback.ts
|
|
7277
7308
|
var tryBridgeFallback = (raw, ctx, opts) => {
|
|
@@ -8016,7 +8047,8 @@ var program = async () => {
|
|
|
8016
8047
|
coverageMaxFiles: coverageMaxFilesArg,
|
|
8017
8048
|
coverageMaxHotspots: coverageMaxHotspotsArg,
|
|
8018
8049
|
coveragePageFit,
|
|
8019
|
-
changed
|
|
8050
|
+
changed,
|
|
8051
|
+
changedDepth
|
|
8020
8052
|
} = deriveArgs(argv);
|
|
8021
8053
|
const getChangedFiles = async (mode, cwd) => {
|
|
8022
8054
|
const collect = async (cmd, args) => {
|
|
@@ -8488,7 +8520,7 @@ var program = async () => {
|
|
|
8488
8520
|
resolutionCache.set(key, resolved);
|
|
8489
8521
|
return resolved;
|
|
8490
8522
|
};
|
|
8491
|
-
const MAX_DEPTH = 5;
|
|
8523
|
+
const MAX_DEPTH = Number.isFinite(Number(changedDepth)) && Number(changedDepth) > 0 ? Number(changedDepth) : 5;
|
|
8492
8524
|
const seen = /* @__PURE__ */ new Set();
|
|
8493
8525
|
const matchesTransitively = async (absTestPath, depth) => {
|
|
8494
8526
|
if (depth > MAX_DEPTH) {
|
|
@@ -8899,7 +8931,6 @@ export {
|
|
|
8899
8931
|
formatJestOutputVitest,
|
|
8900
8932
|
isFilesObject,
|
|
8901
8933
|
isStackLine,
|
|
8902
|
-
isTransportError,
|
|
8903
8934
|
isTruthy,
|
|
8904
8935
|
linesFromDetails,
|
|
8905
8936
|
linkifyPadded,
|
|
@@ -8910,8 +8941,6 @@ export {
|
|
|
8910
8941
|
mkPrettyFns,
|
|
8911
8942
|
osc8,
|
|
8912
8943
|
parseActionsFromTokens,
|
|
8913
|
-
parseMethodPathFromTitle,
|
|
8914
|
-
pickRelevantHttp,
|
|
8915
8944
|
preferredEditorHref,
|
|
8916
8945
|
printCompactCoverage,
|
|
8917
8946
|
printDetailedCoverage,
|
|
@@ -8926,7 +8955,6 @@ export {
|
|
|
8926
8955
|
renderTable,
|
|
8927
8956
|
renderVitestFromJestJSON,
|
|
8928
8957
|
resolveImportWithRoot,
|
|
8929
|
-
routeSimilarityScore,
|
|
8930
8958
|
rule,
|
|
8931
8959
|
runJestBootstrap,
|
|
8932
8960
|
stripAnsiSimple,
|