Source code
Revision control
Copy as Markdown
Other Tools
Test Info: Warnings
- This test gets skipped with pattern: asan
- Manifest: devtools/client/webconsole/test/browser/_webconsole.toml
/* Any copyright is dedicated to the Public Domain.
// Test that stack traces are shown when primitive values are thrown instead of
// error objects.
"use strict";
const TEST_URI = `data:text/html,<!DOCTYPE html><meta charset=utf8>Test uncaught exception`;
add_task(async function () {
const hud = await openNewTabAndConsole(TEST_URI);
await checkThrowingWithStack(hud, `"tomato"`, "Uncaught tomato");
await checkThrowingWithStack(hud, `""`, "Uncaught <empty string>");
await checkThrowingWithStack(hud, `42`, "Uncaught 42");
await checkThrowingWithStack(hud, `0`, "Uncaught 0");
await checkThrowingWithStack(hud, `null`, "Uncaught null");
await checkThrowingWithStack(hud, `undefined`, "Uncaught undefined");
await checkThrowingWithStack(hud, `false`, "Uncaught false");
await checkThrowingWithStack(
hud,
`new Error("watermelon")`,
"Uncaught Error: watermelon"
);
await checkThrowingWithStack(
hud,
`(err = new Error("lettuce"), err.name = "VegetableError", err)`,
"Uncaught VegetableError: lettuce"
);
await checkThrowingWithStack(
hud,
`{ fav: "eggplant" }`,
`Uncaught Object { fav: "eggplant" }`
);
info("Check custom error with name and message getters");
// register the class
await SpecialPowers.spawn(gBrowser.selectedBrowser, [], function () {
const script = content.document.createElement("script");
script.append(
content.document.createTextNode(
`
class CustomError extends Error {
get name() {
return "CustomErrorName";
}
get message() {
return "custom-error-message";
}
}`.trim()
)
);
content.document.body.append(script);
});
await checkThrowingWithStack(
hud,
`new CustomError()`,
"Uncaught CustomErrorName: custom-error-message",
// Additional frames: the stacktrace contains the CustomError call
[1]
);
info("Check that object in errors can be expanded");
const rejectedObjectMessage = findErrorMessage(hud, "eggplant");
const oi = rejectedObjectMessage.querySelector(".tree");
ok(true, "The object was rendered in an ObjectInspector");
info("Expanding the object");
const onOiExpanded = waitFor(() => {
return oi.querySelectorAll(".node").length === 3;
});
oi.querySelector(".theme-twisty").click();
await onOiExpanded;
ok(
oi.querySelector(".theme-twisty").classList.contains("open"),
"Object expanded"
);
// The object inspector now looks like:
// Object { fav: "eggplant" }
// | fav: "eggplant"
// | <prototype>: Object { ... }
const oiNodes = oi.querySelectorAll(".node");
is(oiNodes.length, 3, "There is the expected number of nodes in the tree");
ok(oiNodes[0].textContent.includes(`Object { fav: "eggplant" }`));
ok(oiNodes[1].textContent.includes(`fav: "eggplant"`));
ok(oiNodes[2].textContent.includes(`<prototype>: Object { \u2026 }`));
});
async function checkThrowingWithStack(
hud,
expression,
expectedMessage,
additionalFrameLines = []
) {
await SpecialPowers.spawn(
gBrowser.selectedBrowser,
[expression],
function (expr) {
const script = content.document.createElement("script");
script.append(
content.document.createTextNode(`
a = () => {throw ${expr}};
b = () => a();
c = () => b();
d = () => c();
d();
`)
);
content.document.body.append(script);
script.remove();
}
);
return checkMessageStack(hud, expectedMessage, [
...additionalFrameLines,
2,
3,
4,
5,
6,
]);
}