/* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ /** * This file tests urlbar telemetry with search related actions. */ "use strict"; const SCALAR_URLBAR = "browser.engagement.navigation.urlbar"; // The preference to enable suggestions in the urlbar. const SUGGEST_URLBAR_PREF = "browser.urlbar.suggest.searches"; // The name of the search engine used to generate suggestions. const SUGGESTION_ENGINE_NAME = "browser_UsageTelemetry usageTelemetrySearchSuggestions.xml"; ChromeUtils.defineESModuleGetters(this, { CustomizableUITestUtils: "resource://testing-common/CustomizableUITestUtils.sys.mjs", Region: "resource://gre/modules/Region.sys.mjs", SearchTestUtils: "resource://testing-common/SearchTestUtils.sys.mjs", UrlbarTestUtils: "resource://testing-common/UrlbarTestUtils.sys.mjs", }); XPCOMUtils.defineLazyModuleGetters(this, { HttpServer: "resource://testing-common/httpd.js", }); let gCUITestUtils = new CustomizableUITestUtils(window); SearchTestUtils.init(this); var gHttpServer = null; var gRequests = []; function submitHandler(request, response) { gRequests.push(request); response.setStatusLine(request.httpVersion, 200, "Ok"); } add_setup(async function () { // Ensure the initial init is complete. await Services.search.init(); // Clear history so that history added by previous tests doesn't mess up this // test when it selects results in the urlbar. await PlacesUtils.history.clear(); let searchExtensions = getChromeDir(getResolvedURI(gTestPath)); searchExtensions.append("search-engines"); await SearchTestUtils.useMochitestEngines(searchExtensions); SearchTestUtils.useMockIdleService(); let response = await fetch(`resource://search-extensions/engines.json`); let json = await response.json(); await SearchTestUtils.updateRemoteSettingsConfig(json.data); let topsitesAttribution = Services.prefs.getStringPref( "browser.partnerlink.campaign.topsites" ); gHttpServer = new HttpServer(); gHttpServer.registerPathHandler(`/cid/${topsitesAttribution}`, submitHandler); gHttpServer.start(-1); await SpecialPowers.pushPrefEnv({ set: [ // Enable search suggestions in the urlbar. [SUGGEST_URLBAR_PREF, true], // Clear historical search suggestions to avoid interference from previous // tests. ["browser.urlbar.maxHistoricalSearchSuggestions", 0], [ "browser.partnerlink.attributionURL", `http://localhost:${gHttpServer.identity.primaryPort}/cid/`, ], ], }); await gCUITestUtils.addSearchBar(); // Make sure to restore the engine once we're done. registerCleanupFunction(async function () { let settingsWritten = SearchTestUtils.promiseSearchNotification( "write-settings-to-disk-complete" ); await SearchTestUtils.updateRemoteSettingsConfig(); await gHttpServer.stop(); gHttpServer = null; await PlacesUtils.history.clear(); gCUITestUtils.removeSearchBar(); await settingsWritten; }); }); function searchInAwesomebar(value, win = window) { return UrlbarTestUtils.promiseAutocompleteResultPopup({ window: win, waitForFocus, value, fireInputEvent: true, }); } async function searchInSearchbar(inputText) { let win = window; await new Promise(r => waitForFocus(r, win)); let sb = BrowserSearch.searchBar; // Write the search query in the searchbar. sb.focus(); sb.value = inputText; sb.textbox.controller.startSearch(inputText); // Wait for the popup to be shown and built. let popup = sb.textbox.popup; await Promise.all([ BrowserTestUtils.waitForEvent(popup, "popupshown"), BrowserTestUtils.waitForEvent(popup.oneOffButtons, "rebuild"), ]); // And then for the search to complete. await BrowserTestUtils.waitForCondition( () => sb.textbox.controller.searchStatus >= Ci.nsIAutoCompleteController.STATUS_COMPLETE_NO_MATCH, "The search in the searchbar must complete." ); } add_task(async function test_simpleQuery_no_attribution() { await Services.search.setDefault( Services.search.getEngineByName("Simple Engine"), Ci.nsISearchService.CHANGE_REASON_UNKNOWN ); let tab = await BrowserTestUtils.openNewForegroundTab( gBrowser, "about:blank" ); info("Simulate entering a simple search."); let promiseLoad = BrowserTestUtils.waitForDocLoadAndStopIt( "https://example.com/?sourceId=Mozilla-search&search=simple+query", tab ); await searchInAwesomebar("simple query"); EventUtils.synthesizeKey("KEY_Enter"); await promiseLoad; await new Promise(resolve => Services.tm.dispatchToMainThread(resolve)); Assert.equal(gRequests.length, 0, "Should not have submitted an attribution"); BrowserTestUtils.removeTab(tab); await Services.search.setDefault( Services.search.getEngineByName("basic"), Ci.nsISearchService.CHANGE_REASON_UNKNOWN ); }); async function checkAttributionRecorded(actionFn, cleanupFn) { let tab = await BrowserTestUtils.openNewForegroundTab( gBrowser, "data:text/plain;charset=utf8,simple%20query" ); let promiseLoad = BrowserTestUtils.waitForDocLoadAndStopIt( "https://mochi.test:8888/browser/browser/components/search/test/browser/?search=simple+query&foo=1", tab ); await actionFn(tab); await promiseLoad; await BrowserTestUtils.waitForCondition( () => gRequests.length == 1, "Should have received an attribution submission" ); Assert.equal( gRequests[0].getHeader("X-Region"), Region.home, "Should have set the region correctly" ); Assert.equal( gRequests[0].getHeader("X-Source"), "searchurl", "Should have set the source correctly" ); Assert.equal( gRequests[0].getHeader("X-Target-url"), "https://mochi.test:8888/browser/browser/components/search/test/browser/?foo=1", "Should have set the target url correctly and stripped the search terms" ); if (cleanupFn) { await cleanupFn(); } BrowserTestUtils.removeTab(tab); gRequests = []; } add_task(async function test_urlbar() { await checkAttributionRecorded(async tab => { await searchInAwesomebar("simple query"); EventUtils.synthesizeKey("KEY_Enter"); }); }); add_task(async function test_searchbar() { await checkAttributionRecorded(async tab => { let sb = BrowserSearch.searchBar; // Write the search query in the searchbar. sb.focus(); sb.value = "simple query"; sb.textbox.controller.startSearch("simple query"); // Wait for the popup to show. await BrowserTestUtils.waitForEvent(sb.textbox.popup, "popupshown"); // And then for the search to complete. await BrowserTestUtils.waitForCondition( () => sb.textbox.controller.searchStatus >= Ci.nsIAutoCompleteController.STATUS_COMPLETE_NO_MATCH, "The search in the searchbar must complete." ); EventUtils.synthesizeKey("KEY_Enter"); }); }); add_task(async function test_context_menu() { let contextMenu; await checkAttributionRecorded( async tab => { info("Select all the text in the page."); await SpecialPowers.spawn(tab.linkedBrowser, [""], async function () { return new Promise(resolve => { content.document.addEventListener( "selectionchange", () => resolve(), { once: true, } ); content.document .getSelection() .selectAllChildren(content.document.body); }); }); info("Open the context menu."); contextMenu = document.getElementById("contentAreaContextMenu"); let shownPromise = BrowserTestUtils.waitForEvent( contextMenu, "popupshown" ); BrowserTestUtils.synthesizeMouseAtCenter( "body", { type: "contextmenu", button: 2 }, gBrowser.selectedBrowser ); await shownPromise; let hiddenPromise = BrowserTestUtils.waitForEvent( contextMenu, "popuphidden" ); info("Click on search."); let searchItem = contextMenu.querySelector("#context-searchselect"); contextMenu.activateItem(searchItem); await hiddenPromise; }, () => { BrowserTestUtils.removeTab(gBrowser.selectedTab); } ); }); add_task(async function test_about_newtab() { await SpecialPowers.pushPrefEnv({ set: [ [ "browser.newtabpage.activity-stream.improvesearch.handoffToAwesomebar", false, ], ], }); let tab = await BrowserTestUtils.openNewForegroundTab( gBrowser, "about:newtab", false ); await SpecialPowers.spawn(tab.linkedBrowser, [], async function () { await ContentTaskUtils.waitForCondition(() => !content.document.hidden); }); info("Trigger a simple search, just text + enter."); let promiseLoad = BrowserTestUtils.waitForDocLoadAndStopIt( "https://mochi.test:8888/browser/browser/components/search/test/browser/?search=simple+query&foo=1", tab ); await typeInSearchField( tab.linkedBrowser, "simple query", "newtab-search-text" ); await BrowserTestUtils.synthesizeKey("VK_RETURN", {}, tab.linkedBrowser); await promiseLoad; await BrowserTestUtils.waitForCondition( () => gRequests.length == 1, "Should have received an attribution submission" ); Assert.equal( gRequests[0].getHeader("X-Region"), Region.home, "Should have set the region correctly" ); Assert.equal( gRequests[0].getHeader("X-Source"), "searchurl", "Should have set the source correctly" ); Assert.equal( gRequests[0].getHeader("X-Target-url"), "https://mochi.test:8888/browser/browser/components/search/test/browser/?foo=1", "Should have set the target url correctly and stripped the search terms" ); BrowserTestUtils.removeTab(tab); await SpecialPowers.popPrefEnv(); gRequests = []; }); add_task(async function test_urlbar_oneOff_click() { let tab = await BrowserTestUtils.openNewForegroundTab( gBrowser, "about:blank" ); info("Type a query."); let promiseLoad = BrowserTestUtils.waitForDocLoadAndStopIt( "https://mochi.test:8888/browser/browser/components/search/test/browser/?search=query&foo=1", tab ); await searchInAwesomebar("query"); info("Click the first one-off button."); UrlbarTestUtils.getOneOffSearchButtons(window) .getSelectableButtons(false)[0] .click(); EventUtils.synthesizeKey("KEY_Enter"); await promiseLoad; await BrowserTestUtils.waitForCondition( () => gRequests.length == 1, "Should have received an attribution submission" ); Assert.equal( gRequests[0].getHeader("X-Region"), Region.home, "Should have set the region correctly" ); Assert.equal( gRequests[0].getHeader("X-Source"), "searchurl", "Should have set the source correctly" ); Assert.equal( gRequests[0].getHeader("X-Target-url"), "https://mochi.test:8888/browser/browser/components/search/test/browser/?foo=1", "Should have set the target url correctly and stripped the search terms" ); BrowserTestUtils.removeTab(tab); gRequests = []; }); add_task(async function test_searchbar_oneOff_click() { // For this test, set the other engine as default, so that we can select // the attribution engine as the first one in the one-offs. await Services.search.setDefault( Services.search.getEngineByName("Simple Engine"), Ci.nsISearchService.CHANGE_REASON_UNKNOWN ); let tab = await BrowserTestUtils.openNewForegroundTab( gBrowser, "about:blank" ); info("Type a query."); let promiseLoad = BrowserTestUtils.waitForDocLoadAndStopIt( "https://mochi.test:8888/browser/browser/components/search/test/browser/?search=searchbar&foo=1", tab ); await searchInSearchbar("searchbar"); info("Click the first one-off button."); BrowserSearch.searchBar.textbox.popup.oneOffButtons .getSelectableButtons(false)[0] .click(); await promiseLoad; await BrowserTestUtils.waitForCondition( () => gRequests.length == 1, "Should have received an attribution submission" ); Assert.equal( gRequests[0].getHeader("X-Region"), Region.home, "Should have set the region correctly" ); Assert.equal( gRequests[0].getHeader("X-Source"), "searchurl", "Should have set the source correctly" ); Assert.equal( gRequests[0].getHeader("X-Target-url"), "https://mochi.test:8888/browser/browser/components/search/test/browser/?foo=1", "Should have set the target url correctly and stripped the search terms" ); BrowserTestUtils.removeTab(tab); // Set back the engine in case of other tests in this file. await Services.search.setDefault( Services.search.getEngineByName("basic"), Ci.nsISearchService.CHANGE_REASON_UNKNOWN ); gRequests = []; });