summaryrefslogtreecommitdiffstats
path: root/devtools/client/framework/test/browser_toolbox_options.js
diff options
context:
space:
mode:
Diffstat (limited to 'devtools/client/framework/test/browser_toolbox_options.js')
-rw-r--r--devtools/client/framework/test/browser_toolbox_options.js556
1 files changed, 556 insertions, 0 deletions
diff --git a/devtools/client/framework/test/browser_toolbox_options.js b/devtools/client/framework/test/browser_toolbox_options.js
new file mode 100644
index 0000000000..3b5d7f2661
--- /dev/null
+++ b/devtools/client/framework/test/browser_toolbox_options.js
@@ -0,0 +1,556 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests that changing preferences in the options panel updates the prefs
+// and toggles appropriate things in the toolbox.
+
+var doc = null,
+ toolbox = null,
+ panelWin = null,
+ modifiedPrefs = [];
+const L10N = new LocalizationHelper(
+ "devtools/client/locales/toolbox.properties"
+);
+const { PrefObserver } = require("resource://devtools/client/shared/prefs.js");
+
+add_task(async function () {
+ const URL =
+ "data:text/html;charset=utf8,test for dynamically registering " +
+ "and unregistering tools";
+ registerNewTool();
+ const tab = await addTab(URL);
+ toolbox = await gDevTools.showToolboxForTab(tab);
+
+ doc = toolbox.doc;
+ await registerNewPerToolboxTool();
+ await testSelectTool();
+ await testOptionsShortcut();
+ await testOptions();
+ await testToggleTools();
+
+ // Test that registered WebExtensions becomes entries in the
+ // options panel and toggling their checkbox toggle the related
+ // preference.
+ await registerNewWebExtensions();
+ await testToggleWebExtensions();
+
+ await cleanup();
+});
+
+function registerNewTool() {
+ const toolDefinition = {
+ id: "testTool",
+ isToolSupported: () => true,
+ visibilityswitch: "devtools.test-tool.enabled",
+ url: "about:blank",
+ label: "someLabel",
+ };
+
+ ok(gDevTools, "gDevTools exists");
+ ok(
+ !gDevTools.getToolDefinitionMap().has("testTool"),
+ "The tool is not registered"
+ );
+
+ gDevTools.registerTool(toolDefinition);
+ ok(
+ gDevTools.getToolDefinitionMap().has("testTool"),
+ "The tool is registered"
+ );
+}
+
+// Register a fake WebExtension to check that it is
+// listed in the toolbox options.
+function registerNewWebExtensions() {
+ // Register some fake extensions and init the related preferences
+ // (similarly to ext-devtools.js).
+ for (let i = 0; i < 2; i++) {
+ const extPref = `devtools.webextensions.fakeExtId${i}.enabled`;
+ Services.prefs.setBoolPref(extPref, true);
+
+ toolbox.registerWebExtension(`fakeUUID${i}`, {
+ name: `Fake WebExtension ${i}`,
+ pref: extPref,
+ });
+ }
+}
+
+function registerNewPerToolboxTool() {
+ const toolDefinition = {
+ id: "test-pertoolbox-tool",
+ isToolSupported: () => true,
+ visibilityswitch: "devtools.test-pertoolbox-tool.enabled",
+ url: "about:blank",
+ label: "perToolboxSomeLabel",
+ };
+
+ ok(gDevTools, "gDevTools exists");
+ ok(
+ !gDevTools.getToolDefinitionMap().has("test-pertoolbox-tool"),
+ "The per-toolbox tool is not registered globally"
+ );
+
+ ok(toolbox, "toolbox exists");
+ ok(
+ !toolbox.hasAdditionalTool("test-pertoolbox-tool"),
+ "The per-toolbox tool is not yet registered to the toolbox"
+ );
+
+ toolbox.addAdditionalTool(toolDefinition);
+
+ ok(
+ !gDevTools.getToolDefinitionMap().has("test-pertoolbox-tool"),
+ "The per-toolbox tool is not registered globally"
+ );
+ ok(
+ toolbox.hasAdditionalTool("test-pertoolbox-tool"),
+ "The per-toolbox tool has been registered to the toolbox"
+ );
+}
+
+async function testSelectTool() {
+ info("Checking to make sure that the options panel can be selected.");
+
+ const onceSelected = toolbox.once("options-selected");
+ toolbox.selectTool("options");
+ await onceSelected;
+ ok(true, "Toolbox selected via selectTool method");
+}
+
+async function testOptionsShortcut() {
+ info("Selecting another tool, then reselecting options panel with keyboard.");
+
+ await toolbox.selectTool("webconsole");
+ is(toolbox.currentToolId, "webconsole", "webconsole is selected");
+ synthesizeKeyShortcut(L10N.getStr("toolbox.help.key"));
+ is(toolbox.currentToolId, "options", "Toolbox selected via shortcut key");
+ synthesizeKeyShortcut(L10N.getStr("toolbox.help.key"));
+ is(toolbox.currentToolId, "webconsole", "webconsole is reselected");
+ synthesizeKeyShortcut(L10N.getStr("toolbox.help.key"));
+ is(toolbox.currentToolId, "options", "Toolbox selected via shortcut key");
+}
+
+async function testOptions() {
+ const tool = toolbox.getPanel("options");
+ panelWin = tool.panelWin;
+ const prefNodes = tool.panelDoc.querySelectorAll(
+ "input[type=checkbox][data-pref]"
+ );
+
+ // Store modified pref names so that they can be cleared on error.
+ for (const node of tool.panelDoc.querySelectorAll("[data-pref]")) {
+ const pref = node.getAttribute("data-pref");
+ modifiedPrefs.push(pref);
+ }
+
+ for (const node of prefNodes) {
+ const prefValue = GetPref(node.getAttribute("data-pref"));
+
+ // Test clicking the checkbox for each options pref
+ await testMouseClick(node, prefValue);
+
+ // Do again with opposite values to reset prefs
+ await testMouseClick(node, !prefValue);
+ }
+
+ const prefSelects = tool.panelDoc.querySelectorAll("select[data-pref]");
+ for (const node of prefSelects) {
+ await testSelect(node);
+ }
+}
+
+async function testSelect(select) {
+ const pref = select.getAttribute("data-pref");
+ const options = Array.from(select.options);
+ info("Checking select for: " + pref);
+
+ is(
+ `${select.options[select.selectedIndex].value}`,
+ `${GetPref(pref)}`,
+ "select starts out selected"
+ );
+
+ for (const option of options) {
+ if (options.indexOf(option) === select.selectedIndex) {
+ continue;
+ }
+
+ const observer = new PrefObserver("devtools.");
+
+ let changeSeen = false;
+ const changeSeenPromise = new Promise(resolve => {
+ observer.once(pref, () => {
+ changeSeen = true;
+ is(
+ `${GetPref(pref)}`,
+ `${option.value}`,
+ "Preference been switched for " + pref
+ );
+ resolve();
+ });
+ });
+
+ select.selectedIndex = options.indexOf(option);
+ const changeEvent = new Event("change");
+ select.dispatchEvent(changeEvent);
+
+ await changeSeenPromise;
+
+ ok(changeSeen, "Correct pref was changed");
+ observer.destroy();
+ }
+}
+
+async function testMouseClick(node, prefValue) {
+ const observer = new PrefObserver("devtools.");
+
+ const pref = node.getAttribute("data-pref");
+ let changeSeen = false;
+ const changeSeenPromise = new Promise(resolve => {
+ observer.once(pref, () => {
+ changeSeen = true;
+ is(GetPref(pref), !prefValue, "New value is correct for " + pref);
+ resolve();
+ });
+ });
+
+ node.scrollIntoView();
+
+ // We use executeSoon here to ensure that the element is in view and
+ // clickable.
+ executeSoon(function () {
+ info("Click event synthesized for pref " + pref);
+ EventUtils.synthesizeMouseAtCenter(node, {}, panelWin);
+ });
+
+ await changeSeenPromise;
+
+ ok(changeSeen, "Correct pref was changed");
+ observer.destroy();
+}
+
+async function testToggleWebExtensions() {
+ const disabledExtensions = new Set();
+ const toggleableWebExtensions = toolbox.listWebExtensions();
+
+ function toggleWebExtension(node) {
+ node.scrollIntoView();
+ EventUtils.synthesizeMouseAtCenter(node, {}, panelWin);
+ }
+
+ function assertExpectedDisabledExtensions() {
+ for (const ext of toggleableWebExtensions) {
+ if (disabledExtensions.has(ext)) {
+ ok(
+ !toolbox.isWebExtensionEnabled(ext.uuid),
+ `The WebExtension "${ext.name}" should be disabled`
+ );
+ } else {
+ ok(
+ toolbox.isWebExtensionEnabled(ext.uuid),
+ `The WebExtension "${ext.name}" should be enabled`
+ );
+ }
+ }
+ }
+
+ function assertAllExtensionsDisabled() {
+ const enabledUUIDs = toggleableWebExtensions
+ .filter(ext => toolbox.isWebExtensionEnabled(ext.uuid))
+ .map(ext => ext.uuid);
+
+ Assert.deepEqual(
+ enabledUUIDs,
+ [],
+ "All the registered WebExtensions should be disabled"
+ );
+ }
+
+ function assertAllExtensionsEnabled() {
+ const disabledUUIDs = toolbox
+ .listWebExtensions()
+ .filter(ext => !toolbox.isWebExtensionEnabled(ext.uuid))
+ .map(ext => ext.uuid);
+
+ Assert.deepEqual(
+ disabledUUIDs,
+ [],
+ "All the registered WebExtensions should be enabled"
+ );
+ }
+
+ function getWebExtensionNodes() {
+ const toolNodes = panelWin.document.querySelectorAll(
+ "#default-tools-box input[type=checkbox]:not([data-unsupported])," +
+ "#additional-tools-box input[type=checkbox]:not([data-unsupported])"
+ );
+
+ return [...toolNodes].filter(node => {
+ return toggleableWebExtensions.some(
+ ({ uuid }) => node.getAttribute("id") === `webext-${uuid}`
+ );
+ });
+ }
+
+ let webExtensionNodes = getWebExtensionNodes();
+
+ is(
+ webExtensionNodes.length,
+ toggleableWebExtensions.length,
+ "There should be a toggle checkbox for every WebExtension registered"
+ );
+
+ for (const ext of toggleableWebExtensions) {
+ ok(
+ toolbox.isWebExtensionEnabled(ext.uuid),
+ `The WebExtension "${ext.name}" is initially enabled`
+ );
+ }
+
+ // Store modified pref names so that they can be cleared on error.
+ for (const ext of toggleableWebExtensions) {
+ modifiedPrefs.push(ext.pref);
+ }
+
+ // Turn each registered WebExtension to disabled.
+ for (const node of webExtensionNodes) {
+ toggleWebExtension(node);
+
+ const toggledExt = toggleableWebExtensions.find(ext => {
+ return node.id == `webext-${ext.uuid}`;
+ });
+ ok(toggledExt, "Found a WebExtension for the checkbox element");
+ disabledExtensions.add(toggledExt);
+
+ assertExpectedDisabledExtensions();
+ }
+
+ assertAllExtensionsDisabled();
+
+ // Turn each registered WebExtension to enabled.
+ for (const node of webExtensionNodes) {
+ toggleWebExtension(node);
+
+ const toggledExt = toggleableWebExtensions.find(ext => {
+ return node.id == `webext-${ext.uuid}`;
+ });
+ ok(toggledExt, "Found a WebExtension for the checkbox element");
+ disabledExtensions.delete(toggledExt);
+
+ assertExpectedDisabledExtensions();
+ }
+
+ assertAllExtensionsEnabled();
+
+ // Unregister the WebExtensions one by one, and check that only the expected
+ // ones have been unregistered, and the remaining onea are still listed.
+ for (const ext of toggleableWebExtensions) {
+ ok(
+ !!toolbox.listWebExtensions().length,
+ "There should still be extensions registered"
+ );
+ toolbox.unregisterWebExtension(ext.uuid);
+
+ const registeredUUIDs = toolbox.listWebExtensions().map(item => item.uuid);
+ ok(
+ !registeredUUIDs.includes(ext.uuid),
+ `the WebExtension "${ext.name}" should have been unregistered`
+ );
+
+ webExtensionNodes = getWebExtensionNodes();
+
+ const checkboxEl = webExtensionNodes.find(
+ el => el.id === `webext-${ext.uuid}`
+ );
+ is(
+ checkboxEl,
+ undefined,
+ "The unregistered WebExtension checkbox should have been removed"
+ );
+
+ is(
+ registeredUUIDs.length,
+ webExtensionNodes.length,
+ "There should be the expected number of WebExtensions checkboxes"
+ );
+ }
+
+ is(
+ toolbox.listWebExtensions().length,
+ 0,
+ "All WebExtensions have been unregistered"
+ );
+
+ webExtensionNodes = getWebExtensionNodes();
+
+ is(
+ webExtensionNodes.length,
+ 0,
+ "There should not be any checkbox for the unregistered WebExtensions"
+ );
+}
+
+function getToolNode(id) {
+ return panelWin.document.getElementById(id);
+}
+
+async function testToggleTools() {
+ const toolNodes = panelWin.document.querySelectorAll(
+ "#default-tools-box input[type=checkbox]:not([data-unsupported])," +
+ "#additional-tools-box input[type=checkbox]:not([data-unsupported])"
+ );
+ const toolNodeIds = [...toolNodes].map(node => node.id);
+ const enabledToolIds = [...toolNodes]
+ .filter(node => node.checked)
+ .map(node => node.id);
+
+ const toggleableTools = gDevTools
+ .getDefaultTools()
+ .filter(tool => {
+ return tool.visibilityswitch;
+ })
+ .concat(gDevTools.getAdditionalTools())
+ .concat(toolbox.getAdditionalTools());
+
+ for (const node of toolNodes) {
+ const id = node.getAttribute("id");
+ ok(
+ toggleableTools.some(tool => tool.id === id),
+ "There should be a toggle checkbox for: " + id
+ );
+ }
+
+ // Store modified pref names so that they can be cleared on error.
+ for (const tool of toggleableTools) {
+ const pref = tool.visibilityswitch;
+ modifiedPrefs.push(pref);
+ }
+
+ // Toggle each tool
+ for (const id of toolNodeIds) {
+ await toggleTool(getToolNode(id));
+ }
+
+ // Toggle again to reset tool enablement state
+ for (const id of toolNodeIds) {
+ await toggleTool(getToolNode(id));
+ }
+
+ // Test that a tool can still be added when no tabs are present:
+ // Disable all tools
+ for (const id of enabledToolIds) {
+ await toggleTool(getToolNode(id));
+ }
+ // Re-enable the tools which are enabled by default
+ for (const id of enabledToolIds) {
+ await toggleTool(getToolNode(id));
+ }
+
+ // Toggle first, middle, and last tools to ensure that toolbox tabs are
+ // inserted in order
+ const firstToolId = toolNodeIds[0];
+ const middleToolId = toolNodeIds[(toolNodeIds.length / 2) | 0];
+ const lastToolId = toolNodeIds[toolNodeIds.length - 1];
+
+ await toggleTool(getToolNode(firstToolId));
+ await toggleTool(getToolNode(firstToolId));
+ await toggleTool(getToolNode(middleToolId));
+ await toggleTool(getToolNode(middleToolId));
+ await toggleTool(getToolNode(lastToolId));
+ await toggleTool(getToolNode(lastToolId));
+}
+
+/**
+ * Toggle tool node checkbox. Note: because toggling the checkbox will result in
+ * re-rendering of the tool list, we must re-query the checkboxes every time.
+ */
+async function toggleTool(node) {
+ const toolId = node.getAttribute("id");
+
+ const registeredPromise = new Promise(resolve => {
+ if (node.checked) {
+ gDevTools.once(
+ "tool-unregistered",
+ checkUnregistered.bind(null, toolId, resolve)
+ );
+ } else {
+ gDevTools.once(
+ "tool-registered",
+ checkRegistered.bind(null, toolId, resolve)
+ );
+ }
+ });
+ node.scrollIntoView();
+ EventUtils.synthesizeMouseAtCenter(node, {}, panelWin);
+
+ await registeredPromise;
+}
+
+function checkUnregistered(toolId, resolve, data) {
+ if (data == toolId) {
+ ok(true, "Correct tool removed");
+ // checking tab on the toolbox
+ ok(
+ !doc.getElementById("toolbox-tab-" + toolId),
+ "Tab removed for " + toolId
+ );
+ } else {
+ ok(false, "Something went wrong, " + toolId + " was not unregistered");
+ }
+ resolve();
+}
+
+async function checkRegistered(toolId, resolve, data) {
+ if (data == toolId) {
+ ok(true, "Correct tool added back");
+ // checking tab on the toolbox
+ const button = await lookupButtonForToolId(toolId);
+ ok(button, "Tab added back for " + toolId);
+ } else {
+ ok(false, "Something went wrong, " + toolId + " was not registered");
+ }
+ resolve();
+}
+
+function GetPref(name) {
+ const type = Services.prefs.getPrefType(name);
+ switch (type) {
+ case Services.prefs.PREF_STRING:
+ return Services.prefs.getCharPref(name);
+ case Services.prefs.PREF_INT:
+ return Services.prefs.getIntPref(name);
+ case Services.prefs.PREF_BOOL:
+ return Services.prefs.getBoolPref(name);
+ default:
+ throw new Error("Unknown type");
+ }
+}
+
+/**
+ * Find the button from specified toolId.
+ * Generally, button which access to the tool panel is in toolbox or
+ * tools menu(in the Chevron menu).
+ */
+async function lookupButtonForToolId(toolId) {
+ let button = doc.getElementById("toolbox-tab-" + toolId);
+ if (!button) {
+ // search from the tools menu.
+ await openChevronMenu(toolbox);
+ button = doc.querySelector("#tools-chevron-menupopup-" + toolId);
+
+ await closeChevronMenu(toolbox);
+ }
+ return button;
+}
+
+async function cleanup() {
+ gDevTools.unregisterTool("testTool");
+ await toolbox.destroy();
+ gBrowser.removeCurrentTab();
+ for (const pref of modifiedPrefs) {
+ Services.prefs.clearUserPref(pref);
+ }
+ toolbox = doc = panelWin = modifiedPrefs = null;
+}