summaryrefslogtreecommitdiffstats
path: root/devtools/startup/tests
diff options
context:
space:
mode:
Diffstat (limited to 'devtools/startup/tests')
-rw-r--r--devtools/startup/tests/browser/browser.toml11
-rw-r--r--devtools/startup/tests/browser/browser_command_line_urls.js154
-rw-r--r--devtools/startup/tests/browser/browser_shim_disable_devtools.js165
-rw-r--r--devtools/startup/tests/browser/command-line.html10
-rw-r--r--devtools/startup/tests/browser/command-line.js3
-rw-r--r--devtools/startup/tests/xpcshell/.eslintrc.js5
-rw-r--r--devtools/startup/tests/xpcshell/test_devtools_shim.js202
-rw-r--r--devtools/startup/tests/xpcshell/xpcshell.toml6
8 files changed, 556 insertions, 0 deletions
diff --git a/devtools/startup/tests/browser/browser.toml b/devtools/startup/tests/browser/browser.toml
new file mode 100644
index 0000000000..ff9433663f
--- /dev/null
+++ b/devtools/startup/tests/browser/browser.toml
@@ -0,0 +1,11 @@
+[DEFAULT]
+tags = "devtools"
+subsuite = "devtools"
+support-files = [
+ "command-line.html",
+ "command-line.js",
+]
+
+["browser_command_line_urls.js"]
+
+["browser_shim_disable_devtools.js"]
diff --git a/devtools/startup/tests/browser/browser_command_line_urls.js b/devtools/startup/tests/browser/browser_command_line_urls.js
new file mode 100644
index 0000000000..4494d635c6
--- /dev/null
+++ b/devtools/startup/tests/browser/browser_command_line_urls.js
@@ -0,0 +1,154 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+/**
+ * Test command line processing of URLs meant to be intepreted by DevTools.
+ */
+
+/* eslint-env browser */
+
+const { DevToolsStartup } = ChromeUtils.importESModule(
+ "resource:///modules/DevToolsStartup.sys.mjs"
+);
+
+const { require } = ChromeUtils.importESModule(
+ "resource://devtools/shared/loader/Loader.sys.mjs"
+);
+const { gDevTools } = require("devtools/client/framework/devtools");
+
+const URL_ROOT = "https://example.org/browser/devtools/startup/tests/browser/";
+
+const startup = new DevToolsStartup();
+// The feature covered here only work when calling firefox from command line
+// while it is already opened. So fake Firefox being already opened:
+startup.initialized = true;
+
+add_task(async function ignoredUrls() {
+ const tabCount = gBrowser.tabs.length;
+
+ // We explicitely try to ignore these URL which looks like line, but are passwords
+ sendUrlViaCommandLine("https://foo@user:123");
+ sendUrlViaCommandLine("https://foo@user:123");
+ sendUrlViaCommandLine("https://foo@123:456");
+
+ // The following is an invalid URL (domain with space)
+ sendUrlViaCommandLine("https://foo /index.html:123:456");
+
+ // eslint-disable-next-line mozilla/no-arbitrary-setTimeout
+ await new Promise(r => setTimeout(r, 1000));
+
+ is(tabCount, gBrowser.tabs.length);
+});
+
+/**
+ * With DevTools closed, but DevToolsStartup "initialized",
+ * the url will be ignored
+ */
+add_task(async function openingWithDevToolsClosed() {
+ const url = URL_ROOT + "command-line.html:5:2";
+
+ const tabCount = gBrowser.tabs.length;
+ const ignoredUrl = sendUrlViaCommandLine(url);
+ ok(ignoredUrl, "The url is ignored when no devtools are opened");
+
+ // eslint-disable-next-line mozilla/no-arbitrary-setTimeout
+ await new Promise(r => setTimeout(r, 1000));
+
+ is(tabCount, gBrowser.tabs.length);
+});
+
+/**
+ * With DevTools opened, but the source isn't in the debugged tab,
+ * the url will also be opened via view-source
+ */
+add_task(async function openingWithDevToolsButUnknownSource() {
+ const url = URL_ROOT + "command-line.html:5:2";
+
+ const tab = BrowserTestUtils.addTab(
+ gBrowser,
+ "data:text/html;charset=utf-8,<title>foo</title>"
+ );
+
+ const toolbox = await gDevTools.showToolboxForTab(gBrowser.selectedTab, {
+ toolId: "jsdebugger",
+ });
+
+ const newTabOpened = BrowserTestUtils.waitForNewTab(gBrowser);
+ sendUrlViaCommandLine(url);
+ const newTab = await newTabOpened;
+ is(
+ newTab.linkedBrowser.documentURI.spec,
+ "view-source:" + URL_ROOT + "command-line.html"
+ );
+
+ await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async function () {
+ const selection = content.getSelection();
+ Assert.equal(
+ selection.toString(),
+ " <title>Command line test page</title>",
+ "The 5th line is selected in view-source"
+ );
+ });
+ await gBrowser.removeTab(newTab);
+
+ await toolbox.destroy();
+ await gBrowser.removeTab(tab);
+});
+
+/**
+ * With DevTools opened, and the source is debugged by the debugger,
+ * the url will be opened in the debugger.
+ */
+add_task(async function openingWithDevToolsAndKnownSource() {
+ const url = URL_ROOT + "command-line.js:5:2";
+
+ const tab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ URL_ROOT + "command-line.html"
+ );
+ const toolbox = await gDevTools.showToolboxForTab(tab, {
+ toolId: "jsdebugger",
+ });
+
+ info("Open a first URL with line and column");
+ sendUrlViaCommandLine(url);
+
+ const dbg = toolbox.getPanel("jsdebugger");
+ const selectedLocation = await BrowserTestUtils.waitForCondition(() => {
+ return dbg._selectors.getSelectedLocation(dbg._getState());
+ });
+ is(selectedLocation.source.url, URL_ROOT + "command-line.js");
+ is(selectedLocation.line, 5);
+ is(selectedLocation.column, 1);
+
+ info("Open another URL with only a line");
+ const url2 = URL_ROOT + "command-line.js:6";
+ sendUrlViaCommandLine(url2);
+ const selectedLocation2 = await BrowserTestUtils.waitForCondition(() => {
+ const location = dbg._selectors.getSelectedLocation(dbg._getState());
+ return location.line == 6 ? location : false;
+ });
+ is(selectedLocation2.source.url, URL_ROOT + "command-line.js");
+ is(selectedLocation2.line, 6);
+ is(selectedLocation2.column, 0);
+
+ await toolbox.destroy();
+ await gBrowser.removeTab(tab);
+});
+
+// Fake opening an existing firefox instance with a URL
+// passed via `-url` command line argument
+function sendUrlViaCommandLine(url) {
+ const cmdLine = Cu.createCommandLine(
+ ["-url", url],
+ null,
+ Ci.nsICommandLine.STATE_REMOTE_EXPLICIT
+ );
+ startup.handle(cmdLine);
+
+ // Return true if DevToolsStartup ignored the url
+ // and let it be in the command line object.
+ return cmdLine.findFlag("url", false) != -1;
+}
diff --git a/devtools/startup/tests/browser/browser_shim_disable_devtools.js b/devtools/startup/tests/browser/browser_shim_disable_devtools.js
new file mode 100644
index 0000000000..1a16902099
--- /dev/null
+++ b/devtools/startup/tests/browser/browser_shim_disable_devtools.js
@@ -0,0 +1,165 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+/* eslint-env browser */
+
+const { require } = ChromeUtils.importESModule(
+ "resource://devtools/shared/loader/Loader.sys.mjs"
+);
+
+const { gDevTools } = require("devtools/client/framework/devtools");
+
+async function simulateMenuOpen(menu) {
+ return new Promise(resolve => {
+ menu.addEventListener("popupshown", resolve, { once: true });
+ menu.dispatchEvent(new MouseEvent("popupshowing"));
+ menu.dispatchEvent(new MouseEvent("popupshown"));
+ });
+}
+
+async function simulateMenuClosed(menu) {
+ return new Promise(resolve => {
+ menu.addEventListener("popuphidden", resolve, { once: true });
+ menu.dispatchEvent(new MouseEvent("popuphiding"));
+ menu.dispatchEvent(new MouseEvent("popuphidden"));
+ });
+}
+
+/**
+ * Test that the preference devtools.policy.disabled disables entry points for devtools.
+ */
+add_task(async function () {
+ info(
+ "Disable DevTools entry points (does not apply to the already created window"
+ );
+ await new Promise(resolve => {
+ const options = { set: [["devtools.policy.disabled", true]] };
+ SpecialPowers.pushPrefEnv(options, resolve);
+ });
+
+ // In DEV_EDITION the browser starts with the developer-button in the toolbar. This
+ // applies to all new windows and forces creating keyboard shortcuts. The preference
+ // tested should not change without restart, but for the needs of the test, remove the
+ // developer-button from the UI before opening a new window.
+ if (AppConstants.MOZ_DEV_EDITION) {
+ CustomizableUI.removeWidgetFromArea("developer-button");
+ }
+
+ info(
+ "Open a new window, all window-specific hooks for DevTools will be disabled."
+ );
+ const win = OpenBrowserWindow({ private: false });
+ await waitForDelayedStartupFinished(win);
+
+ info(
+ "Open a new tab on the new window to ensure the focus is on the new window"
+ );
+ const tab = BrowserTestUtils.addTab(
+ win.gBrowser,
+ "data:text/html;charset=utf-8,<title>foo</title>"
+ );
+ await BrowserTestUtils.browserLoaded(win.gBrowser.getBrowserForTab(tab));
+
+ info(
+ "Synthesize a DevTools shortcut, the toolbox should not open on this new window."
+ );
+ synthesizeToggleToolboxKey(win);
+
+ // There is no event to wait for here as this shortcut should have no effect.
+ /* eslint-disable mozilla/no-arbitrary-setTimeout */
+ await new Promise(r => setTimeout(r, 1000));
+
+ is(gDevTools._toolboxesPerCommands.size, 0, "No toolbox has been opened");
+
+ info("Open the context menu for the content page.");
+ const contextMenu = win.document.getElementById("contentAreaContextMenu");
+ const popupShownPromise = BrowserTestUtils.waitForEvent(
+ contextMenu,
+ "popupshown"
+ );
+ EventUtils.synthesizeMouseAtCenter(
+ win.document.documentElement,
+ { type: "contextmenu", button: 2 },
+ win
+ );
+ await popupShownPromise;
+
+ const inspectElementItem = contextMenu.querySelector(`#context-inspect`);
+ ok(
+ inspectElementItem.hidden,
+ "The inspect element item is hidden in the context menu"
+ );
+
+ info("Close the context menu");
+ const onContextMenuHidden = BrowserTestUtils.waitForEvent(
+ contextMenu,
+ "popuphidden"
+ );
+ contextMenu.hidePopup();
+ await onContextMenuHidden;
+
+ info("Open the menubar Tools menu");
+ const toolsMenuPopup = win.document.getElementById("menu_ToolsPopup");
+ const browserToolsMenu = win.document.getElementById("browserToolsMenu");
+ ok(
+ !browserToolsMenu.hidden,
+ "The Browser Tools item of the tools menu is visible"
+ );
+
+ await simulateMenuOpen(toolsMenuPopup);
+ const subMenu = win.document.getElementById("menuWebDeveloperPopup");
+
+ info("Open the Browser Tools sub-menu");
+ await simulateMenuOpen(subMenu);
+
+ const visibleMenuItems = Array.from(
+ subMenu.querySelectorAll("menuitem")
+ ).filter(item => !item.hidden);
+
+ const { menuitems } = require("devtools/client/menus");
+ for (const devtoolsItem of menuitems) {
+ ok(
+ !visibleMenuItems.some(item => item.id === devtoolsItem.id),
+ "DevTools menu item is not visible in the Browser Tools menu"
+ );
+ }
+
+ info("Close out the menu popups");
+ await simulateMenuClosed(subMenu);
+ await simulateMenuClosed(toolsMenuPopup);
+
+ win.gBrowser.removeTab(tab);
+
+ info("Close the test window");
+ const winClosed = BrowserTestUtils.windowClosed(win);
+ win.BrowserTryToCloseWindow();
+ await winClosed;
+});
+
+function waitForDelayedStartupFinished(win) {
+ return new Promise(resolve => {
+ Services.obs.addObserver(function observer(subject, topic) {
+ if (win == subject) {
+ Services.obs.removeObserver(
+ observer,
+ "browser-delayed-startup-finished"
+ );
+ resolve();
+ }
+ }, "browser-delayed-startup-finished");
+ });
+}
+
+/**
+ * Helper to call the toggle devtools shortcut.
+ */
+function synthesizeToggleToolboxKey(win) {
+ info("Trigger the toogle toolbox shortcut");
+ if (Services.appinfo.OS == "Darwin") {
+ EventUtils.synthesizeKey("i", { accelKey: true, altKey: true }, win);
+ } else {
+ EventUtils.synthesizeKey("i", { accelKey: true, shiftKey: true }, win);
+ }
+}
diff --git a/devtools/startup/tests/browser/command-line.html b/devtools/startup/tests/browser/command-line.html
new file mode 100644
index 0000000000..c8f14b95bd
--- /dev/null
+++ b/devtools/startup/tests/browser/command-line.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset="UTF-8">
+ <title>Command line test page</title>
+</head>
+<body>
+ <script src="command-line.js"></script>
+</body>
+</html>
diff --git a/devtools/startup/tests/browser/command-line.js b/devtools/startup/tests/browser/command-line.js
new file mode 100644
index 0000000000..c15c83407b
--- /dev/null
+++ b/devtools/startup/tests/browser/command-line.js
@@ -0,0 +1,3 @@
+"use strict";
+
+console.log("command line script");
diff --git a/devtools/startup/tests/xpcshell/.eslintrc.js b/devtools/startup/tests/xpcshell/.eslintrc.js
new file mode 100644
index 0000000000..6581a0bf32
--- /dev/null
+++ b/devtools/startup/tests/xpcshell/.eslintrc.js
@@ -0,0 +1,5 @@
+"use strict";
+
+module.exports = {
+ extends: "../../../.eslintrc.xpcshell.js",
+};
diff --git a/devtools/startup/tests/xpcshell/test_devtools_shim.js b/devtools/startup/tests/xpcshell/test_devtools_shim.js
new file mode 100644
index 0000000000..6e0ef2789d
--- /dev/null
+++ b/devtools/startup/tests/xpcshell/test_devtools_shim.js
@@ -0,0 +1,202 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const { DevToolsShim } = ChromeUtils.importESModule(
+ "chrome://devtools-startup/content/DevToolsShim.sys.mjs"
+);
+
+// Test the DevToolsShim
+
+/**
+ * Create a mocked version of DevTools that records all calls made to methods expected
+ * to be called by DevToolsShim.
+ */
+function createMockDevTools() {
+ const methods = [
+ "on",
+ "off",
+ "emit",
+ "saveDevToolsSession",
+ "restoreDevToolsSession",
+ ];
+
+ const mock = {
+ callLog: {},
+ };
+
+ for (const method of methods) {
+ // Create a stub for method, that only pushes its arguments in the inner callLog
+ mock[method] = function (...args) {
+ mock.callLog[method].push(args);
+ };
+ mock.callLog[method] = [];
+ }
+
+ return mock;
+}
+
+/**
+ * Check if a given method was called an expected number of times, and finally check the
+ * arguments provided to the last call, if appropriate.
+ */
+function checkCalls(mock, method, length, lastArgs) {
+ Assert.strictEqual(
+ mock.callLog[method].length,
+ length,
+ "Devtools.on was called the expected number of times"
+ );
+
+ // If we don't want to check the last call or if the method was never called, bail out.
+ if (!lastArgs || length === 0) {
+ return;
+ }
+
+ for (let i = 0; i < lastArgs.length; i++) {
+ const expectedArg = lastArgs[i];
+ Assert.strictEqual(
+ mock.callLog[method][length - 1][i],
+ expectedArg,
+ `Devtools.${method} was called with the expected argument (index ${i})`
+ );
+ }
+}
+
+function test_register_unregister() {
+ ok(!DevToolsShim.isInitialized(), "DevTools are not initialized");
+
+ DevToolsShim.register(createMockDevTools());
+ ok(DevToolsShim.isInitialized(), "DevTools are initialized");
+
+ DevToolsShim.unregister();
+ ok(!DevToolsShim.isInitialized(), "DevTools are not initialized");
+}
+
+function test_on_is_forwarded_to_devtools() {
+ ok(!DevToolsShim.isInitialized(), "DevTools are not initialized");
+
+ function cb1() {}
+ function cb2() {}
+ const mock = createMockDevTools();
+
+ DevToolsShim.on("test_event", cb1);
+ DevToolsShim.register(mock);
+ checkCalls(mock, "on", 1, ["test_event", cb1]);
+
+ DevToolsShim.on("other_event", cb2);
+ checkCalls(mock, "on", 2, ["other_event", cb2]);
+}
+
+function test_off_called_before_registering_devtools() {
+ ok(!DevToolsShim.isInitialized(), "DevTools are not initialized");
+
+ function cb1() {}
+ const mock = createMockDevTools();
+
+ DevToolsShim.on("test_event", cb1);
+ DevToolsShim.off("test_event", cb1);
+
+ DevToolsShim.register(mock);
+ checkCalls(mock, "on", 0);
+}
+
+function test_off_called_before_with_bad_callback() {
+ ok(!DevToolsShim.isInitialized(), "DevTools are not initialized");
+
+ function cb1() {}
+ function cb2() {}
+ const mock = createMockDevTools();
+
+ DevToolsShim.on("test_event", cb1);
+ DevToolsShim.off("test_event", cb2);
+
+ DevToolsShim.register(mock);
+ // on should still be called
+ checkCalls(mock, "on", 1, ["test_event", cb1]);
+ // Calls to off should not be held and forwarded.
+ checkCalls(mock, "off", 0);
+}
+
+function test_events() {
+ ok(!DevToolsShim.isInitialized(), "DevTools are not initialized");
+
+ const mock = createMockDevTools();
+ // Check emit was not called.
+ checkCalls(mock, "emit", 0);
+
+ // Check emit is called once with the devtools-registered event.
+ DevToolsShim.register(mock);
+ checkCalls(mock, "emit", 1, ["devtools-registered"]);
+
+ // Check emit is called once with the devtools-unregistered event.
+ DevToolsShim.unregister();
+ checkCalls(mock, "emit", 2, ["devtools-unregistered"]);
+}
+
+function test_restore_session_apis() {
+ // Backup method that will be updated for the test.
+ const initDevToolsBackup = DevToolsShim.initDevTools;
+
+ // Create fake session objects to restore.
+ const sessionWithoutDevTools = {};
+ const sessionWithDevTools = {
+ browserConsole: true,
+ };
+
+ Services.prefs.setBoolPref("devtools.policy.disabled", true);
+ ok(!DevToolsShim.isInitialized(), "DevTools are not initialized");
+ ok(!DevToolsShim.isEnabled(), "DevTools are not enabled");
+
+ // Check that save & restore DevToolsSession don't initialize the tools and don't
+ // crash.
+ DevToolsShim.saveDevToolsSession({});
+ DevToolsShim.restoreDevToolsSession(sessionWithDevTools);
+ ok(!DevToolsShim.isInitialized(), "DevTools are still not initialized");
+
+ Services.prefs.setBoolPref("devtools.policy.disabled", false);
+ ok(DevToolsShim.isEnabled(), "DevTools are enabled");
+ ok(!DevToolsShim.isInitialized(), "DevTools are not initialized");
+
+ // Check that DevTools are not initialized when calling restoreDevToolsSession without
+ // DevTools related data.
+ DevToolsShim.restoreDevToolsSession(sessionWithoutDevTools);
+ ok(!DevToolsShim.isInitialized(), "DevTools are still not initialized");
+
+ const mock = createMockDevTools();
+ DevToolsShim.initDevTools = () => {
+ // Next call to restoreDevToolsSession is expected to initialize DevTools, which we
+ // simulate here by registering our mock.
+ DevToolsShim.register(mock);
+ };
+
+ DevToolsShim.restoreDevToolsSession(sessionWithDevTools);
+ checkCalls(mock, "restoreDevToolsSession", 1, [sessionWithDevTools]);
+
+ ok(DevToolsShim.isInitialized(), "DevTools are initialized");
+
+ DevToolsShim.saveDevToolsSession({});
+ checkCalls(mock, "saveDevToolsSession", 1, []);
+
+ // Restore initDevTools backup.
+ DevToolsShim.initDevTools = initDevToolsBackup;
+}
+
+function run_test() {
+ test_register_unregister();
+ DevToolsShim.unregister();
+
+ test_on_is_forwarded_to_devtools();
+ DevToolsShim.unregister();
+
+ test_off_called_before_registering_devtools();
+ DevToolsShim.unregister();
+
+ test_off_called_before_with_bad_callback();
+ DevToolsShim.unregister();
+
+ test_restore_session_apis();
+ DevToolsShim.unregister();
+
+ test_events();
+}
diff --git a/devtools/startup/tests/xpcshell/xpcshell.toml b/devtools/startup/tests/xpcshell/xpcshell.toml
new file mode 100644
index 0000000000..2e7e69614b
--- /dev/null
+++ b/devtools/startup/tests/xpcshell/xpcshell.toml
@@ -0,0 +1,6 @@
+[DEFAULT]
+tags = "devtools"
+firefox-appdir = "browser"
+skip-if = ["os == 'android'"]
+
+["test_devtools_shim.js"]