Source code

Revision control

Copy as Markdown

Other Tools

Test Info:

/* Any copyright is dedicated to the Public Domain.
// Tests that you can configure local mode mappings from options panel
const TEST_URI = "data:text/html,local mode options";
const l10n = new Localization(["devtools/client/toolbox-options.ftl"], true);
const { LocalModeMappings } = ChromeUtils.importESModule(
"resource://devtools/client/framework/LocalModeMappings.sys.mjs"
);
add_task(async function () {
const tab = await addTab(TEST_URI);
const toolbox = await gDevTools.showToolboxForTab(tab, { toolId: "options" });
const panel = toolbox.getCurrentPanel();
const { panelDoc } = panel;
await createMapping(toolbox, panelDoc, "./local-mode", "firefox.localhost");
await navigateToMapping(panelDoc, 0);
is(gBrowser.selectedBrowser.currentURI.spec, "https://firefox.localhost/");
await SpecialPowers.spawn(gBrowser.selectedBrowser, [], function () {
is(
content.document.documentElement.textContent,
"Local mode HTML test page\n",
"The opened page is the local html page"
);
});
await createMapping(toolbox, panelDoc, ".", "firefox1.localhost");
await navigateToMapping(panelDoc, 1);
is(gBrowser.selectedBrowser.currentURI.spec, "https://firefox1.localhost/");
await SpecialPowers.spawn(gBrowser.selectedBrowser, [], function () {
Assert.stringContains(
content.document.documentElement.textContent,
"browser_toolbox_options_local_mode.js",
"The html page is a directory listing which includes the current test"
);
});
await editMappingWithConflict(toolbox, panelDoc);
await disableMapping(toolbox, panelDoc, 1);
await removeMapping(toolbox, panelDoc, 1);
await checkBuggyPath(toolbox, panelDoc);
await checkAutoReloadErrorPage(toolbox, panelDoc);
await toolbox.destroy();
gBrowser.removeCurrentTab();
});
function getMappingsCount(doc) {
return doc.querySelectorAll("#local-mode-mappings li").length;
}
async function createMapping(toolbox, doc, localTestFolder, expectedOrigin) {
const newMapping = doc.querySelector(`.local-mode-new-mapping`);
const folderPath = prepareMockFilePicker(localTestFolder);
ok(folderPath, "Got the current folder path");
const previousMappingCount = getMappingsCount(doc);
const onMappingsUpdated = LocalModeMappings.once("updated");
newMapping.click();
info("Wait for the mappings to be updated");
await onMappingsUpdated;
info("Wait for the new mapping to be displayed");
await waitFor(() => getMappingsCount(doc) > previousMappingCount);
// Always expected the new mapping to be displayed last
const originInput = [
...doc.querySelectorAll(`.local-mode-origin-line input`),
].at(-1);
is(
doc.activeElement,
originInput,
"The new mapping's origin input is focused"
);
is(originInput.value, expectedOrigin, "The default origin input is correct");
const folderLink = [...doc.querySelectorAll(`.local-mode-folder-line a`)].at(
-1
);
is(folderLink.textContent, folderPath, "The folder path looks correct");
}
/**
* Helper to easily find a DOM element via its selector in a given local mode mapping
*/
function queryMappingElementSelector(doc, mappingIndex, selector = "") {
return doc.querySelector(
`#local-mode-mappings li:nth-child(${mappingIndex + 1}) ${selector}`
);
}
async function navigateToMapping(doc, mappingIndex) {
const openButton = queryMappingElementSelector(
doc,
mappingIndex,
".local-mode-mapping-navigate-to"
);
const waitForDevToolsReload = await watchForDevToolsReload(
gBrowser.selectedBrowser
);
openButton.click();
info("Wait for navigation to be fully processed by DevTools");
await waitForDevToolsReload();
}
async function editMappingWithConflict(toolbox, doc) {
const mappingIndex = 1;
const originElement = queryMappingElementSelector(
doc,
mappingIndex,
".local-mode-origin-input"
);
info("Update the origin input element with a conflicting origin");
originElement.focus();
originElement.value = "";
EventUtils.sendString("firefox.localhost", doc.defaultView);
info("Wait for the input to be flagged as invalid");
await waitFor(() => originElement.matches(":invalid"));
const originError = queryMappingElementSelector(
doc,
mappingIndex,
".local-mode-origin-error"
);
is(
originError.textContent,
l10n.formatValueSync("options-local-mode-origin-conflict")
);
}
async function disableMapping(toolbox, doc, mappingIndex) {
const toggleButton = queryMappingElementSelector(
doc,
mappingIndex,
".local-mode-mapping-toggle"
);
ok(
!queryMappingElementSelector(doc, mappingIndex).classList.contains(
"disabled"
),
"The mapping is enabled"
);
const onMappingsUpdated = LocalModeMappings.once("updated");
toggleButton.click();
info("Wait for the mapping to be displayed as disabled");
await waitFor(() =>
queryMappingElementSelector(doc, mappingIndex).classList.contains(
"disabled"
)
);
info("Wait for the mappings to be updated");
await onMappingsUpdated;
}
async function removeMapping(toolbox, doc, mappingIndex) {
const removeButton = queryMappingElementSelector(
doc,
mappingIndex,
".local-mode-mapping-remove"
);
const onMappingsUpdated = LocalModeMappings.once("updated");
const onConfirmAccepted = BrowserTestUtils.promiseAlertDialogOpen("accept");
removeButton.click();
await onConfirmAccepted;
info("Wait for the mapping to be displayed as disabled");
await waitFor(() => !queryMappingElementSelector(doc, mappingIndex));
info("Wait for the mappings to be updated");
await onMappingsUpdated;
}
async function checkBuggyPath(toolbox, doc) {
const buggyPath = "/buggy-path/foo";
const mappingIndex = 0;
ok(
!doc.querySelector(".local-mode-folder-warning"),
"There is no warning displayed before hacking the path"
);
info("Set the first mapping to a buggy, non-existing path");
// Use internal preference as the folder picker wouldn't allow us to do that,
// nor can we easily remove our local test folder
const onMappingsUpdated = LocalModeMappings.once("updated");
Services.prefs.setStringPref(
`${LocalModeMappings.LOCAL_MODE_MAPPINGS_PREF_PREFIX}${mappingIndex}.path`,
buggyPath
);
await onMappingsUpdated;
const folderLink = queryMappingElementSelector(
doc,
mappingIndex,
".local-mode-folder-line a"
);
is(folderLink.textContent, buggyPath);
ok(
doc.querySelector(".local-mode-folder-error"),
"The warning is now displayed"
);
}
async function checkAutoReloadErrorPage(toolbox, panelDoc) {
info("Reload on an origin that is no longer mapped");
is(gBrowser.selectedBrowser.currentURI.spec, "https://firefox1.localhost/");
// This will trigger a neterror page
const onErrorPageLoaded = BrowserTestUtils.waitForErrorPage(
gBrowser.selectedBrowser
);
await reloadSelectedTab({ isErrorPage: true, waitForLoad: false });
await onErrorPageLoaded;
info("Re-create a mapping matching the currently loaded URL");
const loaded = BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
await createMapping(toolbox, panelDoc, "./local-mode/", "firefox1.localhost");
info("Wait for the page to be reloaded");
await loaded;
info("Assert that the reloaded page worked correctly");
await SpecialPowers.spawn(gBrowser.selectedBrowser, [], function () {
is(
content.document.documentElement.textContent,
"Local mode HTML test page\n",
"The opened page is the local html page"
);
});
}
function _getSupportsFile(path) {
const cr = Cc["@mozilla.org/chrome/chrome-registry;1"].getService(
Ci.nsIChromeRegistry
);
const uri = Services.io.newURI(CHROME_URL_ROOT + path);
const fileurl = cr.convertChromeURL(uri);
return fileurl.QueryInterface(Ci.nsIFileURL);
}
function prepareMockFilePicker(pathOrFile) {
const isFile = typeof pathOrFile.isFile === "function" && pathOrFile.isFile();
const file = isFile ? pathOrFile : _getSupportsFile(pathOrFile).file;
// Mock the file picker to select a test addon
const MockFilePicker = SpecialPowers.MockFilePicker;
MockFilePicker.init();
MockFilePicker.setFiles([file]);
return file.path;
}