Files
bun.sh/test/js/bun/test/stack.test.ts
SUZUKI Sosuke 9479bb8a5b Enable async stack traces (#22517)
### What does this PR do?

Enables async stack traces

### How did you verify your code works?

Added tests

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2025-09-11 17:53:06 -07:00

153 lines
4.0 KiB
TypeScript

import { $ } from "bun";
import { expect, test } from "bun:test";
import "harness";
import { bunEnv, bunExe, normalizeBunSnapshot } from "harness";
import { join } from "node:path";
test("name property is used for function calls in Error.stack", () => {
function WRONG() {
return new Error().stack;
}
expect(WRONG()).not.toContain("at RIGHT");
expect(WRONG()).toContain("at WRONG");
Object.defineProperty(WRONG, "name", { value: "RIGHT" });
expect(WRONG()).not.toContain("at WRONG");
expect(WRONG()).toContain("at RIGHT");
});
test("name property is used for function calls in Bun.inspect", () => {
// ** whitespace **
// ** whitespace **
// ** whitespace **
// ** whitespace **
// ** whitespace **
// ** whitespace **
function WRONG() {
try {
throw new Error();
} catch (e) {
return Bun.inspect(e);
}
}
// ** whitespace **
// ** whitespace **
// ** whitespace **
// ** whitespace **
// ** whitespace **
// ** whitespace **
expect(WRONG()).not.toContain("at RIGHT");
expect(WRONG()).toContain("at WRONG");
Object.defineProperty(WRONG, "name", { value: "RIGHT" });
expect(WRONG()).not.toContain("at WRONG");
expect(WRONG()).toContain("at RIGHT");
});
test.todo("name property is used for function calls in Bun.inspect with bound object", () => {
// ** whitespace **
// ** whitespace **
// ** whitespace **
// ** whitespace **
// ** whitespace **
// ** whitespace **
let WRONG = function WRONG() {
try {
throw new Error();
} catch (e) {
return Bun.inspect(e);
}
};
WRONG = WRONG.bind({});
// ** whitespace **
// ** whitespace **
// ** whitespace **
// ** whitespace **
// ** whitespace **
// ** whitespace **
expect(WRONG()).not.toContain("at RIGHT");
expect(WRONG()).toContain("at WRONG");
Object.defineProperty(WRONG, "name", { value: "RIGHT", writable: true, configurable: true });
console.log(WRONG());
expect(WRONG()).not.toContain("at WRONG");
expect(WRONG()).toContain("at RIGHT");
});
test("err.line and err.column are set", () => {
expect([join(import.meta.dir, "err-stack-fixture.js")]).toRun(
JSON.stringify(
{
line: 3,
column: 17,
originalLine: 1,
originalColumn: 18,
},
null,
2,
) + "\n",
);
});
test("throwing inside an error suppresses the error and prints the stack", async () => {
$.throws(false);
$.env(bunEnv);
const result = await $`${bunExe()} run ${join(import.meta.dir, "err-custom-fixture.js")}`;
const { stderr, exitCode } = result;
expect(stderr.toString().trim().split("\n").slice(0, -1).join("\n").trim()).toMatchInlineSnapshot(`
"error: My custom error message
{
message: "My custom error message",
name: [Getter],
line: 42,
sourceURL: "http://example.com/test.js",
}
at http://example.com/test.js:42"
`);
expect(exitCode).toBe(1);
});
test("throwing inside an error suppresses the error and continues printing properties on the object", async () => {
$.throws(false);
$.env(bunEnv);
const result = await $`${bunExe()} run ${join(import.meta.dir, "err-fd-fixture.js")}`;
const { stderr, exitCode } = result;
expect(stderr.toString().trim()).toStartWith(`ENOENT: no such file or directory, open 'this-file-path-is-bad'
path: "this-file-path-is-bad",
syscall: "open",
errno: -2,
code: "ENOENT"
`);
expect(exitCode).toBe(1);
});
test("Async functions frame should be included in stack trace", async () => {
async function foo() {
return await bar();
}
async function bar() {
return await baz();
}
async function baz() {
await 1;
return await qux();
}
async function qux() {
return new Error("error from qux");
}
const error = await foo();
console.log(error.stack);
expect(normalizeBunSnapshot(error.stack!)).toMatchInlineSnapshot(`
"Error: error from qux
at qux (file:NN:NN)
at baz (file:NN:NN)
at async bar (file:NN:NN)
at async foo (file:NN:NN)
at async <anonymous> (file:NN:NN)"
`);
});