diff options
Diffstat (limited to 'devtools/startup/tests')
-rw-r--r-- | devtools/startup/tests/browser/browser.toml | 11 | ||||
-rw-r--r-- | devtools/startup/tests/browser/browser_command_line_urls.js | 154 | ||||
-rw-r--r-- | devtools/startup/tests/browser/browser_shim_disable_devtools.js | 165 | ||||
-rw-r--r-- | devtools/startup/tests/browser/command-line.html | 10 | ||||
-rw-r--r-- | devtools/startup/tests/browser/command-line.js | 3 | ||||
-rw-r--r-- | devtools/startup/tests/xpcshell/.eslintrc.js | 5 | ||||
-rw-r--r-- | devtools/startup/tests/xpcshell/test_devtools_shim.js | 202 | ||||
-rw-r--r-- | devtools/startup/tests/xpcshell/xpcshell.toml | 6 |
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"] |