Source code

Revision control

Copy as Markdown

Other Tools

Test Info:

/* eslint max-len: ["error", 80] */
const { AddonTestUtils } = ChromeUtils.importESModule(
);
const { ExtensionParent } = ChromeUtils.importESModule(
"resource://gre/modules/ExtensionParent.sys.mjs"
);
AddonTestUtils.initMochitest(this);
// This test function helps to detect when an addon options browser have been
// inserted in the about:addons page.
function waitOptionsBrowserInserted() {
return new Promise(resolve => {
async function listener(eventName, browser) {
// wait for a webextension XUL browser element that is owned by the
// "about:addons" page.
if (browser.ownerGlobal.top.location.href == "about:addons") {
ExtensionParent.apiManager.off("extension-browser-inserted", listener);
resolve(browser);
}
}
ExtensionParent.apiManager.on("extension-browser-inserted", listener);
});
}
add_task(async function enableHtmlViews() {
await SpecialPowers.pushPrefEnv({
set: [["extensions.htmlaboutaddons.inline-options.enabled", true]],
});
});
add_task(async function testInlineOptions() {
const HEIGHT_SHORT = 300;
const HEIGHT_TALL = 600;
let id = "inline@mochi.test";
let extension = ExtensionTestUtils.loadExtension({
manifest: {
browser_specific_settings: { gecko: { id } },
options_ui: {
page: "options.html",
},
},
files: {
"options.html": `
<html>
<head>
<style type="text/css">
body > p { height: ${HEIGHT_SHORT}px; margin: 0; }
body.bigger > p { height: ${HEIGHT_TALL}px; }
</style>
<script src="options.js"></script>
</head>
<body>
<p>Some text</p>
</body>
</html>
`,
"options.js": () => {
browser.test.onMessage.addListener(msg => {
if (msg == "toggle-class") {
document.body.classList.toggle("bigger");
} else if (msg == "get-height") {
browser.test.sendMessage("height", document.body.clientHeight);
}
});
browser.test.sendMessage("options-loaded", window.location.href);
},
},
useAddonManager: "temporary",
});
await extension.startup();
let win = await loadInitialView("extension");
let doc = win.document;
// Make sure we found the right card.
let card = getAddonCard(win, id);
ok(card, "Found the card");
// The preferences option should be visible.
let preferences = card.querySelector('[action="preferences"]');
ok(!preferences.hidden, "The preferences option is visible");
// Open the preferences page.
let loaded = waitForViewLoad(win);
preferences.click();
await loaded;
// Verify we're on the preferences tab.
card = doc.querySelector("addon-card");
is(card.addon.id, id, "The right page was loaded");
let { deck, tabGroup } = card.details;
let { selectedViewName } = deck;
is(selectedViewName, "preferences", "The preferences tab is shown");
info("Check that there are two buttons and they're visible");
let detailsBtn = tabGroup.querySelector('[name="details"]');
ok(!detailsBtn.hidden, "The details button is visible");
let prefsBtn = tabGroup.querySelector('[name="preferences"]');
ok(!prefsBtn.hidden, "The preferences button is visible");
// Wait for the browser to load.
let url = await extension.awaitMessage("options-loaded");
// Check the attributes of the options browser.
let browser = card.querySelector("inline-options-browser browser");
ok(browser, "The visible view has a browser");
is(
browser.currentURI.spec,
card.addon.optionsURL,
"The browser has the expected options URL"
);
is(url, card.addon.optionsURL, "Browser has the expected options URL loaded");
Assert.greater(browser.clientWidth, 0, "The browser has a width");
ok(
card.querySelector('[action="preferences"]').hidden,
"The preferences option is hidden now"
);
async function waitForHeightChange(expectedHeight) {
await TestUtils.waitForCondition(
() => browser.clientHeight === expectedHeight
);
// Wait for the resize event to be sent to the remote browser.
await new Promise(r =>
window.requestAnimationFrame(() => window.requestAnimationFrame(r))
);
}
await waitForHeightChange(HEIGHT_SHORT);
// Check resizing the browser through extension CSS.
await extension.sendMessage("get-height");
let height = await extension.awaitMessage("height");
is(height, HEIGHT_SHORT, "The height is smaller to start");
is(height, browser.clientHeight, "The browser is the same size");
info("Resize the browser to be taller");
await extension.sendMessage("toggle-class");
await waitForHeightChange(HEIGHT_TALL);
await extension.sendMessage("get-height");
height = await extension.awaitMessage("height");
is(height, HEIGHT_TALL, "The height is bigger now");
is(height, browser.clientHeight, "The browser is the same size");
info("Shrink the browser again");
await extension.sendMessage("toggle-class");
await waitForHeightChange(HEIGHT_SHORT);
await extension.sendMessage("get-height");
height = await extension.awaitMessage("height");
is(height, HEIGHT_SHORT, "The browser shrunk back");
is(height, browser.clientHeight, "The browser is the same size");
info("Switching to details view");
detailsBtn.click();
info("Check the browser dimensions to make sure it's hidden");
is(browser.clientWidth, 0, "The browser is hidden now");
info("Switch back, check browser is shown");
prefsBtn.click();
Assert.greater(browser.clientWidth, 0, "The browser has a width");
await closeView(win);
await extension.unload();
});
// Regression test against bug 1409697
add_task(async function testCardRerender() {
let id = "rerender@mochi.test";
let extension = ExtensionTestUtils.loadExtension({
manifest: {
browser_specific_settings: { gecko: { id } },
options_ui: {
page: "options.html",
},
},
files: {
"options.html": `
<html>
<body>
<p>Some text</p>
</body>
</html>
`,
},
useAddonManager: "permanent",
});
await extension.startup();
let win = await loadInitialView("extension");
let doc = win.document;
let card = getAddonCard(win, id);
let loaded = waitForViewLoad(win);
card.querySelector('[action="expand"]').click();
await loaded;
card = doc.querySelector("addon-card");
let browserAdded = waitOptionsBrowserInserted();
card.querySelector('.tab-button[name="preferences"]').click();
await browserAdded;
is(
doc.querySelectorAll("inline-options-browser").length,
1,
"There is 1 inline-options-browser"
);
is(doc.querySelectorAll("browser").length, 1, "There is 1 browser");
info("Reload the add-on and ensure there's still only one browser");
let updated = BrowserTestUtils.waitForEvent(card, "update");
card.addon.reload();
await updated;
// Since the add-on was disabled, we'll be on the details tab.
is(card.details.deck.selectedViewName, "details", "View changed to details");
is(
doc.querySelectorAll("inline-options-browser").length,
1,
"There is 1 inline-options-browser"
);
is(doc.querySelectorAll("browser").length, 0, "The browser was destroyed");
// Load the permissions tab again.
browserAdded = waitOptionsBrowserInserted();
card.querySelector('.tab-button[name="preferences"]').click();
await browserAdded;
// Switching to preferences will create a new browser element.
is(
card.details.deck.selectedViewName,
"preferences",
"View switched to preferences"
);
is(
doc.querySelectorAll("inline-options-browser").length,
1,
"There is 1 inline-options-browser"
);
is(doc.querySelectorAll("browser").length, 1, "There is a new browser");
info("Re-rendering card to ensure a second browser isn't added");
updated = BrowserTestUtils.waitForEvent(card, "update");
card.render();
await updated;
is(
card.details.deck.selectedViewName,
"details",
"Rendering reverted to the details view"
);
is(
doc.querySelectorAll("inline-options-browser").length,
1,
"There is still only 1 inline-options-browser after re-render"
);
is(doc.querySelectorAll("browser").length, 0, "There is no browser");
let newBrowserAdded = waitOptionsBrowserInserted();
card.showPrefs();
await newBrowserAdded;
is(
doc.querySelectorAll("inline-options-browser").length,
1,
"There is still only 1 inline-options-browser after opening preferences"
);
is(doc.querySelectorAll("browser").length, 1, "There is 1 browser");
await closeView(win);
await extension.unload();
});
add_task(async function testRemovedOnDisable() {
let id = "disable@mochi.test";
const xpiFile = AddonTestUtils.createTempWebExtensionFile({
manifest: {
browser_specific_settings: { gecko: { id } },
options_ui: {
page: "options.html",
},
},
files: {
"options.html": "<h1>Options!</h1>",
},
});
let addon = await AddonManager.installTemporaryAddon(xpiFile);
let win = await loadInitialView("extension");
let doc = win.document;
// Opens the prefs page.
let loaded = waitForViewLoad(win);
getAddonCard(win, id).querySelector("[action=preferences]").click();
await loaded;
let inlineOptions = doc.querySelector("inline-options-browser");
ok(inlineOptions, "There's an inline-options-browser element");
ok(inlineOptions.querySelector("browser"), "The browser exists");
let card = getAddonCard(win, id);
let { deck } = card.details;
is(deck.selectedViewName, "preferences", "Preferences are the active tab");
info("Disabling the add-on");
let updated = BrowserTestUtils.waitForEvent(card, "update");
await addon.disable();
await updated;
is(deck.selectedViewName, "details", "Details are now the active tab");
ok(inlineOptions, "There's an inline-options-browser element");
ok(!inlineOptions.querySelector("browser"), "The browser has been removed");
info("Enabling the add-on");
updated = BrowserTestUtils.waitForEvent(card, "update");
await addon.enable();
await updated;
is(deck.selectedViewName, "details", "Details are still the active tab");
ok(inlineOptions, "There's an inline-options-browser element");
ok(!inlineOptions.querySelector("browser"), "The browser is not created yet");
info("Switching to preferences tab");
let changed = BrowserTestUtils.waitForEvent(deck, "view-changed");
let browserAdded = waitOptionsBrowserInserted();
deck.selectedViewName = "preferences";
await changed;
await browserAdded;
is(deck.selectedViewName, "preferences", "Preferences are selected");
ok(inlineOptions, "There's an inline-options-browser element");
ok(inlineOptions.querySelector("browser"), "The browser is re-created");
await closeView(win);
await addon.uninstall();
});
add_task(async function testUpgradeTemporary() {
let id = "upgrade-temporary@mochi.test";
async function loadExtension(version) {
let extension = ExtensionTestUtils.loadExtension({
manifest: {
browser_specific_settings: { gecko: { id } },
version,
options_ui: {
page: "options.html",
},
},
files: {
"options.html": `
<html>
<head>
<script src="options.js"></script>
</head>
<body>
<p>Version <pre>${version}</pre></p>
</body>
</html>
`,
"options.js": () => {
browser.test.onMessage.addListener(msg => {
if (msg === "get-version") {
let version = document.querySelector("pre").textContent;
browser.test.sendMessage("version", version);
}
});
window.onload = () => browser.test.sendMessage("options-loaded");
},
},
useAddonManager: "temporary",
});
await extension.startup();
return extension;
}
let firstExtension = await loadExtension("1");
let win = await loadInitialView("extension");
let doc = win.document;
let card = getAddonCard(win, id);
let loaded = waitForViewLoad(win);
card.querySelector('[action="expand"]').click();
await loaded;
card = doc.querySelector("addon-card");
let browserAdded = waitOptionsBrowserInserted();
card.querySelector('.tab-button[name="preferences"]').click();
await browserAdded;
await firstExtension.awaitMessage("options-loaded");
await firstExtension.sendMessage("get-version");
let version = await firstExtension.awaitMessage("version");
is(version, "1", "Version 1 page is loaded");
let updated = BrowserTestUtils.waitForEvent(card, "update");
browserAdded = waitOptionsBrowserInserted();
let secondExtension = await loadExtension("2");
await updated;
await browserAdded;
await secondExtension.awaitMessage("options-loaded");
await secondExtension.sendMessage("get-version");
version = await secondExtension.awaitMessage("version");
is(version, "2", "Version 2 page is loaded");
let { deck } = card.details;
is(deck.selectedViewName, "preferences", "Preferences are still shown");
await closeView(win);
await firstExtension.unload();
await secondExtension.unload();
});
add_task(async function testReloadExtension() {
let id = "reload@mochi.test";
let xpiFile = AddonTestUtils.createTempWebExtensionFile({
manifest: {
browser_specific_settings: { gecko: { id } },
options_ui: {
page: "options.html",
},
},
files: {
"options.html": `
<html>
<head>
</head>
<body>
<p>Options</p>
</body>
</html>
`,
},
});
let addon = await AddonManager.installTemporaryAddon(xpiFile);
let win = await loadInitialView("extension");
let doc = win.document;
let card = getAddonCard(win, id);
let loaded = waitForViewLoad(win);
card.querySelector('[action="expand"]').click();
await loaded;
card = doc.querySelector("addon-card");
let { deck } = card.details;
is(deck.selectedViewName, "details", "Details load first");
let browserAdded = waitOptionsBrowserInserted();
card.querySelector('.tab-button[name="preferences"]').click();
await browserAdded;
is(deck.selectedViewName, "preferences", "Preferences are shown");
let updated = BrowserTestUtils.waitForEvent(card, "update");
browserAdded = waitOptionsBrowserInserted();
let addonStarted = AddonTestUtils.promiseWebExtensionStartup(id);
await addon.reload();
await addonStarted;
await updated;
await browserAdded;
is(deck.selectedViewName, "preferences", "Preferences are still shown");
await closeView(win);
await addon.uninstall();
});
async function testSelectPosition(optionsBrowser, zoom) {
let popupShownPromise = BrowserTestUtils.waitForSelectPopupShown(window);
await BrowserTestUtils.synthesizeMouseAtCenter("select", {}, optionsBrowser);
let popup = await popupShownPromise;
let popupLeft = popup.shadowRoot.querySelector(
".menupopup-arrowscrollbox"
).screenX;
let browserLeft = optionsBrowser.screenX * zoom;
Assert.lessOrEqual(
Math.abs(popupLeft - browserLeft),
1,
`Popup should be correctly positioned: ${popupLeft} vs. ${browserLeft}`
);
popup.hidePopup();
}
async function testOptionsZoom(type = "full") {
let id = `${type}-zoom@mochi.test`;
let zoomProp = `${type}Zoom`;
let extension = ExtensionTestUtils.loadExtension({
manifest: {
browser_specific_settings: { gecko: { id } },
options_ui: {
page: "options.html",
},
},
files: {
"options.html": `
<!doctype html>
<script src="options.js"></script>
<body style="height: 500px">
<p>Some text</p>
<p>
<select>
<option>A</option>
<option>B</option>
</select>
</p>
</body>
`,
"options.js": () => {
window.addEventListener("load", function () {
browser.test.sendMessage("options-loaded");
});
},
},
useAddonManager: "permanent",
});
await extension.startup();
let win = await loadInitialView("extension");
let doc = win.document;
gBrowser.selectedBrowser[zoomProp] = 2;
let card = getAddonCard(win, id);
let loaded = waitForViewLoad(win);
card.querySelector('[action="expand"]').click();
await loaded;
card = doc.querySelector("addon-card");
let browserAdded = waitOptionsBrowserInserted();
card.querySelector('.tab-button[name="preferences"]').click();
let optionsBrowser = await browserAdded;
// Wait for the browser to load.
await extension.awaitMessage("options-loaded");
is(optionsBrowser[zoomProp], 2, `Options browser inherited ${zoomProp}`);
await testSelectPosition(optionsBrowser, type == "full" ? 2 : 1);
gBrowser.selectedBrowser[zoomProp] = 0.5;
is(
optionsBrowser[zoomProp],
0.5,
`Options browser reacts to ${zoomProp} change`
);
await closeView(win);
await extension.unload();
}
add_task(function testOptionsFullZoom() {
return testOptionsZoom("full");
});
add_task(function testOptionsTextZoom() {
return testOptionsZoom("text");
});
add_task(async function testInputAndQuickFind() {
let extension = ExtensionTestUtils.loadExtension({
manifest: {
options_ui: {
page: "options.html",
},
},
files: {
"options.html": `
<html>
<body>
<input name="some-input" type="text">
<script src="options.js"></script>
</body>
</html>
`,
"options.js": () => {
let input = document.querySelector("input");
browser.test.assertEq(
"some-input",
input.getAttribute("name"),
"Expected options page input"
);
input.addEventListener("input", event => {
browser.test.sendMessage("input-changed", event.target.value);
});
browser.test.sendMessage("options-loaded", window.location.href);
},
},
useAddonManager: "temporary",
});
await extension.startup();
let win = await loadInitialView("extension");
let doc = win.document;
// Make sure we found the right card.
let card = getAddonCard(win, extension.id);
ok(card, "Found the card");
// The preferences option should be visible.
let preferences = card.querySelector('[action="preferences"]');
ok(!preferences.hidden, "The preferences option is visible");
// Open the preferences page.
let loaded = waitForViewLoad(win);
preferences.click();
await loaded;
// Verify we're on the preferences tab.
card = doc.querySelector("addon-card");
is(card.addon.id, extension.id, "The right page was loaded");
// Wait for the browser to load.
let url = await extension.awaitMessage("options-loaded");
// Check the attributes of the options browser.
let browser = card.querySelector("inline-options-browser browser");
ok(browser, "The visible view has a browser");
ok(card.addon.optionsURL.length, "Options URL is not empty");
is(
browser.currentURI.spec,
card.addon.optionsURL,
"The browser has the expected options URL"
);
is(url, card.addon.optionsURL, "Browser has the expected options URL loaded");
// Focus the options browser.
browser.focus();
// Focus the input in the options page.
await SpecialPowers.spawn(browser, [], () => {
content.document.querySelector("input").focus();
});
info("input in options page should be focused, typing...");
// Type '/'.
EventUtils.synthesizeKey("/");
let inputValue = await extension.awaitMessage("input-changed");
is(inputValue, "/", "Expected input to contain a slash");
await closeView(win);
await extension.unload();
});