/* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ // Basic smoke tests for the `browser.urlbar.experimental.hideHeuristic` pref, // which hides the heuristic result. Each task performs a search that triggers a // specific heuristic, verifies that it's hidden or shown as appropriate, and // verifies that it's picked when enter is pressed. // // If/when it becomes the default, we should update existing tests as necessary // and remove this one. "use strict"; // Allow more time for Mac machines so they don't time out in verify mode. if (AppConstants.platform == "macosx") { requestLongerTimeout(3); } add_setup(async function () { await SpecialPowers.pushPrefEnv({ set: [ ["browser.urlbar.experimental.hideHeuristic", true], ["browser.urlbar.suggest.quickactions", false], ], }); await PlacesUtils.bookmarks.eraseEverything(); await PlacesUtils.history.clear(); }); // UrlbarUtils.RESULT_GROUP.HEURISTIC_EXTENSION should be hidden. add_task(async function extension() { await BrowserTestUtils.withNewTab("about:blank", async () => { await withVisits(async visitURLs => { // Add an extension provider that returns a heuristic. let url = "http://example.com/extension-test"; let provider = new UrlbarTestUtils.TestProvider({ name: "ExtensionTest", type: UrlbarUtils.PROVIDER_TYPE.EXTENSION, results: [ Object.assign( new UrlbarResult( UrlbarUtils.RESULT_TYPE.URL, UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL, { url, title: "Test", } ), { heuristic: true } ), ], }); UrlbarProvidersManager.registerProvider(provider); // Do a search that fetches the provider's result and check it. let heuristic = await search({ value: "test", expectedGroup: UrlbarUtils.RESULT_GROUP.HEURISTIC_EXTENSION, }); Assert.equal(heuristic.payload.url, url, "Heuristic URL is correct"); // Check the other visit results. await checkVisitResults(visitURLs); // Press enter to verify the heuristic result is loaded. await synthesizeEnterAndAwaitLoad(url); UrlbarProvidersManager.unregisterProvider(provider); }); }); }); // UrlbarUtils.RESULT_GROUP.HEURISTIC_OMNIBOX should be hidden. add_task(async function omnibox() { await BrowserTestUtils.withNewTab("about:blank", async () => { // Load an extension. let extension = ExtensionTestUtils.loadExtension({ manifest: { omnibox: { keyword: "omniboxtest", }, }, background() { /* global browser */ browser.omnibox.onInputEntered.addListener(() => { browser.test.sendMessage("onInputEntered"); }); }, }); await extension.startup(); // Do a search using the omnibox keyword and check the hidden heuristic // result. let heuristic = await search({ value: "omniboxtest foo", expectedGroup: UrlbarUtils.RESULT_GROUP.HEURISTIC_OMNIBOX, }); Assert.equal( heuristic.payload.keyword, "omniboxtest", "Heuristic keyword is correct" ); // Press enter to verify the heuristic result is picked. let messagePromise = extension.awaitMessage("onInputEntered"); EventUtils.synthesizeKey("KEY_Enter"); await messagePromise; await extension.unload(); }); }); // UrlbarUtils.RESULT_GROUP.HEURISTIC_SEARCH_TIP should be shown. add_task(async function searchTip() { await SpecialPowers.pushPrefEnv({ set: [["browser.urlbar.searchTips.test.ignoreShowLimits", true]], }); await BrowserTestUtils.withNewTab( { gBrowser: window.gBrowser, url: "about:newtab", // `withNewTab` hangs waiting for about:newtab to load without this. waitForLoad: false, }, async () => { await UrlbarTestUtils.promisePopupOpen(window, () => {}); Assert.ok(true, "View opened"); Assert.equal(UrlbarTestUtils.getResultCount(window), 1); let result = await UrlbarTestUtils.getDetailsOfResultAt(window, 0); Assert.equal(result.type, UrlbarUtils.RESULT_TYPE.TIP); Assert.ok(result.heuristic); Assert.ok(UrlbarTestUtils.getSelectedElement(window), "Selection exists"); } ); await SpecialPowers.popPrefEnv(); }); // UrlbarUtils.RESULT_GROUP.HEURISTIC_ENGINE_ALIAS should be hidden. add_task(async function engineAlias() { await BrowserTestUtils.withNewTab("about:blank", async () => { await withVisits(async visitURLs => { // Add an engine with an alias. await withEngine({ keyword: "test" }, async () => { // Do a search using the alias and check the hidden heuristic result. // The heuristic will be HEURISTIC_FALLBACK, not HEURISTIC_ENGINE_ALIAS, // because two searches are performed and // `UrlbarTestUtils.promiseAutocompleteResultPopup` waits for both. The // first returns a HEURISTIC_ENGINE_ALIAS that triggers search mode and // then an immediate second search, which returns HEURISTIC_FALLBACK. let heuristic = await search({ value: "test foo", expectedGroup: UrlbarUtils.RESULT_GROUP.HEURISTIC_FALLBACK, }); Assert.equal( heuristic.payload.engine, "Example", "Heuristic engine is correct" ); Assert.equal( heuristic.payload.query, "foo", "Heuristic query is correct" ); await UrlbarTestUtils.assertSearchMode(window, { engineName: "Example", entry: "typed", }); // Check the other visit results. await checkVisitResults(visitURLs); // Press enter to verify the heuristic result is loaded. await synthesizeEnterAndAwaitLoad("https://example.com/?q=foo"); }); }); }); }); // UrlbarUtils.RESULT_GROUP.HEURISTIC_BOOKMARK_KEYWORD should be hidden. add_task(async function bookmarkKeyword() { await BrowserTestUtils.withNewTab("about:blank", async () => { await withVisits(async visitURLs => { // Add a bookmark with a keyword. let keyword = "bm"; let bm = await PlacesUtils.bookmarks.insert({ parentGuid: PlacesUtils.bookmarks.unfiledGuid, url: "http://example.com/?q=%s", title: "test", }); await PlacesUtils.keywords.insert({ keyword, url: bm.url }); // Do a search using the keyword and check the hidden heuristic result. let heuristic = await search({ value: "bm foo", expectedGroup: UrlbarUtils.RESULT_GROUP.HEURISTIC_BOOKMARK_KEYWORD, }); Assert.equal( heuristic.payload.keyword, keyword, "Heuristic keyword is correct" ); let heuristicURL = "http://example.com/?q=foo"; Assert.equal( heuristic.payload.url, heuristicURL, "Heuristic URL is correct" ); // Check the other visit results. await checkVisitResults(visitURLs); // Press enter to verify the heuristic result is loaded. await synthesizeEnterAndAwaitLoad(heuristicURL); await PlacesUtils.bookmarks.eraseEverything(); await UrlbarTestUtils.formHistory.clear(window); }); }); }); // UrlbarUtils.RESULT_GROUP.HEURISTIC_AUTOFILL should be hidden. add_task(async function autofill() { await BrowserTestUtils.withNewTab("about:blank", async () => { await withVisits(async visitURLs => { // Do a search that triggers autofill and check the hidden heuristic // result. let heuristic = await search({ value: "ex", expectedGroup: UrlbarUtils.RESULT_GROUP.HEURISTIC_AUTOFILL, }); Assert.ok(heuristic.autofill, "Heuristic is autofill"); let heuristicURL = "http://example.com/"; Assert.equal( heuristic.payload.url, heuristicURL, "Heuristic URL is correct" ); Assert.equal(gURLBar.value, "example.com/", "Input has been autofilled"); // Check the other visit results. await checkVisitResults(visitURLs); // Press enter to verify the heuristic result is loaded. await synthesizeEnterAndAwaitLoad(heuristicURL); }); }); }); // UrlbarUtils.RESULT_GROUP.HEURISTIC_FALLBACK with an unknown URL should be // hidden. add_task(async function fallback_unknownURL() { await BrowserTestUtils.withNewTab("about:blank", async () => { // Do a search for an unknown URL and check the hidden heuristic result. let url = "http://example.com/unknown-url"; let heuristic = await search({ value: url, expectedGroup: UrlbarUtils.RESULT_GROUP.HEURISTIC_FALLBACK, }); Assert.equal(heuristic.payload.url, url, "Heuristic URL is correct"); // Press enter to verify the heuristic result is loaded. await synthesizeEnterAndAwaitLoad(url); }); }); // UrlbarUtils.RESULT_GROUP.HEURISTIC_FALLBACK with the search restriction token // should be hidden. add_task(async function fallback_searchRestrictionToken() { await BrowserTestUtils.withNewTab("about:blank", async () => { await withVisits(async visitURLs => { // Add a mock default engine so we don't hit the network. await withEngine({ makeDefault: true }, async () => { // Do a search with `?` and check the hidden heuristic result. let heuristic = await search({ value: UrlbarTokenizer.RESTRICT.SEARCH + " foo", expectedGroup: UrlbarUtils.RESULT_GROUP.HEURISTIC_FALLBACK, }); Assert.equal( heuristic.payload.engine, "Example", "Heuristic engine is correct" ); Assert.equal( heuristic.payload.query, "foo", "Heuristic query is correct" ); await UrlbarTestUtils.assertSearchMode(window, { engineName: "Example", entry: "typed", }); // Check the other visit results. await checkVisitResults(visitURLs); // Press enter to verify the heuristic result is loaded. await synthesizeEnterAndAwaitLoad("https://example.com/?q=foo"); await UrlbarTestUtils.formHistory.clear(window); }); }); }); }); // UrlbarUtils.RESULT_GROUP.HEURISTIC_FALLBACK with a search string that falls // back to a search result should be hidden. add_task(async function fallback_search() { await BrowserTestUtils.withNewTab("about:blank", async () => { await withVisits(async visitURLs => { // Add a mock default engine so we don't hit the network. await withEngine({ makeDefault: true }, async () => { // Do a search and check the hidden heuristic result. let heuristic = await search({ value: "foo", expectedGroup: UrlbarUtils.RESULT_GROUP.HEURISTIC_FALLBACK, }); Assert.equal( heuristic.payload.engine, "Example", "Heuristic engine is correct" ); Assert.equal( heuristic.payload.query, "foo", "Heuristic query is correct" ); // Check the other visit results. await checkVisitResults(visitURLs); // Press enter to verify the heuristic result is loaded. await synthesizeEnterAndAwaitLoad("https://example.com/?q=foo"); await UrlbarTestUtils.formHistory.clear(window); }); }); }); }); // Picking a non-heuristic result should work correctly (and not pick the // heuristic). add_task(async function pickNonHeuristic() { await BrowserTestUtils.withNewTab("about:blank", async () => { await withVisits(async visitURLs => { // Do a search that triggers autofill and check the hidden heuristic // result. let heuristic = await search({ value: "ex", expectedGroup: UrlbarUtils.RESULT_GROUP.HEURISTIC_AUTOFILL, }); Assert.ok(heuristic.autofill, "Heuristic is autofill"); Assert.equal( heuristic.payload.url, "http://example.com/", "Heuristic URL is correct" ); // Pick the first visit result. Assert.notEqual( heuristic.payload.url, visitURLs[0], "Sanity check: Heuristic and first results have different URLs" ); EventUtils.synthesizeKey("KEY_ArrowDown"); await synthesizeEnterAndAwaitLoad(visitURLs[0]); }); }); }); /** * Adds `maxRichResults` visits, calls your callback, and clears history. We add * `maxRichResults` visits to verify that the view correctly contains the * maximum number of results when the heuristic is hidden. * * @param {Function} callback * The callback to call after adding visits. Can be async */ async function withVisits(callback) { let urls = []; for (let i = 0; i < UrlbarPrefs.get("maxRichResults"); i++) { urls.push("http://example.com/foo/" + i); } await PlacesTestUtils.addVisits(urls); // The URLs will appear in the view in reverse order so that newer visits are // first. Reverse the array now so callers to `checkVisitResults` or // `checkVisitResults` itself doesn't need to do it. urls.reverse(); await callback(urls); await PlacesUtils.history.clear(); } /** * Adds a search engine, calls your callback, and removes the engine. * * @param {object} options * Options object * @param {string} [options.keyword] * The keyword/alias for the engine. * @param {boolean} [options.makeDefault] * Whether to make the engine default. * @param {Function} callback * The callback to call after changing the default search engine. Can be async */ async function withEngine( { keyword = undefined, makeDefault = false }, callback ) { await SearchTestUtils.installSearchExtension({ keyword }); let engine = Services.search.getEngineByName("Example"); let originalEngine; if (makeDefault) { originalEngine = await Services.search.getDefault(); await Services.search.setDefault( engine, Ci.nsISearchService.CHANGE_REASON_UNKNOWN ); } await callback(); if (originalEngine) { await Services.search.setDefault( originalEngine, Ci.nsISearchService.CHANGE_REASON_UNKNOWN ); } await Services.search.removeEngine(engine); } /** * Asserts the view contains visit results with the given URLs. * * @param {Array} expectedURLs * The expected urls. */ async function checkVisitResults(expectedURLs) { Assert.equal( UrlbarTestUtils.getResultCount(window), expectedURLs.length, "The view has other results" ); for (let i = 0; i < expectedURLs.length; i++) { let result = await UrlbarTestUtils.getDetailsOfResultAt(window, i); Assert.equal( result.type, UrlbarUtils.RESULT_TYPE.URL, "Other result type is correct at index " + i ); Assert.equal( result.source, UrlbarUtils.RESULT_SOURCE.HISTORY, "Other result source is correct at index " + i ); Assert.equal( result.url, expectedURLs[i], "Other result URL is correct at index " + i ); } } /** * Performs a search and makes some basic assertions under the assumption that * the heuristic should be hidden. * * @param {object} options * Options object * @param {string} options.value * The search string. * @param {UrlbarUtils.RESULT_GROUP} options.expectedGroup * The expected result group of the hidden heuristic. * @returns {UrlbarResult} * The hidden heuristic result. */ async function search({ value, expectedGroup }) { await UrlbarTestUtils.promiseAutocompleteResultPopup({ window, waitForFocus, value, fireInputEvent: true, }); // _resultForCurrentValue should be the hidden heuristic result. let { _resultForCurrentValue: result } = gURLBar; Assert.ok(result, "_resultForCurrentValue is defined"); Assert.ok(result.heuristic, "_resultForCurrentValue.heuristic is true"); Assert.equal( UrlbarUtils.getResultGroup(result), expectedGroup, "_resultForCurrentValue has expected group" ); Assert.ok(!UrlbarTestUtils.getSelectedElement(window), "No selection exists"); return result; } /** * Synthesizes the enter key and waits for a load in the current tab. * * @param {string} expectedURL * The URL that should load. */ async function synthesizeEnterAndAwaitLoad(expectedURL) { let loadPromise = BrowserTestUtils.browserLoaded( gBrowser.selectedBrowser, false, expectedURL ); EventUtils.synthesizeKey("KEY_Enter"); await loadPromise; await PlacesUtils.history.clear(); }