Source code
Revision control
Copy as Markdown
Other Tools
/* Any copyright is dedicated to the Public Domain.
/**
* This test searches for selected text with the context menu using both the
* default and default-private engines in both non-private and private windows.
*/
const { AddonTestUtils } = ChromeUtils.importESModule(
);
requestLongerTimeout(3);
AddonTestUtils.initMochitest(this);
const ENGINE_ITEM_ID = "context-searchselect";
const PRIVATE_ENGINE_ITEM_ID = "context-searchselect-private";
const ENGINE_NAME = "mozSearch";
const ENGINE_URL =
const PRIVATE_ENGINE_NAME = "mozPrivateSearch";
const ENGINE_DATA = new Map([
[ENGINE_NAME, ENGINE_URL],
[PRIVATE_ENGINE_NAME, PRIVATE_ENGINE_URL],
]);
const TEST_PAGE_URL =
let engine;
let privateEngine;
let extensions = [];
let oldDefaultEngine;
let oldDefaultPrivateEngine;
add_setup(async function () {
await SpecialPowers.pushPrefEnv({
set: [["test.wait300msAfterTabSwitch", true]],
});
await Services.search.init();
for (let [name, search_url] of ENGINE_DATA) {
let extension = ExtensionTestUtils.loadExtension({
manifest: {
chrome_settings_overrides: {
search_provider: {
name,
search_url,
search_url_get_params: "test={searchTerms}",
},
},
},
});
await extension.startup();
await AddonTestUtils.waitForSearchProviderStartup(extension);
extensions.push(extension);
}
engine = await Services.search.getEngineByName(ENGINE_NAME);
Assert.ok(engine, "Got a search engine");
oldDefaultEngine = await Services.search.getDefault();
await Services.search.setDefault(
engine,
Ci.nsISearchService.CHANGE_REASON_UNKNOWN
);
privateEngine = await Services.search.getEngineByName(PRIVATE_ENGINE_NAME);
Assert.ok(privateEngine, "Got a search engine");
oldDefaultPrivateEngine = await Services.search.getDefaultPrivate();
});
/**
* Selects some text in the current tab, opens the context menu, and checks the
* given menuitem.
*
* @param {object} options
* Options object.
* @param {Window} options.win
* The context menu will be opened in the current tab in this browser window.
* @param {string} options.itemIdToCheck
* The menuitem element ID to check.
* @param {boolean} options.expectedShown
* Whether the menuitem is expected to be visible.
* @param {string} options.expectedLabel
* The expected label of the menuitem.
* @returns {object}
* An object: `{ contextMenu, item }`
*/
async function checkContextMenu({
win,
itemIdToCheck,
expectedShown,
expectedLabel,
}) {
let contextMenu = win.document.getElementById("contentAreaContextMenu");
Assert.ok(contextMenu, "Got context menu XUL");
let tab = win.gBrowser.selectedTab;
await SpecialPowers.spawn(tab.linkedBrowser, [""], async function () {
return new Promise(resolve => {
content.document.addEventListener(
"selectionchange",
function () {
resolve();
},
{ once: true }
);
content.document.getSelection().selectAllChildren(content.document.body);
});
});
let eventDetails = { type: "contextmenu", button: 2 };
let popupPromise = BrowserTestUtils.waitForEvent(contextMenu, "popupshown");
info("Synthesizing context menu mouse event");
BrowserTestUtils.synthesizeMouseAtCenter(
"body",
eventDetails,
win.gBrowser.selectedBrowser
);
info("Waiting for context menu to open");
await popupPromise;
info("Context menu opened");
let searchItem = win.document.getElementById(itemIdToCheck);
Assert.ok(searchItem, "Got search context menu item");
Assert.equal(
!searchItem.hidden,
expectedShown,
"Search item should be shown or hidden as expected"
);
if (!searchItem.hidden) {
Assert.equal(searchItem.label, expectedLabel, "Check context menu label");
Assert.equal(
searchItem.disabled,
false,
"Check that search context menu item is enabled"
);
}
return { contextMenu, item: searchItem };
}
/**
* Opens a new tab and calls `checkContextMenu()`. If the menuitem is expected
* to be visible, then this function also clicks it and verifies that a SERP is
* loaded, either in a new tab or in a new private window.
*
* @param {object} options
* Options object.
* @param {Window} options.win
* The new tab and `checkContextMenu()` will be called on this window.
* @param {string} options.itemIdToCheck
* The menuitem element ID to check.
* @param {boolean} options.expectedShown
* Whether the menuitem is expected to be visible.
* @param {string} options.expectedLabel
* The expected label of the menuitem.
* @param {string} options.expectedBaseUrl
* The expected base URL of the SERP loaded by the menuitem.
* @param {boolean} options.expectedOpensNewPrivateWindow
* Whether the SERP is expected to load in a new private window.
*/
async function checkContextMenuAndClickItem({
win,
itemIdToCheck,
expectedShown,
expectedLabel,
expectedBaseUrl,
expectedOpensNewPrivateWindow,
}) {
await BrowserTestUtils.withNewTab(
{
gBrowser: win.gBrowser,
url: TEST_PAGE_URL,
},
async () => {
let { contextMenu, item } = await checkContextMenu({
win,
itemIdToCheck,
expectedShown,
expectedLabel,
});
if (item.hidden) {
let hiddenPromise = BrowserTestUtils.waitForEvent(
contextMenu,
"popuphidden"
);
info("Closing context menu");
contextMenu.hidePopup();
info("Waiting for context menu to close");
await hiddenPromise;
info("Context menu closed");
return;
}
let expectedUrl = expectedBaseUrl + "?test=test%2520search";
let loadPromise = expectedOpensNewPrivateWindow
? BrowserTestUtils.waitForNewWindow({ url: expectedUrl })
: BrowserTestUtils.waitForNewTab(win.gBrowser, expectedUrl, true);
info("Clicking search item");
contextMenu.activateItem(item);
let message = expectedOpensNewPrivateWindow
? "Waiting for new SERP private window to load"
: "Waiting for new SERP tab to load";
info(`${message}: ${expectedUrl}`);
let serpTabOrWin = await loadPromise;
info("SERP loaded");
let isSerpWin = serpTabOrWin instanceof Ci.nsIDOMWindow;
let serpWin = isSerpWin ? serpTabOrWin : win;
let browser = serpWin.gBrowser.selectedBrowser;
await SpecialPowers.spawn(browser, [], async function () {
Assert.ok(
!/error/.test(content.document.body.innerHTML),
"Ensure there were no errors loading the search page"
);
});
if (isSerpWin) {
await BrowserTestUtils.closeWindow(serpTabOrWin);
} else {
BrowserTestUtils.removeTab(serpTabOrWin);
}
}
);
}
// The main test task. Runs through all possible combinations of the relevant
// variables.
add_task(async function test() {
for (let separatePrivateDefault of [false, true]) {
for (let separatePrivateDefaultUiEnabled of [false, true]) {
for (let inPrivateWindow of [false, true]) {
for (let checkPrivateItem of [false, true]) {
for (let defaultPrivateEngine of [engine, privateEngine]) {
await computeExpectedAndDoTest({
separatePrivateDefault,
separatePrivateDefaultUiEnabled,
defaultPrivateEngine,
inPrivateWindow,
checkPrivateItem,
});
}
}
}
}
}
});
/**
* Figures out the expected behavior based on some initial conditions and calls
* `doTest()`.
*
* @param {object} options
* Options object.
* @param {boolean} options.separatePrivateDefault
* The value to set for the `separatePrivateDefault` pref.
* @param {boolean} options.separatePrivateDefaultUiEnabled
* The value to set for the `separatePrivateDefault.ui.enabled` pref.
* @param {nsISearchEngine} options.defaultPrivateEngine
* The engine to set as the default private engine.
* @param {boolean} options.inPrivateWindow
* Whether the test should start in a private window.
* @param {boolean} options.checkPrivateItem
* Which menuitem to check. False means the non-private search menuitem, and
* true means the private search menuitem.
*/
async function computeExpectedAndDoTest({
separatePrivateDefault,
separatePrivateDefaultUiEnabled,
defaultPrivateEngine,
inPrivateWindow,
checkPrivateItem,
}) {
// When `separatePrivateDefault.ui.enabled` is false, `setDefaultPrivate()`
// will set the non-private default, which would make this test more complex
// and isn't the point anyway, so avoid that by just not setting the default
// private engine in that case.
if (!separatePrivateDefaultUiEnabled) {
defaultPrivateEngine = null;
}
let expectedShown =
!checkPrivateItem || (!inPrivateWindow && separatePrivateDefaultUiEnabled);
let expectedLabel;
let expectedBaseUrl;
let expectedOpensNewPrivateWindow;
if (expectedShown) {
let shouldUsePrivateEngine =
defaultPrivateEngine && defaultPrivateEngine != engine;
if (checkPrivateItem) {
expectedLabel =
!inPrivateWindow && shouldUsePrivateEngine
? "Search with " + PRIVATE_ENGINE_NAME + " in a Private Window"
: "Search in a Private Window";
expectedOpensNewPrivateWindow = !inPrivateWindow;
} else {
shouldUsePrivateEngine &&= inPrivateWindow;
let expectedName = shouldUsePrivateEngine
? PRIVATE_ENGINE_NAME
: ENGINE_NAME;
expectedLabel =
"Search " + expectedName + " for \u201ctest%20search\u201d";
expectedOpensNewPrivateWindow = false;
}
expectedBaseUrl = shouldUsePrivateEngine ? PRIVATE_ENGINE_URL : ENGINE_URL;
}
let itemIdToCheck = checkPrivateItem
? PRIVATE_ENGINE_ITEM_ID
: ENGINE_ITEM_ID;
await doTest({
separatePrivateDefault,
separatePrivateDefaultUiEnabled,
defaultPrivateEngine,
inPrivateWindow,
itemIdToCheck,
expectedShown,
expectedLabel,
expectedBaseUrl,
expectedOpensNewPrivateWindow,
});
}
/**
* Does one test: Sets prefs to the requested values, sets a default private
* engine, opens a private window to run the test in if requested, and checks
* and clicks the given context menu item.
*
* @param {object} options
* Options object.
* @param {boolean} options.separatePrivateDefault
* The value to set for the `separatePrivateDefault` pref.
* @param {boolean} options.separatePrivateDefaultUiEnabled
* The value to set for the `separatePrivateDefault.ui.enabled` pref.
* @param {nsISearchEngine} options.defaultPrivateEngine
* The engine to set as the default private engine.
* @param {boolean} options.inPrivateWindow
* Whether the test should start in a private window.
* @param {string} options.itemIdToCheck
* The menuitem element ID to check.
* @param {boolean} options.expectedShown
* Whether the menuitem is expected to be visible.
* @param {string} options.expectedLabel
* The expected label of the menuitem.
* @param {string} options.expectedBaseUrl
* The expected base URL of the SERP loaded by the menuitem.
* @param {boolean} options.expectedOpensNewPrivateWindow
* Whether the SERP is expected to load in a new private window.
*/
async function doTest({
separatePrivateDefault,
separatePrivateDefaultUiEnabled,
defaultPrivateEngine,
inPrivateWindow,
itemIdToCheck,
expectedShown,
expectedLabel,
expectedBaseUrl,
expectedOpensNewPrivateWindow,
}) {
info(
"Doing test: " +
JSON.stringify(
{
separatePrivateDefault,
separatePrivateDefaultUiEnabled,
defaultPrivateEngine: defaultPrivateEngine?.name,
inPrivateWindow,
itemIdToCheck,
expectedShown,
expectedLabel,
expectedBaseUrl,
expectedOpensNewPrivateWindow,
},
null,
2
)
);
await SpecialPowers.pushPrefEnv({
set: [
["browser.search.separatePrivateDefault", separatePrivateDefault],
[
"browser.search.separatePrivateDefault.ui.enabled",
separatePrivateDefaultUiEnabled,
],
],
});
if (defaultPrivateEngine) {
await Services.search.setDefaultPrivate(
defaultPrivateEngine,
Ci.nsISearchService.CHANGE_REASON_UNKNOWN
);
}
let win = inPrivateWindow
? await BrowserTestUtils.openNewBrowserWindow({ private: true })
: window;
await checkContextMenuAndClickItem({
win,
itemIdToCheck,
expectedShown,
expectedLabel,
expectedBaseUrl,
expectedOpensNewPrivateWindow,
});
if (inPrivateWindow) {
await BrowserTestUtils.closeWindow(win);
}
await SpecialPowers.popPrefEnv();
}
// We can't do the unload within registerCleanupFunction as that's too late for
// the test to be happy. Do it into a cleanup "test" here instead.
add_task(async function cleanup() {
await Services.search.setDefault(
oldDefaultEngine,
Ci.nsISearchService.CHANGE_REASON_UNKNOWN
);
await Services.search.setDefaultPrivate(
oldDefaultPrivateEngine,
Ci.nsISearchService.CHANGE_REASON_UNKNOWN
);
await Services.search.removeEngine(engine);
await Services.search.removeEngine(privateEngine);
for (let extension of extensions) {
await extension.unload();
}
});