summaryrefslogtreecommitdiffstats
path: root/browser/components/urlbar/tests/browser/browser_oneOffs.js
diff options
context:
space:
mode:
Diffstat (limited to 'browser/components/urlbar/tests/browser/browser_oneOffs.js')
-rw-r--r--browser/components/urlbar/tests/browser/browser_oneOffs.js980
1 files changed, 980 insertions, 0 deletions
diff --git a/browser/components/urlbar/tests/browser/browser_oneOffs.js b/browser/components/urlbar/tests/browser/browser_oneOffs.js
new file mode 100644
index 0000000000..d9a6a8d416
--- /dev/null
+++ b/browser/components/urlbar/tests/browser/browser_oneOffs.js
@@ -0,0 +1,980 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Tests the one-off search buttons in the urlbar.
+ */
+
+"use strict";
+
+const TEST_ENGINE_BASENAME = "searchSuggestionEngine.xml";
+
+let gMaxResults;
+let engine;
+
+XPCOMUtils.defineLazyGetter(this, "oneOffSearchButtons", () => {
+ return UrlbarTestUtils.getOneOffSearchButtons(window);
+});
+
+add_setup(async function () {
+ gMaxResults = Services.prefs.getIntPref("browser.urlbar.maxRichResults");
+
+ // Add a search suggestion engine and move it to the front so that it appears
+ // as the first one-off.
+ engine = await SearchTestUtils.promiseNewSearchEngine({
+ url: getRootDirectory(gTestPath) + TEST_ENGINE_BASENAME,
+ });
+ await Services.search.moveEngine(engine, 0);
+
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["browser.search.separatePrivateDefault.ui.enabled", false],
+ ["browser.urlbar.suggest.quickactions", false],
+ ["browser.urlbar.shortcuts.quickactions", true],
+ ],
+ });
+
+ registerCleanupFunction(async function () {
+ await PlacesUtils.history.clear();
+ await UrlbarTestUtils.formHistory.clear();
+ });
+
+ // Initialize history with enough visits to fill up the view.
+ await PlacesUtils.history.clear();
+ await UrlbarTestUtils.formHistory.clear();
+ for (let i = 0; i < gMaxResults; i++) {
+ await PlacesTestUtils.addVisits(
+ "http://example.com/browser_urlbarOneOffs.js/?" + i
+ );
+ }
+
+ // Add some more visits to the last URL added above so that the top-sites view
+ // will be non-empty.
+ for (let i = 0; i < 5; i++) {
+ await PlacesTestUtils.addVisits(
+ "http://example.com/browser_urlbarOneOffs.js/?" + (gMaxResults - 1)
+ );
+ }
+ await updateTopSites(sites => {
+ return sites && sites[0] && sites[0].url.startsWith("http://example.com/");
+ });
+
+ // Move the mouse away from the view so that a result or one-off isn't
+ // inadvertently highlighted. See bug 1659011.
+ EventUtils.synthesizeMouse(
+ gURLBar.inputField,
+ 0,
+ 0,
+ { type: "mousemove" },
+ window
+ );
+});
+
+// Opens the view without showing the one-offs. They should be hidden and arrow
+// key selection should work properly.
+add_task(async function noOneOffs() {
+ // Do a search for "@" since we hide the one-offs in that case.
+ let value = "@";
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ window,
+ value,
+ fireInputEvent: true,
+ });
+ await TestUtils.waitForCondition(
+ () => !oneOffSearchButtons._rebuilding,
+ "Waiting for one-offs to finish rebuilding"
+ );
+
+ Assert.equal(
+ UrlbarTestUtils.getOneOffSearchButtonsVisible(window),
+ false,
+ "One-offs should be hidden"
+ );
+ assertState(-1, -1, value);
+
+ // Get the result count. We don't particularly care what the results are,
+ // just what the count is so that we can key through them all.
+ let resultCount = UrlbarTestUtils.getResultCount(window);
+
+ // Key down through all results.
+ for (let i = 0; i < resultCount; i++) {
+ EventUtils.synthesizeKey("KEY_ArrowDown");
+ assertState(i, -1);
+ }
+
+ // Key down again. Nothing should be selected.
+ EventUtils.synthesizeKey("KEY_ArrowDown");
+ assertState(-1, -1, value);
+
+ // Key down again. The first result should be selected.
+ EventUtils.synthesizeKey("KEY_ArrowDown");
+ assertState(0, -1);
+
+ // Key up. Nothing should be selected.
+ EventUtils.synthesizeKey("KEY_ArrowUp");
+ assertState(-1, -1, value);
+
+ // Key up through all the results.
+ for (let i = resultCount - 1; i >= 0; i--) {
+ EventUtils.synthesizeKey("KEY_ArrowUp");
+ assertState(i, -1);
+ }
+
+ // Key up again. Nothing should be selected.
+ EventUtils.synthesizeKey("KEY_ArrowUp");
+ assertState(-1, -1, value);
+
+ await hidePopup();
+});
+
+// Opens the top-sites view. The one-offs should be shown.
+add_task(async function topSites() {
+ // Do a search that shows top sites.
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ window,
+ value: "",
+ fireInputEvent: true,
+ });
+ await TestUtils.waitForCondition(
+ () => !oneOffSearchButtons._rebuilding,
+ "Waiting for one-offs to finish rebuilding"
+ );
+
+ // There's one top sites result, the page with a lot of visits from init.
+ let resultURL = "example.com/browser_urlbarOneOffs.js/?" + (gMaxResults - 1);
+ Assert.equal(UrlbarTestUtils.getResultCount(window), 1, "Result count");
+
+ Assert.equal(
+ UrlbarTestUtils.getOneOffSearchButtonsVisible(window),
+ true,
+ "One-offs are visible"
+ );
+
+ assertState(-1, -1, "");
+
+ // Key down into the result.
+ EventUtils.synthesizeKey("KEY_ArrowDown");
+ assertState(0, -1, resultURL);
+
+ // Key down through each one-off.
+ let numButtons = oneOffSearchButtons.getSelectableButtons(true).length;
+ for (let i = 0; i < numButtons; i++) {
+ EventUtils.synthesizeKey("KEY_ArrowDown");
+ assertState(-1, i, "");
+ }
+
+ // Key down again. The selection should go away.
+ EventUtils.synthesizeKey("KEY_ArrowDown");
+ assertState(-1, -1, "");
+
+ // Key down again. The result should be selected.
+ EventUtils.synthesizeKey("KEY_ArrowDown");
+ assertState(0, -1, resultURL);
+
+ // Key back up. The selection should go away.
+ EventUtils.synthesizeKey("KEY_ArrowUp");
+ assertState(-1, -1, "");
+
+ // Key up again. The selection should wrap back around to the one-offs. Key
+ // up through all the one-offs.
+ for (let i = numButtons - 1; i >= 0; i--) {
+ EventUtils.synthesizeKey("KEY_ArrowUp");
+ assertState(-1, i, "");
+ }
+
+ // Key up. The result should be selected.
+ EventUtils.synthesizeKey("KEY_ArrowUp");
+ assertState(0, -1, resultURL);
+
+ // Key up again. The selection should go away.
+ EventUtils.synthesizeKey("KEY_ArrowUp");
+ assertState(-1, -1, "");
+
+ await hidePopup();
+});
+
+// Keys up and down through the non-top-sites view, i.e., the view that's shown
+// when the input has been edited.
+add_task(async function editedView() {
+ // Use a typed value that returns the visits added above but that doesn't
+ // trigger autofill since that would complicate the test.
+ let typedValue = "browser_urlbarOneOffs";
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ window,
+ value: typedValue,
+ fireInputEvent: true,
+ });
+ await UrlbarTestUtils.waitForAutocompleteResultAt(window, gMaxResults - 1);
+ let heuristicResult = await UrlbarTestUtils.getDetailsOfResultAt(window, 0);
+ assertState(0, -1, typedValue);
+
+ // Key down through each result. The first result is already selected, which
+ // is why gMaxResults - 1 is the correct number of times to do this.
+ for (let i = 0; i < gMaxResults - 1; i++) {
+ EventUtils.synthesizeKey("KEY_ArrowDown");
+ // i starts at zero so that the textValue passed to assertState is correct.
+ // But that means that i + 1 is the expected selected index, since initially
+ // (when this loop starts) the first result is selected.
+ assertState(
+ i + 1,
+ -1,
+ "example.com/browser_urlbarOneOffs.js/?" + (gMaxResults - i - 1)
+ );
+ Assert.ok(
+ !BrowserTestUtils.is_visible(heuristicResult.element.action),
+ "The heuristic action should not be visible"
+ );
+ }
+
+ // Key down through each one-off.
+ let numButtons = oneOffSearchButtons.getSelectableButtons(true).length;
+ for (let i = 0; i < numButtons; i++) {
+ EventUtils.synthesizeKey("KEY_ArrowDown");
+ assertState(-1, i, typedValue);
+ Assert.equal(
+ BrowserTestUtils.is_visible(heuristicResult.element.action),
+ !oneOffSearchButtons.selectedButton.classList.contains(
+ "search-setting-button"
+ ),
+ "The heuristic action should be visible when a one-off button is selected"
+ );
+ }
+
+ // Key down once more. The selection should wrap around to the first result.
+ EventUtils.synthesizeKey("KEY_ArrowDown");
+ assertState(0, -1, typedValue);
+ Assert.ok(
+ BrowserTestUtils.is_visible(heuristicResult.element.action),
+ "The heuristic action should be visible"
+ );
+
+ // Now key up. The selection should wrap back around to the one-offs. Key
+ // up through all the one-offs.
+ for (let i = numButtons - 1; i >= 0; i--) {
+ EventUtils.synthesizeKey("KEY_ArrowUp");
+ assertState(-1, i, typedValue);
+ Assert.equal(
+ BrowserTestUtils.is_visible(heuristicResult.element.action),
+ !oneOffSearchButtons.selectedButton.classList.contains(
+ "search-setting-button"
+ ),
+ "The heuristic action should be visible when a one-off button is selected"
+ );
+ }
+
+ // Key up through each non-heuristic result.
+ for (let i = gMaxResults - 2; i >= 0; i--) {
+ EventUtils.synthesizeKey("KEY_ArrowUp");
+ assertState(
+ i + 1,
+ -1,
+ "example.com/browser_urlbarOneOffs.js/?" + (gMaxResults - i - 1)
+ );
+ Assert.ok(
+ !BrowserTestUtils.is_visible(heuristicResult.element.action),
+ "The heuristic action should not be visible"
+ );
+ }
+
+ // Key up once more. The heuristic result should be selected.
+ EventUtils.synthesizeKey("KEY_ArrowUp");
+ assertState(0, -1, typedValue);
+ Assert.ok(
+ BrowserTestUtils.is_visible(heuristicResult.element.action),
+ "The heuristic action should be visible"
+ );
+
+ await hidePopup();
+});
+
+// Checks that "Search with Current Search Engine" items are updated to "Search
+// with One-Off Engine" when a one-off is selected.
+add_task(async function searchWith() {
+ // Enable suggestions for this subtest so we can check non-heuristic results.
+ let oldDefaultEngine = await Services.search.getDefault();
+ await Services.search.setDefault(
+ engine,
+ Ci.nsISearchService.CHANGE_REASON_UNKNOWN
+ );
+ await SpecialPowers.pushPrefEnv({
+ set: [["browser.urlbar.suggest.searches", true]],
+ });
+
+ let typedValue = "foo";
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ window,
+ value: typedValue,
+ });
+ let result = await UrlbarTestUtils.getDetailsOfResultAt(window, 0);
+ assertState(0, -1, typedValue);
+
+ Assert.equal(
+ result.displayed.action,
+ "Search with " + (await Services.search.getDefault()).name,
+ "Sanity check: first result's action text"
+ );
+
+ // Alt+Down to the second one-off. Now the first result and the second
+ // one-off should both be selected.
+ EventUtils.synthesizeKey("KEY_ArrowDown", { altKey: true, repeat: 2 });
+ assertState(0, 1, typedValue);
+
+ let engineName = oneOffSearchButtons.selectedButton.engine.name;
+ Assert.notEqual(
+ engineName,
+ (await Services.search.getDefault()).name,
+ "Sanity check: Second one-off engine should not be the current engine"
+ );
+ result = await UrlbarTestUtils.getDetailsOfResultAt(window, 0);
+ Assert.equal(
+ result.displayed.action,
+ "Search with " + engineName,
+ "First result's action text should be updated"
+ );
+
+ // Check non-heuristic results.
+ await hidePopup();
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ window,
+ value: typedValue,
+ });
+
+ EventUtils.synthesizeKey("KEY_ArrowDown");
+ result = await UrlbarTestUtils.getDetailsOfResultAt(window, 1);
+ assertState(1, -1, typedValue + "foo");
+ Assert.equal(
+ result.displayed.action,
+ "Search with " + engine.name,
+ "Sanity check: second result's action text"
+ );
+ Assert.ok(!result.heuristic, "The second result is not heuristic.");
+ EventUtils.synthesizeKey("KEY_ArrowDown", { altKey: true, repeat: 2 });
+ assertState(1, 1, typedValue + "foo");
+
+ engineName = oneOffSearchButtons.selectedButton.engine.name;
+ Assert.notEqual(
+ engineName,
+ (await Services.search.getDefault()).name,
+ "Sanity check: Second one-off engine should not be the current engine"
+ );
+ result = await UrlbarTestUtils.getDetailsOfResultAt(window, 1);
+
+ Assert.equal(
+ result.displayed.action,
+ "Search with " + engineName,
+ "Second result's action text should be updated"
+ );
+
+ await SpecialPowers.popPrefEnv();
+ await Services.search.setDefault(
+ oldDefaultEngine,
+ Ci.nsISearchService.CHANGE_REASON_UNKNOWN
+ );
+ await hidePopup();
+});
+
+// Clicks a one-off with an engine.
+add_task(async function oneOffClick() {
+ gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser);
+
+ // We are explicitly using something that looks like a url, to make the test
+ // stricter. Even if it looks like a url, we should search.
+ let typedValue = "foo.bar";
+
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ window,
+ value: typedValue,
+ });
+ await UrlbarTestUtils.getDetailsOfResultAt(window, 0);
+ assertState(0, -1, typedValue);
+ let oneOffs = oneOffSearchButtons.getSelectableButtons(true);
+
+ let searchPromise = UrlbarTestUtils.promiseSearchComplete(window);
+ EventUtils.synthesizeMouseAtCenter(oneOffs[0], {});
+ await searchPromise;
+ Assert.ok(UrlbarTestUtils.isPopupOpen(window), "Urlbar view is still open.");
+ await UrlbarTestUtils.assertSearchMode(window, {
+ engineName: oneOffs[0].engine.name,
+ entry: "oneoff",
+ });
+ await UrlbarTestUtils.exitSearchMode(window, { backspace: true });
+
+ gBrowser.removeTab(gBrowser.selectedTab);
+ await UrlbarTestUtils.formHistory.clear();
+});
+
+// Presses the Return key when a one-off with an engine is selected.
+add_task(async function oneOffReturn() {
+ gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser);
+
+ // We are explicitly using something that looks like a url, to make the test
+ // stricter. Even if it looks like a url, we should search.
+ let typedValue = "foo.bar";
+
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ window,
+ value: typedValue,
+ fireInputEvent: true,
+ });
+ await UrlbarTestUtils.getDetailsOfResultAt(window, 0);
+ assertState(0, -1, typedValue);
+ let oneOffs = oneOffSearchButtons.getSelectableButtons(true);
+
+ // Alt+Down to select the first one-off.
+ EventUtils.synthesizeKey("KEY_ArrowDown", { altKey: true });
+ assertState(0, 0, typedValue);
+
+ let searchPromise = UrlbarTestUtils.promiseSearchComplete(window);
+ EventUtils.synthesizeKey("KEY_Enter");
+ await searchPromise;
+ Assert.ok(UrlbarTestUtils.isPopupOpen(window), "Urlbar view is still open.");
+ await UrlbarTestUtils.assertSearchMode(window, {
+ engineName: oneOffs[0].engine.name,
+ entry: "oneoff",
+ });
+ await UrlbarTestUtils.exitSearchMode(window, { backspace: true });
+
+ gBrowser.removeTab(gBrowser.selectedTab);
+ await UrlbarTestUtils.formHistory.clear();
+ await hidePopup();
+});
+
+// When all engines and local shortcuts are hidden except for the current
+// engine, the one-offs container should be hidden.
+add_task(async function allOneOffsHiddenExceptCurrentEngine() {
+ // Disable all the engines but the current one, check the oneoffs are
+ // hidden and that moving up selects the last match.
+ let defaultEngine = await Services.search.getDefault();
+ let engines = (await Services.search.getVisibleEngines()).filter(
+ e => e.name != defaultEngine.name
+ );
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["browser.search.hiddenOneOffs", engines.map(e => e.name).join(",")],
+ ...UrlbarUtils.LOCAL_SEARCH_MODES.map(m => [
+ `browser.urlbar.${m.pref}`,
+ false,
+ ]),
+ ],
+ });
+
+ let typedValue = "foo";
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ window,
+ value: typedValue,
+ fireInputEvent: true,
+ });
+ await UrlbarTestUtils.getDetailsOfResultAt(window, 0);
+ assertState(0, -1);
+ Assert.equal(
+ UrlbarTestUtils.getOneOffSearchButtonsVisible(window),
+ false,
+ "The one-off buttons should be hidden"
+ );
+ EventUtils.synthesizeKey("KEY_ArrowUp");
+ assertState(0, -1);
+ await hidePopup();
+ await SpecialPowers.popPrefEnv();
+});
+
+// The one-offs should be hidden when searching with an "@engine" search engine
+// alias.
+add_task(async function hiddenWhenUsingSearchAlias() {
+ let typedValue = "@example";
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ window,
+ value: typedValue,
+ fireInputEvent: true,
+ });
+ await UrlbarTestUtils.getDetailsOfResultAt(window, 0);
+ Assert.equal(
+ UrlbarTestUtils.getOneOffSearchButtonsVisible(window),
+ false,
+ "Should not be showing the one-off buttons"
+ );
+ await hidePopup();
+
+ typedValue = "not an engine alias";
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ window,
+ value: typedValue,
+ fireInputEvent: true,
+ });
+ await UrlbarTestUtils.getDetailsOfResultAt(window, 0);
+ Assert.equal(
+ UrlbarTestUtils.getOneOffSearchButtonsVisible(window),
+ true,
+ "Should be showing the one-off buttons"
+ );
+ await hidePopup();
+});
+
+// Makes sure the local shortcuts exist.
+add_task(async function localShortcuts() {
+ oneOffSearchButtons.invalidateCache();
+ await doLocalShortcutsShownTest();
+});
+
+// Clicks a local shortcut button.
+add_task(async function localShortcutClick() {
+ // We are explicitly using something that looks like a url, to make the test
+ // stricter. Even if it looks like a url, we should search.
+ let typedValue = "foo.bar";
+
+ oneOffSearchButtons.invalidateCache();
+ let rebuildPromise = BrowserTestUtils.waitForEvent(
+ oneOffSearchButtons,
+ "rebuild"
+ );
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ window,
+ value: typedValue,
+ });
+ await rebuildPromise;
+
+ let buttons = oneOffSearchButtons.localButtons;
+ Assert.ok(buttons.length, "Sanity check: Local shortcuts exist");
+
+ for (let button of buttons) {
+ Assert.ok(button.source, "Sanity check: Button has a source");
+ let searchPromise = UrlbarTestUtils.promiseSearchComplete(window);
+ EventUtils.synthesizeMouseAtCenter(button, {});
+ await searchPromise;
+ Assert.ok(
+ UrlbarTestUtils.isPopupOpen(window),
+ "Urlbar view is still open."
+ );
+ await UrlbarTestUtils.assertSearchMode(window, {
+ source: button.source,
+ entry: "oneoff",
+ });
+ }
+
+ await UrlbarTestUtils.exitSearchMode(window, { backspace: true });
+ await hidePopup();
+});
+
+// Presses the Return key when a local shortcut is selected.
+add_task(async function localShortcutReturn() {
+ // We are explicitly using something that looks like a url, to make the test
+ // stricter. Even if it looks like a url, we should search.
+ let typedValue = "foo.bar";
+
+ oneOffSearchButtons.invalidateCache();
+ let rebuildPromise = BrowserTestUtils.waitForEvent(
+ oneOffSearchButtons,
+ "rebuild"
+ );
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ window,
+ value: typedValue,
+ });
+ await rebuildPromise;
+
+ let buttons = oneOffSearchButtons.localButtons;
+ Assert.ok(buttons.length, "Sanity check: Local shortcuts exist");
+
+ let allButtons = oneOffSearchButtons.getSelectableButtons(false);
+ let firstLocalIndex = allButtons.length - buttons.length;
+
+ for (let i = 0; i < buttons.length; i++) {
+ let button = buttons[i];
+
+ // Alt+Down enough times to select the button.
+ let index = firstLocalIndex + i;
+ EventUtils.synthesizeKey("KEY_ArrowDown", {
+ altKey: true,
+ repeat: index + 1,
+ });
+ await TestUtils.waitForCondition(
+ () => oneOffSearchButtons.selectedButtonIndex == index,
+ "Waiting for local shortcut to become selected"
+ );
+
+ let expectedSelectedResultIndex = -1;
+ let count = UrlbarTestUtils.getResultCount(window);
+ if (count > 0) {
+ let result = await UrlbarTestUtils.getDetailsOfResultAt(window, 0);
+ if (result.heuristic) {
+ expectedSelectedResultIndex = 0;
+ }
+ }
+ assertState(expectedSelectedResultIndex, index, typedValue);
+
+ Assert.ok(button.source, "Sanity check: Button has a source");
+ let searchPromise = UrlbarTestUtils.promiseSearchComplete(window);
+ EventUtils.synthesizeKey("KEY_Enter");
+ await searchPromise;
+ Assert.ok(
+ UrlbarTestUtils.isPopupOpen(window),
+ "Urlbar view is still open."
+ );
+ await UrlbarTestUtils.assertSearchMode(window, {
+ source: button.source,
+ entry: "oneoff",
+ });
+ }
+
+ await UrlbarTestUtils.exitSearchMode(window, { backspace: true });
+ await hidePopup();
+});
+
+// With an empty search string, clicking a local shortcut should result in no
+// heuristic result.
+add_task(async function localShortcutEmptySearchString() {
+ oneOffSearchButtons.invalidateCache();
+ let rebuildPromise = BrowserTestUtils.waitForEvent(
+ oneOffSearchButtons,
+ "rebuild"
+ );
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ window,
+ value: "",
+ });
+ await rebuildPromise;
+
+ Assert.equal(
+ UrlbarTestUtils.getOneOffSearchButtonsVisible(window),
+ true,
+ "One-offs are visible"
+ );
+
+ let buttons = oneOffSearchButtons.localButtons;
+ Assert.ok(buttons.length, "Sanity check: Local shortcuts exist");
+
+ for (let button of buttons) {
+ Assert.ok(button.source, "Sanity check: Button has a source");
+ let searchPromise = UrlbarTestUtils.promiseSearchComplete(window);
+ EventUtils.synthesizeMouseAtCenter(button, {});
+ await searchPromise;
+ Assert.ok(
+ UrlbarTestUtils.isPopupOpen(window),
+ "Urlbar view is still open."
+ );
+ Assert.equal(
+ UrlbarTestUtils.getOneOffSearchButtonsVisible(window),
+ true,
+ "One-offs are visible"
+ );
+ await UrlbarTestUtils.assertSearchMode(window, {
+ source: button.source,
+ entry: "oneoff",
+ });
+
+ let resultCount = UrlbarTestUtils.getResultCount(window);
+ if (!resultCount) {
+ Assert.equal(
+ gURLBar.panel.getAttribute("noresults"),
+ "true",
+ "Panel has no results, therefore should have noresults attribute"
+ );
+ continue;
+ }
+ Assert.ok(
+ !gURLBar.panel.hasAttribute("noresults"),
+ "Panel has results, therefore should not have noresults attribute"
+ );
+ let result = await UrlbarTestUtils.getDetailsOfResultAt(window, 0);
+ Assert.ok(!result.heuristic, "The first result should not be heuristic");
+ }
+
+ await UrlbarTestUtils.exitSearchMode(window, { backspace: true });
+
+ await hidePopup();
+});
+
+// Trigger SearchOneOffs.willHide() outside of SearchOneOffs.__rebuild(). Ensure
+// that we always show the correct engines in the one-offs. This effectively
+// tests SearchOneOffs._engineInfo.domWasUpdated.
+add_task(async function avoidWillHideRace() {
+ // We set maxHistoricalSearchSuggestions to 0 since this test depends on
+ // UrlbarView calling SearchOneOffs.willHide(). That only happens when the
+ // Urlbar is in search mode after a query that returned no results.
+ await SpecialPowers.pushPrefEnv({
+ set: [["browser.urlbar.maxHistoricalSearchSuggestions", 0]],
+ });
+
+ oneOffSearchButtons.invalidateCache();
+
+ // Accel+K triggers SearchOneOffs.willHide() from UrlbarView instead of from
+ // SearchOneOffs.__rebuild.
+ let searchPromise = UrlbarTestUtils.promiseSearchComplete(window);
+ EventUtils.synthesizeKey("k", { accelKey: true });
+ await searchPromise;
+ Assert.ok(
+ UrlbarTestUtils.getOneOffSearchButtonsVisible(window),
+ "One-offs should be visible"
+ );
+ await UrlbarTestUtils.promisePopupClose(window);
+
+ info("Hide all engines but the test engine.");
+ let oldDefaultEngine = await Services.search.getDefault();
+ await Services.search.setDefault(
+ engine,
+ Ci.nsISearchService.CHANGE_REASON_UNKNOWN
+ );
+ let engines = (await Services.search.getVisibleEngines()).filter(
+ e => e.name != engine.name
+ );
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["browser.search.hiddenOneOffs", engines.map(e => e.name).join(",")],
+ ...UrlbarUtils.LOCAL_SEARCH_MODES.map(m => [
+ `browser.urlbar.${m.pref}`,
+ false,
+ ]),
+ ],
+ });
+ Assert.ok(
+ !oneOffSearchButtons._engineInfo,
+ "_engineInfo should be nulled out."
+ );
+
+ // This call to SearchOneOffs.willHide() should repopulate _engineInfo but not
+ // rebuild the one-offs. _engineInfo.willHide will be true and thus UrlbarView
+ // will not open.
+ EventUtils.synthesizeKey("k", { accelKey: true });
+ // We can't wait for UrlbarTestUtils.promiseSearchComplete here since we
+ // expect the popup will not open. We wait for _engineInfo to be populated
+ // instead.
+ await BrowserTestUtils.waitForCondition(
+ () => !!oneOffSearchButtons._engineInfo,
+ "_engineInfo is set."
+ );
+ Assert.ok(!UrlbarTestUtils.isPopupOpen(window), "The UrlbarView is closed.");
+ Assert.equal(
+ oneOffSearchButtons._engineInfo.willHide,
+ true,
+ "_engineInfo should be repopulated and willHide should be true."
+ );
+ Assert.equal(
+ oneOffSearchButtons._engineInfo.domWasUpdated,
+ undefined,
+ "domWasUpdated should not be populated since we haven't yet tried to rebuild the one-offs."
+ );
+
+ // Now search. The view will open and the one-offs will rebuild, although
+ // the one-offs will not be shown since there is only one engine.
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ window,
+ value: "test",
+ });
+ Assert.equal(
+ oneOffSearchButtons._engineInfo.domWasUpdated,
+ true,
+ "domWasUpdated should be true"
+ );
+ Assert.ok(
+ !UrlbarTestUtils.getOneOffSearchButtonsVisible(window),
+ "One-offs should be hidden since there is only one engine."
+ );
+ await UrlbarTestUtils.promisePopupClose(window);
+
+ await SpecialPowers.popPrefEnv();
+ await Services.search.setDefault(
+ oldDefaultEngine,
+ Ci.nsISearchService.CHANGE_REASON_UNKNOWN
+ );
+ await SpecialPowers.popPrefEnv();
+});
+
+// Hides each of the local shortcuts one at a time. The search buttons should
+// automatically rebuild themselves.
+add_task(async function individualLocalShortcutsHidden() {
+ for (let { pref, source } of UrlbarUtils.LOCAL_SEARCH_MODES) {
+ await SpecialPowers.pushPrefEnv({
+ set: [[`browser.urlbar.${pref}`, false]],
+ });
+
+ let rebuildPromise = BrowserTestUtils.waitForEvent(
+ oneOffSearchButtons,
+ "rebuild"
+ );
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ window,
+ value: "",
+ });
+ await rebuildPromise;
+
+ Assert.equal(
+ UrlbarTestUtils.getOneOffSearchButtonsVisible(window),
+ true,
+ "One-offs are visible"
+ );
+
+ let buttons = oneOffSearchButtons.localButtons;
+ Assert.ok(buttons.length, "Sanity check: Local shortcuts exist");
+
+ let otherModes = UrlbarUtils.LOCAL_SEARCH_MODES.filter(
+ m => m.source != source
+ );
+ Assert.equal(
+ buttons.length,
+ otherModes.length,
+ "Expected number of enabled local shortcut buttons"
+ );
+
+ for (let i = 0; i < buttons.length; i++) {
+ Assert.equal(
+ buttons[i].source,
+ otherModes[i].source,
+ "Button has the expected source"
+ );
+ }
+
+ await hidePopup();
+ await SpecialPowers.popPrefEnv();
+ }
+});
+
+// Hides all the local shortcuts at once.
+add_task(async function allLocalShortcutsHidden() {
+ await SpecialPowers.pushPrefEnv({
+ set: UrlbarUtils.LOCAL_SEARCH_MODES.map(m => [
+ `browser.urlbar.${m.pref}`,
+ false,
+ ]),
+ });
+
+ let rebuildPromise = BrowserTestUtils.waitForEvent(
+ oneOffSearchButtons,
+ "rebuild"
+ );
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ window,
+ value: "",
+ });
+ await rebuildPromise;
+
+ Assert.equal(
+ UrlbarTestUtils.getOneOffSearchButtonsVisible(window),
+ true,
+ "One-offs are visible"
+ );
+
+ Assert.equal(
+ oneOffSearchButtons.localButtons.length,
+ 0,
+ "All local shortcuts should be hidden"
+ );
+
+ Assert.greater(
+ oneOffSearchButtons.getSelectableButtons(false).filter(b => b.engine)
+ .length,
+ 0,
+ "Engine one-offs should not be hidden"
+ );
+
+ await hidePopup();
+ await SpecialPowers.popPrefEnv();
+});
+
+// Hides all the engines but none of the local shortcuts.
+add_task(async function localShortcutsShownWhenEnginesHidden() {
+ let engines = await Services.search.getVisibleEngines();
+ await SpecialPowers.pushPrefEnv({
+ set: [["browser.search.hiddenOneOffs", engines.map(e => e.name).join(",")]],
+ });
+
+ let rebuildPromise = BrowserTestUtils.waitForEvent(
+ oneOffSearchButtons,
+ "rebuild"
+ );
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ window,
+ value: "",
+ });
+ await rebuildPromise;
+
+ Assert.equal(
+ UrlbarTestUtils.getOneOffSearchButtonsVisible(window),
+ true,
+ "One-offs are visible"
+ );
+
+ Assert.equal(
+ oneOffSearchButtons.localButtons.length,
+ UrlbarUtils.LOCAL_SEARCH_MODES.length,
+ "All local shortcuts are visible"
+ );
+
+ Assert.equal(
+ oneOffSearchButtons.getSelectableButtons(false).filter(b => b.engine)
+ .length,
+ 0,
+ "All engine one-offs are hidden"
+ );
+
+ await hidePopup();
+ await SpecialPowers.popPrefEnv();
+});
+
+/**
+ * Checks that the local shortcuts are shown correctly.
+ */
+async function doLocalShortcutsShownTest() {
+ let rebuildPromise = BrowserTestUtils.waitForEvent(
+ oneOffSearchButtons,
+ "rebuild"
+ );
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ window,
+ value: "doLocalShortcutsShownTest",
+ });
+ await rebuildPromise;
+
+ let buttons = oneOffSearchButtons.localButtons;
+ Assert.equal(buttons.length, 4, "Expected number of local shortcuts");
+
+ let expectedSource;
+ let seenIDs = new Set();
+ for (let button of buttons) {
+ Assert.ok(
+ !seenIDs.has(button.id),
+ "Should not have already seen button.id"
+ );
+ seenIDs.add(button.id);
+ switch (button.id) {
+ case "urlbar-engine-one-off-item-bookmarks":
+ expectedSource = UrlbarUtils.RESULT_SOURCE.BOOKMARKS;
+ break;
+ case "urlbar-engine-one-off-item-tabs":
+ expectedSource = UrlbarUtils.RESULT_SOURCE.TABS;
+ break;
+ case "urlbar-engine-one-off-item-history":
+ expectedSource = UrlbarUtils.RESULT_SOURCE.HISTORY;
+ break;
+ case "urlbar-engine-one-off-item-actions":
+ expectedSource = UrlbarUtils.RESULT_SOURCE.ACTIONS;
+ break;
+ default:
+ Assert.ok(false, `Unexpected local shortcut ID: ${button.id}`);
+ break;
+ }
+ Assert.equal(button.source, expectedSource, "Expected button.source");
+ }
+
+ await hidePopup();
+}
+
+function assertState(result, oneOff, textValue = undefined) {
+ Assert.equal(
+ UrlbarTestUtils.getSelectedRowIndex(window),
+ result,
+ "Expected result should be selected"
+ );
+ Assert.equal(
+ oneOffSearchButtons.selectedButtonIndex,
+ oneOff,
+ "Expected one-off should be selected"
+ );
+ if (textValue !== undefined) {
+ Assert.equal(gURLBar.value, textValue, "Expected value");
+ }
+}
+
+function hidePopup() {
+ return UrlbarTestUtils.promisePopupClose(window, () => {
+ EventUtils.synthesizeKey("KEY_Escape");
+ });
+}