Skip to content

Commit d385854

Browse files
fix(reporter): split err.stack at trailing frame block so embedded stacks render once
1 parent f6e8d9a commit d385854

2 files changed

Lines changed: 105 additions & 18 deletions

File tree

lib/reporters/base.mjs

Lines changed: 45 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -402,38 +402,65 @@ var getFullErrorStack = function (err, seen) {
402402
}
403403

404404
var message;
405+
var usedInspect = false;
405406

406407
if (typeof err.inspect === "function") {
407408
message = err.inspect() + "";
409+
usedInspect = true;
408410
} else if (err.message && typeof err.message.toString === "function") {
409411
message = err.message + "";
410412
} else {
411413
message = "";
412414
}
413415

414-
var msg;
415-
var stack = err.stack || message;
416-
var index = message ? stack.indexOf(message) : -1;
416+
var rawStack = err.stack || message;
417+
var lines = rawStack.split("\n");
418+
var lastLine = lines.length - 1;
419+
while (lastLine >= 0 && lines[lastLine] === "") {
420+
lastLine--;
421+
}
422+
var frameStart = lastLine + 1;
423+
for (var i = lastLine; i >= 0; i--) {
424+
if (/^\s+at\s/.test(lines[i])) {
425+
frameStart = i;
426+
} else {
427+
break;
428+
}
429+
}
417430

418-
if (index === -1) {
419-
msg = message;
431+
var msg;
432+
var stack;
433+
var splitSucceeded = false;
434+
if (frameStart <= lastLine) {
435+
stack = lines.slice(frameStart, lastLine + 1).join("\n");
436+
msg =
437+
usedInspect || frameStart === 0
438+
? message
439+
: lines.slice(0, frameStart).join("\n");
440+
splitSucceeded = true;
420441
} else {
421-
index += message.length;
422-
msg = stack.slice(0, index);
423-
// remove msg from stack
424-
stack = stack.slice(index + 1);
425-
426-
if (err.cause) {
427-
seen = seen || new Set();
428-
seen.add(err);
429-
const causeStack = getFullErrorStack(err.cause, seen);
430-
stack +=
431-
"\n Caused by: " +
432-
causeStack.msg +
433-
(causeStack.stack ? "\n" + causeStack.stack : "");
442+
var index = message ? rawStack.indexOf(message) : -1;
443+
if (index === -1) {
444+
msg = message;
445+
stack = rawStack;
446+
} else {
447+
index += message.length;
448+
msg = rawStack.slice(0, index);
449+
stack = rawStack.slice(index + 1);
450+
splitSucceeded = true;
434451
}
435452
}
436453

454+
if (splitSucceeded && err.cause) {
455+
seen = seen || new Set();
456+
seen.add(err);
457+
const causeStack = getFullErrorStack(err.cause, seen);
458+
stack +=
459+
"\n Caused by: " +
460+
causeStack.msg +
461+
(causeStack.stack ? "\n" + causeStack.stack : "");
462+
}
463+
437464
return {
438465
message,
439466
msg,

test/reporters/base.spec.js

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -491,6 +491,66 @@ describe("Base reporter", function () {
491491
expect(errOut, "to be", "1) test title:\n Error\n foo\n bar");
492492
});
493493

494+
describe("message and stack splitting", function () {
495+
it("should render the message and frames when the stack is only frames", function () {
496+
var err = {
497+
message: "Error",
498+
stack: " at foo (foo.js:1:1)\n at bar (bar.js:2:2)",
499+
showDiff: false,
500+
};
501+
var test = makeTest(err);
502+
503+
list([test]);
504+
505+
var errOut = stdout.join("\n").trim();
506+
expect(
507+
errOut,
508+
"to be",
509+
"1) test title:\n Error\n at foo (foo.js:1:1)\n at bar (bar.js:2:2)",
510+
);
511+
});
512+
513+
it("should not duplicate an embedded stack when the message's frames are rewritten in the stack", function () {
514+
var embeddedMessage =
515+
"An error occured with following trace:\n\nError\n at inner (/original/foo.js:1:1)";
516+
var rewritten = embeddedMessage.replace(/\/original\//g, "/filtered/");
517+
var err = {
518+
message: embeddedMessage,
519+
stack: "Error: " + rewritten + "\n at runTest (lib/runner.js:1:1)",
520+
showDiff: false,
521+
};
522+
var test = makeTest(err);
523+
524+
list([test]);
525+
526+
var errOut = stdout.join("\n").trim();
527+
expect(
528+
errOut,
529+
"to be",
530+
"1) test title:\n Error: An error occured with following trace:\n\nError\n at inner (/filtered/foo.js:1:1)\n at runTest (lib/runner.js:1:1)",
531+
);
532+
});
533+
534+
it("should split identically when the rewritten stack has a trailing newline", function () {
535+
var embeddedMessage =
536+
"An error occured with following trace:\n\nError\n at inner (/original/foo.js:1:1)";
537+
var rewritten = embeddedMessage.replace(/\/original\//g, "/filtered/");
538+
var err = {
539+
message: embeddedMessage,
540+
stack: "Error: " + rewritten + "\n at runTest (lib/runner.js:1:1)\n",
541+
showDiff: false,
542+
};
543+
var test = makeTest(err);
544+
list([test]);
545+
var errOut = stdout.join("\n").trim();
546+
expect(
547+
errOut,
548+
"to be",
549+
"1) test title:\n Error: An error occured with following trace:\n\nError\n at inner (/filtered/foo.js:1:1)\n at runTest (lib/runner.js:1:1)",
550+
);
551+
});
552+
});
553+
494554
describe("error causes", function () {
495555
it("should append any error cause trail to stack traces", function () {
496556
var err = {

0 commit comments

Comments
 (0)