From 9ad464bd38f231ed60d7932fe5df9030cfa69534 Mon Sep 17 00:00:00 2001 From: mostafanazari702 Date: Fri, 22 May 2026 23:37:48 +0200 Subject: [PATCH] split err.stack at the trailing frame block so embedded stacks in err.message render once --- lib/reporters/base.js | 56 +++++++++++++++++++++++++------------ test/reporters/base.spec.js | 25 +++++++++++++++++ 2 files changed, 63 insertions(+), 18 deletions(-) diff --git a/lib/reporters/base.js b/lib/reporters/base.js index 4a0bae09b9..a5a2b5e511 100644 --- a/lib/reporters/base.js +++ b/lib/reporters/base.js @@ -404,38 +404,58 @@ var getFullErrorStack = function (err, seen) { } var message; + var usedInspect = false; if (typeof err.inspect === "function") { message = err.inspect() + ""; + usedInspect = true; } else if (err.message && typeof err.message.toString === "function") { message = err.message + ""; } else { message = ""; } - var msg; - var stack = err.stack || message; - var index = message ? stack.indexOf(message) : -1; + var rawStack = err.stack || message; + var lines = rawStack.split("\n"); + var frameStart = lines.length; + for (var i = lines.length - 1; i >= 0; i--) { + if (/^\s+at\s/.test(lines[i])) { + frameStart = i; + } else { + break; + } + } - if (index === -1) { - msg = message; + var msg; + var stack; + var splitSucceeded = false; + if (frameStart < lines.length) { + stack = lines.slice(frameStart).join("\n"); + msg = usedInspect ? message : lines.slice(0, frameStart).join("\n"); + splitSucceeded = true; } else { - index += message.length; - msg = stack.slice(0, index); - // remove msg from stack - stack = stack.slice(index + 1); - - if (err.cause) { - seen = seen || new Set(); - seen.add(err); - const causeStack = getFullErrorStack(err.cause, seen); - stack += - "\n Caused by: " + - causeStack.msg + - (causeStack.stack ? "\n" + causeStack.stack : ""); + var index = message ? rawStack.indexOf(message) : -1; + if (index === -1) { + msg = message; + stack = rawStack; + } else { + index += message.length; + msg = rawStack.slice(0, index); + stack = rawStack.slice(index + 1); + splitSucceeded = true; } } + if (splitSucceeded && err.cause) { + seen = seen || new Set(); + seen.add(err); + const causeStack = getFullErrorStack(err.cause, seen); + stack += + "\n Caused by: " + + causeStack.msg + + (causeStack.stack ? "\n" + causeStack.stack : ""); + } + return { message, msg, diff --git a/test/reporters/base.spec.js b/test/reporters/base.spec.js index d74e009e54..a8beaca0d0 100644 --- a/test/reporters/base.spec.js +++ b/test/reporters/base.spec.js @@ -568,6 +568,31 @@ describe("Base reporter", function () { ); }); + it("should not duplicate the message when it contains an embedded stack", function () { + var embeddedMessage = + "An error occured with following trace:\n\nError\n at inner (foo.js:1:1)\n at outer (foo.js:2:2)"; + var err = { + message: embeddedMessage, + stack: + "Error: " + + embeddedMessage + + "\n at outer (foo.js:2:2)\n at runTest (lib/runner.js:1:1)", + showDiff: false, + }; + var test = makeTest(err); + + list([test]); + + var errOut = stdout.join("\n").trim(); + expect( + errOut.match(/Error: An error occured with following trace:/g), + "to have length", + 1, + ); + expect(errOut, "to contain", "at runTest (lib/runner.js:1:1)"); + expect(errOut.match(/at inner \(foo\.js:1:1\)/g), "to have length", 1); + }); + it("should not add cause trail if error does not contain message", function () { var err = { message: "Error",