summaryrefslogtreecommitdiffstats
path: root/devtools/client/webconsole/test/browser/browser_webconsole_stubs_page_error.js
diff options
context:
space:
mode:
Diffstat (limited to 'devtools/client/webconsole/test/browser/browser_webconsole_stubs_page_error.js')
-rw-r--r--devtools/client/webconsole/test/browser/browser_webconsole_stubs_page_error.js254
1 files changed, 254 insertions, 0 deletions
diff --git a/devtools/client/webconsole/test/browser/browser_webconsole_stubs_page_error.js b/devtools/client/webconsole/test/browser/browser_webconsole_stubs_page_error.js
new file mode 100644
index 0000000000..d6610b7309
--- /dev/null
+++ b/devtools/client/webconsole/test/browser/browser_webconsole_stubs_page_error.js
@@ -0,0 +1,254 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const {
+ STUBS_UPDATE_ENV,
+ createCommandsForTab,
+ getCleanedPacket,
+ getSerializedPacket,
+ getStubFile,
+ writeStubsToFile,
+} = require(`${CHROME_URL_ROOT}stub-generator-helpers`);
+
+const TEST_URI =
+ "https://example.com/browser/devtools/client/webconsole/test/browser/test-console-api.html";
+const STUB_FILE = "pageError.js";
+
+add_task(async function () {
+ await pushPref("javascript.options.asyncstack_capture_debuggee_only", false);
+
+ const isStubsUpdate = Services.env.get(STUBS_UPDATE_ENV) == "true";
+ info(`${isStubsUpdate ? "Update" : "Check"} ${STUB_FILE}`);
+
+ const generatedStubs = await generatePageErrorStubs();
+
+ if (isStubsUpdate) {
+ await writeStubsToFile(STUB_FILE, generatedStubs);
+ ok(true, `${STUB_FILE} was updated`);
+ return;
+ }
+
+ const existingStubs = getStubFile(STUB_FILE);
+ const FAILURE_MSG =
+ "The pageError stubs file needs to be updated by running `" +
+ `mach test ${getCurrentTestFilePath()} --headless --setenv WEBCONSOLE_STUBS_UPDATE=true` +
+ "`";
+
+ if (generatedStubs.size !== existingStubs.rawPackets.size) {
+ ok(false, FAILURE_MSG);
+ return;
+ }
+
+ let failed = false;
+ for (const [key, packet] of generatedStubs) {
+ const packetStr = getSerializedPacket(packet, {
+ sortKeys: true,
+ replaceActorIds: true,
+ });
+ const existingPacketStr = getSerializedPacket(
+ existingStubs.rawPackets.get(key),
+ { sortKeys: true, replaceActorIds: true }
+ );
+ is(packetStr, existingPacketStr, `"${key}" packet has expected value`);
+ failed = failed || packetStr !== existingPacketStr;
+ }
+
+ if (failed) {
+ ok(false, FAILURE_MSG);
+ } else {
+ ok(true, "Stubs are up to date");
+ }
+});
+
+async function generatePageErrorStubs() {
+ const stubs = new Map();
+
+ const tab = await addTab(TEST_URI);
+ const commands = await createCommandsForTab(tab);
+ await commands.targetCommand.startListening();
+ const resourceCommand = commands.resourceCommand;
+
+ // The resource-watcher only supports a single call to watch/unwatch per
+ // instance, so we attach a unique watch callback, which will forward the
+ // resource to `handleErrorMessage`, dynamically updated for each command.
+ let handleErrorMessage = function () {};
+
+ const onErrorMessageAvailable = resources => {
+ for (const resource of resources) {
+ handleErrorMessage(resource);
+ }
+ };
+ await resourceCommand.watchResources([resourceCommand.TYPES.ERROR_MESSAGE], {
+ onAvailable: onErrorMessageAvailable,
+ });
+
+ for (const [key, code] of getCommands()) {
+ const onPageError = new Promise(resolve => {
+ handleErrorMessage = packet => resolve(packet);
+ });
+
+ // On e10s, the exception is triggered in child process
+ // and is ignored by test harness
+ // expectUncaughtException should be called for each uncaught exception.
+ if (!Services.appinfo.browserTabsRemoteAutostart) {
+ expectUncaughtException();
+ }
+
+ // Note: This needs to use ContentTask rather than SpecialPowers.spawn
+ // because the latter includes cross-process stack information.
+ await ContentTask.spawn(gBrowser.selectedBrowser, code, function (subCode) {
+ const script = content.document.createElement("script");
+ script.append(content.document.createTextNode(subCode));
+ content.document.body.append(script);
+ script.remove();
+ });
+
+ const packet = await onPageError;
+ stubs.set(key, getCleanedPacket(key, packet));
+ }
+
+ return stubs;
+}
+
+function getCommands() {
+ const pageError = new Map();
+
+ pageError.set(
+ "ReferenceError: asdf is not defined",
+ `
+ function bar() {
+ asdf()
+ }
+ function foo() {
+ bar()
+ }
+
+ foo()
+`
+ );
+
+ pageError.set(
+ "SyntaxError: redeclaration of let a",
+ `
+ let a, a;
+`
+ );
+
+ pageError.set(
+ "TypeError longString message",
+ `throw new Error("Long error ".repeat(10000))`
+ );
+
+ const evilDomain = `https://evil.com/?`;
+ const badDomain = `https://not-so-evil.com/?`;
+ const paramLength = 200;
+ const longParam = "a".repeat(paramLength);
+
+ const evilURL = `${evilDomain}${longParam}`;
+ const badURL = `${badDomain}${longParam}`;
+
+ pageError.set(
+ `throw string with URL`,
+ `throw "“${evilURL}“ is evil and “${badURL}“ is not good either"`
+ );
+
+ pageError.set(`throw ""`, `throw ""`);
+ pageError.set(`throw "tomato"`, `throw "tomato"`);
+ pageError.set(`throw false`, `throw false`);
+ pageError.set(`throw 0`, `throw 0`);
+ pageError.set(`throw null`, `throw null`);
+ pageError.set(`throw undefined`, `throw undefined`);
+ pageError.set(`throw Symbol`, `throw Symbol("potato")`);
+ pageError.set(`throw Object`, `throw {vegetable: "cucumber"}`);
+ pageError.set(`throw Error Object`, `throw new Error("pumpkin")`);
+ pageError.set(
+ `throw Error Object with custom name`,
+ `
+ var err = new Error("pineapple");
+ err.name = "JuicyError";
+ err.flavor = "delicious";
+ throw err;
+ `
+ );
+ pageError.set(
+ `throw Error Object with error cause`,
+ `
+ var originalError = new SyntaxError("original error")
+ var err = new Error("something went wrong", {
+ cause: originalError
+ });
+ throw err;
+ `
+ );
+ pageError.set(
+ `throw Error Object with cause chain`,
+ `
+ 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;
+ `
+ );
+ pageError.set(
+ `throw Error Object with cyclical cause chain`,
+ `
+ var a = new Error("err-a", { cause: b})
+ var b = new Error("err-b", { cause: a })
+ throw b;
+ `
+ );
+ pageError.set(
+ `throw Error Object with falsy cause`,
+ `throw new Error("null cause", { cause: null });`
+ );
+ pageError.set(
+ `throw Error Object with number cause`,
+ `throw new Error("number cause", { cause: 0 });`
+ );
+ pageError.set(
+ `throw Error Object with string cause`,
+ `throw new Error("string cause", { cause: "cause message" });`
+ );
+ pageError.set(
+ `throw Error Object with object cause`,
+ `throw new Error("object cause", { cause: { code: 234, message: "ERR_234"} });`
+ );
+ pageError.set(`Promise reject ""`, `Promise.reject("")`);
+ pageError.set(`Promise reject "tomato"`, `Promise.reject("tomato")`);
+ pageError.set(`Promise reject false`, `Promise.reject(false)`);
+ pageError.set(`Promise reject 0`, `Promise.reject(0)`);
+ pageError.set(`Promise reject null`, `Promise.reject(null)`);
+ pageError.set(`Promise reject undefined`, `Promise.reject(undefined)`);
+ pageError.set(`Promise reject Symbol`, `Promise.reject(Symbol("potato"))`);
+ pageError.set(
+ `Promise reject Object`,
+ `Promise.reject({vegetable: "cucumber"})`
+ );
+ pageError.set(
+ `Promise reject Error Object`,
+ `Promise.reject(new Error("pumpkin"))`
+ );
+ pageError.set(
+ `Promise reject Error Object with custom name`,
+ `
+ var err = new Error("pineapple");
+ err.name = "JuicyError";
+ err.flavor = "delicious";
+ Promise.reject(err);
+ `
+ );
+ pageError.set(
+ `Promise reject Error Object with error cause`,
+ `Promise.resolve().then(() => {
+ try {
+ unknownFunc();
+ } catch(e) {
+ throw new Error("something went wrong", { cause: e })
+ }
+ })`
+ );
+ return pageError;
+}