556 lines
15 KiB
JavaScript
556 lines
15 KiB
JavaScript
/* 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;
|
|
}
|