/* 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"; const SCALAR_SEARCHMODE = "browser.engagement.navigation.urlbar_searchmode"; // The preference to enable suggestions in the urlbar. const SUGGEST_URLBAR_PREF = "browser.urlbar.suggest.searches"; ChromeUtils.defineESModuleGetters(this, { SearchSERPTelemetry: "moz-src:///browser/components/search/SearchSERPTelemetry.sys.mjs", }); function searchInAwesomebar(value, win = window) { return UrlbarTestUtils.promiseAutocompleteResultPopup({ window: win, waitForFocus, value, fireInputEvent: true, }); } /** * Click one of the entries in the urlbar suggestion popup. * * @param {string} resultTitle * The title of the result to click on. * @param {number} button [optional] * which button to click. * @returns {number} * The index of the result that was clicked, or -1 if not found. */ async function clickURLBarSuggestion(resultTitle, button = 1) { await UrlbarTestUtils.promiseSearchComplete(window); const count = UrlbarTestUtils.getResultCount(window); for (let i = 0; i < count; i++) { let result = await UrlbarTestUtils.getDetailsOfResultAt(window, i); if (result.displayed.title == resultTitle) { // This entry is the search suggestion we're looking for. let element = await UrlbarTestUtils.waitForAutocompleteResultAt( window, i ); if (button == 1) { EventUtils.synthesizeMouseAtCenter(element, {}); } else if (button == 2) { EventUtils.synthesizeMouseAtCenter(element, { type: "mousedown", button: 2, }); } return i; } } return -1; } /** * Create an engine to generate search suggestions and add it as default * for this test. * * @param {Function} taskFn * The function to run with the new search engine as default. */ async function withNewSearchEngine(taskFn) { let suggestionEngine = await SearchTestUtils.installOpenSearchEngine({ url: getRootDirectory(gTestPath) + "urlbarTelemetrySearchSuggestions.xml", }); let previousEngine = await SearchService.getDefault(); await SearchService.setDefault( suggestionEngine, SearchService.CHANGE_REASON.UNKNOWN ); try { await taskFn(suggestionEngine); } finally { await SearchService.setDefault( previousEngine, SearchService.CHANGE_REASON.UNKNOWN ); await SearchService.removeEngine(suggestionEngine); } } add_setup(async function () { await SearchTestUtils.installSearchExtension( { name: "MozSearch", keyword: "mozalias", search_url: "https://example.com/", }, { setAsDefault: true } ); // Make it the first one-off engine. let engine = SearchService.getEngineByName("MozSearch"); await SearchService.moveEngine(engine, 0); // Enable search suggestions in the urlbar. let suggestionsEnabled = Services.prefs.getBoolPref(SUGGEST_URLBAR_PREF); Services.prefs.setBoolPref(SUGGEST_URLBAR_PREF, true); // Enable local telemetry recording for the duration of the tests. let oldCanRecord = Services.telemetry.canRecordExtended; Services.telemetry.canRecordExtended = true; // 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(); // Clear historical search suggestions to avoid interference from previous // tests. await SpecialPowers.pushPrefEnv({ set: [["browser.urlbar.maxHistoricalSearchSuggestions", 0]], }); // This test assumes that general results are shown before suggestions. await SpecialPowers.pushPrefEnv({ set: [["browser.urlbar.showSearchSuggestionsFirst", false]], }); await SpecialPowers.pushPrefEnv({ set: [["browser.urlbar.scotchBonnet.enableOverride", false]], }); // Make sure to restore the engine once we're done. registerCleanupFunction(async function () { Services.telemetry.canRecordExtended = oldCanRecord; Services.prefs.setBoolPref(SUGGEST_URLBAR_PREF, suggestionsEnabled); await PlacesUtils.history.clear(); await UrlbarTestUtils.formHistory.clear(); }); }); add_task(async function test_simpleQuery() { Services.telemetry.clearScalars(); Services.telemetry.clearEvents(); clearSAPTelemetry(); let tab = await BrowserTestUtils.openNewForegroundTab( gBrowser, "about:blank" ); info("Simulate entering a simple search."); let p = BrowserTestUtils.browserLoaded(tab.linkedBrowser); await searchInAwesomebar("simple query"); EventUtils.synthesizeKey("KEY_Enter"); await p; // Check if the scalars contain the expected values. const scalars = TelemetryTestUtils.getProcessScalars("parent", true, false); TelemetryTestUtils.assertKeyedScalar( scalars, SCALAR_URLBAR, "search_enter", 1 ); Assert.equal( Object.keys(scalars[SCALAR_URLBAR]).length, 1, "This search must only increment one entry in the scalar." ); // SAP counts are incremented only for the urlbar source, since the internal // @search keyword was not used. await SearchUITestUtils.assertSAPTelemetry({ engineName: "MozSearch", source: "urlbar", count: 1, }); BrowserTestUtils.removeTab(tab); }); add_task(async function test_searchMode_enter() { Services.telemetry.clearScalars(); Services.telemetry.clearEvents(); let tab = await BrowserTestUtils.openNewForegroundTab( gBrowser, "about:blank" ); info("Enter search mode using an alias and a query."); let p = BrowserTestUtils.browserLoaded(tab.linkedBrowser); await searchInAwesomebar("mozalias query"); EventUtils.synthesizeKey("KEY_Enter"); await p; // Check if the scalars contain the expected values. const scalars = TelemetryTestUtils.getProcessScalars("parent", true, false); TelemetryTestUtils.assertKeyedScalar( scalars, SCALAR_SEARCHMODE, "search_enter", 1 ); Assert.equal( Object.keys(scalars[SCALAR_SEARCHMODE]).length, 1, "This search must only increment one entry in the scalar." ); BrowserTestUtils.removeTab(tab); }); // Performs a search using the first result, a one-off button, and the Return // (Enter) key. add_task(async function test_oneOff_enter() { Services.telemetry.clearScalars(); Services.telemetry.clearEvents(); clearSAPTelemetry(); let tab = await BrowserTestUtils.openNewForegroundTab( gBrowser, "about:blank" ); info("Perform a one-off search using the first engine."); let p = BrowserTestUtils.browserLoaded(tab.linkedBrowser); await searchInAwesomebar("query"); info("Pressing Alt+Down to take us to the first one-off engine."); let searchPromise = UrlbarTestUtils.promiseSearchComplete(window); EventUtils.synthesizeKey("KEY_ArrowDown", { altKey: true }); let engine = UrlbarTestUtils.getOneOffSearchButtons(window).selectedButton.engine; EventUtils.synthesizeKey("KEY_Enter"); await searchPromise; await UrlbarTestUtils.assertSearchMode(window, { engineName: engine.name, entry: "oneoff", }); // Now that we're in search mode, execute the search. EventUtils.synthesizeKey("KEY_Enter"); await p; // Check if the scalars contain the expected values. const scalars = TelemetryTestUtils.getProcessScalars("parent", true, false); TelemetryTestUtils.assertKeyedScalar( scalars, SCALAR_SEARCHMODE, "search_enter", 1 ); Assert.equal( Object.keys(scalars[SCALAR_SEARCHMODE]).length, 1, "This search must only increment one entry in the scalar." ); // SAP counts should be incremented, but only the urlbar_searchmode source // since aliases aren't counted separately in search mode. await SearchUITestUtils.assertSAPTelemetry({ engineName: "MozSearch", source: "urlbar_searchmode", count: 1, }); BrowserTestUtils.removeTab(tab); }); // Clicks the first suggestion offered by the test search engine. add_task(async function test_suggestion_click() { Services.telemetry.clearScalars(); Services.telemetry.clearEvents(); await UrlbarTestUtils.formHistory.clear(); clearSAPTelemetry(); await withNewSearchEngine(async function (engine) { let tab = await BrowserTestUtils.openNewForegroundTab( gBrowser, "about:blank" ); info("Type a query. Suggestions should be generated by the test engine."); let p = BrowserTestUtils.browserLoaded(tab.linkedBrowser); await searchInAwesomebar("query"); info("Clicking the urlbar suggestion."); await clickURLBarSuggestion("queryfoo"); await p; // Check if the scalars contain the expected values. const scalars = TelemetryTestUtils.getProcessScalars("parent", true, false); TelemetryTestUtils.assertKeyedScalar( scalars, SCALAR_URLBAR, "search_suggestion", 1 ); Assert.equal( Object.keys(scalars[SCALAR_URLBAR]).length, 1, "This search must only increment one entry in the scalar." ); await SearchUITestUtils.assertSAPTelemetry({ engineName: engine.name, source: "urlbar", count: 1, }); BrowserTestUtils.removeTab(tab); }); }); // Clicks the first suggestion offered by the test search engine when in search // mode. add_task(async function test_searchmode_suggestion_click() { Services.telemetry.clearScalars(); Services.telemetry.clearEvents(); clearSAPTelemetry(); await withNewSearchEngine(async function (engine) { let tab = await BrowserTestUtils.openNewForegroundTab( gBrowser, "about:blank" ); info("Type a query. Suggestions should be generated by the test engine."); await searchInAwesomebar("query"); await UrlbarTestUtils.enterSearchMode(window, { engineName: engine.name, }); info("Clicking the urlbar suggestion."); let p = BrowserTestUtils.browserLoaded(tab.linkedBrowser); await clickURLBarSuggestion("queryfoo"); await p; // Check if the scalars contain the expected values. const scalars = TelemetryTestUtils.getProcessScalars("parent", true, false); TelemetryTestUtils.assertKeyedScalar( scalars, SCALAR_SEARCHMODE, "search_suggestion", 1 ); Assert.equal( Object.keys(scalars[SCALAR_SEARCHMODE]).length, 1, "This search must only increment one entry in the scalar." ); await SearchUITestUtils.assertSAPTelemetry({ engineName: engine.name, source: "urlbar_searchmode", count: 1, }); BrowserTestUtils.removeTab(tab); }); }); // Clicks a form history result. add_task(async function test_formHistory_click() { Services.telemetry.clearScalars(); Services.telemetry.clearEvents(); clearSAPTelemetry(); await UrlbarTestUtils.formHistory.clear(); await UrlbarTestUtils.formHistory.add(["foobar"]); await SpecialPowers.pushPrefEnv({ set: [["browser.urlbar.maxHistoricalSearchSuggestions", 1]], }); await withNewSearchEngine(async engine => { let tab = await BrowserTestUtils.openNewForegroundTab( gBrowser, "about:blank" ); info("Type a query. There should be form history."); let p = BrowserTestUtils.browserLoaded(tab.linkedBrowser); await searchInAwesomebar("foo"); info("Clicking the form history."); await clickURLBarSuggestion("foobar"); await p; // Check if the scalars contain the expected values. const scalars = TelemetryTestUtils.getProcessScalars("parent", true, false); TelemetryTestUtils.assertKeyedScalar( scalars, SCALAR_URLBAR, "search_formhistory", 1 ); Assert.equal( Object.keys(scalars[SCALAR_URLBAR]).length, 1, "This search must only increment one entry in the scalar." ); await SearchUITestUtils.assertSAPTelemetry({ engineName: engine.name, source: "urlbar", count: 1, }); BrowserTestUtils.removeTab(tab); await UrlbarTestUtils.formHistory.clear(); await SpecialPowers.popPrefEnv(); }); }); add_task(async function test_privateWindow() { // This test assumes the showSearchTerms feature is not enabled, // as multiple searches are made one after another, relying on // urlbar as the keyed scalar SAP, not urlbar_persisted. await SpecialPowers.pushPrefEnv({ set: [["browser.urlbar.showSearchTerms.featureGate", false]], }); // Override the search telemetry search provider info to // count in-content SAP telemetry for our test engine. SearchSERPTelemetry.overrideSearchTelemetryForTests([ { telemetryId: "example", searchPageRegexp: "^https://example\\.com/", queryParamNames: ["q"], }, ]); clearSAPTelemetry(); // First, do a bunch of searches in a private window. let win = await BrowserTestUtils.openNewBrowserWindow({ private: true }); info("Search in a private window and the pref does not exist"); let p = BrowserTestUtils.browserLoaded(win.gBrowser.selectedBrowser); await searchInAwesomebar("query", win); EventUtils.synthesizeKey("KEY_Enter", undefined, win); await p; await SearchUITestUtils.assertSAPTelemetry({ engineName: "MozSearch", source: "urlbar", count: 1, }); let scalars = TelemetryTestUtils.getProcessScalars("parent", true); TelemetryTestUtils.assertKeyedScalar( scalars, "browser.search.content.urlbar", "example:organic:none", 1 ); info("Search again in a private window after setting the pref to true"); Services.prefs.setBoolPref("browser.engagement.search_counts.pbm", true); p = BrowserTestUtils.browserLoaded(win.gBrowser.selectedBrowser); await searchInAwesomebar("another query", win); EventUtils.synthesizeKey("KEY_Enter", undefined, win); await p; // SAP counts should not be incremented. await SearchUITestUtils.assertSAPTelemetry({ engineName: "MozSearch", source: "urlbar", count: 1, }); scalars = TelemetryTestUtils.getProcessScalars("parent", true); TelemetryTestUtils.assertKeyedScalar( scalars, "browser.search.content.urlbar", "example:organic:none", 1 ); info("Search again in a private window after setting the pref to false"); Services.prefs.setBoolPref("browser.engagement.search_counts.pbm", false); p = BrowserTestUtils.browserLoaded(win.gBrowser.selectedBrowser); await searchInAwesomebar("another query", win); EventUtils.synthesizeKey("KEY_Enter", undefined, win); await p; await SearchUITestUtils.assertSAPTelemetry({ engineName: "MozSearch", source: "urlbar", count: 2, }); scalars = TelemetryTestUtils.getProcessScalars("parent", true); TelemetryTestUtils.assertKeyedScalar( scalars, "browser.search.content.urlbar", "example:organic:none", 2 ); info("Search again in a private window after clearing the pref"); Services.prefs.clearUserPref("browser.engagement.search_counts.pbm"); p = BrowserTestUtils.browserLoaded(win.gBrowser.selectedBrowser); await searchInAwesomebar("another query", win); EventUtils.synthesizeKey("KEY_Enter", undefined, win); await p; await SearchUITestUtils.assertSAPTelemetry({ engineName: "MozSearch", source: "urlbar", count: 3, }); scalars = TelemetryTestUtils.getProcessScalars("parent", true); TelemetryTestUtils.assertKeyedScalar( scalars, "browser.search.content.urlbar", "example:organic:none", 3 ); await BrowserTestUtils.closeWindow(win); // Now, do a bunch of searches in a non-private window. Telemetry should // always be recorded regardless of the pref's value. win = await BrowserTestUtils.openNewBrowserWindow(); info("Search in a non-private window and the pref does not exist"); p = BrowserTestUtils.browserLoaded(win.gBrowser.selectedBrowser); await searchInAwesomebar("query", win); EventUtils.synthesizeKey("KEY_Enter", undefined, win); await p; await SearchUITestUtils.assertSAPTelemetry({ engineName: "MozSearch", source: "urlbar", count: 4, }); scalars = TelemetryTestUtils.getProcessScalars("parent", true); TelemetryTestUtils.assertKeyedScalar( scalars, "browser.search.content.urlbar", "example:organic:none", 4 ); info("Search again in a non-private window after setting the pref to true"); Services.prefs.setBoolPref("browser.engagement.search_counts.pbm", true); p = BrowserTestUtils.browserLoaded(win.gBrowser.selectedBrowser); await searchInAwesomebar("another query", win); EventUtils.synthesizeKey("KEY_Enter", undefined, win); await p; await SearchUITestUtils.assertSAPTelemetry({ engineName: "MozSearch", source: "urlbar", count: 5, }); scalars = TelemetryTestUtils.getProcessScalars("parent", true); TelemetryTestUtils.assertKeyedScalar( scalars, "browser.search.content.urlbar", "example:organic:none", 5 ); info("Search again in a non-private window after setting the pref to false"); Services.prefs.setBoolPref("browser.engagement.search_counts.pbm", false); p = BrowserTestUtils.browserLoaded(win.gBrowser.selectedBrowser); await searchInAwesomebar("another query", win); EventUtils.synthesizeKey("KEY_Enter", undefined, win); await p; await SearchUITestUtils.assertSAPTelemetry({ engineName: "MozSearch", source: "urlbar", count: 6, }); scalars = TelemetryTestUtils.getProcessScalars("parent", true); TelemetryTestUtils.assertKeyedScalar( scalars, "browser.search.content.urlbar", "example:organic:none", 6 ); info("Search again in a non-private window after clearing the pref"); Services.prefs.clearUserPref("browser.engagement.search_counts.pbm"); p = BrowserTestUtils.browserLoaded(win.gBrowser.selectedBrowser); await searchInAwesomebar("another query", win); EventUtils.synthesizeKey("KEY_Enter", undefined, win); await p; await SearchUITestUtils.assertSAPTelemetry({ engineName: "MozSearch", source: "urlbar", count: 7, }); scalars = TelemetryTestUtils.getProcessScalars("parent", true); TelemetryTestUtils.assertKeyedScalar( scalars, "browser.search.content.urlbar", "example:organic:none", 7 ); await BrowserTestUtils.closeWindow(win); // Reset the search provider info. SearchSERPTelemetry.overrideSearchTelemetryForTests(); await UrlbarTestUtils.formHistory.clear(); await SpecialPowers.popPrefEnv(); });