headlamp 0.1.13 → 0.1.14

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/index.js CHANGED
@@ -3675,11 +3675,11 @@ var tintPct = (pct) => {
3675
3675
  var bar = (pct, width = DEFAULT_BAR_WIDTH) => {
3676
3676
  const filled = Math.round(pct / PERCENT_MAX * width);
3677
3677
  const solid = supportsUnicode() ? "\u2588" : "#";
3678
- const empty = supportsUnicode() ? "\u2591" : "-";
3678
+ const empty2 = supportsUnicode() ? "\u2591" : "-";
3679
3679
  const good = tintPct(pct);
3680
3680
  const MIN_REMAINING = 0;
3681
3681
  return `${good(solid.repeat(filled))}${ansi.gray(
3682
- empty.repeat(Math.max(MIN_REMAINING, width - filled))
3682
+ empty2.repeat(Math.max(MIN_REMAINING, width - filled))
3683
3683
  )}`;
3684
3684
  };
3685
3685
 
@@ -6601,11 +6601,25 @@ var extractBridgePath2 = (raw, cwd) => {
6601
6601
  const jsonPath = (matches[matches.length - 1][1] ?? "").trim().replace(/^['"`]|['"`]$/g, "");
6602
6602
  return path10.isAbsolute(jsonPath) ? jsonPath : path10.resolve(cwd, jsonPath).replace(/\\/g, "/");
6603
6603
  };
6604
- var isTransportError = (msg) => {
6605
- const lowercaseMessage = (msg ?? "").toLowerCase();
6606
- return /\bsocket hang up\b|\beconnreset\b|\betimedout\b|\beconnrefused\b|\bwrite epipe\b/.test(
6607
- lowercaseMessage
6608
- );
6604
+
6605
+ // src/lib/formatter/bridge/utils.ts
6606
+ var import_json52 = __toESM(require_lib(), 1);
6607
+
6608
+ // src/lib/formatter/bridge/http.ts
6609
+ var envNumber = (name, fallback) => {
6610
+ const parsed = Number(process.env[name]);
6611
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
6612
+ };
6613
+ var HEADLAMP_HTTP_WINDOW_MS = () => envNumber("HEADLAMP_HTTP_WINDOW_MS", 3e3);
6614
+ var HEADLAMP_HTTP_STRICT_WINDOW_MS = () => envNumber("HEADLAMP_HTTP_STRICT_WINDOW_MS", 600);
6615
+ var HEADLAMP_HTTP_MIN_SCORE = () => envNumber("HEADLAMP_HTTP_MIN_SCORE", 1200);
6616
+ var HEADLAMP_HTTP_DIFF_LIMIT = () => envNumber("HEADLAMP_HTTP_DIFF_LIMIT", 6);
6617
+ var HEADLAMP_HTTP_SHOW_MISS = () => process.env.HEADLAMP_HTTP_MISS === "1";
6618
+ var asHttpList = (candidateValue) => Array.isArray(candidateValue) ? candidateValue : [];
6619
+ var summarizeUrl = (method, url, route) => {
6620
+ const base = route || url || "";
6621
+ const qs = url && url.includes("?") ? ` ? ${url.split("?")[1]}` : "";
6622
+ return [method || "", base, qs].filter(Boolean).join(" ").trim();
6609
6623
  };
6610
6624
  var parseMethodPathFromTitle = (title) => {
6611
6625
  if (!title) {
@@ -6614,6 +6628,42 @@ var parseMethodPathFromTitle = (title) => {
6614
6628
  const matchResult = title.match(/\b(GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS)\s+([^\s)]+)/i);
6615
6629
  return matchResult ? { method: matchResult[1]?.toUpperCase(), path: matchResult[2] } : {};
6616
6630
  };
6631
+ var eventsNear = (http, ts, testPath, windowMs = HEADLAMP_HTTP_WINDOW_MS()) => {
6632
+ if (typeof ts !== "number" || !Number.isFinite(ts)) {
6633
+ return [];
6634
+ }
6635
+ return http.filter((event) => {
6636
+ const timeOk = typeof event.timestampMs === "number" && Math.abs(event.timestampMs - ts) <= windowMs;
6637
+ const pathOk = !testPath || event.testPath === testPath;
6638
+ return timeOk && pathOk;
6639
+ });
6640
+ };
6641
+ var isHttpStatusNumber = (statusNumber) => typeof statusNumber === "number" && statusNumber >= 100 && statusNumber <= 599;
6642
+ var inferHttpNumbersFromText = (lines) => {
6643
+ const text = lines.join("\n");
6644
+ const match = text.match(/Expected:\s*(\d{3})[\s\S]*?Received:\s*(\d{3})/i);
6645
+ if (match) {
6646
+ return { expectedNumber: Number(match[1]), receivedNumber: Number(match[2]) };
6647
+ }
6648
+ return {};
6649
+ };
6650
+ var titleSuggestsHttp = (title) => {
6651
+ const { method, path: parsedPath } = parseMethodPathFromTitle(title);
6652
+ return Boolean(method || parsedPath && parsedPath.startsWith("/"));
6653
+ };
6654
+ var hasStatusSemantics = (assertionLike) => {
6655
+ if (!assertionLike) {
6656
+ return false;
6657
+ }
6658
+ if (isHttpStatusNumber(assertionLike.expectedNumber) || isHttpStatusNumber(assertionLike.receivedNumber)) {
6659
+ return true;
6660
+ }
6661
+ const combinedRaw = `${assertionLike.matcher ?? ""} ${assertionLike.message ?? ""}`;
6662
+ const combinedMessage = combinedRaw.toLowerCase();
6663
+ return /\bstatus(code)?\b|\btohaves(tatus|tatuscode)\b/.test(combinedMessage);
6664
+ };
6665
+ var fileSuggestsHttp = (relPath2) => /(?:^|\/)(routes?|api|controllers?|e2e|integration)(?:\/|\.test\.)/i.test(relPath2);
6666
+ var isHttpRelevant = (ctx) => ctx.hasTransportSignal || ctx.httpCountInSameTest > 0 || titleSuggestsHttp(ctx.title) || hasStatusSemantics(ctx.assertion) || fileSuggestsHttp(ctx.relPath);
6617
6667
  var routeSimilarityScore = (hint, evt) => {
6618
6668
  if (!hint.path && !hint.method) {
6619
6669
  return 0;
@@ -6634,15 +6684,12 @@ var routeSimilarityScore = (hint, evt) => {
6634
6684
  }
6635
6685
  return methodOk * 10;
6636
6686
  };
6637
- var envNumber = (name, fallback) => {
6638
- const parsed = Number(process.env[name]);
6639
- return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
6687
+ var isTransportError = (msg) => {
6688
+ const lowercaseMessage = (msg ?? "").toLowerCase();
6689
+ return /\bsocket hang up\b|\beconnreset\b|\betimedout\b|\beconnrefused\b|\bwrite epipe\b/.test(
6690
+ lowercaseMessage
6691
+ );
6640
6692
  };
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
6693
  var scoreHttpForAssertion = (assertion, titleHint) => (candidateEvent) => {
6647
6694
  const tsA = assertion.timestampMs;
6648
6695
  const tsH = candidateEvent.timestampMs;
@@ -6688,7 +6735,386 @@ var pickRelevantHttp = (assertion, http, ctx) => {
6688
6735
  };
6689
6736
 
6690
6737
  // src/lib/formatter/bridge/utils.ts
6691
- var import_json52 = __toESM(require_lib(), 1);
6738
+ var colorTokens2 = {
6739
+ pass: Colors.Success,
6740
+ fail: Colors.Failure,
6741
+ skip: Colors.Skip,
6742
+ todo: Colors.Todo,
6743
+ passPill: (text) => BackgroundColors.Success(ansi.white(` ${text} `)),
6744
+ failPill: (text) => BackgroundColors.Failure(ansi.white(` ${text} `))
6745
+ };
6746
+ var joinLines = (chunks) => chunks.join("\n");
6747
+ var empty = [];
6748
+ var concat = (...xs) => xs.flat();
6749
+ var by = (keySelector) => (left, right) => keySelector(left) - keySelector(right);
6750
+ var isObject = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
6751
+ var stripBridgeEventsFromConsole = (maybeConsole) => {
6752
+ if (!Array.isArray(maybeConsole)) {
6753
+ return maybeConsole;
6754
+ }
6755
+ return maybeConsole.filter((entry) => {
6756
+ try {
6757
+ const raw = Array.isArray(entry.message) ? entry.message.map(String).join(" ") : String(entry.message ?? "");
6758
+ return !raw.includes("[JEST-BRIDGE-EVENT]");
6759
+ } catch {
6760
+ return true;
6761
+ }
6762
+ });
6763
+ };
6764
+ var parseBridgeConsole = (consoleEntries) => {
6765
+ const http = [];
6766
+ const assertions = [];
6767
+ if (!Array.isArray(consoleEntries)) {
6768
+ return { http, assertions };
6769
+ }
6770
+ for (const entry of consoleEntries) {
6771
+ const rec = entry;
6772
+ const rawMsgVal = rec && typeof rec.message !== "undefined" ? rec.message : "";
6773
+ const raw = Array.isArray(rawMsgVal) ? rawMsgVal.map(String).join(" ") : String(rawMsgVal ?? "");
6774
+ if (!raw.includes("[JEST-BRIDGE-EVENT]")) {
6775
+ continue;
6776
+ }
6777
+ const jsonText = raw.split("[JEST-BRIDGE-EVENT]").pop()?.trim() ?? "";
6778
+ try {
6779
+ const evt = import_json52.default.parse(jsonText);
6780
+ const type = evt?.type;
6781
+ if (type === "httpResponse") {
6782
+ const timestampMs = Number(evt.timestampMs ?? Date.now());
6783
+ http.push({
6784
+ kind: "response",
6785
+ timestampMs,
6786
+ method: evt.method,
6787
+ url: evt.url,
6788
+ route: evt.route,
6789
+ statusCode: evt.statusCode,
6790
+ durationMs: evt.durationMs,
6791
+ contentType: evt.contentType,
6792
+ requestId: evt.requestId,
6793
+ json: evt.json,
6794
+ bodyPreview: evt.bodyPreview,
6795
+ testPath: evt.testPath,
6796
+ currentTestName: evt.currentTestName
6797
+ });
6798
+ } else if (type === "httpAbort") {
6799
+ http.push({
6800
+ kind: "abort",
6801
+ timestampMs: Number(evt.timestampMs ?? Date.now()),
6802
+ method: evt.method,
6803
+ url: evt.url,
6804
+ route: evt.route,
6805
+ durationMs: evt.durationMs,
6806
+ testPath: evt.testPath,
6807
+ currentTestName: evt.currentTestName
6808
+ });
6809
+ } else if (type === "httpResponseBatch") {
6810
+ const list = asHttpList(evt?.events);
6811
+ for (const item of list) {
6812
+ const anyItem = item;
6813
+ http.push({
6814
+ timestampMs: Number(anyItem.timestampMs ?? Date.now()),
6815
+ method: anyItem.method,
6816
+ url: anyItem.url,
6817
+ route: anyItem.route,
6818
+ statusCode: anyItem.statusCode,
6819
+ durationMs: anyItem.durationMs,
6820
+ contentType: anyItem.contentType,
6821
+ requestId: anyItem.requestId,
6822
+ json: anyItem.json,
6823
+ bodyPreview: anyItem.bodyPreview,
6824
+ testPath: evt.testPath,
6825
+ currentTestName: evt.currentTestName
6826
+ });
6827
+ }
6828
+ } else if (type === "assertionFailure") {
6829
+ assertions.push({
6830
+ timestampMs: typeof evt.timestampMs === "number" ? evt.timestampMs : void 0,
6831
+ matcher: evt.matcher,
6832
+ expectedNumber: typeof evt.expectedNumber === "number" ? evt.expectedNumber : void 0,
6833
+ receivedNumber: typeof evt.receivedNumber === "number" ? evt.receivedNumber : void 0,
6834
+ message: typeof evt.message === "string" ? evt.message : void 0,
6835
+ stack: typeof evt.stack === "string" ? evt.stack : void 0,
6836
+ testPath: evt.testPath,
6837
+ currentTestName: evt.currentTestName,
6838
+ expectedPreview: typeof evt.expectedPreview === "string" ? evt.expectedPreview : void 0,
6839
+ actualPreview: typeof evt.actualPreview === "string" ? evt.actualPreview : void 0
6840
+ });
6841
+ }
6842
+ } catch {
6843
+ }
6844
+ }
6845
+ return { http, assertions };
6846
+ };
6847
+ var renderRunHeader = ({ ctx, onlyFailures }) => onlyFailures ? empty : [`${BackgroundColors.Run(ansi.white(" RUN "))} ${ansi.dim(ctx.cwd)}`, ""];
6848
+ var renderPerFileOverviewBlock = (rel, testResults, onlyFailures) => onlyFailures ? empty : buildPerFileOverview(rel, testResults);
6849
+ var renderFileBadge = (rel, failedCount, onlyFailures) => onlyFailures && failedCount === 0 ? empty : [buildFileBadgeLine(rel, failedCount)];
6850
+ var condenseBlankRuns = (lines) => {
6851
+ const out = [];
6852
+ let lastBlank = false;
6853
+ for (const ln of lines) {
6854
+ const isBlank = !stripAnsiSimple(String(ln ?? "")).trim();
6855
+ if (isBlank) {
6856
+ if (!lastBlank) {
6857
+ out.push("");
6858
+ }
6859
+ lastBlank = true;
6860
+ } else {
6861
+ out.push(String(ln));
6862
+ lastBlank = false;
6863
+ }
6864
+ }
6865
+ return out;
6866
+ };
6867
+ var mergeMsgLines = (primaryRaw, detailMsgs) => {
6868
+ const primary = primaryRaw.trim() ? primaryRaw.split(/\r?\n/) : [];
6869
+ const key = (line) => stripAnsiSimple(line).trim();
6870
+ const seen = new Set(primary.map(key));
6871
+ const merged = [...primary];
6872
+ for (const msg of detailMsgs) {
6873
+ const msgKey = key(String(msg ?? ""));
6874
+ if (!msgKey) {
6875
+ continue;
6876
+ }
6877
+ if (!seen.has(msgKey)) {
6878
+ merged.push(msg);
6879
+ seen.add(msgKey);
6880
+ }
6881
+ }
6882
+ return condenseBlankRuns(merged);
6883
+ };
6884
+ var renderFileLevelFailure = (file, ctx) => {
6885
+ if (!(file.failureMessage || file.testExecError)) {
6886
+ return empty;
6887
+ }
6888
+ const base = linesFromDetails(file.failureDetails);
6889
+ const exec = linesFromDetails(
6890
+ Array.isArray(file.testExecError) ? file.testExecError : [file.testExecError]
6891
+ );
6892
+ const combinedDetails = {
6893
+ stacks: [...base.stacks, ...exec.stacks],
6894
+ messages: [...base.messages, ...exec.messages]
6895
+ };
6896
+ const msgLines = mergeMsgLines(file.failureMessage || "", combinedDetails.messages);
6897
+ const mergedForStack = collapseStacks([...msgLines, ...combinedDetails.stacks]);
6898
+ const synthLoc = deepestProjectLoc(mergedForStack, ctx.projectHint);
6899
+ 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)}`) : [];
6900
+ const code = buildCodeFrameSection(msgLines, ctx, synthLoc);
6901
+ const pretty = buildPrettyDiffSection(file.failureDetails, msgLines);
6902
+ const message = buildMessageSection(msgLines, combinedDetails, ctx, {
6903
+ suppressDiff: pretty.length > 0,
6904
+ stackPreview
6905
+ });
6906
+ const consoleBlock = buildConsoleSection(stripBridgeEventsFromConsole(file.console ?? null));
6907
+ const stackTail = ctx.showStacks && stackPreview.length === 0 ? (() => {
6908
+ const tail = mergedForStack.filter((ln) => isStackLine(stripAnsiSimple(ln))).slice(-4).map((ln) => ` ${colorStackLine(String(ln), ctx.projectHint)}`);
6909
+ return tail.length ? [ansi.dim(" Stack:"), ...tail, ""] : empty;
6910
+ })() : empty;
6911
+ return concat(code, pretty, message, consoleBlock, stackTail);
6912
+ };
6913
+ var renderHttpCard = (args) => {
6914
+ const { file, relPath: rel, assertion, assertionEvents, httpSorted } = args;
6915
+ const nameMatches = (left, right) => !!left && !!right && (left === right || left.includes(right) || right.includes(left));
6916
+ const inSameCtx = (testPath, testName) => httpSorted.filter(
6917
+ (event) => event.testPath === testPath && nameMatches(event.currentTestName, testName)
6918
+ );
6919
+ const perTestSlice = inSameCtx(file.testFilePath, assertion.fullName);
6920
+ const corresponding = assertionEvents.find(
6921
+ (event) => event.testPath === file.testFilePath && nameMatches(event.currentTestName, assertion.fullName)
6922
+ ) ?? assertion;
6923
+ const nearByTime = eventsNear(
6924
+ httpSorted,
6925
+ corresponding?.timestampMs,
6926
+ file.testFilePath
6927
+ );
6928
+ const hasAbort = perTestSlice.some((event) => event.kind === "abort");
6929
+ const hasTransport = isTransportError(corresponding?.message) || hasAbort;
6930
+ const httpLikely = isHttpRelevant({
6931
+ assertion: corresponding,
6932
+ title: assertion.fullName,
6933
+ relPath: rel,
6934
+ httpCountInSameTest: perTestSlice.length || nearByTime.length,
6935
+ hasTransportSignal: hasTransport
6936
+ });
6937
+ if (!httpLikely) {
6938
+ return empty;
6939
+ }
6940
+ const HEADLAMP_HTTP_DIFF_LIMIT_LOCAL = () => HEADLAMP_HTTP_DIFF_LIMIT();
6941
+ const safeParseJSON = (text) => {
6942
+ try {
6943
+ return text ? import_json52.default.parse(text) : void 0;
6944
+ } catch {
6945
+ return void 0;
6946
+ }
6947
+ };
6948
+ const expPreview = corresponding?.expectedPreview;
6949
+ const actPreview = corresponding?.actualPreview;
6950
+ const parsedExpected = safeParseJSON(expPreview);
6951
+ const parsedActual = safeParseJSON(actPreview);
6952
+ let corr = corresponding;
6953
+ if (!isHttpStatusNumber(corr.expectedNumber) && !isHttpStatusNumber(corr.receivedNumber)) {
6954
+ const inferred = inferHttpNumbersFromText(
6955
+ (assertion.failureMessages?.join("\n") || file.failureMessage || "").split("\n")
6956
+ );
6957
+ if (isHttpStatusNumber(inferred.expectedNumber) || isHttpStatusNumber(inferred.receivedNumber)) {
6958
+ corr = { ...corr, ...inferred };
6959
+ }
6960
+ }
6961
+ const relevant = pickRelevantHttp(
6962
+ {
6963
+ timestampMs: corr?.timestampMs,
6964
+ expectedNumber: corr?.expectedNumber,
6965
+ receivedNumber: corr?.receivedNumber,
6966
+ matcher: corr?.matcher,
6967
+ message: corr?.message,
6968
+ stack: corr?.stack,
6969
+ testPath: file.testFilePath,
6970
+ currentTestName: assertion.title
6971
+ },
6972
+ httpSorted,
6973
+ {
6974
+ testPath: file.testFilePath,
6975
+ currentTestName: assertion.fullName,
6976
+ title: assertion.fullName
6977
+ }
6978
+ );
6979
+ if (hasTransport) {
6980
+ const tsBase = corr?.timestampMs ?? 0;
6981
+ const [nearestAbort] = perTestSlice.filter((event) => event.kind === "abort").sort(
6982
+ (left, right) => Math.abs(tsBase - (left.timestampMs ?? 0)) - Math.abs(tsBase - (right.timestampMs ?? 0))
6983
+ );
6984
+ if (nearestAbort) {
6985
+ const ms = nearestAbort.durationMs;
6986
+ return [
6987
+ " HTTP:",
6988
+ `
6989
+ ${summarizeUrl(nearestAbort.method, nearestAbort.url, nearestAbort.route)} ${ansi.dim("->")} ${ansi.yellow("connection aborted")}`,
6990
+ ms != null ? ` ${ansi.dim(`(${ms}ms)`)} ` : "",
6991
+ "\n"
6992
+ ];
6993
+ }
6994
+ return HEADLAMP_HTTP_SHOW_MISS() ? [
6995
+ " HTTP:",
6996
+ `
6997
+ ${ansi.dim("Transport error; no matching HTTP exchange in window.")}`,
6998
+ "\n"
6999
+ ] : empty;
7000
+ }
7001
+ if (!relevant) {
7002
+ return HEADLAMP_HTTP_SHOW_MISS() ? [
7003
+ " HTTP:",
7004
+ `
7005
+ ${ansi.dim("No relevant HTTP exchange found. (HEADLAMP_HTTP_MISS=0 to hide)")}`,
7006
+ "\n"
7007
+ ] : empty;
7008
+ }
7009
+ const jsonDiff = (expected, actual, limit = HEADLAMP_HTTP_DIFF_LIMIT_LOCAL()) => {
7010
+ const out = [];
7011
+ const queue = [
7012
+ { pathSoFar: "$", expectedValue: expected, actualValue: actual }
7013
+ ];
7014
+ while (queue.length && out.length < limit) {
7015
+ const { pathSoFar, expectedValue, actualValue } = queue.shift();
7016
+ const expectedIsObject = expectedValue && typeof expectedValue === "object";
7017
+ const actualIsObject = actualValue && typeof actualValue === "object";
7018
+ if (!expectedIsObject && !actualIsObject) {
7019
+ if (JSON.stringify(expectedValue) !== JSON.stringify(actualValue)) {
7020
+ out.push({
7021
+ kind: "changed",
7022
+ path: pathSoFar,
7023
+ preview: `${String(expectedValue)} \u2192 ${String(actualValue)}`
7024
+ });
7025
+ }
7026
+ } else if (expectedIsObject && !actualIsObject) {
7027
+ out.push({ kind: "changed", path: pathSoFar, preview: "[object] \u2192 primitive" });
7028
+ } else if (!expectedIsObject && actualIsObject) {
7029
+ out.push({ kind: "changed", path: pathSoFar, preview: "primitive \u2192 [object]" });
7030
+ } else {
7031
+ const expectedKeys = new Set(Object.keys(expectedValue));
7032
+ const actualKeys = new Set(Object.keys(actualValue));
7033
+ for (const key of expectedKeys) {
7034
+ if (!actualKeys.has(key) && out.length < limit) {
7035
+ out.push({ kind: "removed", path: `${pathSoFar}.${key}` });
7036
+ }
7037
+ }
7038
+ for (const key of actualKeys) {
7039
+ if (!expectedKeys.has(key) && out.length < limit) {
7040
+ out.push({ kind: "added", path: `${pathSoFar}.${key}` });
7041
+ }
7042
+ }
7043
+ for (const key of expectedKeys) {
7044
+ if (actualKeys.has(key) && out.length < limit) {
7045
+ queue.push({
7046
+ pathSoFar: `${pathSoFar}.${key}`,
7047
+ expectedValue: expectedValue[key],
7048
+ actualValue: actualValue[key]
7049
+ });
7050
+ }
7051
+ }
7052
+ }
7053
+ }
7054
+ return out;
7055
+ };
7056
+ const importantMessages = (json) => {
7057
+ const msgs = [];
7058
+ try {
7059
+ const obj = isObject(json) ? json : {};
7060
+ const push = (msg) => {
7061
+ if (typeof msg === "string" && msg.trim()) {
7062
+ msgs.push(msg);
7063
+ }
7064
+ };
7065
+ push(obj.displayMessage);
7066
+ push(obj.message);
7067
+ if (Array.isArray(obj.errors)) {
7068
+ for (const event of obj.errors) {
7069
+ push(isObject(event) ? event.message : void 0);
7070
+ }
7071
+ }
7072
+ if (Array.isArray(obj.data)) {
7073
+ for (const event of obj.data) {
7074
+ push(isObject(event) ? event.message : void 0);
7075
+ }
7076
+ }
7077
+ } catch {
7078
+ }
7079
+ return msgs.slice(0, 2);
7080
+ };
7081
+ const where = summarizeUrl(relevant.method, relevant.url, relevant.route);
7082
+ const header = [
7083
+ " HTTP:",
7084
+ `
7085
+ ${where} ${ansi.dim("->")} ${relevant.statusCode ?? "?"}`,
7086
+ typeof relevant.durationMs === "number" ? ` ${ansi.dim(`(${relevant.durationMs}ms)`)} ` : " ",
7087
+ relevant.contentType ? ansi.dim(`(${relevant.contentType})`) : "",
7088
+ relevant.requestId ? ansi.dim(` reqId=${relevant.requestId}`) : ""
7089
+ ].join("");
7090
+ const expVsAct = typeof corr?.expectedNumber === "number" || typeof corr?.receivedNumber === "number" ? (() => {
7091
+ const exp = corr?.expectedNumber != null ? String(corr.expectedNumber) : "?";
7092
+ const got = corr?.receivedNumber != null ? String(corr.receivedNumber) : String(relevant.statusCode ?? "?");
7093
+ return `
7094
+ Expected: ${ansi.yellow(exp)} Received: ${ansi.yellow(got)}`;
7095
+ })() : "";
7096
+ const why = importantMessages(parsedActual ?? relevant.json).map((msg) => `
7097
+ Why: ${ansi.white(msg)}`).slice(0, 1).join("") || "";
7098
+ const diff = (() => {
7099
+ const rightActual = parsedActual ?? relevant.json;
7100
+ if (!parsedExpected || !rightActual) {
7101
+ return "";
7102
+ }
7103
+ const changes = jsonDiff(parsedExpected, rightActual);
7104
+ if (!changes.length) {
7105
+ return "";
7106
+ }
7107
+ const body = changes.map((change) => {
7108
+ const marker = change.kind === "added" ? "+" : change.kind === "removed" ? "-" : "~";
7109
+ const preview = change.preview ? `: ${ansi.dim(change.preview)}` : "";
7110
+ return `
7111
+ ${marker} ${change.path}${preview}`;
7112
+ }).join("");
7113
+ return `
7114
+ Diff:${body}`;
7115
+ })();
7116
+ return [header, expVsAct, why, diff, "\n"].filter(Boolean);
7117
+ };
6692
7118
  var coerceJestJsonToBridge = (raw) => {
6693
7119
  if (raw && typeof raw === "object" && "aggregated" in raw) {
6694
7120
  return raw;
@@ -6731,71 +7157,99 @@ var coerceJestJsonToBridge = (raw) => {
6731
7157
  }
6732
7158
  };
6733
7159
  };
6734
- var colorTokens2 = {
6735
- pass: Colors.Success,
6736
- fail: Colors.Failure,
6737
- skip: Colors.Skip,
6738
- todo: Colors.Todo,
6739
- passPill: (text) => BackgroundColors.Success(ansi.white(` ${text} `)),
6740
- failPill: (text) => BackgroundColors.Failure(ansi.white(` ${text} `))
6741
- };
6742
- var by = (keySelector) => (left, right) => keySelector(left) - keySelector(right);
6743
- var isObject = (candidateValue) => !!candidateValue && typeof candidateValue === "object";
6744
- var asHttpList = (candidateValue) => Array.isArray(candidateValue) ? candidateValue : [];
6745
- var summarizeUrl = (method, url, route) => {
6746
- const base = route || url || "";
6747
- const qs = url && url.includes("?") ? ` ? ${url.split("?")[1]}` : "";
6748
- return [method || "", base, qs].filter(Boolean).join(" ").trim();
6749
- };
6750
- var stripBridgeEventsFromConsole = (maybeConsole) => {
6751
- if (!Array.isArray(maybeConsole)) {
6752
- return maybeConsole;
6753
- }
6754
- return maybeConsole.filter((entry) => {
7160
+ var renderFailedAssertion = (args) => {
7161
+ const { file, relPath: rel, assertion, ctx, assertionEvents, httpSorted } = args;
7162
+ const header = `${rel} > ${assertion.fullName}`;
7163
+ const bullet = (text) => `${Colors.Failure("\xD7")} ${ansi.white(text)}`;
7164
+ const failureMessage = file.failureMessage || "";
7165
+ const detailMsgs = linesFromDetails(assertion.failureDetails || file.failureDetails).messages;
7166
+ const primaryBlock = assertion.failureMessages?.length ? assertion.failureMessages.join("\n") : failureMessage;
7167
+ const messagesArray = mergeMsgLines(primaryBlock, detailMsgs);
7168
+ const details = linesFromDetails(assertion.failureDetails || file.failureDetails);
7169
+ const mergedForStack = collapseStacks([...messagesArray, ...details.stacks]);
7170
+ const deepestLoc = deepestProjectLoc(mergedForStack, ctx.projectHint);
7171
+ const locLink = deepestLoc ? (() => {
7172
+ const href = preferredEditorHref(deepestLoc.file, deepestLoc.line, ctx.editorCmd);
7173
+ const base = `${deepestLoc.file.split("/").pop()}:${deepestLoc.line}`;
7174
+ return osc8(base, href);
7175
+ })() : void 0;
7176
+ const headerLine = `${ansi.white(header)}${locLink ? ` ${ansi.dim(`(${locLink})`)}` : ""}`;
7177
+ const msgLines = messagesArray.join("\n").split("\n");
7178
+ const assertFallback = deepestLoc || assertion.location && { file: file.testFilePath, line: assertion.location.line };
7179
+ const matcherMsg = (() => {
6755
7180
  try {
6756
- const raw = Array.isArray(entry.message) ? entry.message.map(String).join(" ") : String(entry.message ?? "");
6757
- return !raw.includes("[JEST-BRIDGE-EVENT]");
7181
+ const arr = assertion.failureDetails || file.failureDetails;
7182
+ if (!arr) {
7183
+ return empty;
7184
+ }
7185
+ for (const detailEntry of arr) {
7186
+ const obj = detailEntry && typeof detailEntry === "object" ? detailEntry : null;
7187
+ const mr = obj && obj.matcherResult && typeof obj.matcherResult === "object" ? obj.matcherResult : null;
7188
+ if (mr && typeof mr.message === "string" && mr.message.trim()) {
7189
+ const name = typeof mr.matcherName === "string" ? mr.matcherName : "";
7190
+ const matcherHeader = name ? ` ${ansi.bold("Matcher:")} ${ansi.yellow(name)}` : "";
7191
+ const bodyHeader = ` ${ansi.bold("Message:")}`;
7192
+ const body = String(mr.message).split(/\r?\n/).slice(0, 6).map((ln) => ` ${ansi.yellow(ln)}`);
7193
+ return [matcherHeader, bodyHeader, ...body, ""].filter(Boolean);
7194
+ }
7195
+ }
6758
7196
  } catch {
6759
- return true;
6760
7197
  }
7198
+ return empty;
7199
+ })();
7200
+ const code = concat(
7201
+ ["", drawFailLine(), bullet(headerLine), ""],
7202
+ buildCodeFrameSection(msgLines, ctx, assertFallback || void 0),
7203
+ [""]
7204
+ );
7205
+ const pretty = buildPrettyDiffSection(assertion.failureDetails || file.failureDetails, msgLines);
7206
+ const hasPretty = pretty.length > 0;
7207
+ 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)}`) : [];
7208
+ const message = buildMessageSection(msgLines, details, ctx, {
7209
+ suppressDiff: hasPretty,
7210
+ stackPreview
6761
7211
  });
7212
+ const httpCard = renderHttpCard({ file, relPath: rel, assertion, assertionEvents, httpSorted });
7213
+ const minimalInfo = msgLines.every((ln) => !ln.trim());
7214
+ const thrown = minimalInfo ? (() => {
7215
+ try {
7216
+ return buildThrownSection(assertion.failureDetails || []);
7217
+ } catch {
7218
+ return empty;
7219
+ }
7220
+ })() : empty;
7221
+ const consoleBlock = buildConsoleSection(stripBridgeEventsFromConsole(file.console ?? null));
7222
+ const stackTail = ctx.showStacks && stackPreview.length === 0 ? (() => {
7223
+ const merged = collapseStacks([...msgLines, ...details.stacks]);
7224
+ const tail = collapseStacks(merged).filter((ln) => isStackLine(stripAnsiSimple(ln))).slice(-4).map((ln) => ` ${colorStackLine(String(ln), ctx.projectHint)}`);
7225
+ return tail.length ? [ansi.dim(" Stack:"), ...tail, ""] : empty;
7226
+ })() : empty;
7227
+ return concat(code, pretty, matcherMsg, message, httpCard, thrown, consoleBlock, stackTail, [
7228
+ drawFailLine(),
7229
+ ""
7230
+ ]);
6762
7231
  };
6763
- var eventsNear = (http, ts, testPath, windowMs = HEADLAMP_HTTP_WINDOW_MS()) => {
6764
- if (typeof ts !== "number" || !Number.isFinite(ts)) {
6765
- return [];
6766
- }
6767
- return http.filter((event) => {
6768
- const timeOk = typeof event.timestampMs === "number" && Math.abs(event.timestampMs - ts) <= windowMs;
6769
- const pathOk = !testPath || event.testPath === testPath;
6770
- return timeOk && pathOk;
6771
- });
6772
- };
6773
- var isHttpStatusNumber = (statusNumber) => typeof statusNumber === "number" && statusNumber >= 100 && statusNumber <= 599;
6774
- var inferHttpNumbersFromText = (lines) => {
6775
- const text = lines.join("\n");
6776
- const match = text.match(/Expected:\s*(\d{3})[\s\S]*?Received:\s*(\d{3})/i);
6777
- if (match) {
6778
- return { expectedNumber: Number(match[1]), receivedNumber: Number(match[2]) };
6779
- }
6780
- return {};
6781
- };
6782
- var titleSuggestsHttp = (title) => {
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);
7232
+ var renderFileBlock = (file, env) => {
7233
+ const rel = file.testFilePath.replace(/\\/g, "/").replace(`${env.ctx.cwd}/`, "");
7234
+ const failed = file.testResults.filter((assertion) => assertion.status === "failed");
7235
+ const { http, assertions } = parseBridgeConsole(file.console);
7236
+ const httpSorted = [...http].sort(by((event) => event.timestampMs));
7237
+ return concat(
7238
+ renderPerFileOverviewBlock(rel, file.testResults, env.onlyFailures),
7239
+ renderFileBadge(rel, failed.length, env.onlyFailures),
7240
+ renderFileLevelFailure(file, env.ctx),
7241
+ ...failed.map(
7242
+ (assertion) => renderFailedAssertion({
7243
+ file,
7244
+ relPath: rel,
7245
+ assertion,
7246
+ ctx: env.ctx,
7247
+ assertionEvents: assertions,
7248
+ httpSorted
7249
+ })
7250
+ )
7251
+ );
6796
7252
  };
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
7253
  var vitestFooter = (agg, durationMs) => {
6800
7254
  const files = [
6801
7255
  agg.numFailedTestSuites ? colorTokens2.fail(`${agg.numFailedTestSuites} failed`) : "",
@@ -6816,462 +7270,24 @@ var vitestFooter = (agg, durationMs) => {
6816
7270
  `${ansi.bold("Time")} ${time} ${thread}`
6817
7271
  ].join("\n");
6818
7272
  };
6819
- var renderVitestFromJestJSON = (data, ctx, opts) => {
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
- }
7273
+ var renderFooter = (data) => {
7269
7274
  const failedCount = data.aggregated.numFailedTests;
7270
- out.push(drawRule(BackgroundColors.Failure(ansi.white(` Failed Tests ${failedCount} `))));
7271
- out.push("");
7272
- out.push(vitestFooter(data.aggregated));
7273
- return out.join("\n");
7275
+ return [
7276
+ drawRule(BackgroundColors.Failure(ansi.white(` Failed Tests ${failedCount} `))),
7277
+ "",
7278
+ vitestFooter(data.aggregated)
7279
+ ];
7274
7280
  };
7281
+ var renderVitestFromJestJSON = (data, ctx, opts) => pipe(
7282
+ concat(
7283
+ renderRunHeader({ ctx, onlyFailures: Boolean(opts?.onlyFailures) }),
7284
+ ...data.testResults.map(
7285
+ (file) => renderFileBlock(file, { ctx, onlyFailures: Boolean(opts?.onlyFailures) })
7286
+ ),
7287
+ renderFooter(data)
7288
+ ),
7289
+ joinLines
7290
+ );
7275
7291
 
7276
7292
  // src/lib/formatter/bridge/tryBridgeFallback.ts
7277
7293
  var tryBridgeFallback = (raw, ctx, opts) => {
@@ -8899,7 +8915,6 @@ export {
8899
8915
  formatJestOutputVitest,
8900
8916
  isFilesObject,
8901
8917
  isStackLine,
8902
- isTransportError,
8903
8918
  isTruthy,
8904
8919
  linesFromDetails,
8905
8920
  linkifyPadded,
@@ -8910,8 +8925,6 @@ export {
8910
8925
  mkPrettyFns,
8911
8926
  osc8,
8912
8927
  parseActionsFromTokens,
8913
- parseMethodPathFromTitle,
8914
- pickRelevantHttp,
8915
8928
  preferredEditorHref,
8916
8929
  printCompactCoverage,
8917
8930
  printDetailedCoverage,
@@ -8926,7 +8939,6 @@ export {
8926
8939
  renderTable,
8927
8940
  renderVitestFromJestJSON,
8928
8941
  resolveImportWithRoot,
8929
- routeSimilarityScore,
8930
8942
  rule,
8931
8943
  runJestBootstrap,
8932
8944
  stripAnsiSimple,