Source code

Revision control

Copy as Markdown

Other Tools

Test Info:

// Tests that the suggestion popup appears at the right times in response to
// focus and user events (mouse, keyboard, drop).
const searchPopup = document.getElementById("PopupSearchAutoComplete");
const kValues = ["long text", "long text 2", "long text 3"];
async function endCustomizing(aWindow = window) {
if (aWindow.document.documentElement.getAttribute("customizing") != "true") {
return true;
}
let eventPromise = BrowserTestUtils.waitForEvent(
aWindow.gNavToolbox,
"aftercustomization"
);
aWindow.gCustomizeMode.exit();
return eventPromise;
}
async function startCustomizing(aWindow = window) {
if (aWindow.document.documentElement.getAttribute("customizing") == "true") {
return true;
}
let eventPromise = BrowserTestUtils.waitForEvent(
aWindow.gNavToolbox,
"customizationready"
);
aWindow.gCustomizeMode.enter();
return eventPromise;
}
let searchbar;
let textbox;
let searchIcon;
let goButton;
let engine;
add_setup(async function () {
searchbar = await gCUITestUtils.addSearchBar();
textbox = searchbar.textbox;
searchIcon = searchbar.querySelector(".searchbar-search-button");
goButton = searchbar.querySelector(".search-go-button");
engine = await SearchTestUtils.promiseNewSearchEngine({
url: getRootDirectory(gTestPath) + "testEngine.xml",
setAsDefault: true,
});
await clearSearchbarHistory();
let addOps = kValues.map(value => {
return { op: "add", fieldname: "searchbar-history", value };
});
info("adding search history values: " + kValues);
await FormHistory.update(addOps);
registerCleanupFunction(async () => {
await clearSearchbarHistory();
gCUITestUtils.removeSearchBar();
});
});
// Adds a task that shouldn't show the search suggestions popup.
function add_no_popup_task(task) {
add_task(async function () {
let sawPopup = false;
function listener() {
sawPopup = true;
}
info("Entering test " + task.name);
searchPopup.addEventListener("popupshowing", listener);
await task();
searchPopup.removeEventListener("popupshowing", listener);
ok(!sawPopup, "Shouldn't have seen the suggestions popup");
info("Leaving test " + task.name);
});
}
// Simulates the full set of events for a context click
function context_click(target) {
for (let event of ["mousedown", "contextmenu"]) {
EventUtils.synthesizeMouseAtCenter(target, { type: event, button: 2 });
}
}
// Right clicking the icon should not open the popup.
add_no_popup_task(async function open_icon_context() {
gURLBar.focus();
let toolbarPopup = document.getElementById("toolbar-context-menu");
let promise = promiseEvent(toolbarPopup, "popupshown");
context_click(searchIcon);
await promise;
promise = promiseEvent(toolbarPopup, "popuphidden");
toolbarPopup.hidePopup();
await promise;
});
// With no text in the search box left clicking the icon should open the popup.
// Clicking the icon again should hide the popup and not show it again.
add_task(async function open_empty() {
gURLBar.focus();
let promise = promiseEvent(searchPopup, "popupshown");
info("Clicking icon");
is(
searchIcon.getAttribute("aria-expanded"),
"false",
"The search icon is not expanded by default"
);
EventUtils.synthesizeMouseAtCenter(searchIcon, {});
await promise;
is(
searchPopup.getAttribute("showonlysettings"),
"true",
"Should only show the settings"
);
is(
searchIcon.getAttribute("aria-expanded"),
"true",
"The search icon is now expanded"
);
is(textbox.mController.searchString, "", "Should be an empty search string");
let image = searchPopup.querySelector(".searchbar-engine-image");
Assert.equal(
image.src,
await engine.getIconURL(16),
"Should have the correct icon"
);
// By giving the textbox some text any next attempt to open the search popup
// from the click handler will try to search for this text.
textbox.value = "foo";
promise = promiseEvent(searchPopup, "popuphidden");
info("Hiding popup");
await EventUtils.promiseNativeMouseEventAndWaitForEvent({
type: "click",
target: searchIcon,
atCenter: true,
eventTypeToWait: "mouseup",
});
await promise;
is(
textbox.mController.searchString,
"",
"Should not have started to search for the new text"
);
is(
searchIcon.getAttribute("aria-expanded"),
"false",
"The search icon should not be expanded"
);
// Cancel the search if it started.
if (textbox.mController.searchString != "") {
textbox.mController.stopSearch();
}
textbox.value = "";
});
// With no text in the search box left clicking it should not open the popup.
add_no_popup_task(function click_doesnt_open_popup() {
gURLBar.focus();
EventUtils.synthesizeMouseAtCenter(textbox, {});
is(
Services.focus.focusedElement,
textbox,
"Should have focused the search bar"
);
is(textbox.selectionStart, 0, "Should have selected all of the text");
is(textbox.selectionEnd, 0, "Should have selected all of the text");
});
// Left clicking in a non-empty search box when unfocused should focus it and open the popup.
add_task(async function click_opens_popup() {
gURLBar.focus();
textbox.value = "foo";
let promise = promiseEvent(searchPopup, "popupshown");
EventUtils.synthesizeMouseAtCenter(textbox, {});
await promise;
isnot(
searchPopup.getAttribute("showonlysettings"),
"true",
"Should show the full popup"
);
is(
Services.focus.focusedElement,
textbox,
"Should have focused the search bar"
);
is(textbox.selectionStart, 0, "Should have selected all of the text");
is(textbox.selectionEnd, 3, "Should have selected all of the text");
promise = promiseEvent(searchPopup, "popuphidden");
searchPopup.hidePopup();
await promise;
textbox.value = "";
});
add_task(async function open_empty_hiddenOneOffs() {
// Disable all the engines but the current one and check the oneoffs.
let defaultEngine = await Services.search.getDefault();
let engines = (await Services.search.getVisibleEngines()).filter(
e => e.name != defaultEngine.name
);
engines.forEach(e => {
e.hideOneOffButton = true;
});
textbox.value = "foo";
let promise = promiseEvent(searchPopup, "popupshown");
EventUtils.synthesizeMouseAtCenter(textbox, {});
await promise;
Assert.ok(
searchPopup.searchOneOffsContainer.hasAttribute("hidden"),
"The one-offs buttons container should have the hidden attribute."
);
Assert.ok(
BrowserTestUtils.isHidden(searchPopup.searchOneOffsContainer),
"The one-off buttons container should be hidden."
);
promise = promiseEvent(searchPopup, "popuphidden");
info("Hiding popup");
await EventUtils.promiseNativeMouseEventAndWaitForEvent({
type: "click",
target: searchIcon,
atCenter: true,
eventTypeToWait: "mouseup",
});
await promise;
engines.forEach(e => {
e.hideOneOffButton = false;
});
textbox.value = "";
});
// Right clicking in a non-empty search box when unfocused should open the edit context menu.
add_no_popup_task(async function right_click_doesnt_open_popup() {
gURLBar.focus();
textbox.value = "foo";
// Can't wait for an event on the actual menu since it is created
// lazily the first time it is displayed.
let promise = new Promise(resolve => {
let listener = event => {
if (searchbar._menupopup && event.target == searchbar._menupopup) {
resolve(searchbar._menupopup);
}
};
window.addEventListener("popupshown", listener);
});
context_click(textbox);
let contextPopup = await promise;
// Assert that the context menu click inside the popup does nothing. If it
// opens something, assert_no_popup_task will make us fail. On macOS this
// doesn't work because of native context menus.
if (!navigator.platform.includes("Mac")) {
context_click(contextPopup);
}
is(
Services.focus.focusedElement,
textbox,
"Should have focused the search bar"
);
is(textbox.selectionStart, 0, "Should have selected all of the text");
is(textbox.selectionEnd, 3, "Should have selected all of the text");
promise = promiseEvent(contextPopup, "popuphidden");
contextPopup.hidePopup();
await promise;
textbox.value = "";
});
// Moving focus away from the search box should close the popup
add_task(async function focus_change_closes_popup() {
gURLBar.focus();
textbox.value = "foo";
let promise = promiseEvent(searchPopup, "popupshown");
EventUtils.synthesizeMouseAtCenter(textbox, {});
await promise;
isnot(
searchPopup.getAttribute("showonlysettings"),
"true",
"Should show the full popup"
);
is(
Services.focus.focusedElement,
textbox,
"Should have focused the search bar"
);
is(textbox.selectionStart, 0, "Should have selected all of the text");
is(textbox.selectionEnd, 3, "Should have selected all of the text");
promise = promiseEvent(searchPopup, "popuphidden");
let promise2 = promiseEvent(searchbar.textbox, "blur");
EventUtils.synthesizeKey("KEY_Tab", { shiftKey: true });
await promise;
await promise2;
textbox.value = "";
});
// Moving focus away from the search box should close the small popup
add_task(async function focus_change_closes_small_popup() {
gURLBar.focus();
let promise = promiseEvent(searchPopup, "popupshown");
// For some reason sending the mouse event immediately doesn't open the popup.
SimpleTest.executeSoon(() => {
EventUtils.synthesizeMouseAtCenter(searchIcon, {});
});
await promise;
is(
searchPopup.getAttribute("showonlysettings"),
"true",
"Should show the small popup"
);
is(
Services.focus.focusedElement,
textbox,
"Should have focused the search bar"
);
promise = promiseEvent(searchPopup, "popuphidden");
let promise2 = promiseEvent(searchbar.textbox, "blur");
EventUtils.synthesizeKey("KEY_Tab", { shiftKey: true });
await promise;
await promise2;
});
// Pressing escape should close the popup.
add_task(async function escape_closes_popup() {
gURLBar.focus();
textbox.value = "foo";
let promise = promiseEvent(searchPopup, "popupshown");
EventUtils.synthesizeMouseAtCenter(textbox, {});
await promise;
isnot(
searchPopup.getAttribute("showonlysettings"),
"true",
"Should show the full popup"
);
is(
Services.focus.focusedElement,
textbox,
"Should have focused the search bar"
);
is(textbox.selectionStart, 0, "Should have selected all of the text");
is(textbox.selectionEnd, 3, "Should have selected all of the text");
promise = promiseEvent(searchPopup, "popuphidden");
EventUtils.synthesizeKey("KEY_Escape");
await promise;
textbox.value = "";
});
// Pressing contextmenu should close the popup.
add_task(async function contextmenu_closes_popup() {
gURLBar.focus();
textbox.value = "foo";
let promise = promiseEvent(searchPopup, "popupshown");
EventUtils.synthesizeMouseAtCenter(textbox, {});
await promise;
isnot(
searchPopup.getAttribute("showonlysettings"),
"true",
"Should show the full popup"
);
is(
Services.focus.focusedElement,
textbox,
"Should have focused the search bar"
);
is(textbox.selectionStart, 0, "Should have selected all of the text");
is(textbox.selectionEnd, 3, "Should have selected all of the text");
let contextPopup = searchbar._menupopup;
let contextMenuShownPromise = promiseEvent(contextPopup, "popupshown");
let searchPopupHiddenPromise = promiseEvent(searchPopup, "popuphidden");
context_click(textbox);
await contextMenuShownPromise;
await searchPopupHiddenPromise;
let contextMenuHiddenPromise = promiseEvent(contextPopup, "popuphidden");
contextPopup.hidePopup();
await contextMenuHiddenPromise;
textbox.value = "";
});
// Tabbing to the search box should open the popup if it contains text.
add_task(async function tab_opens_popup() {
gURLBar.focus();
textbox.value = "foo";
let promise = promiseEvent(searchPopup, "popupshown");
EventUtils.synthesizeKey("KEY_Tab");
await promise;
isnot(
searchPopup.getAttribute("showonlysettings"),
"true",
"Should show the full popup"
);
is(
Services.focus.focusedElement,
textbox,
"Should have focused the search bar"
);
is(textbox.selectionStart, 0, "Should have selected all of the text");
is(textbox.selectionEnd, 3, "Should have selected all of the text");
promise = promiseEvent(searchPopup, "popuphidden");
searchPopup.hidePopup();
await promise;
textbox.value = "";
});
// Tabbing to the search box should not open the popup if it doesn't contain text.
add_no_popup_task(function tab_doesnt_open_popup() {
gURLBar.focus();
textbox.value = "foo";
EventUtils.synthesizeKey("KEY_Tab");
is(
Services.focus.focusedElement,
textbox,
"Should have focused the search bar"
);
is(textbox.selectionStart, 0, "Should have selected all of the text");
is(textbox.selectionEnd, 3, "Should have selected all of the text");
textbox.value = "";
});
// Switching back to the window when the search box has focus from mouse should not open the popup.
add_task(async function refocus_window_doesnt_open_popup_mouse() {
gURLBar.focus();
textbox.value = "foo";
let promise = promiseEvent(searchPopup, "popupshown");
EventUtils.synthesizeMouseAtCenter(searchbar, {});
await promise;
isnot(
searchPopup.getAttribute("showonlysettings"),
"true",
"Should show the full popup"
);
is(
Services.focus.focusedElement,
textbox,
"Should have focused the search bar"
);
is(textbox.selectionStart, 0, "Should have selected all of the text");
is(textbox.selectionEnd, 3, "Should have selected all of the text");
promise = promiseEvent(searchPopup, "popuphidden");
let newWin = OpenBrowserWindow();
await new Promise(resolve => waitForFocus(resolve, newWin));
await promise;
function listener() {
ok(false, "Should not have shown the popup.");
}
searchPopup.addEventListener("popupshowing", listener);
promise = promiseEvent(searchbar.textbox, "focus");
newWin.close();
await promise;
// Wait a few ticks to allow any focus handlers to show the popup if they are going to.
await new Promise(resolve => executeSoon(resolve));
await new Promise(resolve => executeSoon(resolve));
await new Promise(resolve => executeSoon(resolve));
searchPopup.removeEventListener("popupshowing", listener);
textbox.value = "";
});
// Switching back to the window when the search box has focus from keyboard should not open the popup.
add_task(async function refocus_window_doesnt_open_popup_keyboard() {
gURLBar.focus();
textbox.value = "foo";
let promise = promiseEvent(searchPopup, "popupshown");
EventUtils.synthesizeKey("KEY_Tab");
await promise;
isnot(
searchPopup.getAttribute("showonlysettings"),
"true",
"Should show the full popup"
);
is(
Services.focus.focusedElement,
textbox,
"Should have focused the search bar"
);
is(textbox.selectionStart, 0, "Should have selected all of the text");
is(textbox.selectionEnd, 3, "Should have selected all of the text");
promise = promiseEvent(searchPopup, "popuphidden");
let newWin = OpenBrowserWindow();
await new Promise(resolve => waitForFocus(resolve, newWin));
await promise;
function listener() {
ok(false, "Should not have shown the popup.");
}
searchPopup.addEventListener("popupshowing", listener);
promise = promiseEvent(searchbar.textbox, "focus");
newWin.close();
await promise;
// Wait a few ticks to allow any focus handlers to show the popup if they are going to.
await new Promise(resolve => executeSoon(resolve));
await new Promise(resolve => executeSoon(resolve));
await new Promise(resolve => executeSoon(resolve));
searchPopup.removeEventListener("popupshowing", listener);
textbox.value = "";
});
// Clicking the search go button shouldn't open the popup
add_no_popup_task(async function search_go_doesnt_open_popup() {
gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser);
gURLBar.focus();
textbox.value = "foo";
searchbar.updateGoButtonVisibility();
let promise = BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
EventUtils.synthesizeMouseAtCenter(goButton, {});
await promise;
textbox.value = "";
gBrowser.removeCurrentTab();
});
// Clicks outside the search popup should close the popup but not consume the click.
add_task(async function dont_consume_clicks() {
gURLBar.focus();
textbox.value = "foo";
let promise = promiseEvent(searchPopup, "popupshown");
EventUtils.synthesizeMouseAtCenter(textbox, {});
await promise;
isnot(
searchPopup.getAttribute("showonlysettings"),
"true",
"Should show the full popup"
);
is(
Services.focus.focusedElement,
textbox,
"Should have focused the search bar"
);
is(textbox.selectionStart, 0, "Should have selected all of the text");
is(textbox.selectionEnd, 3, "Should have selected all of the text");
promise = promiseEvent(searchPopup, "popuphidden");
await EventUtils.promiseNativeMouseEventAndWaitForEvent({
type: "click",
target: gURLBar.inputField,
atCenter: true,
eventTypeToWait: "mouseup",
});
await promise;
is(
Services.focus.focusedElement,
gURLBar.inputField,
"Should have focused the URL bar"
);
textbox.value = "";
});
// Dropping text to the searchbar should open the popup
add_task(async function drop_opens_popup() {
CustomizableUI.addWidgetToArea("home-button", "nav-bar");
// The previous task leaves focus in the URL bar. However, in that case drags
// can be interpreted as being selection drags by the drag manager, which
// breaks the drag synthesis from EventUtils.js below. To avoid this, focus
// the browser content instead.
let focusEventPromise = BrowserTestUtils.waitForEvent(
gBrowser.selectedBrowser,
"focus"
);
gBrowser.selectedBrowser.focus();
await focusEventPromise;
let promise = promiseEvent(searchPopup, "popupshown");
// Use a source for the drop that is outside of the search bar area, to avoid
// it receiving a mousedown and causing the popup to sometimes open.
let homeButton = document.getElementById("home-button");
EventUtils.synthesizeDrop(
homeButton,
textbox,
[[{ type: "text/plain", data: "foo" }]],
"move",
window
);
await promise;
isnot(
searchPopup.getAttribute("showonlysettings"),
"true",
"Should show the full popup"
);
is(
Services.focus.focusedElement,
textbox,
"Should have focused the search bar"
);
promise = promiseEvent(searchPopup, "popuphidden");
searchPopup.hidePopup();
await promise;
textbox.value = "";
CustomizableUI.removeWidgetFromArea("home-button");
});
// Moving the caret using the cursor keys should not close the popup.
add_task(async function dont_rollup_oncaretmove() {
gURLBar.focus();
textbox.value = "long text";
let promise = promiseEvent(searchPopup, "popupshown");
EventUtils.synthesizeMouseAtCenter(textbox, {});
await promise;
// Deselect the text
EventUtils.synthesizeKey("KEY_ArrowRight");
is(
textbox.selectionStart,
9,
"Should have moved the caret (selectionStart after deselect right)"
);
is(
textbox.selectionEnd,
9,
"Should have moved the caret (selectionEnd after deselect right)"
);
is(searchPopup.state, "open", "Popup should still be open");
EventUtils.synthesizeKey("KEY_ArrowLeft");
is(
textbox.selectionStart,
8,
"Should have moved the caret (selectionStart after left)"
);
is(
textbox.selectionEnd,
8,
"Should have moved the caret (selectionEnd after left)"
);
is(searchPopup.state, "open", "Popup should still be open");
EventUtils.synthesizeKey("KEY_ArrowRight");
is(
textbox.selectionStart,
9,
"Should have moved the caret (selectionStart after right)"
);
is(
textbox.selectionEnd,
9,
"Should have moved the caret (selectionEnd after right)"
);
is(searchPopup.state, "open", "Popup should still be open");
// Ensure caret movement works while a suggestion is selected.
is(textbox.popup.selectedIndex, -1, "No selected item in list");
EventUtils.synthesizeKey("KEY_ArrowDown");
is(textbox.popup.selectedIndex, 0, "Selected item in list");
is(
textbox.selectionStart,
9,
"Should have moved the caret to the end (selectionStart after selection)"
);
is(
textbox.selectionEnd,
9,
"Should have moved the caret to the end (selectionEnd after selection)"
);
EventUtils.synthesizeKey("KEY_ArrowLeft");
is(
textbox.selectionStart,
8,
"Should have moved the caret again (selectionStart after left)"
);
is(
textbox.selectionEnd,
8,
"Should have moved the caret again (selectionEnd after left)"
);
is(searchPopup.state, "open", "Popup should still be open");
EventUtils.synthesizeKey("KEY_ArrowLeft");
is(
textbox.selectionStart,
7,
"Should have moved the caret (selectionStart after left)"
);
is(
textbox.selectionEnd,
7,
"Should have moved the caret (selectionEnd after left)"
);
is(searchPopup.state, "open", "Popup should still be open");
EventUtils.synthesizeKey("KEY_ArrowRight");
is(
textbox.selectionStart,
8,
"Should have moved the caret (selectionStart after right)"
);
is(
textbox.selectionEnd,
8,
"Should have moved the caret (selectionEnd after right)"
);
is(searchPopup.state, "open", "Popup should still be open");
if (!navigator.platform.includes("Mac")) {
EventUtils.synthesizeKey("KEY_Home");
is(
textbox.selectionStart,
0,
"Should have moved the caret (selectionStart after home)"
);
is(
textbox.selectionEnd,
0,
"Should have moved the caret (selectionEnd after home)"
);
is(searchPopup.state, "open", "Popup should still be open");
}
// Close the popup again
promise = promiseEvent(searchPopup, "popuphidden");
EventUtils.synthesizeKey("KEY_Escape");
await promise;
textbox.value = "";
});
// Entering customization mode shouldn't open the popup.
add_task(async function dont_open_in_customization() {
gURLBar.focus();
textbox.value = "foo";
let promise = promiseEvent(searchPopup, "popupshown");
EventUtils.synthesizeKey("KEY_Tab");
await promise;
isnot(
searchPopup.getAttribute("showonlysettings"),
"true",
"Should show the full popup"
);
info("Entering customization mode");
let sawPopup = false;
function listener() {
sawPopup = true;
}
searchPopup.addEventListener("popupshowing", listener);
await gCUITestUtils.openMainMenu();
promise = promiseEvent(searchPopup, "popuphidden");
await startCustomizing();
await promise;
searchPopup.removeEventListener("popupshowing", listener);
ok(!sawPopup, "Shouldn't have seen the suggestions popup");
await endCustomizing();
textbox.value = "";
});
add_task(async function cleanup() {
info("removing search history values: " + kValues);
let removeOps = kValues.map(value => {
return { op: "remove", fieldname: "searchbar-history", value };
});
FormHistory.update(removeOps);
});