656 lines
20 KiB
JavaScript
656 lines
20 KiB
JavaScript
/* eslint max-len: ["error", 80] */
|
|
|
|
const { AddonTestUtils } = ChromeUtils.importESModule(
|
|
"resource://testing-common/AddonTestUtils.sys.mjs"
|
|
);
|
|
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.documentGlobal.top.location.href == "about:addons") {
|
|
ExtensionParent.apiManager.off("extension-browser-inserted", listener);
|
|
resolve(browser);
|
|
}
|
|
}
|
|
ExtensionParent.apiManager.on("extension-browser-inserted", listener);
|
|
});
|
|
}
|
|
|
|
add_setup(async () => {
|
|
await SpecialPowers.pushPrefEnv({
|
|
set: [
|
|
["extensions.htmlaboutaddons.inline-options.enabled", true],
|
|
["widget.macos.allow-native-select", false],
|
|
],
|
|
});
|
|
});
|
|
|
|
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 SimpleTest.promiseFocus(optionsBrowser);
|
|
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();
|
|
});
|