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/cli.cjs +545 -529
- package/dist/cli.cjs.map +4 -4
- package/dist/index.js +545 -533
- package/dist/index.js.map +4 -4
- package/package.json +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -3047,11 +3047,11 @@ var tintPct = (pct) => {
|
|
|
3047
3047
|
var bar = (pct, width = DEFAULT_BAR_WIDTH) => {
|
|
3048
3048
|
const filled = Math.round(pct / PERCENT_MAX * width);
|
|
3049
3049
|
const solid = supportsUnicode() ? "\u2588" : "#";
|
|
3050
|
-
const
|
|
3050
|
+
const empty2 = supportsUnicode() ? "\u2591" : "-";
|
|
3051
3051
|
const good = tintPct(pct);
|
|
3052
3052
|
const MIN_REMAINING = 0;
|
|
3053
3053
|
return `${good(solid.repeat(filled))}${ansi.gray(
|
|
3054
|
-
|
|
3054
|
+
empty2.repeat(Math.max(MIN_REMAINING, width - filled))
|
|
3055
3055
|
)}`;
|
|
3056
3056
|
};
|
|
3057
3057
|
|
|
@@ -5387,11 +5387,25 @@ var extractBridgePath2 = (raw, cwd) => {
|
|
|
5387
5387
|
const jsonPath = (matches[matches.length - 1][1] ?? "").trim().replace(/^['"`]|['"`]$/g, "");
|
|
5388
5388
|
return path10.isAbsolute(jsonPath) ? jsonPath : path10.resolve(cwd, jsonPath).replace(/\\/g, "/");
|
|
5389
5389
|
};
|
|
5390
|
-
|
|
5391
|
-
|
|
5392
|
-
|
|
5393
|
-
|
|
5394
|
-
|
|
5390
|
+
|
|
5391
|
+
// src/lib/formatter/bridge/utils.ts
|
|
5392
|
+
var import_json52 = __toESM(require_lib(), 1);
|
|
5393
|
+
|
|
5394
|
+
// src/lib/formatter/bridge/http.ts
|
|
5395
|
+
var envNumber = (name, fallback) => {
|
|
5396
|
+
const parsed = Number(process.env[name]);
|
|
5397
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
|
5398
|
+
};
|
|
5399
|
+
var HEADLAMP_HTTP_WINDOW_MS = () => envNumber("HEADLAMP_HTTP_WINDOW_MS", 3e3);
|
|
5400
|
+
var HEADLAMP_HTTP_STRICT_WINDOW_MS = () => envNumber("HEADLAMP_HTTP_STRICT_WINDOW_MS", 600);
|
|
5401
|
+
var HEADLAMP_HTTP_MIN_SCORE = () => envNumber("HEADLAMP_HTTP_MIN_SCORE", 1200);
|
|
5402
|
+
var HEADLAMP_HTTP_DIFF_LIMIT = () => envNumber("HEADLAMP_HTTP_DIFF_LIMIT", 6);
|
|
5403
|
+
var HEADLAMP_HTTP_SHOW_MISS = () => process.env.HEADLAMP_HTTP_MISS === "1";
|
|
5404
|
+
var asHttpList = (candidateValue) => Array.isArray(candidateValue) ? candidateValue : [];
|
|
5405
|
+
var summarizeUrl = (method, url, route) => {
|
|
5406
|
+
const base = route || url || "";
|
|
5407
|
+
const qs = url && url.includes("?") ? ` ? ${url.split("?")[1]}` : "";
|
|
5408
|
+
return [method || "", base, qs].filter(Boolean).join(" ").trim();
|
|
5395
5409
|
};
|
|
5396
5410
|
var parseMethodPathFromTitle = (title) => {
|
|
5397
5411
|
if (!title) {
|
|
@@ -5400,6 +5414,42 @@ var parseMethodPathFromTitle = (title) => {
|
|
|
5400
5414
|
const matchResult = title.match(/\b(GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS)\s+([^\s)]+)/i);
|
|
5401
5415
|
return matchResult ? { method: matchResult[1]?.toUpperCase(), path: matchResult[2] } : {};
|
|
5402
5416
|
};
|
|
5417
|
+
var eventsNear = (http, ts, testPath, windowMs = HEADLAMP_HTTP_WINDOW_MS()) => {
|
|
5418
|
+
if (typeof ts !== "number" || !Number.isFinite(ts)) {
|
|
5419
|
+
return [];
|
|
5420
|
+
}
|
|
5421
|
+
return http.filter((event) => {
|
|
5422
|
+
const timeOk = typeof event.timestampMs === "number" && Math.abs(event.timestampMs - ts) <= windowMs;
|
|
5423
|
+
const pathOk = !testPath || event.testPath === testPath;
|
|
5424
|
+
return timeOk && pathOk;
|
|
5425
|
+
});
|
|
5426
|
+
};
|
|
5427
|
+
var isHttpStatusNumber = (statusNumber) => typeof statusNumber === "number" && statusNumber >= 100 && statusNumber <= 599;
|
|
5428
|
+
var inferHttpNumbersFromText = (lines) => {
|
|
5429
|
+
const text = lines.join("\n");
|
|
5430
|
+
const match = text.match(/Expected:\s*(\d{3})[\s\S]*?Received:\s*(\d{3})/i);
|
|
5431
|
+
if (match) {
|
|
5432
|
+
return { expectedNumber: Number(match[1]), receivedNumber: Number(match[2]) };
|
|
5433
|
+
}
|
|
5434
|
+
return {};
|
|
5435
|
+
};
|
|
5436
|
+
var titleSuggestsHttp = (title) => {
|
|
5437
|
+
const { method, path: parsedPath } = parseMethodPathFromTitle(title);
|
|
5438
|
+
return Boolean(method || parsedPath && parsedPath.startsWith("/"));
|
|
5439
|
+
};
|
|
5440
|
+
var hasStatusSemantics = (assertionLike) => {
|
|
5441
|
+
if (!assertionLike) {
|
|
5442
|
+
return false;
|
|
5443
|
+
}
|
|
5444
|
+
if (isHttpStatusNumber(assertionLike.expectedNumber) || isHttpStatusNumber(assertionLike.receivedNumber)) {
|
|
5445
|
+
return true;
|
|
5446
|
+
}
|
|
5447
|
+
const combinedRaw = `${assertionLike.matcher ?? ""} ${assertionLike.message ?? ""}`;
|
|
5448
|
+
const combinedMessage = combinedRaw.toLowerCase();
|
|
5449
|
+
return /\bstatus(code)?\b|\btohaves(tatus|tatuscode)\b/.test(combinedMessage);
|
|
5450
|
+
};
|
|
5451
|
+
var fileSuggestsHttp = (relPath2) => /(?:^|\/)(routes?|api|controllers?|e2e|integration)(?:\/|\.test\.)/i.test(relPath2);
|
|
5452
|
+
var isHttpRelevant = (ctx) => ctx.hasTransportSignal || ctx.httpCountInSameTest > 0 || titleSuggestsHttp(ctx.title) || hasStatusSemantics(ctx.assertion) || fileSuggestsHttp(ctx.relPath);
|
|
5403
5453
|
var routeSimilarityScore = (hint, evt) => {
|
|
5404
5454
|
if (!hint.path && !hint.method) {
|
|
5405
5455
|
return 0;
|
|
@@ -5420,15 +5470,12 @@ var routeSimilarityScore = (hint, evt) => {
|
|
|
5420
5470
|
}
|
|
5421
5471
|
return methodOk * 10;
|
|
5422
5472
|
};
|
|
5423
|
-
var
|
|
5424
|
-
const
|
|
5425
|
-
return
|
|
5473
|
+
var isTransportError = (msg) => {
|
|
5474
|
+
const lowercaseMessage = (msg ?? "").toLowerCase();
|
|
5475
|
+
return /\bsocket hang up\b|\beconnreset\b|\betimedout\b|\beconnrefused\b|\bwrite epipe\b/.test(
|
|
5476
|
+
lowercaseMessage
|
|
5477
|
+
);
|
|
5426
5478
|
};
|
|
5427
|
-
var HEADLAMP_HTTP_WINDOW_MS = () => envNumber("HEADLAMP_HTTP_WINDOW_MS", 3e3);
|
|
5428
|
-
var HEADLAMP_HTTP_STRICT_WINDOW_MS = () => envNumber("HEADLAMP_HTTP_STRICT_WINDOW_MS", 600);
|
|
5429
|
-
var HEADLAMP_HTTP_MIN_SCORE = () => envNumber("HEADLAMP_HTTP_MIN_SCORE", 1200);
|
|
5430
|
-
var HEADLAMP_HTTP_DIFF_LIMIT = () => envNumber("HEADLAMP_HTTP_DIFF_LIMIT", 6);
|
|
5431
|
-
var HEADLAMP_HTTP_SHOW_MISS = () => process.env.HEADLAMP_HTTP_MISS === "1";
|
|
5432
5479
|
var scoreHttpForAssertion = (assertion, titleHint) => (candidateEvent) => {
|
|
5433
5480
|
const tsA = assertion.timestampMs;
|
|
5434
5481
|
const tsH = candidateEvent.timestampMs;
|
|
@@ -5474,7 +5521,386 @@ var pickRelevantHttp = (assertion, http, ctx) => {
|
|
|
5474
5521
|
};
|
|
5475
5522
|
|
|
5476
5523
|
// src/lib/formatter/bridge/utils.ts
|
|
5477
|
-
var
|
|
5524
|
+
var colorTokens2 = {
|
|
5525
|
+
pass: Colors.Success,
|
|
5526
|
+
fail: Colors.Failure,
|
|
5527
|
+
skip: Colors.Skip,
|
|
5528
|
+
todo: Colors.Todo,
|
|
5529
|
+
passPill: (text) => BackgroundColors.Success(ansi.white(` ${text} `)),
|
|
5530
|
+
failPill: (text) => BackgroundColors.Failure(ansi.white(` ${text} `))
|
|
5531
|
+
};
|
|
5532
|
+
var joinLines = (chunks) => chunks.join("\n");
|
|
5533
|
+
var empty = [];
|
|
5534
|
+
var concat = (...xs) => xs.flat();
|
|
5535
|
+
var by = (keySelector) => (left, right) => keySelector(left) - keySelector(right);
|
|
5536
|
+
var isObject = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
5537
|
+
var stripBridgeEventsFromConsole = (maybeConsole) => {
|
|
5538
|
+
if (!Array.isArray(maybeConsole)) {
|
|
5539
|
+
return maybeConsole;
|
|
5540
|
+
}
|
|
5541
|
+
return maybeConsole.filter((entry) => {
|
|
5542
|
+
try {
|
|
5543
|
+
const raw = Array.isArray(entry.message) ? entry.message.map(String).join(" ") : String(entry.message ?? "");
|
|
5544
|
+
return !raw.includes("[JEST-BRIDGE-EVENT]");
|
|
5545
|
+
} catch {
|
|
5546
|
+
return true;
|
|
5547
|
+
}
|
|
5548
|
+
});
|
|
5549
|
+
};
|
|
5550
|
+
var parseBridgeConsole = (consoleEntries) => {
|
|
5551
|
+
const http = [];
|
|
5552
|
+
const assertions = [];
|
|
5553
|
+
if (!Array.isArray(consoleEntries)) {
|
|
5554
|
+
return { http, assertions };
|
|
5555
|
+
}
|
|
5556
|
+
for (const entry of consoleEntries) {
|
|
5557
|
+
const rec = entry;
|
|
5558
|
+
const rawMsgVal = rec && typeof rec.message !== "undefined" ? rec.message : "";
|
|
5559
|
+
const raw = Array.isArray(rawMsgVal) ? rawMsgVal.map(String).join(" ") : String(rawMsgVal ?? "");
|
|
5560
|
+
if (!raw.includes("[JEST-BRIDGE-EVENT]")) {
|
|
5561
|
+
continue;
|
|
5562
|
+
}
|
|
5563
|
+
const jsonText = raw.split("[JEST-BRIDGE-EVENT]").pop()?.trim() ?? "";
|
|
5564
|
+
try {
|
|
5565
|
+
const evt = import_json52.default.parse(jsonText);
|
|
5566
|
+
const type = evt?.type;
|
|
5567
|
+
if (type === "httpResponse") {
|
|
5568
|
+
const timestampMs = Number(evt.timestampMs ?? Date.now());
|
|
5569
|
+
http.push({
|
|
5570
|
+
kind: "response",
|
|
5571
|
+
timestampMs,
|
|
5572
|
+
method: evt.method,
|
|
5573
|
+
url: evt.url,
|
|
5574
|
+
route: evt.route,
|
|
5575
|
+
statusCode: evt.statusCode,
|
|
5576
|
+
durationMs: evt.durationMs,
|
|
5577
|
+
contentType: evt.contentType,
|
|
5578
|
+
requestId: evt.requestId,
|
|
5579
|
+
json: evt.json,
|
|
5580
|
+
bodyPreview: evt.bodyPreview,
|
|
5581
|
+
testPath: evt.testPath,
|
|
5582
|
+
currentTestName: evt.currentTestName
|
|
5583
|
+
});
|
|
5584
|
+
} else if (type === "httpAbort") {
|
|
5585
|
+
http.push({
|
|
5586
|
+
kind: "abort",
|
|
5587
|
+
timestampMs: Number(evt.timestampMs ?? Date.now()),
|
|
5588
|
+
method: evt.method,
|
|
5589
|
+
url: evt.url,
|
|
5590
|
+
route: evt.route,
|
|
5591
|
+
durationMs: evt.durationMs,
|
|
5592
|
+
testPath: evt.testPath,
|
|
5593
|
+
currentTestName: evt.currentTestName
|
|
5594
|
+
});
|
|
5595
|
+
} else if (type === "httpResponseBatch") {
|
|
5596
|
+
const list = asHttpList(evt?.events);
|
|
5597
|
+
for (const item of list) {
|
|
5598
|
+
const anyItem = item;
|
|
5599
|
+
http.push({
|
|
5600
|
+
timestampMs: Number(anyItem.timestampMs ?? Date.now()),
|
|
5601
|
+
method: anyItem.method,
|
|
5602
|
+
url: anyItem.url,
|
|
5603
|
+
route: anyItem.route,
|
|
5604
|
+
statusCode: anyItem.statusCode,
|
|
5605
|
+
durationMs: anyItem.durationMs,
|
|
5606
|
+
contentType: anyItem.contentType,
|
|
5607
|
+
requestId: anyItem.requestId,
|
|
5608
|
+
json: anyItem.json,
|
|
5609
|
+
bodyPreview: anyItem.bodyPreview,
|
|
5610
|
+
testPath: evt.testPath,
|
|
5611
|
+
currentTestName: evt.currentTestName
|
|
5612
|
+
});
|
|
5613
|
+
}
|
|
5614
|
+
} else if (type === "assertionFailure") {
|
|
5615
|
+
assertions.push({
|
|
5616
|
+
timestampMs: typeof evt.timestampMs === "number" ? evt.timestampMs : void 0,
|
|
5617
|
+
matcher: evt.matcher,
|
|
5618
|
+
expectedNumber: typeof evt.expectedNumber === "number" ? evt.expectedNumber : void 0,
|
|
5619
|
+
receivedNumber: typeof evt.receivedNumber === "number" ? evt.receivedNumber : void 0,
|
|
5620
|
+
message: typeof evt.message === "string" ? evt.message : void 0,
|
|
5621
|
+
stack: typeof evt.stack === "string" ? evt.stack : void 0,
|
|
5622
|
+
testPath: evt.testPath,
|
|
5623
|
+
currentTestName: evt.currentTestName,
|
|
5624
|
+
expectedPreview: typeof evt.expectedPreview === "string" ? evt.expectedPreview : void 0,
|
|
5625
|
+
actualPreview: typeof evt.actualPreview === "string" ? evt.actualPreview : void 0
|
|
5626
|
+
});
|
|
5627
|
+
}
|
|
5628
|
+
} catch {
|
|
5629
|
+
}
|
|
5630
|
+
}
|
|
5631
|
+
return { http, assertions };
|
|
5632
|
+
};
|
|
5633
|
+
var renderRunHeader = ({ ctx, onlyFailures }) => onlyFailures ? empty : [`${BackgroundColors.Run(ansi.white(" RUN "))} ${ansi.dim(ctx.cwd)}`, ""];
|
|
5634
|
+
var renderPerFileOverviewBlock = (rel, testResults, onlyFailures) => onlyFailures ? empty : buildPerFileOverview(rel, testResults);
|
|
5635
|
+
var renderFileBadge = (rel, failedCount, onlyFailures) => onlyFailures && failedCount === 0 ? empty : [buildFileBadgeLine(rel, failedCount)];
|
|
5636
|
+
var condenseBlankRuns = (lines) => {
|
|
5637
|
+
const out = [];
|
|
5638
|
+
let lastBlank = false;
|
|
5639
|
+
for (const ln of lines) {
|
|
5640
|
+
const isBlank = !stripAnsiSimple(String(ln ?? "")).trim();
|
|
5641
|
+
if (isBlank) {
|
|
5642
|
+
if (!lastBlank) {
|
|
5643
|
+
out.push("");
|
|
5644
|
+
}
|
|
5645
|
+
lastBlank = true;
|
|
5646
|
+
} else {
|
|
5647
|
+
out.push(String(ln));
|
|
5648
|
+
lastBlank = false;
|
|
5649
|
+
}
|
|
5650
|
+
}
|
|
5651
|
+
return out;
|
|
5652
|
+
};
|
|
5653
|
+
var mergeMsgLines = (primaryRaw, detailMsgs) => {
|
|
5654
|
+
const primary = primaryRaw.trim() ? primaryRaw.split(/\r?\n/) : [];
|
|
5655
|
+
const key = (line) => stripAnsiSimple(line).trim();
|
|
5656
|
+
const seen = new Set(primary.map(key));
|
|
5657
|
+
const merged = [...primary];
|
|
5658
|
+
for (const msg of detailMsgs) {
|
|
5659
|
+
const msgKey = key(String(msg ?? ""));
|
|
5660
|
+
if (!msgKey) {
|
|
5661
|
+
continue;
|
|
5662
|
+
}
|
|
5663
|
+
if (!seen.has(msgKey)) {
|
|
5664
|
+
merged.push(msg);
|
|
5665
|
+
seen.add(msgKey);
|
|
5666
|
+
}
|
|
5667
|
+
}
|
|
5668
|
+
return condenseBlankRuns(merged);
|
|
5669
|
+
};
|
|
5670
|
+
var renderFileLevelFailure = (file, ctx) => {
|
|
5671
|
+
if (!(file.failureMessage || file.testExecError)) {
|
|
5672
|
+
return empty;
|
|
5673
|
+
}
|
|
5674
|
+
const base = linesFromDetails(file.failureDetails);
|
|
5675
|
+
const exec = linesFromDetails(
|
|
5676
|
+
Array.isArray(file.testExecError) ? file.testExecError : [file.testExecError]
|
|
5677
|
+
);
|
|
5678
|
+
const combinedDetails = {
|
|
5679
|
+
stacks: [...base.stacks, ...exec.stacks],
|
|
5680
|
+
messages: [...base.messages, ...exec.messages]
|
|
5681
|
+
};
|
|
5682
|
+
const msgLines = mergeMsgLines(file.failureMessage || "", combinedDetails.messages);
|
|
5683
|
+
const mergedForStack = collapseStacks([...msgLines, ...combinedDetails.stacks]);
|
|
5684
|
+
const synthLoc = deepestProjectLoc(mergedForStack, ctx.projectHint);
|
|
5685
|
+
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)}`) : [];
|
|
5686
|
+
const code = buildCodeFrameSection(msgLines, ctx, synthLoc);
|
|
5687
|
+
const pretty = buildPrettyDiffSection(file.failureDetails, msgLines);
|
|
5688
|
+
const message = buildMessageSection(msgLines, combinedDetails, ctx, {
|
|
5689
|
+
suppressDiff: pretty.length > 0,
|
|
5690
|
+
stackPreview
|
|
5691
|
+
});
|
|
5692
|
+
const consoleBlock = buildConsoleSection(stripBridgeEventsFromConsole(file.console ?? null));
|
|
5693
|
+
const stackTail = ctx.showStacks && stackPreview.length === 0 ? (() => {
|
|
5694
|
+
const tail = mergedForStack.filter((ln) => isStackLine(stripAnsiSimple(ln))).slice(-4).map((ln) => ` ${colorStackLine(String(ln), ctx.projectHint)}`);
|
|
5695
|
+
return tail.length ? [ansi.dim(" Stack:"), ...tail, ""] : empty;
|
|
5696
|
+
})() : empty;
|
|
5697
|
+
return concat(code, pretty, message, consoleBlock, stackTail);
|
|
5698
|
+
};
|
|
5699
|
+
var renderHttpCard = (args) => {
|
|
5700
|
+
const { file, relPath: rel, assertion, assertionEvents, httpSorted } = args;
|
|
5701
|
+
const nameMatches = (left, right) => !!left && !!right && (left === right || left.includes(right) || right.includes(left));
|
|
5702
|
+
const inSameCtx = (testPath, testName) => httpSorted.filter(
|
|
5703
|
+
(event) => event.testPath === testPath && nameMatches(event.currentTestName, testName)
|
|
5704
|
+
);
|
|
5705
|
+
const perTestSlice = inSameCtx(file.testFilePath, assertion.fullName);
|
|
5706
|
+
const corresponding = assertionEvents.find(
|
|
5707
|
+
(event) => event.testPath === file.testFilePath && nameMatches(event.currentTestName, assertion.fullName)
|
|
5708
|
+
) ?? assertion;
|
|
5709
|
+
const nearByTime = eventsNear(
|
|
5710
|
+
httpSorted,
|
|
5711
|
+
corresponding?.timestampMs,
|
|
5712
|
+
file.testFilePath
|
|
5713
|
+
);
|
|
5714
|
+
const hasAbort = perTestSlice.some((event) => event.kind === "abort");
|
|
5715
|
+
const hasTransport = isTransportError(corresponding?.message) || hasAbort;
|
|
5716
|
+
const httpLikely = isHttpRelevant({
|
|
5717
|
+
assertion: corresponding,
|
|
5718
|
+
title: assertion.fullName,
|
|
5719
|
+
relPath: rel,
|
|
5720
|
+
httpCountInSameTest: perTestSlice.length || nearByTime.length,
|
|
5721
|
+
hasTransportSignal: hasTransport
|
|
5722
|
+
});
|
|
5723
|
+
if (!httpLikely) {
|
|
5724
|
+
return empty;
|
|
5725
|
+
}
|
|
5726
|
+
const HEADLAMP_HTTP_DIFF_LIMIT_LOCAL = () => HEADLAMP_HTTP_DIFF_LIMIT();
|
|
5727
|
+
const safeParseJSON = (text) => {
|
|
5728
|
+
try {
|
|
5729
|
+
return text ? import_json52.default.parse(text) : void 0;
|
|
5730
|
+
} catch {
|
|
5731
|
+
return void 0;
|
|
5732
|
+
}
|
|
5733
|
+
};
|
|
5734
|
+
const expPreview = corresponding?.expectedPreview;
|
|
5735
|
+
const actPreview = corresponding?.actualPreview;
|
|
5736
|
+
const parsedExpected = safeParseJSON(expPreview);
|
|
5737
|
+
const parsedActual = safeParseJSON(actPreview);
|
|
5738
|
+
let corr = corresponding;
|
|
5739
|
+
if (!isHttpStatusNumber(corr.expectedNumber) && !isHttpStatusNumber(corr.receivedNumber)) {
|
|
5740
|
+
const inferred = inferHttpNumbersFromText(
|
|
5741
|
+
(assertion.failureMessages?.join("\n") || file.failureMessage || "").split("\n")
|
|
5742
|
+
);
|
|
5743
|
+
if (isHttpStatusNumber(inferred.expectedNumber) || isHttpStatusNumber(inferred.receivedNumber)) {
|
|
5744
|
+
corr = { ...corr, ...inferred };
|
|
5745
|
+
}
|
|
5746
|
+
}
|
|
5747
|
+
const relevant = pickRelevantHttp(
|
|
5748
|
+
{
|
|
5749
|
+
timestampMs: corr?.timestampMs,
|
|
5750
|
+
expectedNumber: corr?.expectedNumber,
|
|
5751
|
+
receivedNumber: corr?.receivedNumber,
|
|
5752
|
+
matcher: corr?.matcher,
|
|
5753
|
+
message: corr?.message,
|
|
5754
|
+
stack: corr?.stack,
|
|
5755
|
+
testPath: file.testFilePath,
|
|
5756
|
+
currentTestName: assertion.title
|
|
5757
|
+
},
|
|
5758
|
+
httpSorted,
|
|
5759
|
+
{
|
|
5760
|
+
testPath: file.testFilePath,
|
|
5761
|
+
currentTestName: assertion.fullName,
|
|
5762
|
+
title: assertion.fullName
|
|
5763
|
+
}
|
|
5764
|
+
);
|
|
5765
|
+
if (hasTransport) {
|
|
5766
|
+
const tsBase = corr?.timestampMs ?? 0;
|
|
5767
|
+
const [nearestAbort] = perTestSlice.filter((event) => event.kind === "abort").sort(
|
|
5768
|
+
(left, right) => Math.abs(tsBase - (left.timestampMs ?? 0)) - Math.abs(tsBase - (right.timestampMs ?? 0))
|
|
5769
|
+
);
|
|
5770
|
+
if (nearestAbort) {
|
|
5771
|
+
const ms = nearestAbort.durationMs;
|
|
5772
|
+
return [
|
|
5773
|
+
" HTTP:",
|
|
5774
|
+
`
|
|
5775
|
+
${summarizeUrl(nearestAbort.method, nearestAbort.url, nearestAbort.route)} ${ansi.dim("->")} ${ansi.yellow("connection aborted")}`,
|
|
5776
|
+
ms != null ? ` ${ansi.dim(`(${ms}ms)`)} ` : "",
|
|
5777
|
+
"\n"
|
|
5778
|
+
];
|
|
5779
|
+
}
|
|
5780
|
+
return HEADLAMP_HTTP_SHOW_MISS() ? [
|
|
5781
|
+
" HTTP:",
|
|
5782
|
+
`
|
|
5783
|
+
${ansi.dim("Transport error; no matching HTTP exchange in window.")}`,
|
|
5784
|
+
"\n"
|
|
5785
|
+
] : empty;
|
|
5786
|
+
}
|
|
5787
|
+
if (!relevant) {
|
|
5788
|
+
return HEADLAMP_HTTP_SHOW_MISS() ? [
|
|
5789
|
+
" HTTP:",
|
|
5790
|
+
`
|
|
5791
|
+
${ansi.dim("No relevant HTTP exchange found. (HEADLAMP_HTTP_MISS=0 to hide)")}`,
|
|
5792
|
+
"\n"
|
|
5793
|
+
] : empty;
|
|
5794
|
+
}
|
|
5795
|
+
const jsonDiff = (expected, actual, limit = HEADLAMP_HTTP_DIFF_LIMIT_LOCAL()) => {
|
|
5796
|
+
const out = [];
|
|
5797
|
+
const queue = [
|
|
5798
|
+
{ pathSoFar: "$", expectedValue: expected, actualValue: actual }
|
|
5799
|
+
];
|
|
5800
|
+
while (queue.length && out.length < limit) {
|
|
5801
|
+
const { pathSoFar, expectedValue, actualValue } = queue.shift();
|
|
5802
|
+
const expectedIsObject = expectedValue && typeof expectedValue === "object";
|
|
5803
|
+
const actualIsObject = actualValue && typeof actualValue === "object";
|
|
5804
|
+
if (!expectedIsObject && !actualIsObject) {
|
|
5805
|
+
if (JSON.stringify(expectedValue) !== JSON.stringify(actualValue)) {
|
|
5806
|
+
out.push({
|
|
5807
|
+
kind: "changed",
|
|
5808
|
+
path: pathSoFar,
|
|
5809
|
+
preview: `${String(expectedValue)} \u2192 ${String(actualValue)}`
|
|
5810
|
+
});
|
|
5811
|
+
}
|
|
5812
|
+
} else if (expectedIsObject && !actualIsObject) {
|
|
5813
|
+
out.push({ kind: "changed", path: pathSoFar, preview: "[object] \u2192 primitive" });
|
|
5814
|
+
} else if (!expectedIsObject && actualIsObject) {
|
|
5815
|
+
out.push({ kind: "changed", path: pathSoFar, preview: "primitive \u2192 [object]" });
|
|
5816
|
+
} else {
|
|
5817
|
+
const expectedKeys = new Set(Object.keys(expectedValue));
|
|
5818
|
+
const actualKeys = new Set(Object.keys(actualValue));
|
|
5819
|
+
for (const key of expectedKeys) {
|
|
5820
|
+
if (!actualKeys.has(key) && out.length < limit) {
|
|
5821
|
+
out.push({ kind: "removed", path: `${pathSoFar}.${key}` });
|
|
5822
|
+
}
|
|
5823
|
+
}
|
|
5824
|
+
for (const key of actualKeys) {
|
|
5825
|
+
if (!expectedKeys.has(key) && out.length < limit) {
|
|
5826
|
+
out.push({ kind: "added", path: `${pathSoFar}.${key}` });
|
|
5827
|
+
}
|
|
5828
|
+
}
|
|
5829
|
+
for (const key of expectedKeys) {
|
|
5830
|
+
if (actualKeys.has(key) && out.length < limit) {
|
|
5831
|
+
queue.push({
|
|
5832
|
+
pathSoFar: `${pathSoFar}.${key}`,
|
|
5833
|
+
expectedValue: expectedValue[key],
|
|
5834
|
+
actualValue: actualValue[key]
|
|
5835
|
+
});
|
|
5836
|
+
}
|
|
5837
|
+
}
|
|
5838
|
+
}
|
|
5839
|
+
}
|
|
5840
|
+
return out;
|
|
5841
|
+
};
|
|
5842
|
+
const importantMessages = (json) => {
|
|
5843
|
+
const msgs = [];
|
|
5844
|
+
try {
|
|
5845
|
+
const obj = isObject(json) ? json : {};
|
|
5846
|
+
const push = (msg) => {
|
|
5847
|
+
if (typeof msg === "string" && msg.trim()) {
|
|
5848
|
+
msgs.push(msg);
|
|
5849
|
+
}
|
|
5850
|
+
};
|
|
5851
|
+
push(obj.displayMessage);
|
|
5852
|
+
push(obj.message);
|
|
5853
|
+
if (Array.isArray(obj.errors)) {
|
|
5854
|
+
for (const event of obj.errors) {
|
|
5855
|
+
push(isObject(event) ? event.message : void 0);
|
|
5856
|
+
}
|
|
5857
|
+
}
|
|
5858
|
+
if (Array.isArray(obj.data)) {
|
|
5859
|
+
for (const event of obj.data) {
|
|
5860
|
+
push(isObject(event) ? event.message : void 0);
|
|
5861
|
+
}
|
|
5862
|
+
}
|
|
5863
|
+
} catch {
|
|
5864
|
+
}
|
|
5865
|
+
return msgs.slice(0, 2);
|
|
5866
|
+
};
|
|
5867
|
+
const where = summarizeUrl(relevant.method, relevant.url, relevant.route);
|
|
5868
|
+
const header = [
|
|
5869
|
+
" HTTP:",
|
|
5870
|
+
`
|
|
5871
|
+
${where} ${ansi.dim("->")} ${relevant.statusCode ?? "?"}`,
|
|
5872
|
+
typeof relevant.durationMs === "number" ? ` ${ansi.dim(`(${relevant.durationMs}ms)`)} ` : " ",
|
|
5873
|
+
relevant.contentType ? ansi.dim(`(${relevant.contentType})`) : "",
|
|
5874
|
+
relevant.requestId ? ansi.dim(` reqId=${relevant.requestId}`) : ""
|
|
5875
|
+
].join("");
|
|
5876
|
+
const expVsAct = typeof corr?.expectedNumber === "number" || typeof corr?.receivedNumber === "number" ? (() => {
|
|
5877
|
+
const exp = corr?.expectedNumber != null ? String(corr.expectedNumber) : "?";
|
|
5878
|
+
const got = corr?.receivedNumber != null ? String(corr.receivedNumber) : String(relevant.statusCode ?? "?");
|
|
5879
|
+
return `
|
|
5880
|
+
Expected: ${ansi.yellow(exp)} Received: ${ansi.yellow(got)}`;
|
|
5881
|
+
})() : "";
|
|
5882
|
+
const why = importantMessages(parsedActual ?? relevant.json).map((msg) => `
|
|
5883
|
+
Why: ${ansi.white(msg)}`).slice(0, 1).join("") || "";
|
|
5884
|
+
const diff = (() => {
|
|
5885
|
+
const rightActual = parsedActual ?? relevant.json;
|
|
5886
|
+
if (!parsedExpected || !rightActual) {
|
|
5887
|
+
return "";
|
|
5888
|
+
}
|
|
5889
|
+
const changes = jsonDiff(parsedExpected, rightActual);
|
|
5890
|
+
if (!changes.length) {
|
|
5891
|
+
return "";
|
|
5892
|
+
}
|
|
5893
|
+
const body = changes.map((change) => {
|
|
5894
|
+
const marker = change.kind === "added" ? "+" : change.kind === "removed" ? "-" : "~";
|
|
5895
|
+
const preview = change.preview ? `: ${ansi.dim(change.preview)}` : "";
|
|
5896
|
+
return `
|
|
5897
|
+
${marker} ${change.path}${preview}`;
|
|
5898
|
+
}).join("");
|
|
5899
|
+
return `
|
|
5900
|
+
Diff:${body}`;
|
|
5901
|
+
})();
|
|
5902
|
+
return [header, expVsAct, why, diff, "\n"].filter(Boolean);
|
|
5903
|
+
};
|
|
5478
5904
|
var coerceJestJsonToBridge = (raw) => {
|
|
5479
5905
|
if (raw && typeof raw === "object" && "aggregated" in raw) {
|
|
5480
5906
|
return raw;
|
|
@@ -5517,71 +5943,99 @@ var coerceJestJsonToBridge = (raw) => {
|
|
|
5517
5943
|
}
|
|
5518
5944
|
};
|
|
5519
5945
|
};
|
|
5520
|
-
var
|
|
5521
|
-
|
|
5522
|
-
|
|
5523
|
-
|
|
5524
|
-
|
|
5525
|
-
|
|
5526
|
-
|
|
5527
|
-
|
|
5528
|
-
|
|
5529
|
-
|
|
5530
|
-
|
|
5531
|
-
|
|
5532
|
-
|
|
5533
|
-
|
|
5534
|
-
|
|
5535
|
-
};
|
|
5536
|
-
|
|
5537
|
-
|
|
5538
|
-
|
|
5539
|
-
|
|
5540
|
-
return maybeConsole.filter((entry) => {
|
|
5946
|
+
var renderFailedAssertion = (args) => {
|
|
5947
|
+
const { file, relPath: rel, assertion, ctx, assertionEvents, httpSorted } = args;
|
|
5948
|
+
const header = `${rel} > ${assertion.fullName}`;
|
|
5949
|
+
const bullet = (text) => `${Colors.Failure("\xD7")} ${ansi.white(text)}`;
|
|
5950
|
+
const failureMessage = file.failureMessage || "";
|
|
5951
|
+
const detailMsgs = linesFromDetails(assertion.failureDetails || file.failureDetails).messages;
|
|
5952
|
+
const primaryBlock = assertion.failureMessages?.length ? assertion.failureMessages.join("\n") : failureMessage;
|
|
5953
|
+
const messagesArray = mergeMsgLines(primaryBlock, detailMsgs);
|
|
5954
|
+
const details = linesFromDetails(assertion.failureDetails || file.failureDetails);
|
|
5955
|
+
const mergedForStack = collapseStacks([...messagesArray, ...details.stacks]);
|
|
5956
|
+
const deepestLoc = deepestProjectLoc(mergedForStack, ctx.projectHint);
|
|
5957
|
+
const locLink = deepestLoc ? (() => {
|
|
5958
|
+
const href = preferredEditorHref(deepestLoc.file, deepestLoc.line, ctx.editorCmd);
|
|
5959
|
+
const base = `${deepestLoc.file.split("/").pop()}:${deepestLoc.line}`;
|
|
5960
|
+
return osc8(base, href);
|
|
5961
|
+
})() : void 0;
|
|
5962
|
+
const headerLine = `${ansi.white(header)}${locLink ? ` ${ansi.dim(`(${locLink})`)}` : ""}`;
|
|
5963
|
+
const msgLines = messagesArray.join("\n").split("\n");
|
|
5964
|
+
const assertFallback = deepestLoc || assertion.location && { file: file.testFilePath, line: assertion.location.line };
|
|
5965
|
+
const matcherMsg = (() => {
|
|
5541
5966
|
try {
|
|
5542
|
-
const
|
|
5543
|
-
|
|
5967
|
+
const arr = assertion.failureDetails || file.failureDetails;
|
|
5968
|
+
if (!arr) {
|
|
5969
|
+
return empty;
|
|
5970
|
+
}
|
|
5971
|
+
for (const detailEntry of arr) {
|
|
5972
|
+
const obj = detailEntry && typeof detailEntry === "object" ? detailEntry : null;
|
|
5973
|
+
const mr = obj && obj.matcherResult && typeof obj.matcherResult === "object" ? obj.matcherResult : null;
|
|
5974
|
+
if (mr && typeof mr.message === "string" && mr.message.trim()) {
|
|
5975
|
+
const name = typeof mr.matcherName === "string" ? mr.matcherName : "";
|
|
5976
|
+
const matcherHeader = name ? ` ${ansi.bold("Matcher:")} ${ansi.yellow(name)}` : "";
|
|
5977
|
+
const bodyHeader = ` ${ansi.bold("Message:")}`;
|
|
5978
|
+
const body = String(mr.message).split(/\r?\n/).slice(0, 6).map((ln) => ` ${ansi.yellow(ln)}`);
|
|
5979
|
+
return [matcherHeader, bodyHeader, ...body, ""].filter(Boolean);
|
|
5980
|
+
}
|
|
5981
|
+
}
|
|
5544
5982
|
} catch {
|
|
5545
|
-
return true;
|
|
5546
5983
|
}
|
|
5984
|
+
return empty;
|
|
5985
|
+
})();
|
|
5986
|
+
const code = concat(
|
|
5987
|
+
["", drawFailLine(), bullet(headerLine), ""],
|
|
5988
|
+
buildCodeFrameSection(msgLines, ctx, assertFallback || void 0),
|
|
5989
|
+
[""]
|
|
5990
|
+
);
|
|
5991
|
+
const pretty = buildPrettyDiffSection(assertion.failureDetails || file.failureDetails, msgLines);
|
|
5992
|
+
const hasPretty = pretty.length > 0;
|
|
5993
|
+
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)}`) : [];
|
|
5994
|
+
const message = buildMessageSection(msgLines, details, ctx, {
|
|
5995
|
+
suppressDiff: hasPretty,
|
|
5996
|
+
stackPreview
|
|
5547
5997
|
});
|
|
5998
|
+
const httpCard = renderHttpCard({ file, relPath: rel, assertion, assertionEvents, httpSorted });
|
|
5999
|
+
const minimalInfo = msgLines.every((ln) => !ln.trim());
|
|
6000
|
+
const thrown = minimalInfo ? (() => {
|
|
6001
|
+
try {
|
|
6002
|
+
return buildThrownSection(assertion.failureDetails || []);
|
|
6003
|
+
} catch {
|
|
6004
|
+
return empty;
|
|
6005
|
+
}
|
|
6006
|
+
})() : empty;
|
|
6007
|
+
const consoleBlock = buildConsoleSection(stripBridgeEventsFromConsole(file.console ?? null));
|
|
6008
|
+
const stackTail = ctx.showStacks && stackPreview.length === 0 ? (() => {
|
|
6009
|
+
const merged = collapseStacks([...msgLines, ...details.stacks]);
|
|
6010
|
+
const tail = collapseStacks(merged).filter((ln) => isStackLine(stripAnsiSimple(ln))).slice(-4).map((ln) => ` ${colorStackLine(String(ln), ctx.projectHint)}`);
|
|
6011
|
+
return tail.length ? [ansi.dim(" Stack:"), ...tail, ""] : empty;
|
|
6012
|
+
})() : empty;
|
|
6013
|
+
return concat(code, pretty, matcherMsg, message, httpCard, thrown, consoleBlock, stackTail, [
|
|
6014
|
+
drawFailLine(),
|
|
6015
|
+
""
|
|
6016
|
+
]);
|
|
5548
6017
|
};
|
|
5549
|
-
var
|
|
5550
|
-
|
|
5551
|
-
|
|
5552
|
-
}
|
|
5553
|
-
|
|
5554
|
-
|
|
5555
|
-
|
|
5556
|
-
|
|
5557
|
-
|
|
5558
|
-
|
|
5559
|
-
|
|
5560
|
-
|
|
5561
|
-
|
|
5562
|
-
|
|
5563
|
-
|
|
5564
|
-
|
|
5565
|
-
|
|
5566
|
-
|
|
5567
|
-
|
|
5568
|
-
|
|
5569
|
-
const { method, path: parsedPath } = parseMethodPathFromTitle(title);
|
|
5570
|
-
return Boolean(method || parsedPath && parsedPath.startsWith("/"));
|
|
5571
|
-
};
|
|
5572
|
-
var hasStatusSemantics = (assertionLike) => {
|
|
5573
|
-
if (!assertionLike) {
|
|
5574
|
-
return false;
|
|
5575
|
-
}
|
|
5576
|
-
if (isHttpStatusNumber(assertionLike.expectedNumber) || isHttpStatusNumber(assertionLike.receivedNumber)) {
|
|
5577
|
-
return true;
|
|
5578
|
-
}
|
|
5579
|
-
const combinedRaw = `${assertionLike.matcher ?? ""} ${assertionLike.message ?? ""}`;
|
|
5580
|
-
const combinedMessage = combinedRaw.toLowerCase();
|
|
5581
|
-
return /\bstatus(code)?\b|\btohaves(tatus|tatuscode)\b/.test(combinedMessage);
|
|
6018
|
+
var renderFileBlock = (file, env) => {
|
|
6019
|
+
const rel = file.testFilePath.replace(/\\/g, "/").replace(`${env.ctx.cwd}/`, "");
|
|
6020
|
+
const failed = file.testResults.filter((assertion) => assertion.status === "failed");
|
|
6021
|
+
const { http, assertions } = parseBridgeConsole(file.console);
|
|
6022
|
+
const httpSorted = [...http].sort(by((event) => event.timestampMs));
|
|
6023
|
+
return concat(
|
|
6024
|
+
renderPerFileOverviewBlock(rel, file.testResults, env.onlyFailures),
|
|
6025
|
+
renderFileBadge(rel, failed.length, env.onlyFailures),
|
|
6026
|
+
renderFileLevelFailure(file, env.ctx),
|
|
6027
|
+
...failed.map(
|
|
6028
|
+
(assertion) => renderFailedAssertion({
|
|
6029
|
+
file,
|
|
6030
|
+
relPath: rel,
|
|
6031
|
+
assertion,
|
|
6032
|
+
ctx: env.ctx,
|
|
6033
|
+
assertionEvents: assertions,
|
|
6034
|
+
httpSorted
|
|
6035
|
+
})
|
|
6036
|
+
)
|
|
6037
|
+
);
|
|
5582
6038
|
};
|
|
5583
|
-
var fileSuggestsHttp = (relPath2) => /(?:^|\/)(routes?|api|controllers?|e2e|integration)(?:\/|\.test\.)/i.test(relPath2);
|
|
5584
|
-
var isHttpRelevant = (ctx) => ctx.hasTransportSignal || ctx.httpCountInSameTest > 0 || titleSuggestsHttp(ctx.title) || hasStatusSemantics(ctx.assertion) || fileSuggestsHttp(ctx.relPath);
|
|
5585
6039
|
var vitestFooter = (agg, durationMs) => {
|
|
5586
6040
|
const files = [
|
|
5587
6041
|
agg.numFailedTestSuites ? colorTokens2.fail(`${agg.numFailedTestSuites} failed`) : "",
|
|
@@ -5602,462 +6056,24 @@ var vitestFooter = (agg, durationMs) => {
|
|
|
5602
6056
|
`${ansi.bold("Time")} ${time} ${thread}`
|
|
5603
6057
|
].join("\n");
|
|
5604
6058
|
};
|
|
5605
|
-
var
|
|
5606
|
-
const out = [];
|
|
5607
|
-
const onlyFailures = Boolean(opts?.onlyFailures);
|
|
5608
|
-
if (!onlyFailures) {
|
|
5609
|
-
out.push(`${BackgroundColors.Run(ansi.white(" RUN "))} ${ansi.dim(ctx.cwd)}`, "");
|
|
5610
|
-
}
|
|
5611
|
-
for (const file of data.testResults) {
|
|
5612
|
-
const rel = file.testFilePath.replace(/\\/g, "/").replace(`${ctx.cwd}/`, "");
|
|
5613
|
-
const failed = file.testResults.filter((assertion) => assertion.status === "failed");
|
|
5614
|
-
if (!onlyFailures) {
|
|
5615
|
-
out.push(...buildPerFileOverview(rel, file.testResults));
|
|
5616
|
-
}
|
|
5617
|
-
if (!(onlyFailures && failed.length === 0)) {
|
|
5618
|
-
out.push(buildFileBadgeLine(rel, failed.length));
|
|
5619
|
-
}
|
|
5620
|
-
let httpSorted = [];
|
|
5621
|
-
let assertionEvents = [];
|
|
5622
|
-
{
|
|
5623
|
-
const parseBridge = (consoleEntries) => {
|
|
5624
|
-
const http = [];
|
|
5625
|
-
const assertions = [];
|
|
5626
|
-
if (!Array.isArray(consoleEntries)) {
|
|
5627
|
-
return { http, assertions };
|
|
5628
|
-
}
|
|
5629
|
-
for (const entry of consoleEntries) {
|
|
5630
|
-
const rec = entry;
|
|
5631
|
-
const rawMsgVal = rec && typeof rec.message !== "undefined" ? rec.message : "";
|
|
5632
|
-
const raw = Array.isArray(rawMsgVal) ? rawMsgVal.map(String).join(" ") : String(rawMsgVal ?? "");
|
|
5633
|
-
if (!raw.includes("[JEST-BRIDGE-EVENT]")) {
|
|
5634
|
-
continue;
|
|
5635
|
-
}
|
|
5636
|
-
const jsonText = raw.split("[JEST-BRIDGE-EVENT]").pop()?.trim() ?? "";
|
|
5637
|
-
try {
|
|
5638
|
-
const evt = import_json52.default.parse(jsonText);
|
|
5639
|
-
const type = evt?.type;
|
|
5640
|
-
if (type === "httpResponse") {
|
|
5641
|
-
const timestampMs = Number(evt.timestampMs ?? Date.now());
|
|
5642
|
-
http.push({
|
|
5643
|
-
kind: "response",
|
|
5644
|
-
timestampMs,
|
|
5645
|
-
method: evt.method,
|
|
5646
|
-
url: evt.url,
|
|
5647
|
-
route: evt.route,
|
|
5648
|
-
statusCode: evt.statusCode,
|
|
5649
|
-
durationMs: evt.durationMs,
|
|
5650
|
-
contentType: evt.contentType,
|
|
5651
|
-
requestId: evt.requestId,
|
|
5652
|
-
json: evt.json,
|
|
5653
|
-
bodyPreview: evt.bodyPreview,
|
|
5654
|
-
testPath: evt.testPath,
|
|
5655
|
-
currentTestName: evt.currentTestName
|
|
5656
|
-
});
|
|
5657
|
-
} else if (type === "httpAbort") {
|
|
5658
|
-
http.push({
|
|
5659
|
-
kind: "abort",
|
|
5660
|
-
timestampMs: Number(evt.timestampMs ?? Date.now()),
|
|
5661
|
-
method: evt.method,
|
|
5662
|
-
url: evt.url,
|
|
5663
|
-
route: evt.route,
|
|
5664
|
-
durationMs: evt.durationMs,
|
|
5665
|
-
testPath: evt.testPath,
|
|
5666
|
-
currentTestName: evt.currentTestName
|
|
5667
|
-
});
|
|
5668
|
-
} else if (type === "httpResponseBatch") {
|
|
5669
|
-
const list = asHttpList(evt?.events);
|
|
5670
|
-
for (const item of list) {
|
|
5671
|
-
const anyItem = item;
|
|
5672
|
-
http.push({
|
|
5673
|
-
timestampMs: Number(anyItem.timestampMs ?? Date.now()),
|
|
5674
|
-
method: anyItem.method,
|
|
5675
|
-
url: anyItem.url,
|
|
5676
|
-
route: anyItem.route,
|
|
5677
|
-
statusCode: anyItem.statusCode,
|
|
5678
|
-
durationMs: anyItem.durationMs,
|
|
5679
|
-
contentType: anyItem.contentType,
|
|
5680
|
-
requestId: anyItem.requestId,
|
|
5681
|
-
json: anyItem.json,
|
|
5682
|
-
bodyPreview: anyItem.bodyPreview,
|
|
5683
|
-
testPath: evt.testPath,
|
|
5684
|
-
currentTestName: evt.currentTestName
|
|
5685
|
-
});
|
|
5686
|
-
}
|
|
5687
|
-
} else if (type === "assertionFailure") {
|
|
5688
|
-
assertions.push({
|
|
5689
|
-
timestampMs: typeof evt.timestampMs === "number" ? evt.timestampMs : void 0,
|
|
5690
|
-
matcher: evt.matcher,
|
|
5691
|
-
expectedNumber: typeof evt.expectedNumber === "number" ? evt.expectedNumber : void 0,
|
|
5692
|
-
receivedNumber: typeof evt.receivedNumber === "number" ? evt.receivedNumber : void 0,
|
|
5693
|
-
message: typeof evt.message === "string" ? evt.message : void 0,
|
|
5694
|
-
stack: typeof evt.stack === "string" ? evt.stack : void 0,
|
|
5695
|
-
testPath: evt.testPath,
|
|
5696
|
-
currentTestName: evt.currentTestName,
|
|
5697
|
-
expectedPreview: typeof evt.expectedPreview === "string" ? evt.expectedPreview : void 0,
|
|
5698
|
-
actualPreview: typeof evt.actualPreview === "string" ? evt.actualPreview : void 0
|
|
5699
|
-
});
|
|
5700
|
-
}
|
|
5701
|
-
} catch {
|
|
5702
|
-
}
|
|
5703
|
-
}
|
|
5704
|
-
return { http, assertions };
|
|
5705
|
-
};
|
|
5706
|
-
const parsed = parseBridge(file.console);
|
|
5707
|
-
httpSorted = [...parsed.http].sort(by((event) => event.timestampMs));
|
|
5708
|
-
assertionEvents = parsed.assertions;
|
|
5709
|
-
}
|
|
5710
|
-
const inSameCtx = (testPath, testName) => httpSorted.filter(
|
|
5711
|
-
(event) => event.testPath === testPath && event.currentTestName === testName
|
|
5712
|
-
);
|
|
5713
|
-
if (file.failureMessage || file.testExecError) {
|
|
5714
|
-
const lines = file.failureMessage.split(/\r?\n/);
|
|
5715
|
-
const combinedDetails = (() => {
|
|
5716
|
-
const base = linesFromDetails(file.failureDetails);
|
|
5717
|
-
const exec = linesFromDetails(
|
|
5718
|
-
Array.isArray(file.testExecError) ? file.testExecError : [file.testExecError]
|
|
5719
|
-
);
|
|
5720
|
-
return {
|
|
5721
|
-
stacks: [...base.stacks, ...exec.stacks],
|
|
5722
|
-
messages: [...base.messages, ...exec.messages]
|
|
5723
|
-
};
|
|
5724
|
-
})();
|
|
5725
|
-
const mergedForStack = collapseStacks([...lines, ...combinedDetails.stacks]);
|
|
5726
|
-
const synthLoc = deepestProjectLoc(mergedForStack, ctx.projectHint);
|
|
5727
|
-
out.push(...buildCodeFrameSection(lines, ctx, synthLoc));
|
|
5728
|
-
const payloadPretty = buildPrettyDiffSection(file.failureDetails, lines);
|
|
5729
|
-
out.push(...payloadPretty);
|
|
5730
|
-
const hasPretty = payloadPretty.length > 0;
|
|
5731
|
-
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)}`) : [];
|
|
5732
|
-
out.push(
|
|
5733
|
-
...buildMessageSection(lines, combinedDetails, ctx, {
|
|
5734
|
-
suppressDiff: hasPretty,
|
|
5735
|
-
stackPreview
|
|
5736
|
-
})
|
|
5737
|
-
);
|
|
5738
|
-
out.push(...buildConsoleSection(stripBridgeEventsFromConsole(file.console ?? null)));
|
|
5739
|
-
if (ctx.showStacks && stackPreview.length === 0) {
|
|
5740
|
-
const tail = mergedForStack.filter((ln) => isStackLine(stripAnsiSimple(ln))).slice(-4).map((ln) => ` ${colorStackLine(String(ln), ctx.projectHint)}`);
|
|
5741
|
-
if (tail.length) {
|
|
5742
|
-
out.push(ansi.dim(" Stack:"), ...tail, "");
|
|
5743
|
-
}
|
|
5744
|
-
}
|
|
5745
|
-
}
|
|
5746
|
-
for (const assertion of failed) {
|
|
5747
|
-
out.push(drawFailLine());
|
|
5748
|
-
const header = `${rel} > ${assertion.fullName}`;
|
|
5749
|
-
const messagesArray = (() => {
|
|
5750
|
-
if (assertion.failureMessages && assertion.failureMessages.length > 0) {
|
|
5751
|
-
return assertion.failureMessages;
|
|
5752
|
-
}
|
|
5753
|
-
if (file.failureMessage && file.failureMessage.trim().length > 0) {
|
|
5754
|
-
return file.failureMessage.split(/\r?\n/);
|
|
5755
|
-
}
|
|
5756
|
-
const linesFromMatcher = linesFromDetails(
|
|
5757
|
-
assertion.failureDetails || file.failureDetails
|
|
5758
|
-
).messages;
|
|
5759
|
-
if (Array.isArray(linesFromMatcher) && linesFromMatcher.length > 0) {
|
|
5760
|
-
return linesFromMatcher;
|
|
5761
|
-
}
|
|
5762
|
-
return [""];
|
|
5763
|
-
})();
|
|
5764
|
-
const details = linesFromDetails(assertion.failureDetails || file.failureDetails);
|
|
5765
|
-
const matcherMsg = (() => {
|
|
5766
|
-
try {
|
|
5767
|
-
const arr = assertion.failureDetails || file.failureDetails;
|
|
5768
|
-
if (!arr) {
|
|
5769
|
-
return [];
|
|
5770
|
-
}
|
|
5771
|
-
for (const detailEntry of arr) {
|
|
5772
|
-
const obj = detailEntry && typeof detailEntry === "object" ? detailEntry : null;
|
|
5773
|
-
const mr = obj && obj.matcherResult && typeof obj.matcherResult === "object" ? obj.matcherResult : null;
|
|
5774
|
-
if (mr && typeof mr.message === "string" && mr.message.trim()) {
|
|
5775
|
-
const name = typeof mr.matcherName === "string" ? mr.matcherName : "";
|
|
5776
|
-
const matcherHeader = name ? ` ${ansi.bold("Matcher:")} ${ansi.yellow(name)}` : "";
|
|
5777
|
-
const bodyHeader = ` ${ansi.bold("Message:")}`;
|
|
5778
|
-
const body = String(mr.message).split(/\r?\n/).slice(0, 6).map((ln) => ` ${ansi.yellow(ln)}`);
|
|
5779
|
-
return [matcherHeader, bodyHeader, ...body, ""].filter(Boolean);
|
|
5780
|
-
}
|
|
5781
|
-
}
|
|
5782
|
-
} catch {
|
|
5783
|
-
}
|
|
5784
|
-
return [];
|
|
5785
|
-
})();
|
|
5786
|
-
const mergedForStack = collapseStacks([...messagesArray, ...details.stacks]);
|
|
5787
|
-
const deepestLoc = deepestProjectLoc(mergedForStack, ctx.projectHint);
|
|
5788
|
-
const locLink = deepestLoc ? (() => {
|
|
5789
|
-
const href = preferredEditorHref(deepestLoc.file, deepestLoc.line, ctx.editorCmd);
|
|
5790
|
-
const base = `${deepestLoc.file.split("/").pop()}:${deepestLoc.line}`;
|
|
5791
|
-
return osc8(base, href);
|
|
5792
|
-
})() : void 0;
|
|
5793
|
-
const bullet = (text) => `${Colors.Failure("\xD7")} ${ansi.white(text)}`;
|
|
5794
|
-
const headerLine = `${ansi.white(header)}${locLink ? ` ${ansi.dim(`(${locLink})`)}` : ""}`;
|
|
5795
|
-
out.push(bullet(headerLine));
|
|
5796
|
-
const msgLines = messagesArray.join("\n").split("\n");
|
|
5797
|
-
const assertFallback = deepestLoc || assertion.location && { file: file.testFilePath, line: assertion.location.line };
|
|
5798
|
-
out.push("", ...buildCodeFrameSection(msgLines, ctx, assertFallback || void 0), "");
|
|
5799
|
-
const pretty = buildPrettyDiffSection(
|
|
5800
|
-
assertion.failureDetails || file.failureDetails,
|
|
5801
|
-
msgLines
|
|
5802
|
-
);
|
|
5803
|
-
out.push(...pretty);
|
|
5804
|
-
const hasPretty = pretty.length > 0;
|
|
5805
|
-
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)}`) : [];
|
|
5806
|
-
if (matcherMsg.length) {
|
|
5807
|
-
out.push(...matcherMsg);
|
|
5808
|
-
}
|
|
5809
|
-
out.push(
|
|
5810
|
-
...buildMessageSection(msgLines, details, ctx, { suppressDiff: hasPretty, stackPreview })
|
|
5811
|
-
);
|
|
5812
|
-
{
|
|
5813
|
-
const HEADLAMP_HTTP_DIFF_LIMIT_LOCAL = () => HEADLAMP_HTTP_DIFF_LIMIT();
|
|
5814
|
-
const safeParseJSON = (text) => {
|
|
5815
|
-
try {
|
|
5816
|
-
return text ? import_json52.default.parse(text) : void 0;
|
|
5817
|
-
} catch {
|
|
5818
|
-
return void 0;
|
|
5819
|
-
}
|
|
5820
|
-
};
|
|
5821
|
-
const jsonDiff = (expected, actual, limit = HEADLAMP_HTTP_DIFF_LIMIT_LOCAL()) => {
|
|
5822
|
-
const outChanges = [];
|
|
5823
|
-
const queue = [];
|
|
5824
|
-
queue.push({
|
|
5825
|
-
pathSoFar: "$",
|
|
5826
|
-
expectedValue: expected,
|
|
5827
|
-
actualValue: actual
|
|
5828
|
-
});
|
|
5829
|
-
while (queue.length && outChanges.length < limit) {
|
|
5830
|
-
const { pathSoFar, expectedValue, actualValue } = queue.shift();
|
|
5831
|
-
const expectedIsObj = expectedValue && typeof expectedValue === "object";
|
|
5832
|
-
const actualIsObj = actualValue && typeof actualValue === "object";
|
|
5833
|
-
if (!expectedIsObj && !actualIsObj) {
|
|
5834
|
-
if (JSON.stringify(expectedValue) !== JSON.stringify(actualValue)) {
|
|
5835
|
-
outChanges.push({
|
|
5836
|
-
kind: "changed",
|
|
5837
|
-
path: pathSoFar,
|
|
5838
|
-
preview: `${String(expectedValue)} \u2192 ${String(actualValue)}`
|
|
5839
|
-
});
|
|
5840
|
-
}
|
|
5841
|
-
} else if (expectedIsObj && !actualIsObj) {
|
|
5842
|
-
outChanges.push({
|
|
5843
|
-
kind: "changed",
|
|
5844
|
-
path: pathSoFar,
|
|
5845
|
-
preview: "[object] \u2192 primitive"
|
|
5846
|
-
});
|
|
5847
|
-
} else if (!expectedIsObj && actualIsObj) {
|
|
5848
|
-
outChanges.push({
|
|
5849
|
-
kind: "changed",
|
|
5850
|
-
path: pathSoFar,
|
|
5851
|
-
preview: "primitive \u2192 [object]"
|
|
5852
|
-
});
|
|
5853
|
-
} else {
|
|
5854
|
-
const expectedKeys = new Set(Object.keys(expectedValue));
|
|
5855
|
-
const actualKeys = new Set(Object.keys(actualValue));
|
|
5856
|
-
for (const key of expectedKeys) {
|
|
5857
|
-
if (!actualKeys.has(key) && outChanges.length < limit) {
|
|
5858
|
-
outChanges.push({ kind: "removed", path: `${pathSoFar}.${key}` });
|
|
5859
|
-
}
|
|
5860
|
-
}
|
|
5861
|
-
for (const key of actualKeys) {
|
|
5862
|
-
if (!expectedKeys.has(key) && outChanges.length < limit) {
|
|
5863
|
-
outChanges.push({ kind: "added", path: `${pathSoFar}.${key}` });
|
|
5864
|
-
}
|
|
5865
|
-
}
|
|
5866
|
-
for (const key of expectedKeys) {
|
|
5867
|
-
if (actualKeys.has(key) && outChanges.length < limit) {
|
|
5868
|
-
queue.push({
|
|
5869
|
-
pathSoFar: `${pathSoFar}.${key}`,
|
|
5870
|
-
expectedValue: expectedValue[key],
|
|
5871
|
-
actualValue: actualValue[key]
|
|
5872
|
-
});
|
|
5873
|
-
}
|
|
5874
|
-
}
|
|
5875
|
-
}
|
|
5876
|
-
}
|
|
5877
|
-
return outChanges;
|
|
5878
|
-
};
|
|
5879
|
-
const importantMessages = (json) => {
|
|
5880
|
-
const msgs = [];
|
|
5881
|
-
try {
|
|
5882
|
-
const obj = isObject(json) ? json : {};
|
|
5883
|
-
const pushMaybe = (candidate) => {
|
|
5884
|
-
if (typeof candidate === "string" && candidate.trim()) {
|
|
5885
|
-
msgs.push(candidate);
|
|
5886
|
-
}
|
|
5887
|
-
};
|
|
5888
|
-
pushMaybe(obj.displayMessage);
|
|
5889
|
-
pushMaybe(obj.message);
|
|
5890
|
-
if (Array.isArray(obj.errors)) {
|
|
5891
|
-
for (const element of obj.errors) {
|
|
5892
|
-
pushMaybe(isObject(element) ? element.message : void 0);
|
|
5893
|
-
}
|
|
5894
|
-
}
|
|
5895
|
-
if (Array.isArray(obj.data)) {
|
|
5896
|
-
for (const element of obj.data) {
|
|
5897
|
-
pushMaybe(isObject(element) ? element.message : void 0);
|
|
5898
|
-
}
|
|
5899
|
-
}
|
|
5900
|
-
} catch {
|
|
5901
|
-
}
|
|
5902
|
-
return msgs.slice(0, 2);
|
|
5903
|
-
};
|
|
5904
|
-
const nameMatches = (leftName, rightName) => !!leftName && !!rightName && (leftName === rightName || leftName.includes(rightName) || rightName.includes(leftName));
|
|
5905
|
-
const corresponding = assertionEvents.find(
|
|
5906
|
-
(aevt) => aevt.testPath === file.testFilePath && nameMatches(aevt.currentTestName, assertion.title)
|
|
5907
|
-
) ?? assertion;
|
|
5908
|
-
const perTestSlice = inSameCtx(file.testFilePath, assertion.title);
|
|
5909
|
-
const nearByTime = eventsNear(
|
|
5910
|
-
httpSorted,
|
|
5911
|
-
corresponding?.timestampMs,
|
|
5912
|
-
file.testFilePath
|
|
5913
|
-
);
|
|
5914
|
-
const hasAbort = perTestSlice.some((event) => event.kind === "abort");
|
|
5915
|
-
const hasTransport = isTransportError(corresponding?.message) || hasAbort;
|
|
5916
|
-
const httpLikely = isHttpRelevant({
|
|
5917
|
-
assertion: corresponding,
|
|
5918
|
-
title: assertion.fullName,
|
|
5919
|
-
relPath: rel,
|
|
5920
|
-
httpCountInSameTest: perTestSlice.length || nearByTime.length,
|
|
5921
|
-
hasTransportSignal: hasTransport
|
|
5922
|
-
});
|
|
5923
|
-
if (!httpLikely) {
|
|
5924
|
-
} else {
|
|
5925
|
-
const expPreview = corresponding?.expectedPreview;
|
|
5926
|
-
const actPreview = corresponding?.actualPreview;
|
|
5927
|
-
const parsedExpected = safeParseJSON(expPreview);
|
|
5928
|
-
const parsedActual = safeParseJSON(actPreview);
|
|
5929
|
-
let corr = corresponding;
|
|
5930
|
-
if (!isHttpStatusNumber(corr.expectedNumber) && !isHttpStatusNumber(corr.receivedNumber)) {
|
|
5931
|
-
const inferred = inferHttpNumbersFromText(msgLines);
|
|
5932
|
-
if (isHttpStatusNumber(inferred.expectedNumber) || isHttpStatusNumber(inferred.receivedNumber)) {
|
|
5933
|
-
corr = { ...corr, ...inferred };
|
|
5934
|
-
}
|
|
5935
|
-
}
|
|
5936
|
-
const relevant = pickRelevantHttp(
|
|
5937
|
-
{
|
|
5938
|
-
timestampMs: corr?.timestampMs,
|
|
5939
|
-
expectedNumber: corr?.expectedNumber,
|
|
5940
|
-
receivedNumber: corr?.receivedNumber,
|
|
5941
|
-
matcher: corr?.matcher,
|
|
5942
|
-
message: corr?.message,
|
|
5943
|
-
stack: corr?.stack,
|
|
5944
|
-
testPath: file.testFilePath,
|
|
5945
|
-
currentTestName: assertion.title
|
|
5946
|
-
},
|
|
5947
|
-
httpSorted,
|
|
5948
|
-
{
|
|
5949
|
-
testPath: file.testFilePath,
|
|
5950
|
-
currentTestName: assertion.title,
|
|
5951
|
-
title: assertion.fullName
|
|
5952
|
-
}
|
|
5953
|
-
);
|
|
5954
|
-
if (hasTransport) {
|
|
5955
|
-
const tsBase = corresponding?.timestampMs ?? 0;
|
|
5956
|
-
const abortCandidates = perTestSlice.filter((event) => event.kind === "abort").sort((leftEvent, rightEvent) => {
|
|
5957
|
-
const deltaLeft = Math.abs(tsBase - (leftEvent.timestampMs ?? 0));
|
|
5958
|
-
const deltaRight = Math.abs(tsBase - (rightEvent.timestampMs ?? 0));
|
|
5959
|
-
return deltaLeft - deltaRight;
|
|
5960
|
-
});
|
|
5961
|
-
const [nearestAbort] = abortCandidates;
|
|
5962
|
-
if (nearestAbort) {
|
|
5963
|
-
out.push(
|
|
5964
|
-
" HTTP:",
|
|
5965
|
-
`
|
|
5966
|
-
${summarizeUrl(nearestAbort.method, nearestAbort.url, nearestAbort.route)} ${ansi.dim("->")} ${ansi.yellow("connection aborted")}`,
|
|
5967
|
-
(() => {
|
|
5968
|
-
const ms = nearestAbort.durationMs;
|
|
5969
|
-
return ms != null ? ` ${ansi.dim(`(${ms}ms)`)} ` : "";
|
|
5970
|
-
})(),
|
|
5971
|
-
"\n"
|
|
5972
|
-
);
|
|
5973
|
-
} else if (relevant) {
|
|
5974
|
-
} else if (HEADLAMP_HTTP_SHOW_MISS()) {
|
|
5975
|
-
out.push(
|
|
5976
|
-
" HTTP:",
|
|
5977
|
-
`
|
|
5978
|
-
${ansi.dim("Transport error; no matching HTTP exchange in window.")}`,
|
|
5979
|
-
"\n"
|
|
5980
|
-
);
|
|
5981
|
-
}
|
|
5982
|
-
}
|
|
5983
|
-
if (!hasTransport && relevant) {
|
|
5984
|
-
const parts = [];
|
|
5985
|
-
const where = summarizeUrl(relevant.method, relevant.url, relevant.route);
|
|
5986
|
-
const line1 = [
|
|
5987
|
-
" HTTP:",
|
|
5988
|
-
`
|
|
5989
|
-
${where} ${ansi.dim("->")} ${relevant.statusCode ?? "?"}`,
|
|
5990
|
-
(() => {
|
|
5991
|
-
const ms = relevant.durationMs;
|
|
5992
|
-
return typeof ms === "number" ? ` ${ansi.dim(`(${ms}ms)`)} ` : " ";
|
|
5993
|
-
})(),
|
|
5994
|
-
relevant.contentType ? ansi.dim(`(${relevant.contentType})`) : "",
|
|
5995
|
-
relevant.requestId ? ansi.dim(` reqId=${relevant.requestId}`) : ""
|
|
5996
|
-
].join("");
|
|
5997
|
-
const expVsAct = (() => {
|
|
5998
|
-
if (typeof corresponding?.expectedNumber === "number" || typeof corresponding?.receivedNumber === "number") {
|
|
5999
|
-
const exp = corresponding?.expectedNumber != null ? String(corresponding.expectedNumber) : "?";
|
|
6000
|
-
const got = corresponding?.receivedNumber != null ? String(corresponding.receivedNumber) : String(relevant.statusCode ?? "?");
|
|
6001
|
-
return `
|
|
6002
|
-
Expected: ${ansi.yellow(exp)} Received: ${ansi.yellow(got)}`;
|
|
6003
|
-
}
|
|
6004
|
-
return "";
|
|
6005
|
-
})();
|
|
6006
|
-
const whyLines = importantMessages(relevant.json).map((msg) => `
|
|
6007
|
-
Why: ${ansi.white(msg)}`).slice(0, 1).join("");
|
|
6008
|
-
const diffLines = (() => {
|
|
6009
|
-
const rightActual = parsedActual ?? relevant.json;
|
|
6010
|
-
if (!parsedExpected || !rightActual) {
|
|
6011
|
-
return "";
|
|
6012
|
-
}
|
|
6013
|
-
const changes = jsonDiff(parsedExpected, rightActual);
|
|
6014
|
-
if (!changes.length) {
|
|
6015
|
-
return "";
|
|
6016
|
-
}
|
|
6017
|
-
const head = "\n Diff:";
|
|
6018
|
-
const body = changes.map((change) => {
|
|
6019
|
-
const marker = change.kind === "added" ? "+" : change.kind === "removed" ? "-" : "~";
|
|
6020
|
-
const previewText = change.preview ? `: ${ansi.dim(change.preview)}` : "";
|
|
6021
|
-
return `
|
|
6022
|
-
${marker} ${change.path}${previewText}`;
|
|
6023
|
-
}).join("");
|
|
6024
|
-
return head + body;
|
|
6025
|
-
})();
|
|
6026
|
-
parts.push(line1, expVsAct, whyLines, diffLines, "\n");
|
|
6027
|
-
out.push(...parts.filter(Boolean));
|
|
6028
|
-
} else if (!hasTransport && !relevant && HEADLAMP_HTTP_SHOW_MISS()) {
|
|
6029
|
-
out.push(
|
|
6030
|
-
" HTTP:",
|
|
6031
|
-
`
|
|
6032
|
-
${ansi.dim("No relevant HTTP exchange found. (HEADLAMP_HTTP_MISS=0 to hide)")}`,
|
|
6033
|
-
"\n"
|
|
6034
|
-
);
|
|
6035
|
-
}
|
|
6036
|
-
}
|
|
6037
|
-
}
|
|
6038
|
-
const minimalInfo = msgLines.every((ln) => !ln.trim());
|
|
6039
|
-
if (minimalInfo) {
|
|
6040
|
-
try {
|
|
6041
|
-
out.push(...buildThrownSection(assertion.failureDetails || []));
|
|
6042
|
-
} catch {
|
|
6043
|
-
}
|
|
6044
|
-
}
|
|
6045
|
-
out.push(...buildConsoleSection(stripBridgeEventsFromConsole(file.console ?? null)));
|
|
6046
|
-
if (ctx.showStacks && stackPreview.length === 0) {
|
|
6047
|
-
const tail = mergedForStack.filter((ln) => isStackLine(stripAnsiSimple(ln))).slice(-4).map((ln) => ` ${colorStackLine(String(ln), ctx.projectHint)}`);
|
|
6048
|
-
if (tail.length) {
|
|
6049
|
-
out.push(ansi.dim(" Stack:"), ...tail, "");
|
|
6050
|
-
}
|
|
6051
|
-
}
|
|
6052
|
-
out.push(drawFailLine(), "");
|
|
6053
|
-
}
|
|
6054
|
-
}
|
|
6059
|
+
var renderFooter = (data) => {
|
|
6055
6060
|
const failedCount = data.aggregated.numFailedTests;
|
|
6056
|
-
|
|
6057
|
-
|
|
6058
|
-
|
|
6059
|
-
|
|
6061
|
+
return [
|
|
6062
|
+
drawRule(BackgroundColors.Failure(ansi.white(` Failed Tests ${failedCount} `))),
|
|
6063
|
+
"",
|
|
6064
|
+
vitestFooter(data.aggregated)
|
|
6065
|
+
];
|
|
6060
6066
|
};
|
|
6067
|
+
var renderVitestFromJestJSON = (data, ctx, opts) => pipe(
|
|
6068
|
+
concat(
|
|
6069
|
+
renderRunHeader({ ctx, onlyFailures: Boolean(opts?.onlyFailures) }),
|
|
6070
|
+
...data.testResults.map(
|
|
6071
|
+
(file) => renderFileBlock(file, { ctx, onlyFailures: Boolean(opts?.onlyFailures) })
|
|
6072
|
+
),
|
|
6073
|
+
renderFooter(data)
|
|
6074
|
+
),
|
|
6075
|
+
joinLines
|
|
6076
|
+
);
|
|
6061
6077
|
|
|
6062
6078
|
// src/lib/formatter/bridge/tryBridgeFallback.ts
|
|
6063
6079
|
var tryBridgeFallback = (raw, ctx, opts) => {
|