356 lines
10 KiB
JavaScript
356 lines
10 KiB
JavaScript
/* Any copyright is dedicated to the Public Domain.
|
|
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
|
|
|
// Check if errors are logged as expected
|
|
|
|
"use strict";
|
|
|
|
const TEST_URI = `data:text/html;charset=utf8,<!DOCTYPE html>Errors`;
|
|
|
|
add_task(async function () {
|
|
const hud = await openNewTabAndConsole(TEST_URI);
|
|
|
|
const TEST_DATA = [
|
|
{
|
|
desc: "ReferenceError: asdf is not defined",
|
|
expression: `
|
|
function bar() {
|
|
asdf()
|
|
}
|
|
function foo() {
|
|
bar()
|
|
}
|
|
|
|
foo()`,
|
|
expected: `Uncaught ReferenceError: asdf is not defined`,
|
|
},
|
|
|
|
{
|
|
desc: "SyntaxError: redeclaration of let a",
|
|
expression: `let a, a;`,
|
|
expected: `SyntaxError: redeclaration of let a`,
|
|
assert: messageEl => {
|
|
info("Check that error notes are displayed");
|
|
const notes = messageEl.querySelectorAll(".error-note .message-body");
|
|
is(notes.length, 1, "There's only one note");
|
|
is(notes[0].innerText, "note: Previously declared at line 1, column 5");
|
|
},
|
|
},
|
|
|
|
{
|
|
desc: "TypeError longString message",
|
|
expression: `throw new Error("Long error ".repeat(10000))`,
|
|
expected: `Uncaught Error: Long error Long error`,
|
|
},
|
|
|
|
{
|
|
desc: `throw string with URL`,
|
|
expression: `throw "“https://evil.com/?${"a".repeat(
|
|
200
|
|
)}“ is evil and “https://not-so-evil.com/?${"b".repeat(
|
|
200
|
|
)}“ is not good either"`,
|
|
expected: `Uncaught “https://evil.com/?${"a".repeat(
|
|
200
|
|
)}“ is evil and “https://not-so-evil.com/?${"b".repeat(
|
|
200
|
|
)}“ is not good either`,
|
|
assert: messageEl => {
|
|
const links = messageEl.querySelectorAll(".message-body a.cropped-url");
|
|
is(links.length, 2, "2 links are displayed");
|
|
|
|
const evilURL = `https://evil.com/?${"a".repeat(200)}`;
|
|
const badURL = `https://not-so-evil.com/?${"b".repeat(200)}`;
|
|
|
|
is(
|
|
links[0].getAttribute("href"),
|
|
evilURL,
|
|
"first link has expected href"
|
|
);
|
|
is(
|
|
links[0].getAttribute("title"),
|
|
evilURL,
|
|
"first link has expected title"
|
|
);
|
|
|
|
is(
|
|
links[1].getAttribute("href"),
|
|
badURL,
|
|
"second link has expected href"
|
|
);
|
|
is(
|
|
links[1].getAttribute("title"),
|
|
badURL,
|
|
"second link has expected title"
|
|
);
|
|
},
|
|
},
|
|
|
|
{
|
|
desc: `throw ""`,
|
|
expression: `throw ""`,
|
|
expected: `Uncaught <empty string>`,
|
|
},
|
|
{
|
|
desc: `throw "tomato"`,
|
|
expression: `throw "tomato"`,
|
|
expected: `Uncaught tomato`,
|
|
},
|
|
{
|
|
desc: `throw false`,
|
|
expression: `throw false`,
|
|
expected: `Uncaught false`,
|
|
},
|
|
{ desc: `throw 0`, expression: `throw 0`, expected: `Uncaught 0` },
|
|
{ desc: `throw null`, expression: `throw null`, expected: `Uncaught null` },
|
|
{
|
|
desc: `throw undefined`,
|
|
expression: `throw undefined`,
|
|
expected: `Uncaught undefined`,
|
|
},
|
|
{
|
|
desc: `throw Symbol`,
|
|
expression: `throw Symbol("potato")`,
|
|
expected: `Uncaught Symbol("potato")`,
|
|
},
|
|
{
|
|
desc: `throw Object`,
|
|
expression: `throw {vegetable: "cucumber"}`,
|
|
expected: `Uncaught Object { vegetable: "cucumber" }`,
|
|
},
|
|
{
|
|
desc: `throw Error Object`,
|
|
expression: `throw new Error("pumpkin")`,
|
|
expected: `Uncaught Error: pumpkin`,
|
|
},
|
|
{
|
|
desc: `throw Error Object with custom name`,
|
|
expression: `
|
|
var err = new Error("pineapple");
|
|
err.name = "JuicyError";
|
|
err.flavor = "delicious";
|
|
throw err;`,
|
|
expected: `Uncaught JuicyError: pineapple`,
|
|
},
|
|
{
|
|
desc: `throw Error Object with error cause`,
|
|
expression: `
|
|
var originalError = new SyntaxError("original error")
|
|
var err = new Error("something went wrong", {
|
|
cause: originalError
|
|
});
|
|
throw err;`,
|
|
expected: `Uncaught Error: something went wrong`,
|
|
assert: messageEl => {
|
|
const causeEl = messageEl.querySelector(".error-rep-cause");
|
|
is(
|
|
causeEl.innerText,
|
|
`Caused by: SyntaxError: original error`,
|
|
"Caused by show expected"
|
|
);
|
|
},
|
|
},
|
|
{
|
|
desc: `throw Error Object with cause chain`,
|
|
expression: `
|
|
var a = new Error("err-a")
|
|
var b = new Error("err-b", { cause: a })
|
|
var c = new Error("err-c", { cause: b })
|
|
var d = new Error("err-d", { cause: c })
|
|
throw d;`,
|
|
expected: `Uncaught Error: err-d`,
|
|
assert: messageEl => {
|
|
const causeEl = messageEl.querySelector(".error-rep-cause");
|
|
is(
|
|
causeEl.innerText,
|
|
[
|
|
"Caused by: Error: err-c",
|
|
"Caused by: Error: err-b",
|
|
"Caused by: Error: err-a",
|
|
].join("\n"),
|
|
"The cause chain is properly displayed"
|
|
);
|
|
},
|
|
},
|
|
{
|
|
desc: `throw Error Object with cyclical cause chain`,
|
|
expression: `
|
|
var a = new Error("err-a", { cause: b})
|
|
var b = new Error("err-b", { cause: a })
|
|
throw b;`,
|
|
expected: `Uncaught Error: err-b`,
|
|
assert: messageEl => {
|
|
const causeEl = messageEl.querySelector(".error-rep-cause");
|
|
is(
|
|
causeEl.innerText,
|
|
// TODO: This is not how we should display cyclical cause chain, but we have it here
|
|
// to ensure it's displaying something that makes _some_ sense.
|
|
// This should be properly handled in Bug 1719605.
|
|
[
|
|
"Caused by: Error: err-a",
|
|
"Caused by: Error: err-b",
|
|
"Caused by: Error: err-a",
|
|
].join("\n"),
|
|
"The cyclical cause chain is properly displayed"
|
|
);
|
|
},
|
|
},
|
|
{
|
|
desc: `throw Error Object with falsy cause`,
|
|
expression: `throw new Error("null cause", { cause: null });`,
|
|
expected: `Uncaught Error: null cause`,
|
|
assert: messageEl => {
|
|
const causeEl = messageEl.querySelector(".error-rep-cause");
|
|
is(
|
|
causeEl.innerText,
|
|
"Caused by: null",
|
|
"The null cause is properly displayed"
|
|
);
|
|
},
|
|
},
|
|
{
|
|
desc: `throw Error Object with number cause`,
|
|
expression: `throw new Error("number cause", { cause: 0 });`,
|
|
expected: `Uncaught Error: number cause`,
|
|
assert: messageEl => {
|
|
const causeEl = messageEl.querySelector(".error-rep-cause");
|
|
is(
|
|
causeEl.innerText,
|
|
"Caused by: 0",
|
|
"The 0 cause is properly displayed"
|
|
);
|
|
},
|
|
},
|
|
{
|
|
desc: `throw Error Object with string cause`,
|
|
expression: `throw new Error("string cause", { cause: "cause message" });`,
|
|
expected: `Uncaught Error: string cause`,
|
|
assert: messageEl => {
|
|
const causeEl = messageEl.querySelector(".error-rep-cause");
|
|
is(
|
|
causeEl.innerText,
|
|
`Caused by: "cause message"`,
|
|
"The string cause is properly displayed"
|
|
);
|
|
},
|
|
},
|
|
{
|
|
desc: `throw Error Object with object cause`,
|
|
expression: `throw new Error("object cause", { cause: { code: 234, message: "ERR_234"} });`,
|
|
expected: `Uncaught Error: object cause`,
|
|
assert: messageEl => {
|
|
const causeEl = messageEl.querySelector(".error-rep-cause");
|
|
is(
|
|
causeEl.innerText,
|
|
`Caused by: Object { … }`,
|
|
"The object cause is properly displayed"
|
|
);
|
|
},
|
|
},
|
|
{
|
|
desc: `Promise reject ""`,
|
|
expression: `Promise.reject("")`,
|
|
expected: `Uncaught (in promise) <empty string>`,
|
|
},
|
|
{
|
|
desc: `Promise reject "tomato"`,
|
|
expression: `Promise.reject("tomato")`,
|
|
expected: `Uncaught (in promise) tomato`,
|
|
},
|
|
{
|
|
desc: `Promise reject false`,
|
|
expression: `Promise.reject(false)`,
|
|
expected: `Uncaught (in promise) false`,
|
|
},
|
|
{
|
|
desc: `Promise reject 0`,
|
|
expression: `Promise.reject(0)`,
|
|
expected: `Uncaught (in promise) 0`,
|
|
},
|
|
{
|
|
desc: `Promise reject null`,
|
|
expression: `Promise.reject(null)`,
|
|
expected: `Uncaught (in promise) null`,
|
|
},
|
|
{
|
|
desc: `Promise reject undefined`,
|
|
expression: `Promise.reject(undefined)`,
|
|
expected: `Uncaught (in promise) undefined`,
|
|
},
|
|
{
|
|
desc: `Promise reject Symbol`,
|
|
expression: `Promise.reject(Symbol("potato"))`,
|
|
expected: `Uncaught (in promise) Symbol("potato")`,
|
|
},
|
|
{
|
|
desc: `Promise reject Object`,
|
|
expression: `Promise.reject({vegetable: "cucumber"})`,
|
|
expected: `Uncaught (in promise) Object { vegetable: "cucumber" }`,
|
|
},
|
|
{
|
|
desc: `Promise reject Error Object`,
|
|
expression: `Promise.reject(new Error("pumpkin"))`,
|
|
expected: `Uncaught (in promise) Error: pumpkin`,
|
|
},
|
|
{
|
|
desc: `Promise reject Error Object with custom name`,
|
|
expression: `
|
|
var err = new Error("pineapple");
|
|
err.name = "JuicyError";
|
|
err.flavor = "delicious";
|
|
Promise.reject(err);`,
|
|
expected: `Uncaught (in promise) JuicyError: pineapple`,
|
|
},
|
|
{
|
|
desc: `Promise reject Error Object with error cause`,
|
|
expression: `Promise.resolve().then(() => {
|
|
try {
|
|
unknownFunc();
|
|
} catch(e) {
|
|
throw new Error("something went wrong", { cause: e })
|
|
}
|
|
})`,
|
|
expected: `Uncaught (in promise) Error: something went wrong`,
|
|
assert: messageEl => {
|
|
const causeEl = messageEl.querySelector(".error-rep-cause");
|
|
is(
|
|
causeEl.innerText,
|
|
`Caused by: ReferenceError: unknownFunc is not defined`,
|
|
"The cause is properly displayed"
|
|
);
|
|
},
|
|
},
|
|
{
|
|
desc: `SuppressedError`,
|
|
expression: `throw new SuppressedError(
|
|
new Error("foo"),
|
|
new Error("bar"),
|
|
"the suppressed error message"
|
|
)`,
|
|
expected: `Uncaught SuppressedError: the suppressed error message`,
|
|
},
|
|
];
|
|
|
|
for (const { desc, expression, expected, assert } of TEST_DATA) {
|
|
info(`Check error: ${desc}`);
|
|
|
|
const onErrorLogged = waitForMessageByType(hud, expected, ".error");
|
|
|
|
await SpecialPowers.spawn(
|
|
gBrowser.selectedBrowser,
|
|
[expression],
|
|
function (expr) {
|
|
const script = content.document.createElement("script");
|
|
script.append(content.document.createTextNode(expr));
|
|
content.document.body.append(script);
|
|
script.remove();
|
|
}
|
|
);
|
|
|
|
const message = await onErrorLogged;
|
|
if (assert) {
|
|
assert(message.node);
|
|
}
|
|
}
|
|
});
|