diff options
Diffstat (limited to 'browser/components/urlbar/tests/engagementTelemetry')
38 files changed, 4792 insertions, 0 deletions
diff --git a/browser/components/urlbar/tests/engagementTelemetry/browser/browser.ini b/browser/components/urlbar/tests/engagementTelemetry/browser/browser.ini new file mode 100644 index 0000000000..762173562d --- /dev/null +++ b/browser/components/urlbar/tests/engagementTelemetry/browser/browser.ini @@ -0,0 +1,52 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +[DEFAULT] +support-files = + head.js + head-exposure.js + head-groups.js + head-interaction.js + head-n_chars_n_words.js + head-sap.js + head-search_mode.js + ../../browser-tips/head.js + ../../ext/browser/head.js + ../../ext/api.js + ../../ext/schema.json +skip-if = + os == "win" && os_version == "6.1" # Skip on Azure - frequent failure + +[browser_glean_telemetry_abandonment_groups.js] +[browser_glean_telemetry_abandonment_interaction.js] +[browser_glean_telemetry_abandonment_interaction_persisted_search_terms_disabled.js] +[browser_glean_telemetry_abandonment_interaction_persisted_search_terms_enabled.js] +[browser_glean_telemetry_abandonment_n_chars_n_words.js] +[browser_glean_telemetry_abandonment_sap.js] +[browser_glean_telemetry_abandonment_search_mode.js] +[browser_glean_telemetry_abandonment_tips.js] +[browser_glean_telemetry_engagement_edge_cases.js] +[browser_glean_telemetry_engagement_groups.js] +[browser_glean_telemetry_engagement_interaction.js] +[browser_glean_telemetry_engagement_interaction_persisted_search_terms_disabled.js] +[browser_glean_telemetry_engagement_interaction_persisted_search_terms_enabled.js] +[browser_glean_telemetry_engagement_n_chars_n_words.js] +[browser_glean_telemetry_engagement_sap.js] +[browser_glean_telemetry_engagement_search_mode.js] +[browser_glean_telemetry_engagement_selected_result.js] +support-files = + ../../../../search/test/browser/trendingSuggestionEngine.sjs +[browser_glean_telemetry_engagement_tips.js] +[browser_glean_telemetry_engagement_type.js] +[browser_glean_telemetry_exposure.js] +[browser_glean_telemetry_impression_groups.js] +[browser_glean_telemetry_impression_interaction.js] +[browser_glean_telemetry_impression_interaction_persisted_search_terms_disabled.js] +[browser_glean_telemetry_impression_interaction_persisted_search_terms_enabled.js] +[browser_glean_telemetry_impression_n_chars_n_words.js] +[browser_glean_telemetry_impression_preferences.js] +[browser_glean_telemetry_impression_sap.js] +[browser_glean_telemetry_impression_search_mode.js] +[browser_glean_telemetry_impression_timing.js] +[browser_glean_telemetry_record_preferences.js] diff --git a/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_abandonment_groups.js b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_abandonment_groups.js new file mode 100644 index 0000000000..6341a21a1a --- /dev/null +++ b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_abandonment_groups.js @@ -0,0 +1,202 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test for the following data of abandonment telemetry. +// - groups +// - results +// - n_results + +add_setup(async function () { + await initGroupTest(); +}); + +add_task(async function heuristics() { + await doHeuristicsTest({ + trigger: () => doBlur(), + assert: () => + assertAbandonmentTelemetry([ + { groups: "heuristic", results: "search_engine" }, + ]), + }); +}); + +add_task(async function adaptive_history() { + await doAdaptiveHistoryTest({ + trigger: () => doBlur(), + assert: () => + assertAbandonmentTelemetry([ + { + groups: "heuristic,adaptive_history", + results: "search_engine,history", + n_results: 2, + }, + ]), + }); +}); + +add_task(async function search_history() { + await doSearchHistoryTest({ + trigger: () => doBlur(), + assert: () => + assertAbandonmentTelemetry([ + { + groups: "heuristic,search_history,search_history", + results: "search_engine,search_history,search_history", + n_results: 3, + }, + ]), + }); +}); + +add_task(async function search_suggest() { + await doSearchSuggestTest({ + trigger: () => doBlur(), + assert: () => + assertAbandonmentTelemetry([ + { + groups: "heuristic,search_suggest,search_suggest", + results: "search_engine,search_suggest,search_suggest", + n_results: 3, + }, + ]), + }); + + await doTailSearchSuggestTest({ + trigger: () => doBlur(), + assert: () => + assertAbandonmentTelemetry([ + { + groups: "heuristic,search_suggest", + results: "search_engine,search_suggest", + n_results: 2, + }, + ]), + }); +}); + +add_task(async function top_pick() { + await doTopPickTest({ + trigger: () => doBlur(), + assert: () => + assertAbandonmentTelemetry([ + { + groups: "heuristic,top_pick,search_suggest,search_suggest", + results: + "search_engine,rs_adm_sponsored,search_suggest,search_suggest", + n_results: 4, + }, + ]), + }); +}); + +add_task(async function top_site() { + await doTopSiteTest({ + trigger: () => doBlur(), + assert: () => + assertAbandonmentTelemetry([ + { + groups: "top_site,suggested_index", + results: "top_site,action", + n_results: 2, + }, + ]), + }); +}); + +add_task(async function remote_tab() { + await doRemoteTabTest({ + trigger: () => doBlur(), + assert: () => + assertAbandonmentTelemetry([ + { + groups: "heuristic,remote_tab", + results: "search_engine,remote_tab", + n_results: 2, + }, + ]), + }); +}); + +add_task(async function addon() { + await doAddonTest({ + trigger: () => doBlur(), + assert: () => + assertAbandonmentTelemetry([ + { + groups: "addon", + results: "addon", + n_results: 1, + }, + ]), + }); +}); + +add_task(async function general() { + await doGeneralBookmarkTest({ + trigger: () => doBlur(), + assert: () => + assertAbandonmentTelemetry([ + { + groups: "heuristic,suggested_index,general", + results: "search_engine,action,bookmark", + n_results: 3, + }, + ]), + }); + + await doGeneralHistoryTest({ + trigger: () => doBlur(), + assert: () => + assertAbandonmentTelemetry([ + { + groups: "heuristic,general", + results: "search_engine,history", + n_results: 2, + }, + ]), + }); +}); + +add_task(async function suggest() { + await doSuggestTest({ + trigger: () => doBlur(), + assert: () => + assertAbandonmentTelemetry([ + { + groups: "heuristic,suggest", + results: "search_engine,rs_adm_nonsponsored", + n_results: 2, + }, + ]), + }); +}); + +add_task(async function about_page() { + await doAboutPageTest({ + trigger: () => doBlur(), + assert: () => + assertAbandonmentTelemetry([ + { + groups: "heuristic,about_page,about_page", + results: "search_engine,history,history", + n_results: 3, + }, + ]), + }); +}); + +add_task(async function suggested_index() { + await doSuggestedIndexTest({ + trigger: () => doBlur(), + assert: () => + assertAbandonmentTelemetry([ + { + groups: "heuristic,suggested_index", + results: "search_engine,unit", + n_results: 2, + }, + ]), + }); +}); diff --git a/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_abandonment_interaction.js b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_abandonment_interaction.js new file mode 100644 index 0000000000..0462833008 --- /dev/null +++ b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_abandonment_interaction.js @@ -0,0 +1,58 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test for the following data of abandonment telemetry. +// - interaction + +add_setup(async function () { + await initInteractionTest(); +}); + +add_task(async function topsites() { + await doTopsitesTest({ + trigger: () => doBlur(), + assert: () => assertAbandonmentTelemetry([{ interaction: "topsites" }]), + }); +}); + +add_task(async function typed() { + await doTypedTest({ + trigger: () => doBlur(), + assert: () => assertAbandonmentTelemetry([{ interaction: "typed" }]), + }); + + await doTypedWithResultsPopupTest({ + trigger: () => doBlur(), + assert: () => assertAbandonmentTelemetry([{ interaction: "typed" }]), + }); +}); + +add_task(async function pasted() { + await doPastedTest({ + trigger: () => doBlur(), + assert: () => assertAbandonmentTelemetry([{ interaction: "pasted" }]), + }); + + await doPastedWithResultsPopupTest({ + trigger: () => doBlur(), + assert: () => assertAbandonmentTelemetry([{ interaction: "pasted" }]), + }); +}); + +add_task(async function topsite_search() { + // TODO: https://bugzilla.mozilla.org/show_bug.cgi?id=1804010 + // assertAbandonmentTelemetry([{ interaction: "topsite_search" }]); +}); + +add_task(async function returned_restarted_refined() { + await doReturnedRestartedRefinedTest({ + trigger: () => doBlur(), + assert: expected => + assertAbandonmentTelemetry([ + { interaction: "typed" }, + { interaction: expected }, + ]), + }); +}); diff --git a/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_abandonment_interaction_persisted_search_terms_disabled.js b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_abandonment_interaction_persisted_search_terms_disabled.js new file mode 100644 index 0000000000..68799544b0 --- /dev/null +++ b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_abandonment_interaction_persisted_search_terms_disabled.js @@ -0,0 +1,48 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test abandonment telemetry with persisted search terms disabled. + +// 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 initInteractionTest(); + + await SpecialPowers.pushPrefEnv({ + set: [["browser.urlbar.showSearchTerms.featureGate", false]], + }); +}); + +add_task(async function persisted_search_terms() { + await doPersistedSearchTermsTest({ + trigger: () => doBlur(), + assert: () => assertAbandonmentTelemetry([{ interaction: "typed" }]), + }); +}); + +add_task(async function persisted_search_terms_restarted_refined() { + await doPersistedSearchTermsRestartedRefinedTest({ + enabled: false, + trigger: () => doBlur(), + assert: expected => assertAbandonmentTelemetry([{ interaction: expected }]), + }); +}); + +add_task( + async function persisted_search_terms_restarted_refined_via_abandonment() { + await doPersistedSearchTermsRestartedRefinedViaAbandonmentTest({ + enabled: false, + trigger: () => doBlur(), + assert: expected => + assertAbandonmentTelemetry([ + { interaction: "typed" }, + { interaction: expected }, + ]), + }); + } +); diff --git a/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_abandonment_interaction_persisted_search_terms_enabled.js b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_abandonment_interaction_persisted_search_terms_enabled.js new file mode 100644 index 0000000000..f0a217805f --- /dev/null +++ b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_abandonment_interaction_persisted_search_terms_enabled.js @@ -0,0 +1,53 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test abandonment telemetry with persisted search terms enabled. + +// 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 initInteractionTest(); + + await SpecialPowers.pushPrefEnv({ + set: [ + ["browser.urlbar.showSearchTerms.featureGate", true], + ["browser.urlbar.showSearchTerms.enabled", true], + ["browser.search.widget.inNavBar", false], + ], + }); +}); + +add_task(async function persisted_search_terms() { + await doPersistedSearchTermsTest({ + trigger: () => doBlur(), + assert: () => + assertAbandonmentTelemetry([{ interaction: "persisted_search_terms" }]), + }); +}); + +add_task(async function persisted_search_terms_restarted_refined() { + await doPersistedSearchTermsRestartedRefinedTest({ + enabled: true, + trigger: () => doBlur(), + assert: expected => assertAbandonmentTelemetry([{ interaction: expected }]), + }); +}); + +add_task( + async function persisted_search_terms_restarted_refined_via_abandonment() { + await doPersistedSearchTermsRestartedRefinedViaAbandonmentTest({ + enabled: true, + trigger: () => doBlur(), + assert: expected => + assertAbandonmentTelemetry([ + { interaction: "persisted_search_terms_restarted" }, + { interaction: expected }, + ]), + }); + } +); diff --git a/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_abandonment_n_chars_n_words.js b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_abandonment_n_chars_n_words.js new file mode 100644 index 0000000000..7427db8cbf --- /dev/null +++ b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_abandonment_n_chars_n_words.js @@ -0,0 +1,36 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test for the following data of abandonment telemetry. +// - n_chars +// - n_words + +add_setup(async function () { + await initNCharsAndNWordsTest(); +}); + +add_task(async function n_chars() { + await doNCharsTest({ + trigger: () => doBlur(), + assert: nChars => assertAbandonmentTelemetry([{ n_chars: nChars }]), + }); + + await doNCharsWithOverMaxTextLengthCharsTest({ + trigger: () => doBlur(), + assert: nChars => assertAbandonmentTelemetry([{ n_chars: nChars }]), + }); +}); + +add_task(async function n_words() { + await doNWordsTest({ + trigger: () => doBlur(), + assert: nWords => assertAbandonmentTelemetry([{ n_words: nWords }]), + }); + + await doNWordsWithOverMaxTextLengthCharsTest({ + trigger: () => doBlur(), + assert: nWords => assertAbandonmentTelemetry([{ n_words: nWords }]), + }); +}); diff --git a/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_abandonment_sap.js b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_abandonment_sap.js new file mode 100644 index 0000000000..3d0af65379 --- /dev/null +++ b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_abandonment_sap.js @@ -0,0 +1,39 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test for the following data of abandonment telemetry. +// - sap + +add_setup(async function () { + await initSapTest(); +}); + +add_task(async function urlbar_newtab() { + await doUrlbarNewTabTest({ + trigger: () => doBlur(), + assert: () => assertAbandonmentTelemetry([{ sap: "urlbar_newtab" }]), + }); +}); + +add_task(async function urlbar() { + await doUrlbarTest({ + trigger: () => doBlur(), + assert: () => assertAbandonmentTelemetry([{ sap: "urlbar" }]), + }); +}); + +add_task(async function handoff() { + await doHandoffTest({ + trigger: () => doBlur(), + assert: () => assertAbandonmentTelemetry([{ sap: "handoff" }]), + }); +}); + +add_task(async function urlbar_addonpage() { + await doUrlbarAddonpageTest({ + trigger: () => doBlur(), + assert: () => assertAbandonmentTelemetry([{ sap: "urlbar_addonpage" }]), + }); +}); diff --git a/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_abandonment_search_mode.js b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_abandonment_search_mode.js new file mode 100644 index 0000000000..7edcc47a30 --- /dev/null +++ b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_abandonment_search_mode.js @@ -0,0 +1,54 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test for the following data of abandonment telemetry. +// - search_mode + +add_setup(async function () { + await initSearchModeTest(); +}); + +add_task(async function not_search_mode() { + await doNotSearchModeTest({ + trigger: () => doBlur(), + assert: () => assertAbandonmentTelemetry([{ search_mode: "" }]), + }); +}); + +add_task(async function search_engine() { + await doSearchEngineTest({ + trigger: () => doBlur(), + assert: () => + assertAbandonmentTelemetry([{ search_mode: "search_engine" }]), + }); +}); + +add_task(async function bookmarks() { + await doBookmarksTest({ + trigger: () => doBlur(), + assert: () => assertAbandonmentTelemetry([{ search_mode: "bookmarks" }]), + }); +}); + +add_task(async function history() { + await doHistoryTest({ + trigger: () => doBlur(), + assert: () => assertAbandonmentTelemetry([{ search_mode: "history" }]), + }); +}); + +add_task(async function tabs() { + await doTabTest({ + trigger: () => doBlur(), + assert: () => assertAbandonmentTelemetry([{ search_mode: "tabs" }]), + }); +}); + +add_task(async function actions() { + await doActionsTest({ + trigger: () => doBlur(), + assert: () => assertAbandonmentTelemetry([{ search_mode: "actions" }]), + }); +}); diff --git a/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_abandonment_tips.js b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_abandonment_tips.js new file mode 100644 index 0000000000..f773b0fb28 --- /dev/null +++ b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_abandonment_tips.js @@ -0,0 +1,88 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test for abandonment telemetry for tips using Glean. + +Services.scriptloader.loadSubScript( + "chrome://mochitests/content/browser/browser/components/urlbar/tests/browser-tips/head.js", + this +); + +add_setup(async function () { + Services.fog.setMetricsFeatureConfig( + JSON.stringify({ "urlbar.abandonment": false }) + ); + + await SpecialPowers.pushPrefEnv({ + set: [ + ["browser.urlbar.searchTips.test.ignoreShowLimits", true], + ["browser.urlbar.showSearchTerms.featureGate", true], + ], + }); + const engine = await SearchTestUtils.promiseNewSearchEngine({ + url: "chrome://mochitests/content/browser/browser/components/urlbar/tests/browser/searchSuggestionEngine.xml", + }); + const originalDefaultEngine = await Services.search.getDefault(); + await Services.search.setDefault( + engine, + Ci.nsISearchService.CHANGE_REASON_UNKNOWN + ); + await Services.search.moveEngine(engine, 0); + + registerCleanupFunction(async function () { + Services.fog.setMetricsFeatureConfig("{}"); + await SpecialPowers.popPrefEnv(); + await Services.search.setDefault( + originalDefaultEngine, + Ci.nsISearchService.CHANGE_REASON_UNKNOWN + ); + resetSearchTipsProvider(); + }); +}); + +add_task(async function tip_persist() { + await doTest(async browser => { + await showPersistSearchTip("test"); + gURLBar.focus(); + await UrlbarTestUtils.promisePopupClose(window, () => { + gURLBar.blur(); + }); + + assertAbandonmentTelemetry([{ results: "tip_persist" }]); + }); +}); + +add_task(async function mouse_down_with_tip() { + await doTest(async browser => { + await showPersistSearchTip("test"); + await UrlbarTestUtils.promisePopupClose(window, () => { + EventUtils.synthesizeMouseAtCenter(browser, {}); + }); + + assertAbandonmentTelemetry([{ results: "tip_persist" }]); + }); +}); + +add_task(async function mouse_down_without_tip() { + await doTest(async browser => { + EventUtils.synthesizeMouseAtCenter(browser, {}); + + assertAbandonmentTelemetry([]); + }); +}); + +async function showPersistSearchTip(word) { + await openPopup(word); + await doEnter(); + await BrowserTestUtils.waitForCondition(async () => { + for (let i = 0; i < UrlbarTestUtils.getResultCount(window); i++) { + const detail = await UrlbarTestUtils.getDetailsOfResultAt(window, i); + if (detail.result.payload?.type === "searchTip_persist") { + return true; + } + } + return false; + }); +} diff --git a/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_engagement_edge_cases.js b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_engagement_edge_cases.js new file mode 100644 index 0000000000..d7b2e775b8 --- /dev/null +++ b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_engagement_edge_cases.js @@ -0,0 +1,218 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test edge cases for engagement. + +Services.scriptloader.loadSubScript( + "chrome://mochitests/content/browser/browser/components/urlbar/tests/ext/browser/head.js", + this +); + +add_setup(async function () { + await setup(); +}); + +/** + * UrlbarProvider that does not add any result. + */ +class NoResponseTestProvider extends UrlbarTestUtils.TestProvider { + constructor() { + super({ name: "TestProviderNoResponse ", results: [] }); + this.#deferred = PromiseUtils.defer(); + } + + get type() { + return UrlbarUtils.PROVIDER_TYPE.HEURISTIC; + } + + async startQuery(context, addCallback) { + await this.#deferred.promise; + } + + done() { + this.#deferred.resolve(); + } + + #deferred = null; +} +const noResponseProvider = new NoResponseTestProvider(); + +/** + * UrlbarProvider that adds a heuristic result immediately as usual. + */ +class AnotherHeuristicProvider extends UrlbarTestUtils.TestProvider { + constructor({ results }) { + super({ name: "TestProviderAnotherHeuristic ", results }); + this.#deferred = PromiseUtils.defer(); + } + + get type() { + return UrlbarUtils.PROVIDER_TYPE.HEURISTIC; + } + + async startQuery(context, addCallback) { + for (const result of this._results) { + addCallback(this, result); + } + + this.#deferred.resolve(context); + } + + onQueryStarted() { + return this.#deferred.promise; + } + + #deferred = null; +} +const anotherHeuristicProvider = new AnotherHeuristicProvider({ + results: [ + Object.assign( + new UrlbarResult( + UrlbarUtils.RESULT_TYPE.URL, + UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL, + { url: "https://example.com/immediate" } + ), + { heuristic: true } + ), + ], +}); + +add_task(async function engagement_before_showing_results() { + await SpecialPowers.pushPrefEnv({ + // Avoid showing search tip. + set: [["browser.urlbar.tipShownCount.searchTip_onboard", 999]], + }); + + // Update chunkResultsDelayMs to delay the call to notifyResults. + const originalChuldResultDelayMs = + UrlbarProvidersManager._chunkResultsDelayMs; + UrlbarProvidersManager._chunkResultsDelayMs = 1000000; + + // Add a provider that waits forever in startQuery() to avoid fireing + // heuristicProviderTimer. + UrlbarProvidersManager.registerProvider(noResponseProvider); + + // Add a provider that add a result immediately as usual. + UrlbarProvidersManager.registerProvider(anotherHeuristicProvider); + + const cleanup = () => { + UrlbarProvidersManager.unregisterProvider(noResponseProvider); + UrlbarProvidersManager.unregisterProvider(anotherHeuristicProvider); + UrlbarProvidersManager._chunkResultsDelayMs = originalChuldResultDelayMs; + }; + registerCleanupFunction(cleanup); + + await doTest(async browser => { + // Try to show the results. + await UrlbarTestUtils.inputIntoURLBar(window, "exam"); + + // Wait until starting the query and filling expected results. + const context = await anotherHeuristicProvider.onQueryStarted(); + const query = UrlbarProvidersManager.queries.get(context); + await BrowserTestUtils.waitForCondition( + () => + query.unsortedResults.some( + r => r.providerName === "HeuristicFallback" + ) && + query.unsortedResults.some( + r => r.providerName === anotherHeuristicProvider.name + ) + ); + + // Type Enter key before showing any results. + await doEnter(); + + assertEngagementTelemetry([ + { + selected_result: "input_field", + selected_result_subtype: "", + provider: undefined, + results: "", + groups: "", + }, + ]); + + // Clear the pending query. + noResponseProvider.done(); + }); + + cleanup(); + await SpecialPowers.popPrefEnv(); +}); + +add_task(async function engagement_after_closing_results() { + const TRIGGERS = [ + () => EventUtils.synthesizeKey("KEY_Escape"), + () => + EventUtils.synthesizeMouseAtCenter( + document.getElementById("customizableui-special-spring2"), + {} + ), + ]; + + for (const trigger of TRIGGERS) { + await doTest(async browser => { + await openPopup("test"); + await UrlbarTestUtils.promisePopupClose(window, () => { + trigger(); + }); + Assert.equal( + gURLBar.value, + "test", + "The inputted text remains even if closing the results" + ); + // The tested trigger should not record abandonment event. + assertAbandonmentTelemetry([]); + + // Endgagement. + await doEnter(); + + assertEngagementTelemetry([ + { + selected_result: "input_field", + selected_result_subtype: "", + provider: undefined, + results: "", + groups: "", + }, + ]); + }); + } +}); + +add_task(async function enter_to_reload_current_url() { + await doTest(async browser => { + // Open a URL once. + await openPopup("https://example.com"); + await doEnter(); + + // Focus the urlbar. + EventUtils.synthesizeMouseAtCenter(gURLBar.inputField, {}); + await BrowserTestUtils.waitForCondition( + () => window.document.activeElement === gURLBar.inputField + ); + await UrlbarTestUtils.promiseSearchComplete(window); + + // Press Enter key to reload the page without selecting any suggestions. + await doEnter(); + + assertEngagementTelemetry([ + { + selected_result: "url", + selected_result_subtype: "", + provider: "HeuristicFallback", + results: "url", + groups: "heuristic", + }, + { + selected_result: "input_field", + selected_result_subtype: "", + provider: undefined, + results: "action", + groups: "suggested_index", + }, + ]); + }); +}); diff --git a/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_engagement_groups.js b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_engagement_groups.js new file mode 100644 index 0000000000..9060835562 --- /dev/null +++ b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_engagement_groups.js @@ -0,0 +1,259 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test for the following data of engagement telemetry. +// - groups +// - results +// - n_results + +add_setup(async function () { + await initGroupTest(); +}); + +add_task(async function heuristics() { + await doHeuristicsTest({ + trigger: () => doEnter(), + assert: () => + assertEngagementTelemetry([ + { groups: "heuristic", results: "search_engine" }, + ]), + }); +}); + +add_task(async function adaptive_history() { + await doAdaptiveHistoryTest({ + trigger: () => doEnter(), + assert: () => + assertEngagementTelemetry([ + { + groups: "heuristic,adaptive_history", + results: "search_engine,history", + n_results: 2, + }, + ]), + }); +}); + +add_task(async function search_history() { + await doSearchHistoryTest({ + trigger: () => doEnter(), + assert: () => + assertEngagementTelemetry([ + { + groups: "heuristic,search_history,search_history", + results: "search_engine,search_history,search_history", + n_results: 3, + }, + ]), + }); +}); + +add_task(async function search_suggest() { + await doSearchSuggestTest({ + trigger: () => doEnter(), + assert: () => + assertEngagementTelemetry([ + { + groups: "heuristic,search_suggest,search_suggest", + results: "search_engine,search_suggest,search_suggest", + n_results: 3, + }, + ]), + }); + + await doTailSearchSuggestTest({ + trigger: () => doEnter(), + assert: () => + assertEngagementTelemetry([ + { + groups: "heuristic,search_suggest", + results: "search_engine,search_suggest", + n_results: 2, + }, + ]), + }); +}); + +add_task(async function top_pick() { + await doTopPickTest({ + trigger: () => doEnter(), + assert: () => + assertEngagementTelemetry([ + { + groups: "heuristic,top_pick,search_suggest,search_suggest", + results: + "search_engine,rs_adm_sponsored,search_suggest,search_suggest", + n_results: 4, + }, + ]), + }); +}); + +add_task(async function top_site() { + await doTopSiteTest({ + trigger: () => doEnter(), + assert: () => + assertEngagementTelemetry([ + { + groups: "top_site,suggested_index", + results: "top_site,action", + n_results: 2, + }, + ]), + }); +}); + +add_task(async function remote_tab() { + await doRemoteTabTest({ + trigger: () => doEnter(), + assert: () => + assertEngagementTelemetry([ + { + groups: "heuristic,remote_tab", + results: "search_engine,remote_tab", + n_results: 2, + }, + ]), + }); +}); + +add_task(async function addon() { + await doAddonTest({ + trigger: () => doEnter(), + assert: () => + assertEngagementTelemetry([ + { + groups: "addon", + results: "addon", + n_results: 1, + }, + ]), + }); +}); + +add_task(async function general() { + await doGeneralBookmarkTest({ + trigger: () => doEnter(), + assert: () => + assertEngagementTelemetry([ + { + groups: "heuristic,suggested_index,general", + results: "search_engine,action,bookmark", + n_results: 3, + }, + ]), + }); + + await doGeneralHistoryTest({ + trigger: () => doEnter(), + assert: () => + assertEngagementTelemetry([ + { + groups: "heuristic,general", + results: "search_engine,history", + n_results: 2, + }, + ]), + }); +}); + +add_task(async function suggest() { + await doSuggestTest({ + trigger: () => doEnter(), + assert: () => + assertEngagementTelemetry([ + { + groups: "heuristic,suggest", + results: "search_engine,rs_adm_nonsponsored", + n_results: 2, + }, + ]), + }); +}); + +add_task(async function about_page() { + await doAboutPageTest({ + trigger: () => doEnter(), + assert: () => + assertEngagementTelemetry([ + { + groups: "heuristic,about_page,about_page", + results: "search_engine,history,history", + n_results: 3, + }, + ]), + }); +}); + +add_task(async function suggested_index() { + await doSuggestedIndexTest({ + trigger: () => + SimpleTest.promiseClipboardChange("100 cm", () => { + EventUtils.synthesizeKey("KEY_Enter"); + }), + assert: () => + assertEngagementTelemetry([ + { + groups: "heuristic,suggested_index", + results: "search_engine,unit", + n_results: 2, + }, + ]), + }); +}); + +add_task(async function always_empty_if_drop_go() { + const expected = [ + { + engagement_type: "drop_go", + groups: "", + results: "", + n_results: 0, + }, + ]; + + await doTest(async browser => { + await doDropAndGo("example.com"); + + assertEngagementTelemetry(expected); + }); + + await doTest(async browser => { + // Open the results view once. + await showResultByArrowDown(); + await UrlbarTestUtils.promisePopupClose(window); + + await doDropAndGo("example.com"); + + assertEngagementTelemetry(expected); + }); +}); + +add_task(async function always_empty_if_paste_go() { + const expected = [ + { + engagement_type: "paste_go", + groups: "", + results: "", + n_results: 0, + }, + ]; + + await doTest(async browser => { + await doPasteAndGo("example.com"); + + assertEngagementTelemetry(expected); + }); + + await doTest(async browser => { + // Open the results view once. + await showResultByArrowDown(); + await UrlbarTestUtils.promisePopupClose(window); + + await doPasteAndGo("example.com"); + + assertEngagementTelemetry(expected); + }); +}); diff --git a/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_engagement_interaction.js b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_engagement_interaction.js new file mode 100644 index 0000000000..9de0de8953 --- /dev/null +++ b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_engagement_interaction.js @@ -0,0 +1,87 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test for the following data of engagement telemetry. +// - interaction + +// 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 initInteractionTest(); +}); + +add_task(async function topsites() { + await doTopsitesTest({ + trigger: () => doEnter(), + assert: () => assertEngagementTelemetry([{ interaction: "topsites" }]), + }); +}); + +add_task(async function typed() { + await doTypedTest({ + trigger: () => doEnter(), + assert: () => assertEngagementTelemetry([{ interaction: "typed" }]), + }); + + await doTypedWithResultsPopupTest({ + trigger: () => doEnter(), + assert: () => assertEngagementTelemetry([{ interaction: "typed" }]), + }); +}); + +add_task(async function dropped() { + await doTest(async browser => { + await doDropAndGo("example.com"); + + assertEngagementTelemetry([{ interaction: "dropped" }]); + }); + + await doTest(async browser => { + await showResultByArrowDown(); + await doDropAndGo("example.com"); + + assertEngagementTelemetry([{ interaction: "dropped" }]); + }); +}); + +add_task(async function pasted() { + await doPastedTest({ + trigger: () => doEnter(), + assert: () => assertEngagementTelemetry([{ interaction: "pasted" }]), + }); + + await doPastedWithResultsPopupTest({ + trigger: () => doEnter(), + assert: () => assertEngagementTelemetry([{ interaction: "pasted" }]), + }); + + await doTest(async browser => { + await doPasteAndGo("www.example.com"); + + assertEngagementTelemetry([{ interaction: "pasted" }]); + }); + + await doTest(async browser => { + await showResultByArrowDown(); + await doPasteAndGo("www.example.com"); + + assertEngagementTelemetry([{ interaction: "pasted" }]); + }); +}); + +add_task(async function topsite_search() { + // TODO: https://bugzilla.mozilla.org/show_bug.cgi?id=1804010 + // assertEngagementTelemetry([{ interaction: "topsite_search" }]); +}); + +add_task(async function returned_restarted_refined() { + await doReturnedRestartedRefinedTest({ + trigger: () => doEnter(), + assert: expected => assertEngagementTelemetry([{ interaction: expected }]), + }); +}); diff --git a/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_engagement_interaction_persisted_search_terms_disabled.js b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_engagement_interaction_persisted_search_terms_disabled.js new file mode 100644 index 0000000000..1bdb4f0b61 --- /dev/null +++ b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_engagement_interaction_persisted_search_terms_disabled.js @@ -0,0 +1,61 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test engagement telemetry with persisted search terms disabled. + +Services.scriptloader.loadSubScript( + "chrome://mochitests/content/browser/browser/components/urlbar/tests/engagementTelemetry/browser/head-interaction.js", + this +); + +// 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 initInteractionTest(); + + await SpecialPowers.pushPrefEnv({ + set: [["browser.urlbar.showSearchTerms.featureGate", false]], + }); +}); + +add_task(async function persisted_search_terms() { + await doPersistedSearchTermsTest({ + trigger: () => doEnter(), + assert: () => + assertEngagementTelemetry([ + { interaction: "typed" }, + { interaction: "typed" }, + ]), + }); +}); + +add_task(async function persisted_search_terms_restarted_refined() { + await doPersistedSearchTermsRestartedRefinedTest({ + enabled: false, + trigger: () => doEnter(), + assert: expected => + assertEngagementTelemetry([ + { interaction: "typed" }, + { interaction: expected }, + ]), + }); +}); + +add_task( + async function persisted_search_terms_restarted_refined_via_abandonment() { + await doPersistedSearchTermsRestartedRefinedViaAbandonmentTest({ + enabled: false, + trigger: () => doEnter(), + assert: expected => + assertEngagementTelemetry([ + { interaction: "typed" }, + { interaction: expected }, + ]), + }); + } +); diff --git a/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_engagement_interaction_persisted_search_terms_enabled.js b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_engagement_interaction_persisted_search_terms_enabled.js new file mode 100644 index 0000000000..33a01fdd22 --- /dev/null +++ b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_engagement_interaction_persisted_search_terms_enabled.js @@ -0,0 +1,60 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test engagement telemetry with persisted search terms enabled. + +// 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 initInteractionTest(); + + await SpecialPowers.pushPrefEnv({ + set: [ + ["browser.urlbar.showSearchTerms.featureGate", true], + ["browser.urlbar.showSearchTerms.enabled", true], + ["browser.search.widget.inNavBar", false], + ], + }); +}); + +add_task(async function persisted_search_terms() { + await doPersistedSearchTermsTest({ + trigger: () => doEnter(), + assert: () => + assertEngagementTelemetry([ + { interaction: "typed" }, + { interaction: "persisted_search_terms" }, + ]), + }); +}); + +add_task(async function persisted_search_terms_restarted_refined() { + await doPersistedSearchTermsRestartedRefinedTest({ + enabled: true, + trigger: () => doEnter(), + assert: expected => + assertEngagementTelemetry([ + { interaction: "typed" }, + { interaction: expected }, + ]), + }); +}); + +add_task( + async function persisted_search_terms_restarted_refined_via_abandonment() { + await doPersistedSearchTermsRestartedRefinedViaAbandonmentTest({ + enabled: true, + trigger: () => doEnter(), + assert: expected => + assertEngagementTelemetry([ + { interaction: "typed" }, + { interaction: expected }, + ]), + }); + } +); diff --git a/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_engagement_n_chars_n_words.js b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_engagement_n_chars_n_words.js new file mode 100644 index 0000000000..498ffd9532 --- /dev/null +++ b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_engagement_n_chars_n_words.js @@ -0,0 +1,36 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test for the following data of engagement telemetry. +// - n_chars +// - n_words + +add_setup(async function () { + await initNCharsAndNWordsTest(); +}); + +add_task(async function n_chars() { + await doNCharsTest({ + trigger: () => doEnter(), + assert: nChars => assertEngagementTelemetry([{ n_chars: nChars }]), + }); + + await doNCharsWithOverMaxTextLengthCharsTest({ + trigger: () => doEnter(), + assert: nChars => assertEngagementTelemetry([{ n_chars: nChars }]), + }); +}); + +add_task(async function n_words() { + await doNWordsTest({ + trigger: () => doEnter(), + assert: nWords => assertEngagementTelemetry([{ n_words: nWords }]), + }); + + await doNWordsWithOverMaxTextLengthCharsTest({ + trigger: () => doEnter(), + assert: nWords => assertEngagementTelemetry([{ n_words: nWords }]), + }); +}); diff --git a/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_engagement_sap.js b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_engagement_sap.js new file mode 100644 index 0000000000..d361d70229 --- /dev/null +++ b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_engagement_sap.js @@ -0,0 +1,33 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test for the following data of engagement telemetry. +// - sap + +add_setup(async function () { + await initSapTest(); +}); + +add_task(async function urlbar() { + await doUrlbarTest({ + trigger: () => doEnter(), + assert: () => + assertEngagementTelemetry([{ sap: "urlbar_newtab" }, { sap: "urlbar" }]), + }); +}); + +add_task(async function handoff() { + await doHandoffTest({ + trigger: () => doEnter(), + assert: () => assertEngagementTelemetry([{ sap: "handoff" }]), + }); +}); + +add_task(async function urlbar_addonpage() { + await doUrlbarAddonpageTest({ + trigger: () => doEnter(), + assert: () => assertEngagementTelemetry([{ sap: "urlbar_addonpage" }]), + }); +}); diff --git a/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_engagement_search_mode.js b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_engagement_search_mode.js new file mode 100644 index 0000000000..62f87f0664 --- /dev/null +++ b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_engagement_search_mode.js @@ -0,0 +1,63 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test for the following data of engagement telemetry. +// - search_mode + +add_setup(async function () { + await initSearchModeTest(); +}); + +add_task(async function not_search_mode() { + await doNotSearchModeTest({ + trigger: () => doEnter(), + assert: () => assertEngagementTelemetry([{ search_mode: "" }]), + }); +}); + +add_task(async function search_engine() { + await doSearchEngineTest({ + trigger: () => doEnter(), + assert: () => assertEngagementTelemetry([{ search_mode: "search_engine" }]), + }); +}); + +add_task(async function bookmarks() { + await doBookmarksTest({ + trigger: () => doEnter(), + assert: () => assertEngagementTelemetry([{ search_mode: "bookmarks" }]), + }); +}); + +add_task(async function history() { + await doHistoryTest({ + trigger: () => doEnter(), + assert: () => assertEngagementTelemetry([{ search_mode: "history" }]), + }); +}); + +add_task(async function tabs() { + await doTabTest({ + trigger: async () => { + const currentTab = gBrowser.selectedTab; + EventUtils.synthesizeKey("KEY_Enter"); + await BrowserTestUtils.waitForCondition( + () => gBrowser.selectedTab !== currentTab + ); + }, + assert: () => assertEngagementTelemetry([{ search_mode: "tabs" }]), + }); +}); + +add_task(async function actions() { + await doActionsTest({ + trigger: async () => { + const onLoad = BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser); + doClickSubButton(".urlbarView-quickaction-row[data-key=addons]"); + await onLoad; + }, + assert: () => assertEngagementTelemetry([{ search_mode: "actions" }]), + }); +}); diff --git a/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_engagement_selected_result.js b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_engagement_selected_result.js new file mode 100644 index 0000000000..c19c511ccc --- /dev/null +++ b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_engagement_selected_result.js @@ -0,0 +1,920 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test for the following data of engagement telemetry. +// - selected_result +// - selected_result_subtype +// - provider +// - results + +// This test has many subtests and can time out in verify mode. +requestLongerTimeout(5); + +Services.scriptloader.loadSubScript( + "chrome://mochitests/content/browser/browser/components/urlbar/tests/ext/browser/head.js", + this +); + +add_setup(async function () { + await setup(); +}); + +add_task(async function selected_result_autofill_about() { + await doTest(async browser => { + await openPopup("about:about"); + await doEnter(); + + assertEngagementTelemetry([ + { + selected_result: "autofill_about", + selected_result_subtype: "", + provider: "Autofill", + results: "autofill_about", + }, + ]); + }); +}); + +add_task(async function selected_result_autofill_adaptive() { + await SpecialPowers.pushPrefEnv({ + set: [["browser.urlbar.autoFill.adaptiveHistory.enabled", true]], + }); + + await doTest(async browser => { + await PlacesTestUtils.addVisits("https://example.com/test"); + await UrlbarUtils.addToInputHistory("https://example.com/test", "exa"); + await openPopup("exa"); + await doEnter(); + + assertEngagementTelemetry([ + { + selected_result: "autofill_adaptive", + selected_result_subtype: "", + provider: "Autofill", + results: "autofill_adaptive", + }, + ]); + }); + + await SpecialPowers.popPrefEnv(); +}); + +add_task(async function selected_result_autofill_origin() { + await doTest(async browser => { + await PlacesTestUtils.addVisits("https://example.com/test"); + await openPopup("exa"); + await doEnter(); + + assertEngagementTelemetry([ + { + selected_result: "autofill_origin", + selected_result_subtype: "", + provider: "Autofill", + results: "autofill_origin,history", + }, + ]); + }); +}); + +add_task(async function selected_result_autofill_url() { + await doTest(async browser => { + await PlacesTestUtils.addVisits("https://example.com/test"); + await openPopup("https://example.com/test"); + await doEnter(); + + assertEngagementTelemetry([ + { + selected_result: "autofill_url", + selected_result_subtype: "", + provider: "Autofill", + results: "autofill_url", + }, + ]); + }); +}); + +add_task(async function selected_result_bookmark() { + await doTest(async browser => { + await PlacesUtils.bookmarks.insert({ + parentGuid: PlacesUtils.bookmarks.unfiledGuid, + url: "https://example.com/bookmark", + title: "bookmark", + }); + + await openPopup("bookmark"); + await selectRowByURL("https://example.com/bookmark"); + await doEnter(); + + assertEngagementTelemetry([ + { + selected_result: "bookmark", + selected_result_subtype: "", + provider: "Places", + results: "search_engine,action,bookmark", + }, + ]); + }); +}); + +add_task(async function selected_result_history() { + await SpecialPowers.pushPrefEnv({ + set: [["browser.urlbar.autoFill", false]], + }); + + await doTest(async browser => { + await PlacesTestUtils.addVisits("https://example.com/test"); + + await openPopup("example"); + await selectRowByURL("https://example.com/test"); + await doEnter(); + + assertEngagementTelemetry([ + { + selected_result: "history", + selected_result_subtype: "", + provider: "Places", + results: "search_engine,history", + }, + ]); + }); + + await SpecialPowers.popPrefEnv(); +}); + +add_task(async function selected_result_keyword() { + await doTest(async browser => { + await PlacesUtils.keywords.insert({ + keyword: "keyword", + url: "https://example.com/?q=%s", + }); + + await openPopup("keyword test"); + await doEnter(); + + assertEngagementTelemetry([ + { + selected_result: "keyword", + selected_result_subtype: "", + provider: "BookmarkKeywords", + results: "keyword", + }, + ]); + + await PlacesUtils.keywords.remove("keyword"); + }); +}); + +add_task(async function selected_result_search_engine() { + await doTest(async browser => { + await openPopup("x"); + await doEnter(); + + assertEngagementTelemetry([ + { + selected_result: "search_engine", + selected_result_subtype: "", + provider: "HeuristicFallback", + results: "search_engine", + }, + ]); + }); +}); + +add_task(async function selected_result_search_suggest() { + await SpecialPowers.pushPrefEnv({ + set: [ + ["browser.urlbar.suggest.searches", true], + ["browser.urlbar.maxHistoricalSearchSuggestions", 2], + ], + }); + + await doTest(async browser => { + await openPopup("foo"); + await selectRowByURL("http://mochi.test:8888/?terms=foofoo"); + await doEnter(); + + assertEngagementTelemetry([ + { + selected_result: "search_suggest", + selected_result_subtype: "", + provider: "SearchSuggestions", + results: "search_engine,search_suggest,search_suggest", + }, + ]); + }); + + await SpecialPowers.popPrefEnv(); +}); + +add_task(async function selected_result_search_history() { + await SpecialPowers.pushPrefEnv({ + set: [ + ["browser.urlbar.suggest.searches", true], + ["browser.urlbar.maxHistoricalSearchSuggestions", 2], + ], + }); + + await doTest(async browser => { + await UrlbarTestUtils.formHistory.add(["foofoo", "foobar"]); + + await openPopup("foo"); + await selectRowByURL("http://mochi.test:8888/?terms=foofoo"); + await doEnter(); + + assertEngagementTelemetry([ + { + selected_result: "search_history", + selected_result_subtype: "", + provider: "SearchSuggestions", + results: "search_engine,search_history,search_history", + }, + ]); + }); + + await SpecialPowers.popPrefEnv(); +}); + +add_task(async function selected_result_url() { + await doTest(async browser => { + await openPopup("https://example.com/"); + await doEnter(); + + assertEngagementTelemetry([ + { + selected_result: "url", + selected_result_subtype: "", + provider: "HeuristicFallback", + results: "url", + }, + ]); + }); +}); + +add_task(async function selected_result_action() { + await doTest(async browser => { + await showResultByArrowDown(); + await selectRowByProvider("quickactions"); + const onLoad = BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser); + doClickSubButton(".urlbarView-quickaction-row[data-key=addons]"); + await onLoad; + + assertEngagementTelemetry([ + { + selected_result: "action", + selected_result_subtype: "addons", + provider: "quickactions", + results: "action", + }, + ]); + }); +}); + +add_task(async function selected_result_tab() { + const tab = BrowserTestUtils.addTab(gBrowser, "https://example.com/"); + + await doTest(async browser => { + await openPopup("example"); + await selectRowByProvider("Places"); + EventUtils.synthesizeKey("KEY_Enter"); + await BrowserTestUtils.waitForCondition(() => gBrowser.selectedTab === tab); + + assertEngagementTelemetry([ + { + selected_result: "tab", + selected_result_subtype: "", + provider: "Places", + results: "search_engine,search_suggest,search_suggest,tab", + }, + ]); + }); + + BrowserTestUtils.removeTab(tab); +}); + +add_task(async function selected_result_remote_tab() { + const remoteTab = await loadRemoteTab("https://example.com"); + + await doTest(async browser => { + await openPopup("example"); + await selectRowByProvider("RemoteTabs"); + await doEnter(); + + assertEngagementTelemetry([ + { + selected_result: "remote_tab", + selected_result_subtype: "", + provider: "RemoteTabs", + results: "search_engine,remote_tab", + }, + ]); + }); + + await remoteTab.unload(); +}); + +add_task(async function selected_result_addon() { + const addon = loadOmniboxAddon({ keyword: "omni" }); + await addon.startup(); + + await doTest(async browser => { + await openPopup("omni test"); + await doEnter(); + + assertEngagementTelemetry([ + { + selected_result: "addon", + selected_result_subtype: "", + provider: "Omnibox", + results: "addon", + }, + ]); + }); + + await addon.unload(); +}); + +add_task(async function selected_result_tab_to_search() { + await SpecialPowers.pushPrefEnv({ + set: [["browser.urlbar.tabToSearch.onboard.interactionsLeft", 0]], + }); + + await SearchTestUtils.installSearchExtension({ + name: "mozengine", + search_url: "https://mozengine/", + }); + + await doTest(async browser => { + for (let i = 0; i < 3; i++) { + await PlacesTestUtils.addVisits(["https://mozengine/"]); + } + + await openPopup("moze"); + await selectRowByProvider("TabToSearch"); + const onComplete = UrlbarTestUtils.promiseSearchComplete(window); + EventUtils.synthesizeKey("KEY_Enter"); + await onComplete; + + assertEngagementTelemetry([ + { + selected_result: "tab_to_search", + selected_result_subtype: "", + provider: "TabToSearch", + results: "search_engine,tab_to_search,history", + }, + ]); + }); + + await SpecialPowers.popPrefEnv(); +}); + +add_task(async function selected_result_top_site() { + await doTest(async browser => { + await addTopSites("https://example.com/"); + await showResultByArrowDown(); + await selectRowByURL("https://example.com/"); + await doEnter(); + + assertEngagementTelemetry([ + { + selected_result: "top_site", + selected_result_subtype: "", + provider: "UrlbarProviderTopSites", + results: "top_site,action", + }, + ]); + }); +}); + +add_task(async function selected_result_calc() { + await SpecialPowers.pushPrefEnv({ + set: [["browser.urlbar.suggest.calculator", true]], + }); + + await doTest(async browser => { + await openPopup("8*8"); + await selectRowByProvider("calculator"); + await SimpleTest.promiseClipboardChange("64", () => { + EventUtils.synthesizeKey("KEY_Enter"); + }); + + assertEngagementTelemetry([ + { + selected_result: "calc", + selected_result_subtype: "", + provider: "calculator", + results: "search_engine,calc", + }, + ]); + }); + + await SpecialPowers.popPrefEnv(); +}); + +add_task(async function selected_result_unit() { + await SpecialPowers.pushPrefEnv({ + set: [["browser.urlbar.unitConversion.enabled", true]], + }); + + await doTest(async browser => { + await openPopup("1m to cm"); + await selectRowByProvider("UnitConversion"); + await SimpleTest.promiseClipboardChange("100 cm", () => { + EventUtils.synthesizeKey("KEY_Enter"); + }); + + assertEngagementTelemetry([ + { + selected_result: "unit", + selected_result_subtype: "", + provider: "UnitConversion", + results: "search_engine,unit", + }, + ]); + }); + + await SpecialPowers.popPrefEnv(); +}); + +add_task(async function selected_result_site_specific_contextual_search() { + await SpecialPowers.pushPrefEnv({ + set: [["browser.urlbar.contextualSearch.enabled", true]], + }); + + await doTest(async browser => { + const extension = await SearchTestUtils.installSearchExtension( + { + name: "Contextual", + search_url: "https://example.com/browser", + }, + { skipUnload: true } + ); + const onLoaded = BrowserTestUtils.browserLoaded( + gBrowser.selectedBrowser, + false, + "https://example.com/" + ); + BrowserTestUtils.loadURIString( + gBrowser.selectedBrowser, + "https://example.com/" + ); + await onLoaded; + + await openPopup("search"); + await selectRowByProvider("UrlbarProviderContextualSearch"); + await doEnter(); + + assertEngagementTelemetry([ + { + selected_result: "site_specific_contextual_search", + selected_result_subtype: "", + provider: "UrlbarProviderContextualSearch", + results: "search_engine,site_specific_contextual_search", + }, + ]); + + await extension.unload(); + }); + + await SpecialPowers.popPrefEnv(); +}); + +add_task(async function selected_result_experimental_addon() { + const extension = await loadExtension({ + background: async () => { + browser.experiments.urlbar.addDynamicResultType("testDynamicType"); + browser.experiments.urlbar.addDynamicViewTemplate("testDynamicType", { + children: [ + { + name: "text", + tag: "span", + attributes: { + role: "button", + }, + }, + ], + }); + browser.urlbar.onBehaviorRequested.addListener(query => { + return "active"; + }, "testProvider"); + browser.urlbar.onResultsRequested.addListener(query => { + return [ + { + type: "dynamic", + source: "local", + payload: { + dynamicType: "testDynamicType", + }, + }, + ]; + }, "testProvider"); + browser.experiments.urlbar.onViewUpdateRequested.addListener(payload => { + return { + text: { + textContent: "This is a dynamic result.", + }, + }; + }, "testProvider"); + }, + }); + + await TestUtils.waitForCondition( + () => + UrlbarProvidersManager.getProvider("testProvider") && + UrlbarResult.getDynamicResultType("testDynamicType"), + "Waiting for provider and dynamic type to be registered" + ); + + await doTest(async browser => { + await openPopup("test"); + EventUtils.synthesizeKey("KEY_ArrowDown"); + await UrlbarTestUtils.promisePopupClose(window, () => + EventUtils.synthesizeKey("KEY_Enter") + ); + + assertEngagementTelemetry([ + { + selected_result: "experimental_addon", + selected_result_subtype: "", + provider: "testProvider", + results: "search_engine,experimental_addon", + }, + ]); + }); + + await extension.unload(); +}); + +add_task(async function selected_result_adm_sponsored() { + const cleanupQuickSuggest = await ensureQuickSuggestInit(); + + await doTest(async browser => { + await openPopup("sponsored"); + await selectRowByURL("https://example.com/sponsored"); + await doEnter(); + + assertEngagementTelemetry([ + { + selected_result: "rs_adm_sponsored", + selected_result_subtype: "", + provider: "UrlbarProviderQuickSuggest", + results: "search_engine,rs_adm_sponsored", + }, + ]); + }); + + cleanupQuickSuggest(); +}); + +add_task(async function selected_result_adm_nonsponsored() { + const cleanupQuickSuggest = await ensureQuickSuggestInit(); + + await doTest(async browser => { + await openPopup("nonsponsored"); + await selectRowByURL("https://example.com/nonsponsored"); + await doEnter(); + + assertEngagementTelemetry([ + { + selected_result: "rs_adm_nonsponsored", + selected_result_subtype: "", + provider: "UrlbarProviderQuickSuggest", + results: "search_engine,rs_adm_nonsponsored", + }, + ]); + }); + + cleanupQuickSuggest(); +}); + +add_task(async function selected_result_input_field() { + const expected = [ + { + selected_result: "input_field", + selected_result_subtype: "", + provider: null, + results: "", + }, + ]; + + await doTest(async browser => { + await doDropAndGo("example.com"); + + assertEngagementTelemetry(expected); + }); + + await doTest(async browser => { + await doPasteAndGo("example.com"); + + assertEngagementTelemetry(expected); + }); +}); + +add_task(async function selected_result_weather() { + await SpecialPowers.pushPrefEnv({ + set: [["browser.urlbar.quickactions.enabled", false]], + }); + + const cleanupQuickSuggest = await ensureQuickSuggestInit(); + await MerinoTestUtils.initWeather(); + + await doTest(async browser => { + await openPopup(MerinoTestUtils.WEATHER_KEYWORD); + await selectRowByProvider("Weather"); + await doEnter(); + + assertEngagementTelemetry([ + { + selected_result: "weather", + selected_result_subtype: "", + provider: "Weather", + results: "search_engine,weather", + }, + ]); + }); + + cleanupQuickSuggest(); + await SpecialPowers.popPrefEnv(); +}); + +add_task(async function selected_result_navigational() { + const cleanupQuickSuggest = await ensureQuickSuggestInit({ + merinoSuggestions: [ + { + title: "Navigational suggestion", + url: "https://example.com/navigational-suggestion", + provider: "top_picks", + is_sponsored: false, + score: 0.25, + block_id: 0, + is_top_pick: true, + }, + ], + }); + + await doTest(async browser => { + await openPopup("only match the Merino suggestion"); + await selectRowByProvider("UrlbarProviderQuickSuggest"); + await doEnter(); + + assertEngagementTelemetry([ + { + selected_result: "merino_top_picks", + selected_result_subtype: "", + provider: "UrlbarProviderQuickSuggest", + results: "search_engine,merino_top_picks", + }, + ]); + }); + + cleanupQuickSuggest(); +}); + +add_task(async function selected_result_dynamic_wikipedia() { + const cleanupQuickSuggest = await ensureQuickSuggestInit({ + merinoSuggestions: [ + { + block_id: 1, + url: "https://example.com/dynamic-wikipedia", + title: "Dynamic Wikipedia suggestion", + click_url: "https://example.com/click", + impression_url: "https://example.com/impression", + advertiser: "dynamic-wikipedia", + provider: "wikipedia", + iab_category: "5 - Education", + }, + ], + }); + + await doTest(async browser => { + await openPopup("only match the Merino suggestion"); + await selectRowByProvider("UrlbarProviderQuickSuggest"); + await doEnter(); + + assertEngagementTelemetry([ + { + selected_result: "merino_wikipedia", + selected_result_subtype: "", + provider: "UrlbarProviderQuickSuggest", + results: "search_engine,merino_wikipedia", + }, + ]); + }); + + cleanupQuickSuggest(); +}); + +add_task(async function selected_result_search_shortcut_button() { + await doTest(async browser => { + const oneOffSearchButtons = UrlbarTestUtils.getOneOffSearchButtons(window); + await openPopup("x"); + Assert.ok(!oneOffSearchButtons.selectedButton); + + // Select oneoff button added for test in setup(). + for (;;) { + EventUtils.synthesizeKey("KEY_ArrowDown"); + if (!oneOffSearchButtons.selectedButton) { + continue; + } + + if ( + oneOffSearchButtons.selectedButton.engine.name.includes( + "searchSuggestionEngine.xml" + ) + ) { + break; + } + } + + // Search immediately. + await doEnter({ shiftKey: true }); + + assertEngagementTelemetry([ + { + selected_result: "search_shortcut_button", + selected_result_subtype: "", + provider: null, + results: "search_engine", + }, + ]); + }); +}); + +add_task(async function selected_result_trending() { + await SpecialPowers.pushPrefEnv({ + set: [ + ["browser.urlbar.suggest.searches", true], + ["browser.urlbar.trending.featureGate", true], + ["browser.urlbar.trending.requireSearchMode", false], + ["browser.urlbar.trending.maxResultsNoSearchMode", 1], + ["browser.urlbar.weather.featureGate", false], + ], + }); + + let defaultEngine = await Services.search.getDefault(); + let extension = await SearchTestUtils.installSearchExtension( + { + name: "mozengine", + search_url: "https://example.org/", + }, + { setAsDefault: true, skipUnload: true } + ); + + SearchTestUtils.useMockIdleService(); + await SearchTestUtils.updateRemoteSettingsConfig([ + { + webExtension: { id: "mozengine@tests.mozilla.org" }, + urls: { + trending: { + fullPath: + "https://example.com/browser/browser/components/search/test/browser/trendingSuggestionEngine.sjs", + query: "", + }, + }, + appliesTo: [{ included: { everywhere: true } }], + default: "yes", + }, + ]); + + await doTest(async browser => { + await openPopup(""); + await selectRowByProvider("SearchSuggestions"); + await doEnter(); + + assertEngagementTelemetry([ + { + selected_result: "trending_search", + selected_result_subtype: "", + provider: "SearchSuggestions", + results: "trending_search", + }, + ]); + }); + + await extension.unload(); + await Services.search.setDefault( + defaultEngine, + Ci.nsISearchService.CHANGE_REASON_UNKNOWN + ); + let settingsWritten = SearchTestUtils.promiseSearchNotification( + "write-settings-to-disk-complete" + ); + await SearchTestUtils.updateRemoteSettingsConfig(); + await settingsWritten; + await PlacesUtils.history.clear(); + await SpecialPowers.popPrefEnv(); +}); + +add_task(async function selected_result_trending_rich() { + await SpecialPowers.pushPrefEnv({ + set: [ + ["browser.urlbar.richSuggestions.featureGate", true], + ["browser.urlbar.suggest.searches", true], + ["browser.urlbar.trending.featureGate", true], + ["browser.urlbar.trending.requireSearchMode", false], + ["browser.urlbar.trending.maxResultsNoSearchMode", 1], + ["browser.urlbar.weather.featureGate", false], + ], + }); + + let defaultEngine = await Services.search.getDefault(); + let extension = await SearchTestUtils.installSearchExtension( + { + name: "mozengine", + search_url: "https://example.org/", + }, + { setAsDefault: true, skipUnload: true } + ); + + SearchTestUtils.useMockIdleService(); + await SearchTestUtils.updateRemoteSettingsConfig([ + { + webExtension: { id: "mozengine@tests.mozilla.org" }, + urls: { + trending: { + fullPath: + "https://example.com/browser/browser/components/search/test/browser/trendingSuggestionEngine.sjs?richsuggestions=true", + query: "", + }, + }, + appliesTo: [{ included: { everywhere: true } }], + default: "yes", + }, + ]); + + await doTest(async browser => { + await openPopup(""); + await selectRowByProvider("SearchSuggestions"); + await doEnter(); + + assertEngagementTelemetry([ + { + selected_result: "trending_search_rich", + selected_result_subtype: "", + provider: "SearchSuggestions", + results: "trending_search_rich", + }, + ]); + }); + + await extension.unload(); + await Services.search.setDefault( + defaultEngine, + Ci.nsISearchService.CHANGE_REASON_UNKNOWN + ); + let settingsWritten = SearchTestUtils.promiseSearchNotification( + "write-settings-to-disk-complete" + ); + await SearchTestUtils.updateRemoteSettingsConfig(); + await settingsWritten; + await PlacesUtils.history.clear(); + await SpecialPowers.popPrefEnv(); +}); + +add_task(async function selected_result_addons() { + await SpecialPowers.pushPrefEnv({ + set: [ + ["browser.urlbar.addons.featureGate", true], + ["browser.urlbar.suggest.searches", false], + ], + }); + + const cleanupQuickSuggest = await ensureQuickSuggestInit({ + merinoSuggestions: [ + { + provider: "amo", + icon: "https://example.com/good-addon.svg", + url: "https://example.com/good-addon", + title: "Good Addon", + description: "This is a good addon", + custom_details: { + amo: { + rating: "4.8", + number_of_ratings: "1234567", + guid: "good@addon", + }, + }, + is_top_pick: true, + }, + ], + }); + + await doTest(async browser => { + await openPopup("only match the Merino suggestion"); + await selectRowByProvider("UrlbarProviderQuickSuggest"); + await doEnter(); + + assertEngagementTelemetry([ + { + selected_result: "merino_amo", + selected_result_subtype: "", + provider: "UrlbarProviderQuickSuggest", + results: "search_engine,merino_amo", + }, + ]); + }); + + cleanupQuickSuggest(); + await SpecialPowers.popPrefEnv(); +}); diff --git a/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_engagement_tips.js b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_engagement_tips.js new file mode 100644 index 0000000000..104e292788 --- /dev/null +++ b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_engagement_tips.js @@ -0,0 +1,175 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test for engagement telemetry for tips using Glean. + +Services.scriptloader.loadSubScript( + "chrome://mochitests/content/browser/browser/components/urlbar/tests/browser-tips/head.js", + this +); + +ChromeUtils.defineESModuleGetters(this, { + PromiseUtils: "resource://gre/modules/PromiseUtils.sys.mjs", +}); + +add_setup(async function () { + makeProfileResettable(); + + Services.fog.setMetricsFeatureConfig( + JSON.stringify({ "urlbar.engagement": false }) + ); + await SpecialPowers.pushPrefEnv({ + set: [["browser.urlbar.quickactions.enabled", false]], + }); + + registerCleanupFunction(async function () { + Services.fog.setMetricsFeatureConfig("{}"); + await SpecialPowers.popPrefEnv(); + }); +}); + +add_task(async function selected_result_tip() { + const testData = [ + { + type: "searchTip_onboard", + expected: "tip_onboard", + }, + { + type: "searchTip_persist", + expected: "tip_persist", + }, + { + type: "searchTip_redirect", + expected: "tip_redirect", + }, + { + type: "test", + expected: "tip_unknown", + }, + ]; + + for (const { type, expected } of testData) { + const deferred = PromiseUtils.defer(); + const provider = new UrlbarTestUtils.TestProvider({ + results: [ + new UrlbarResult( + UrlbarUtils.RESULT_TYPE.TIP, + UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL, + { + type, + helpUrl: "https://example.com/", + titleL10n: { id: "urlbar-search-tips-confirm" }, + buttons: [ + { + url: "https://example.com/", + l10n: { id: "urlbar-search-tips-confirm" }, + }, + ], + } + ), + ], + priority: 1, + onEngagement: () => { + deferred.resolve(); + }, + }); + UrlbarProvidersManager.registerProvider(provider); + + await doTest(async browser => { + await openPopup("example"); + await selectRowByType(type); + EventUtils.synthesizeKey("VK_RETURN"); + await deferred.promise; + + assertEngagementTelemetry([ + { + selected_result: expected, + results: expected, + }, + ]); + }); + + UrlbarProvidersManager.unregisterProvider(provider); + } +}); + +add_task(async function selected_result_intervention_clear() { + await doInterventionTest( + SEARCH_STRINGS.CLEAR, + "intervention_clear", + "chrome://browser/content/sanitize.xhtml", + [ + { + selected_result: "intervention_clear", + results: "search_engine,intervention_clear", + }, + ] + ); +}); + +add_task(async function selected_result_intervention_refresh() { + await doInterventionTest( + SEARCH_STRINGS.REFRESH, + "intervention_refresh", + "chrome://global/content/resetProfile.xhtml", + [ + { + selected_result: "intervention_refresh", + results: "search_engine,intervention_refresh", + }, + ] + ); +}); + +add_task(async function selected_result_intervention_update() { + // Updates are disabled for MSIX packages, this test is irrelevant for them. + if ( + AppConstants.platform === "win" && + Services.sysinfo.getProperty("hasWinPackageId") + ) { + return; + } + await UpdateUtils.setAppUpdateAutoEnabled(false); + await initUpdate({ queryString: "&noUpdates=1" }); + UrlbarProviderInterventions.checkForBrowserUpdate(true); + await processUpdateSteps([ + { + panelId: "checkingForUpdates", + checkActiveUpdate: null, + continueFile: CONTINUE_CHECK, + }, + { + panelId: "noUpdatesFound", + checkActiveUpdate: null, + continueFile: null, + }, + ]); + + await doInterventionTest( + SEARCH_STRINGS.UPDATE, + "intervention_update_refresh", + "chrome://global/content/resetProfile.xhtml", + [ + { + selected_result: "intervention_update", + results: "search_engine,intervention_update", + }, + ] + ); +}); + +async function doInterventionTest(keyword, type, dialog, expectedTelemetry) { + await doTest(async browser => { + await openPopup(keyword); + await selectRowByType(type); + const onDialog = BrowserTestUtils.promiseAlertDialog("cancel", dialog, { + isSubDialog: true, + }); + EventUtils.synthesizeKey("VK_RETURN"); + await onDialog; + + assertEngagementTelemetry(expectedTelemetry); + }); +} diff --git a/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_engagement_type.js b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_engagement_type.js new file mode 100644 index 0000000000..b2b2233b52 --- /dev/null +++ b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_engagement_type.js @@ -0,0 +1,151 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test for the following data of engagement telemetry. +// - engagement_type + +add_setup(async function () { + await setup(); +}); + +add_task(async function engagement_type_click() { + await doTest(async browser => { + await openPopup("x"); + await doClick(); + + assertEngagementTelemetry([{ engagement_type: "click" }]); + }); +}); + +add_task(async function engagement_type_enter() { + await doTest(async browser => { + await openPopup("x"); + await doEnter(); + + assertEngagementTelemetry([{ engagement_type: "enter" }]); + }); +}); + +add_task(async function engagement_type_go_button() { + await doTest(async browser => { + await openPopup("x"); + EventUtils.synthesizeMouseAtCenter(gURLBar.goButton, {}); + + assertEngagementTelemetry([{ engagement_type: "go_button" }]); + }); +}); + +add_task(async function engagement_type_drop_go() { + await doTest(async browser => { + await doDropAndGo("example.com"); + + assertEngagementTelemetry([{ engagement_type: "drop_go" }]); + }); +}); + +add_task(async function engagement_type_paste_go() { + await doTest(async browser => { + await doPasteAndGo("www.example.com"); + + assertEngagementTelemetry([{ engagement_type: "paste_go" }]); + }); +}); + +add_task(async function engagement_type_dismiss() { + const cleanupQuickSuggest = await ensureQuickSuggestInit({ + // eslint-disable-next-line mozilla/valid-lazy + config: lazy.QuickSuggestTestUtils.BEST_MATCH_CONFIG, + }); + + for (const isBestMatchTest of [true, false]) { + const prefs = isBestMatchTest + ? [ + ["browser.urlbar.bestMatch.enabled", true], + ["browser.urlbar.bestMatch.blockingEnabled", true], + ["browser.urlbar.quicksuggest.blockingEnabled", false], + ] + : [ + ["browser.urlbar.bestMatch.enabled", false], + ["browser.urlbar.bestMatch.blockingEnabled", false], + ["browser.urlbar.quicksuggest.blockingEnabled", true], + ]; + await SpecialPowers.pushPrefEnv({ set: prefs }); + + await doTest(async browser => { + await openPopup("sponsored"); + + const originalResultCount = UrlbarTestUtils.getResultCount(window); + await selectRowByURL("https://example.com/sponsored"); + if (UrlbarPrefs.get("resultMenu")) { + UrlbarTestUtils.openResultMenuAndPressAccesskey(window, "D"); + } else { + doClickSubButton(".urlbarView-button-block"); + } + await BrowserTestUtils.waitForCondition( + () => originalResultCount != UrlbarTestUtils.getResultCount(window) + ); + + assertEngagementTelemetry([{ engagement_type: "dismiss" }]); + + // The view should stay open after dismissing the result. Now pick the + // heuristic result. Another "click" engagement event should be recorded. + Assert.ok( + gURLBar.view.isOpen, + "View should remain open after dismissing result" + ); + await doClick(); + assertEngagementTelemetry([ + { engagement_type: "dismiss" }, + { engagement_type: "click", interaction: "typed" }, + ]); + }); + + await doTest(async browser => { + await openPopup("sponsored"); + + const originalResultCount = UrlbarTestUtils.getResultCount(window); + await selectRowByURL("https://example.com/sponsored"); + EventUtils.synthesizeKey("KEY_Delete", { shiftKey: true }); + await BrowserTestUtils.waitForCondition( + () => originalResultCount != UrlbarTestUtils.getResultCount(window) + ); + + assertEngagementTelemetry([{ engagement_type: "dismiss" }]); + }); + + await SpecialPowers.popPrefEnv(); + } + + cleanupQuickSuggest(); +}); + +add_task(async function engagement_type_help() { + const cleanupQuickSuggest = await ensureQuickSuggestInit(); + + for (const isBestMatchTest of [true, false]) { + await SpecialPowers.pushPrefEnv({ + set: [["browser.urlbar.bestMatch.enabled", isBestMatchTest]], + }); + + await doTest(async browser => { + await openPopup("sponsored"); + await selectRowByURL("https://example.com/sponsored"); + const onTabOpened = BrowserTestUtils.waitForNewTab(gBrowser); + if (UrlbarPrefs.get("resultMenu")) { + UrlbarTestUtils.openResultMenuAndPressAccesskey(window, "L"); + } else { + doClickSubButton(".urlbarView-button-help"); + } + const tab = await onTabOpened; + BrowserTestUtils.removeTab(tab); + + assertEngagementTelemetry([{ engagement_type: "help" }]); + }); + + await SpecialPowers.popPrefEnv(); + } + + cleanupQuickSuggest(); +}); diff --git a/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_exposure.js b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_exposure.js new file mode 100644 index 0000000000..7529f29455 --- /dev/null +++ b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_exposure.js @@ -0,0 +1,107 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const SPONSORED_QUERY = "sponsored"; +const NONSPONSORED_QUERY = "nonsponsored"; + +// test for exposure events +add_setup(async function () { + await initExposureTest(); +}); + +add_task(async function exposureSponsoredOnEngagement() { + await doExposureTest({ + prefs: [ + ["browser.urlbar.exposureResults", "rs_adm_sponsored"], + ["browser.urlbar.showExposureResults", true], + ], + query: SPONSORED_QUERY, + trigger: () => doClick(), + assert: () => assertExposureTelemetry([{ results: "rs_adm_sponsored" }]), + }); +}); + +add_task(async function exposureSponsoredOnAbandonment() { + await doExposureTest({ + prefs: [ + ["browser.urlbar.exposureResults", "rs_adm_sponsored"], + ["browser.urlbar.showExposureResults", true], + ], + query: SPONSORED_QUERY, + trigger: () => doBlur(), + assert: () => assertExposureTelemetry([{ results: "rs_adm_sponsored" }]), + }); +}); + +add_task(async function exposureFilter() { + await doExposureTest({ + prefs: [ + ["browser.urlbar.exposureResults", "rs_adm_sponsored"], + ["browser.urlbar.showExposureResults", false], + ], + query: SPONSORED_QUERY, + select: async () => { + // assert that the urlbar has no results + Assert.equal(await getResultByType("rs_adm_sponsored"), null); + }, + trigger: () => doBlur(), + assert: () => assertExposureTelemetry([{ results: "rs_adm_sponsored" }]), + }); +}); + +add_task(async function innerQueryExposure() { + await doExposureTest({ + prefs: [ + ["browser.urlbar.exposureResults", "rs_adm_sponsored"], + ["browser.urlbar.showExposureResults", true], + ], + query: NONSPONSORED_QUERY, + select: () => {}, + trigger: async () => { + // delete the old query + gURLBar.select(); + EventUtils.synthesizeKey("KEY_Backspace"); + await openPopup(SPONSORED_QUERY); + await defaultSelect(SPONSORED_QUERY); + await doClick(); + }, + assert: () => assertExposureTelemetry([{ results: "rs_adm_sponsored" }]), + }); +}); + +add_task(async function innerQueryInvertedExposure() { + await doExposureTest({ + prefs: [ + ["browser.urlbar.exposureResults", "rs_adm_sponsored"], + ["browser.urlbar.showExposureResults", true], + ], + query: SPONSORED_QUERY, + select: () => {}, + trigger: async () => { + // delete the old query + gURLBar.select(); + EventUtils.synthesizeKey("KEY_Backspace"); + await openPopup(NONSPONSORED_QUERY); + await defaultSelect(SPONSORED_QUERY); + await doClick(); + }, + assert: () => assertExposureTelemetry([{ results: "rs_adm_sponsored" }]), + }); +}); + +add_task(async function multipleProviders() { + await doExposureTest({ + prefs: [ + [ + "browser.urlbar.exposureResults", + "rs_adm_sponsored,rs_adm_nonsponsored", + ], + ["browser.urlbar.showExposureResults", true], + ], + query: NONSPONSORED_QUERY, + trigger: () => doClick(), + assert: () => assertExposureTelemetry([{ results: "rs_adm_nonsponsored" }]), + }); +}); diff --git a/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_impression_groups.js b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_impression_groups.js new file mode 100644 index 0000000000..199460e312 --- /dev/null +++ b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_impression_groups.js @@ -0,0 +1,223 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test for the following data of impression telemetry. +// - groups +// - results +// - n_results + +add_setup(async function () { + await initGroupTest(); + // Increase the pausing time to ensure to ready for all suggestions. + await SpecialPowers.pushPrefEnv({ + set: [ + [ + "browser.urlbar.searchEngagementTelemetry.pauseImpressionIntervalMs", + 500, + ], + ], + }); +}); + +add_task(async function heuristics() { + await doHeuristicsTest({ + trigger: () => waitForPauseImpression(), + assert: () => + assertImpressionTelemetry([ + { reason: "pause", groups: "heuristic", results: "search_engine" }, + ]), + }); +}); + +add_task(async function adaptive_history() { + await doAdaptiveHistoryTest({ + trigger: () => waitForPauseImpression(), + assert: () => + assertImpressionTelemetry([ + { + reason: "pause", + groups: "heuristic,adaptive_history", + results: "search_engine,history", + n_results: 2, + }, + ]), + }); +}); + +add_task(async function search_history() { + await doSearchHistoryTest({ + trigger: () => waitForPauseImpression(), + assert: () => + assertImpressionTelemetry([ + { + reason: "pause", + groups: "heuristic,search_history,search_history", + results: "search_engine,search_history,search_history", + n_results: 3, + }, + ]), + }); +}); + +add_task(async function search_suggest() { + await doSearchSuggestTest({ + trigger: () => waitForPauseImpression(), + assert: () => + assertImpressionTelemetry([ + { + reason: "pause", + groups: "heuristic,search_suggest,search_suggest", + results: "search_engine,search_suggest,search_suggest", + n_results: 3, + }, + ]), + }); + + await doTailSearchSuggestTest({ + trigger: () => waitForPauseImpression(), + assert: () => + assertImpressionTelemetry([ + { + reason: "pause", + groups: "heuristic,search_suggest", + results: "search_engine,search_suggest", + n_results: 2, + }, + ]), + }); +}); + +add_task(async function top_pick() { + await doTopPickTest({ + trigger: () => waitForPauseImpression(), + assert: () => + assertImpressionTelemetry([ + { + reason: "pause", + groups: "heuristic,top_pick,search_suggest,search_suggest", + results: + "search_engine,rs_adm_sponsored,search_suggest,search_suggest", + n_results: 4, + }, + ]), + }); +}); + +add_task(async function top_site() { + await doTopSiteTest({ + trigger: () => waitForPauseImpression(), + assert: () => + assertImpressionTelemetry([ + { + reason: "pause", + groups: "top_site,suggested_index", + results: "top_site,action", + n_results: 2, + }, + ]), + }); +}); + +add_task(async function remote_tab() { + await doRemoteTabTest({ + trigger: () => waitForPauseImpression(), + assert: () => + assertImpressionTelemetry([ + { + reason: "pause", + groups: "heuristic,remote_tab", + results: "search_engine,remote_tab", + n_results: 2, + }, + ]), + }); +}); + +add_task(async function addon() { + await doAddonTest({ + trigger: () => waitForPauseImpression(), + assert: () => + assertImpressionTelemetry([ + { + reason: "pause", + groups: "addon", + results: "addon", + n_results: 1, + }, + ]), + }); +}); + +add_task(async function general() { + await doGeneralBookmarkTest({ + trigger: () => waitForPauseImpression(), + assert: () => + assertImpressionTelemetry([ + { + reason: "pause", + groups: "heuristic,suggested_index,general", + results: "search_engine,action,bookmark", + n_results: 3, + }, + ]), + }); + + await doGeneralHistoryTest({ + trigger: () => waitForPauseImpression(), + assert: () => + assertImpressionTelemetry([ + { + reason: "pause", + groups: "heuristic,general", + results: "search_engine,history", + n_results: 2, + }, + ]), + }); +}); + +add_task(async function suggest() { + await doSuggestTest({ + trigger: () => waitForPauseImpression(), + assert: () => + assertImpressionTelemetry([ + { + groups: "heuristic,suggest", + results: "search_engine,rs_adm_nonsponsored", + n_results: 2, + }, + ]), + }); +}); + +add_task(async function about_page() { + await doAboutPageTest({ + trigger: () => waitForPauseImpression(), + assert: () => + assertImpressionTelemetry([ + { + reason: "pause", + groups: "heuristic,about_page,about_page", + results: "search_engine,history,history", + n_results: 3, + }, + ]), + }); +}); + +add_task(async function suggested_index() { + await doSuggestedIndexTest({ + trigger: () => waitForPauseImpression(), + assert: () => + assertImpressionTelemetry([ + { + reason: "pause", + groups: "heuristic,suggested_index", + results: "search_engine,unit", + n_results: 2, + }, + ]), + }); +}); diff --git a/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_impression_interaction.js b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_impression_interaction.js new file mode 100644 index 0000000000..f783bf766c --- /dev/null +++ b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_impression_interaction.js @@ -0,0 +1,63 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test for the following data of impression telemetry. +// - interaction + +add_setup(async function () { + await initInteractionTest(); +}); + +add_task(async function topsites() { + await doTopsitesTest({ + trigger: () => waitForPauseImpression(), + assert: () => + assertImpressionTelemetry([{ reason: "pause", interaction: "topsites" }]), + }); +}); + +add_task(async function typed() { + await doTypedTest({ + trigger: () => waitForPauseImpression(), + assert: () => + assertImpressionTelemetry([{ reason: "pause", interaction: "typed" }]), + }); + + await doTypedWithResultsPopupTest({ + trigger: () => waitForPauseImpression(), + assert: () => + assertImpressionTelemetry([{ reason: "pause", interaction: "typed" }]), + }); +}); + +add_task(async function pasted() { + await doPastedTest({ + trigger: () => waitForPauseImpression(), + assert: () => + assertImpressionTelemetry([{ reason: "pause", interaction: "pasted" }]), + }); + + await doPastedWithResultsPopupTest({ + trigger: () => waitForPauseImpression(), + assert: () => + assertImpressionTelemetry([{ reason: "pause", interaction: "pasted" }]), + }); +}); + +add_task(async function topsite_search() { + // TODO: https://bugzilla.mozilla.org/show_bug.cgi?id=1804010 + // assertImpressionTelemetry([{ interaction: "topsite_search" }]); +}); + +add_task(async function returned_restarted_refined() { + await doReturnedRestartedRefinedTest({ + trigger: () => waitForPauseImpression(), + assert: expected => + assertImpressionTelemetry([ + { reason: "pause" }, + { reason: "pause", interaction: expected }, + ]), + }); +}); diff --git a/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_impression_interaction_persisted_search_terms_disabled.js b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_impression_interaction_persisted_search_terms_disabled.js new file mode 100644 index 0000000000..af7134b3a0 --- /dev/null +++ b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_impression_interaction_persisted_search_terms_disabled.js @@ -0,0 +1,57 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test impression telemetry with persisted search terms disabled. + +// 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 initInteractionTest(); + + await SpecialPowers.pushPrefEnv({ + set: [["browser.urlbar.showSearchTerms.featureGate", false]], + }); +}); + +add_task(async function persisted_search_terms() { + await doPersistedSearchTermsTest({ + trigger: () => waitForPauseImpression(), + assert: () => + assertImpressionTelemetry([ + { reason: "pause" }, + { reason: "pause", interaction: "typed" }, + ]), + }); +}); + +add_task(async function persisted_search_terms_restarted_refined() { + await doPersistedSearchTermsRestartedRefinedTest({ + enabled: false, + trigger: () => waitForPauseImpression(), + assert: expected => + assertImpressionTelemetry([ + { reason: "pause" }, + { reason: "pause", interaction: expected }, + ]), + }); +}); + +add_task( + async function persisted_search_terms_restarted_refined_via_abandonment() { + await doPersistedSearchTermsRestartedRefinedViaAbandonmentTest({ + enabled: false, + trigger: () => waitForPauseImpression(), + assert: expected => + assertImpressionTelemetry([ + { reason: "pause" }, + { reason: "pause" }, + { reason: "pause", interaction: expected }, + ]), + }); + } +); diff --git a/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_impression_interaction_persisted_search_terms_enabled.js b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_impression_interaction_persisted_search_terms_enabled.js new file mode 100644 index 0000000000..a29ff98b78 --- /dev/null +++ b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_impression_interaction_persisted_search_terms_enabled.js @@ -0,0 +1,61 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test impression telemetry with persisted search terms enabled. + +// 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 initInteractionTest(); + + await SpecialPowers.pushPrefEnv({ + set: [ + ["browser.urlbar.showSearchTerms.featureGate", true], + ["browser.urlbar.showSearchTerms.enabled", true], + ["browser.search.widget.inNavBar", false], + ], + }); +}); + +add_task(async function interaction_persisted_search_terms() { + await doPersistedSearchTermsTest({ + trigger: () => waitForPauseImpression(), + assert: () => + assertImpressionTelemetry([ + { reason: "pause" }, + { reason: "pause", interaction: "persisted_search_terms" }, + ]), + }); +}); + +add_task(async function interaction_persisted_search_terms_restarted_refined() { + await doPersistedSearchTermsRestartedRefinedTest({ + enabled: true, + trigger: () => waitForPauseImpression(), + assert: expected => + assertImpressionTelemetry([ + { reason: "pause" }, + { reason: "pause", interaction: expected }, + ]), + }); +}); + +add_task( + async function interaction_persisted_search_terms_restarted_refined_via_abandonment() { + await doPersistedSearchTermsRestartedRefinedViaAbandonmentTest({ + enabled: true, + trigger: () => waitForPauseImpression(), + assert: expected => + assertImpressionTelemetry([ + { reason: "pause" }, + { reason: "pause" }, + { reason: "pause", interaction: expected }, + ]), + }); + } +); diff --git a/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_impression_n_chars_n_words.js b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_impression_n_chars_n_words.js new file mode 100644 index 0000000000..528cc318e0 --- /dev/null +++ b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_impression_n_chars_n_words.js @@ -0,0 +1,40 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test for the following data of impression telemetry. +// - n_chars +// - n_words + +add_setup(async function () { + await initNCharsAndNWordsTest(); +}); + +add_task(async function n_chars() { + await doNCharsTest({ + trigger: () => waitForPauseImpression(), + assert: nChars => + assertImpressionTelemetry([{ reason: "pause", n_chars: nChars }]), + }); + + await doNCharsWithOverMaxTextLengthCharsTest({ + trigger: () => waitForPauseImpression(), + assert: nChars => + assertImpressionTelemetry([{ reason: "pause", n_chars: nChars }]), + }); +}); + +add_task(async function n_words() { + await doNWordsTest({ + trigger: () => waitForPauseImpression(), + assert: nWords => + assertImpressionTelemetry([{ reason: "pause", n_words: nWords }]), + }); + + await doNWordsWithOverMaxTextLengthCharsTest({ + trigger: () => waitForPauseImpression(), + assert: nWords => + assertImpressionTelemetry([{ reason: "pause", n_words: nWords }]), + }); +}); diff --git a/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_impression_preferences.js b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_impression_preferences.js new file mode 100644 index 0000000000..344e238e24 --- /dev/null +++ b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_impression_preferences.js @@ -0,0 +1,41 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test the impression telemetry behavior with its preferences. + +add_setup(async function () { + await setup(); +}); + +add_task(async function pauseImpressionIntervalMs() { + const additionalInterval = 1000; + const originalInterval = UrlbarPrefs.get( + "searchEngagementTelemetry.pauseImpressionIntervalMs" + ); + await SpecialPowers.pushPrefEnv({ + set: [ + [ + "browser.urlbar.searchEngagementTelemetry.pauseImpressionIntervalMs", + originalInterval + additionalInterval, + ], + ], + }); + + await doTest(async browser => { + await openPopup("https://example.com"); + + // eslint-disable-next-line mozilla/no-arbitrary-setTimeout + await new Promise(r => setTimeout(r, originalInterval)); + await Services.fog.testFlushAllChildren(); + assertImpressionTelemetry([]); + + // eslint-disable-next-line mozilla/no-arbitrary-setTimeout + await new Promise(r => setTimeout(r, additionalInterval)); + await Services.fog.testFlushAllChildren(); + assertImpressionTelemetry([{ sap: "urlbar_newtab" }]); + }); + + await SpecialPowers.popPrefEnv(); +}); diff --git a/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_impression_sap.js b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_impression_sap.js new file mode 100644 index 0000000000..482b906024 --- /dev/null +++ b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_impression_sap.js @@ -0,0 +1,38 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test for the following data of impression telemetry. +// - sap + +add_setup(async function () { + await initSapTest(); +}); + +add_task(async function urlbar() { + await doUrlbarTest({ + trigger: () => waitForPauseImpression(), + assert: () => + assertImpressionTelemetry([ + { reason: "pause", sap: "urlbar_newtab" }, + { reason: "pause", sap: "urlbar" }, + ]), + }); +}); + +add_task(async function handoff() { + await doHandoffTest({ + trigger: () => waitForPauseImpression(), + assert: () => + assertImpressionTelemetry([{ reason: "pause", sap: "handoff" }]), + }); +}); + +add_task(async function urlbar_addonpage() { + await doUrlbarAddonpageTest({ + trigger: () => waitForPauseImpression(), + assert: () => + assertImpressionTelemetry([{ reason: "pause", sap: "urlbar_addonpage" }]), + }); +}); diff --git a/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_impression_search_mode.js b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_impression_search_mode.js new file mode 100644 index 0000000000..727afa3cef --- /dev/null +++ b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_impression_search_mode.js @@ -0,0 +1,72 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test for the following data of impression telemetry. +// - search_mode + +add_setup(async function () { + await initSearchModeTest(); + // Increase the pausing time to ensure entering search mode. + await SpecialPowers.pushPrefEnv({ + set: [ + [ + "browser.urlbar.searchEngagementTelemetry.pauseImpressionIntervalMs", + 1000, + ], + ], + }); +}); + +add_task(async function not_search_mode() { + await doNotSearchModeTest({ + trigger: () => waitForPauseImpression(), + assert: () => + assertImpressionTelemetry([{ reason: "pause", search_mode: "" }]), + }); +}); + +add_task(async function search_engine() { + await doSearchEngineTest({ + trigger: () => waitForPauseImpression(), + assert: () => + assertImpressionTelemetry([ + { reason: "pause", search_mode: "search_engine" }, + ]), + }); +}); + +add_task(async function bookmarks() { + await doBookmarksTest({ + trigger: () => waitForPauseImpression(), + assert: () => + assertImpressionTelemetry([ + { reason: "pause", search_mode: "bookmarks" }, + ]), + }); +}); + +add_task(async function history() { + await doHistoryTest({ + trigger: () => waitForPauseImpression(), + assert: () => + assertImpressionTelemetry([{ reason: "pause", search_mode: "history" }]), + }); +}); + +add_task(async function tabs() { + await doTabTest({ + trigger: () => waitForPauseImpression(), + assert: () => + assertImpressionTelemetry([{ reason: "pause", search_mode: "tabs" }]), + }); +}); + +add_task(async function actions() { + await doActionsTest({ + trigger: () => waitForPauseImpression(), + assert: () => + assertImpressionTelemetry([{ reason: "pause", search_mode: "actions" }]), + }); +}); diff --git a/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_impression_timing.js b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_impression_timing.js new file mode 100644 index 0000000000..31f64996f3 --- /dev/null +++ b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_impression_timing.js @@ -0,0 +1,91 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test for the taking timing for the impression telemetry. + +add_setup(async function () { + await setup(); +}); + +add_task(async function cancelImpressionTimerByEngagementEvent() { + const additionalInterval = 1000; + const originalInterval = UrlbarPrefs.get( + "searchEngagementTelemetry.pauseImpressionIntervalMs" + ); + await SpecialPowers.pushPrefEnv({ + set: [ + [ + "browser.urlbar.searchEngagementTelemetry.pauseImpressionIntervalMs", + originalInterval + additionalInterval, + ], + ], + }); + + for (const trigger of [doEnter, doBlur]) { + await doTest(async browser => { + await openPopup("https://example.com"); + await trigger(); + + // Check whether the impression timer was canceled. + await new Promise(r => + // eslint-disable-next-line mozilla/no-arbitrary-setTimeout + setTimeout(r, originalInterval + additionalInterval) + ); + assertImpressionTelemetry([]); + }); + } + + await SpecialPowers.popPrefEnv(); +}); + +add_task(async function cancelInpressionTimerByType() { + const originalInterval = UrlbarPrefs.get( + "searchEngagementTelemetry.pauseImpressionIntervalMs" + ); + + await doTest(async browser => { + await openPopup("x"); + await new Promise(r => + // eslint-disable-next-line mozilla/no-arbitrary-setTimeout + setTimeout(r, originalInterval / 10) + ); + assertImpressionTelemetry([]); + + EventUtils.synthesizeKey(" "); + EventUtils.synthesizeKey("z"); + await UrlbarTestUtils.promiseSearchComplete(window); + assertImpressionTelemetry([]); + await waitForPauseImpression(); + + assertImpressionTelemetry([{ n_chars: 3 }]); + }); +}); + +add_task(async function oneImpressionInOneSession() { + await doTest(async browser => { + await openPopup("x"); + await waitForPauseImpression(); + + // Sanity check. + assertImpressionTelemetry([{ n_chars: 1 }]); + + // Add a keyword to start new query. + EventUtils.synthesizeKey(" "); + EventUtils.synthesizeKey("z"); + await UrlbarTestUtils.promiseSearchComplete(window); + await waitForPauseImpression(); + + // No more taking impression telemetry. + assertImpressionTelemetry([{ n_chars: 1 }]); + + // Finish the current session. + await doEnter(); + + // Should take pause impression since new session started. + await openPopup("x z y"); + await waitForPauseImpression(); + assertImpressionTelemetry([{ n_chars: 1 }, { n_chars: 5 }]); + }); +}); diff --git a/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_record_preferences.js b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_record_preferences.js new file mode 100644 index 0000000000..6760385841 --- /dev/null +++ b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_record_preferences.js @@ -0,0 +1,54 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test for preference telemetry. + +add_setup(async function () { + await Services.fog.testFlushAllChildren(); + Services.fog.testResetFOG(); + + // Create a new window in order to initialize TelemetryEvent of + // UrlbarController. + const win = await BrowserTestUtils.openNewBrowserWindow(); + registerCleanupFunction(async function () { + await BrowserTestUtils.closeWindow(win); + }); +}); + +add_task(async function prefMaxRichResults() { + Assert.equal( + Glean.urlbar.prefMaxResults.testGetValue(), + UrlbarPrefs.get("maxRichResults"), + "Record prefMaxResults when UrlbarController is initialized" + ); + + await SpecialPowers.pushPrefEnv({ + set: [["browser.urlbar.maxRichResults", 0]], + }); + Assert.equal( + Glean.urlbar.prefMaxResults.testGetValue(), + UrlbarPrefs.get("maxRichResults"), + "Record prefMaxResults when the maxRichResults pref is updated" + ); +}); + +add_task(async function prefSuggestTopsites() { + Assert.equal( + Glean.urlbar.prefSuggestTopsites.testGetValue(), + UrlbarPrefs.get("suggest.topsites"), + "Record prefSuggestTopsites when UrlbarController is initialized" + ); + + await SpecialPowers.pushPrefEnv({ + set: [ + ["browser.urlbar.suggest.topsites", !UrlbarPrefs.get("suggest.topsites")], + ], + }); + Assert.equal( + Glean.urlbar.prefSuggestTopsites.testGetValue(), + UrlbarPrefs.get("suggest.topsites"), + "Record prefSuggestTopsites when the suggest.topsites pref is updated" + ); +}); diff --git a/browser/components/urlbar/tests/engagementTelemetry/browser/head-exposure.js b/browser/components/urlbar/tests/engagementTelemetry/browser/head-exposure.js new file mode 100644 index 0000000000..4ce2c6f869 --- /dev/null +++ b/browser/components/urlbar/tests/engagementTelemetry/browser/head-exposure.js @@ -0,0 +1,47 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/* import-globals-from head.js */ + +async function doExposureTest({ + prefs, + query, + trigger, + assert, + select = defaultSelect, +}) { + const cleanupQuickSuggest = await ensureQuickSuggestInit(); + await SpecialPowers.pushPrefEnv({ + set: prefs, + }); + + await doTest(async () => { + await openPopup(query); + await select(query); + + await trigger(); + await assert(); + }); + + await SpecialPowers.popPrefEnv(); + cleanupQuickSuggest(); +} + +async function defaultSelect(query) { + await selectRowByURL(`https://example.com/${query}`); +} + +async function getResultByType(provider) { + for (let i = 0; i < UrlbarTestUtils.getResultCount(window); i++) { + const detail = await UrlbarTestUtils.getDetailsOfResultAt(window, i); + const telemetryType = UrlbarUtils.searchEngagementTelemetryType( + detail.result + ); + if (telemetryType === provider) { + return detail.result; + } + } + return null; +} diff --git a/browser/components/urlbar/tests/engagementTelemetry/browser/head-groups.js b/browser/components/urlbar/tests/engagementTelemetry/browser/head-groups.js new file mode 100644 index 0000000000..b34221d749 --- /dev/null +++ b/browser/components/urlbar/tests/engagementTelemetry/browser/head-groups.js @@ -0,0 +1,294 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/* import-globals-from head.js */ + +async function doHeuristicsTest({ trigger, assert }) { + await doTest(async browser => { + await openPopup("x"); + + await trigger(); + await assert(); + }); +} + +async function doAdaptiveHistoryTest({ trigger, assert }) { + await SpecialPowers.pushPrefEnv({ + set: [["browser.urlbar.autoFill", false]], + }); + + await doTest(async browser => { + await PlacesTestUtils.addVisits(["https://example.com/test"]); + await UrlbarUtils.addToInputHistory("https://example.com/test", "examp"); + + await openPopup("exa"); + await selectRowByURL("https://example.com/test"); + + await trigger(); + await assert(); + }); + + await SpecialPowers.popPrefEnv(); +} + +async function doSearchHistoryTest({ trigger, assert }) { + await SpecialPowers.pushPrefEnv({ + set: [ + ["browser.urlbar.suggest.searches", true], + ["browser.urlbar.maxHistoricalSearchSuggestions", 2], + ], + }); + + await doTest(async browser => { + await UrlbarTestUtils.formHistory.add(["foofoo", "foobar"]); + + await openPopup("foo"); + await selectRowByURL("http://mochi.test:8888/?terms=foofoo"); + + await trigger(); + await assert(); + }); + + await SpecialPowers.popPrefEnv(); +} + +async function doSearchSuggestTest({ trigger, assert }) { + await SpecialPowers.pushPrefEnv({ + set: [ + ["browser.urlbar.suggest.searches", true], + ["browser.urlbar.maxHistoricalSearchSuggestions", 2], + ], + }); + + await doTest(async browser => { + await openPopup("foo"); + await selectRowByURL("http://mochi.test:8888/?terms=foofoo"); + + await trigger(); + await assert(); + }); + + await SpecialPowers.popPrefEnv(); +} + +async function doTailSearchSuggestTest({ trigger, assert }) { + const cleanup = await _useTailSuggestionsEngine(); + + await doTest(async browser => { + await openPopup("hello"); + await selectRowByProvider("SearchSuggestions"); + + await trigger(); + await assert(); + }); + + await cleanup(); +} + +async function doTopPickTest({ trigger, assert }) { + const cleanupQuickSuggest = await ensureQuickSuggestInit({ + // eslint-disable-next-line mozilla/valid-lazy + config: lazy.QuickSuggestTestUtils.BEST_MATCH_CONFIG, + }); + + await SpecialPowers.pushPrefEnv({ + set: [["browser.urlbar.bestMatch.enabled", true]], + }); + + await doTest(async browser => { + await openPopup("sponsored"); + await selectRowByURL("https://example.com/sponsored"); + + await trigger(); + await assert(); + }); + + await SpecialPowers.popPrefEnv(); + cleanupQuickSuggest(); +} + +async function doTopSiteTest({ trigger, assert }) { + await doTest(async browser => { + await addTopSites("https://example.com/"); + + await showResultByArrowDown(); + await selectRowByURL("https://example.com/"); + + await trigger(); + await assert(); + }); +} + +async function doRemoteTabTest({ trigger, assert }) { + const remoteTab = await loadRemoteTab("https://example.com"); + + await doTest(async browser => { + await openPopup("example"); + await selectRowByProvider("RemoteTabs"); + + await trigger(); + await assert(); + }); + + await remoteTab.unload(); +} + +async function doAddonTest({ trigger, assert }) { + const addon = loadOmniboxAddon({ keyword: "omni" }); + await addon.startup(); + + await doTest(async browser => { + await openPopup("omni test"); + + await trigger(); + await assert(); + }); + + await addon.unload(); +} + +async function doGeneralBookmarkTest({ trigger, assert }) { + await doTest(async browser => { + await PlacesUtils.bookmarks.insert({ + parentGuid: PlacesUtils.bookmarks.unfiledGuid, + url: "https://example.com/bookmark", + title: "bookmark", + }); + + await openPopup("bookmark"); + await selectRowByURL("https://example.com/bookmark"); + + await trigger(); + await assert(); + }); +} + +async function doGeneralHistoryTest({ trigger, assert }) { + await SpecialPowers.pushPrefEnv({ + set: [["browser.urlbar.autoFill", false]], + }); + + await doTest(async browser => { + await PlacesTestUtils.addVisits("https://example.com/test"); + + await openPopup("example"); + await selectRowByURL("https://example.com/test"); + + await trigger(); + await assert(); + }); + + await SpecialPowers.popPrefEnv(); +} + +async function doSuggestTest({ trigger, assert }) { + const cleanupQuickSuggest = await ensureQuickSuggestInit(); + await SpecialPowers.pushPrefEnv({ + set: [["browser.urlbar.bestMatch.enabled", false]], + }); + + await doTest(async browser => { + await openPopup("nonsponsored"); + await selectRowByURL("https://example.com/nonsponsored"); + + await trigger(); + await assert(); + }); + + await SpecialPowers.popPrefEnv(); + cleanupQuickSuggest(); +} + +async function doAboutPageTest({ trigger, assert }) { + await SpecialPowers.pushPrefEnv({ + set: [["browser.urlbar.maxRichResults", 3]], + }); + + await doTest(async browser => { + await openPopup("about:"); + await selectRowByURL("about:robots"); + + await trigger(); + await assert(); + }); + + await SpecialPowers.popPrefEnv(); +} + +async function doSuggestedIndexTest({ trigger, assert }) { + await SpecialPowers.pushPrefEnv({ + set: [["browser.urlbar.unitConversion.enabled", true]], + }); + + await doTest(async browser => { + await openPopup("1m to cm"); + await selectRowByProvider("UnitConversion"); + + await trigger(); + await assert(); + }); + + await SpecialPowers.popPrefEnv(); +} + +/** + * Creates a search engine that returns tail suggestions and sets it as the + * default engine. + * + * @returns {Function} + * A cleanup function that will revert the default search engine and stop http + * server. + */ +async function _useTailSuggestionsEngine() { + await SpecialPowers.pushPrefEnv({ + set: [ + ["browser.search.suggest.enabled", true], + ["browser.urlbar.suggest.searches", true], + ["browser.urlbar.richSuggestions.tail", true], + ], + }); + + const engineName = "TailSuggestions"; + const httpServer = new HttpServer(); + httpServer.start(-1); + httpServer.registerPathHandler("/suggest", (req, resp) => { + const params = new URLSearchParams(req.queryString); + const searchStr = params.get("q"); + const suggestions = [ + searchStr, + [searchStr + "-tail"], + [], + { + "google:suggestdetail": [{ t: "-tail", mp: "… " }], + }, + ]; + resp.setHeader("Content-Type", "application/json", false); + resp.write(JSON.stringify(suggestions)); + }); + + await SearchTestUtils.installSearchExtension({ + name: engineName, + search_url: `http://localhost:${httpServer.identity.primaryPort}/search`, + suggest_url: `http://localhost:${httpServer.identity.primaryPort}/suggest`, + suggest_url_get_params: "?q={searchTerms}", + search_form: `http://localhost:${httpServer.identity.primaryPort}/search?q={searchTerms}`, + }); + + const tailEngine = Services.search.getEngineByName(engineName); + const originalEngine = await Services.search.getDefault(); + Services.search.setDefault( + tailEngine, + Ci.nsISearchService.CHANGE_REASON_UNKNOWN + ); + + return async () => { + Services.search.setDefault( + originalEngine, + Ci.nsISearchService.CHANGE_REASON_UNKNOWN + ); + httpServer.stop(() => {}); + await SpecialPowers.popPrefEnv(); + }; +} diff --git a/browser/components/urlbar/tests/engagementTelemetry/browser/head-interaction.js b/browser/components/urlbar/tests/engagementTelemetry/browser/head-interaction.js new file mode 100644 index 0000000000..6bc0fd3b0e --- /dev/null +++ b/browser/components/urlbar/tests/engagementTelemetry/browser/head-interaction.js @@ -0,0 +1,238 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/* import-globals-from head.js */ + +async function doTopsitesTest({ trigger, assert }) { + await doTest(async browser => { + await addTopSites("https://example.com/"); + + await showResultByArrowDown(); + await selectRowByURL("https://example.com/"); + + await trigger(); + await assert(); + }); +} + +async function doTypedTest({ trigger, assert }) { + await doTest(async browser => { + await openPopup("x"); + + await trigger(); + await assert(); + }); +} + +async function doTypedWithResultsPopupTest({ trigger, assert }) { + await doTest(async browser => { + await showResultByArrowDown(); + EventUtils.synthesizeKey("x"); + await UrlbarTestUtils.promiseSearchComplete(window); + + await trigger(); + await assert(); + }); +} + +async function doPastedTest({ trigger, assert }) { + await doTest(async browser => { + await doPaste("www.example.com"); + + await trigger(); + await assert(); + }); +} + +async function doPastedWithResultsPopupTest({ trigger, assert }) { + await doTest(async browser => { + await showResultByArrowDown(); + await doPaste("x"); + + await trigger(); + await assert(); + }); +} + +async function doReturnedRestartedRefinedTest({ trigger, assert }) { + const testData = [ + { + firstInput: "x", + // Just move the focus to the URL bar after blur. + secondInput: null, + expected: "returned", + }, + { + firstInput: "x", + secondInput: "x", + expected: "returned", + }, + { + firstInput: "x", + secondInput: "y", + expected: "restarted", + }, + { + firstInput: "x", + secondInput: "x y", + expected: "refined", + }, + { + firstInput: "x y", + secondInput: "x", + expected: "refined", + }, + ]; + + for (const { firstInput, secondInput, expected } of testData) { + await doTest(async browser => { + await openPopup(firstInput); + await waitForPauseImpression(); + await doBlur(); + + await UrlbarTestUtils.promisePopupOpen(window, () => { + document.getElementById("Browser:OpenLocation").doCommand(); + }); + if (secondInput) { + for (let i = 0; i < secondInput.length; i++) { + EventUtils.synthesizeKey(secondInput.charAt(i)); + } + } + await UrlbarTestUtils.promiseSearchComplete(window); + + await trigger(); + await assert(expected); + }); + } +} + +async function doPersistedSearchTermsTest({ trigger, assert }) { + await doTest(async browser => { + await openPopup("x"); + await waitForPauseImpression(); + await doEnter(); + + await openPopup("x"); + + await trigger(); + await assert(); + }); +} + +async function doPersistedSearchTermsRestartedRefinedTest({ + enabled, + trigger, + assert, +}) { + const testData = [ + { + firstInput: "x", + // Just move the focus to the URL bar after engagement. + secondInput: null, + expected: enabled ? "persisted_search_terms" : "topsites", + }, + { + firstInput: "x", + secondInput: "x", + expected: enabled ? "persisted_search_terms" : "typed", + }, + { + firstInput: "x", + secondInput: "y", + expected: enabled ? "persisted_search_terms_restarted" : "typed", + }, + { + firstInput: "x", + secondInput: "x y", + expected: enabled ? "persisted_search_terms_refined" : "typed", + }, + { + firstInput: "x y", + secondInput: "x", + expected: enabled ? "persisted_search_terms_refined" : "typed", + }, + ]; + + for (const { firstInput, secondInput, expected } of testData) { + await doTest(async browser => { + await openPopup(firstInput); + await waitForPauseImpression(); + await doEnter(); + + await UrlbarTestUtils.promisePopupOpen(window, () => { + EventUtils.synthesizeKey("l", { accelKey: true }); + }); + if (secondInput) { + for (let i = 0; i < secondInput.length; i++) { + EventUtils.synthesizeKey(secondInput.charAt(i)); + } + } + await UrlbarTestUtils.promiseSearchComplete(window); + + await trigger(); + await assert(expected); + }); + } +} + +async function doPersistedSearchTermsRestartedRefinedViaAbandonmentTest({ + enabled, + trigger, + assert, +}) { + const testData = [ + { + firstInput: "x", + // Just move the focus to the URL bar after blur. + secondInput: null, + expected: enabled ? "persisted_search_terms" : "returned", + }, + { + firstInput: "x", + secondInput: "x", + expected: enabled ? "persisted_search_terms" : "returned", + }, + { + firstInput: "x", + secondInput: "y", + expected: enabled ? "persisted_search_terms_restarted" : "restarted", + }, + { + firstInput: "x", + secondInput: "x y", + expected: enabled ? "persisted_search_terms_refined" : "refined", + }, + { + firstInput: "x y", + secondInput: "x", + expected: enabled ? "persisted_search_terms_refined" : "refined", + }, + ]; + + for (const { firstInput, secondInput, expected } of testData) { + await doTest(async browser => { + await openPopup("any search"); + await waitForPauseImpression(); + await doEnter(); + + await openPopup(firstInput); + await waitForPauseImpression(); + await doBlur(); + + await UrlbarTestUtils.promisePopupOpen(window, () => { + EventUtils.synthesizeKey("l", { accelKey: true }); + }); + if (secondInput) { + for (let i = 0; i < secondInput.length; i++) { + EventUtils.synthesizeKey(secondInput.charAt(i)); + } + } + await UrlbarTestUtils.promiseSearchComplete(window); + + await trigger(); + await assert(expected); + }); + } +} diff --git a/browser/components/urlbar/tests/engagementTelemetry/browser/head-n_chars_n_words.js b/browser/components/urlbar/tests/engagementTelemetry/browser/head-n_chars_n_words.js new file mode 100644 index 0000000000..6d4c61c7f0 --- /dev/null +++ b/browser/components/urlbar/tests/engagementTelemetry/browser/head-n_chars_n_words.js @@ -0,0 +1,56 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/* import-globals-from head.js */ + +async function doNCharsTest({ trigger, assert }) { + for (const input of ["x", "xx", "xx x", "xx x "]) { + await doTest(async browser => { + await openPopup(input); + + await trigger(); + await assert(input.length); + }); + } +} + +async function doNCharsWithOverMaxTextLengthCharsTest({ trigger, assert }) { + await doTest(async browser => { + let input = ""; + for (let i = 0; i < UrlbarUtils.MAX_TEXT_LENGTH * 2; i++) { + input += "x"; + } + await openPopup(input); + + await trigger(); + await assert(UrlbarUtils.MAX_TEXT_LENGTH * 2); + }); +} + +async function doNWordsTest({ trigger, assert }) { + for (const input of ["x", "xx", "xx x", "xx x "]) { + await doTest(async browser => { + await openPopup(input); + + await trigger(); + const splits = input.trim().split(" "); + await assert(splits.length); + }); + } +} + +async function doNWordsWithOverMaxTextLengthCharsTest({ trigger, assert }) { + await doTest(async browser => { + const word = "1234 "; + let input = ""; + while (input.length < UrlbarUtils.MAX_TEXT_LENGTH * 2) { + input += word; + } + await openPopup(input); + + await trigger(); + await assert(UrlbarUtils.MAX_TEXT_LENGTH / word.length); + }); +} diff --git a/browser/components/urlbar/tests/engagementTelemetry/browser/head-sap.js b/browser/components/urlbar/tests/engagementTelemetry/browser/head-sap.js new file mode 100644 index 0000000000..be4e852b1c --- /dev/null +++ b/browser/components/urlbar/tests/engagementTelemetry/browser/head-sap.js @@ -0,0 +1,66 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/* import-globals-from head.js */ + +async function doUrlbarNewTabTest({ trigger, assert }) { + await doTest(async browser => { + await openPopup("x"); + + await trigger(); + await assert(); + }); +} + +async function doUrlbarTest({ trigger, assert }) { + await doTest(async browser => { + await openPopup("x"); + await waitForPauseImpression(); + await doEnter(); + await openPopup("y"); + + await trigger(); + await assert(); + }); +} + +async function doHandoffTest({ trigger, assert }) { + await doTest(async browser => { + BrowserTestUtils.loadURIString(browser, "about:newtab"); + await BrowserTestUtils.browserStopped(browser, "about:newtab"); + await SpecialPowers.spawn(browser, [], function () { + const searchInput = content.document.querySelector(".fake-editable"); + searchInput.click(); + }); + EventUtils.synthesizeKey("x"); + await UrlbarTestUtils.promiseSearchComplete(window); + + await trigger(); + await assert(); + }); +} + +async function doUrlbarAddonpageTest({ trigger, assert }) { + const extensionData = { + files: { + "page.html": "<!DOCTYPE html>hello", + }, + }; + const extension = ExtensionTestUtils.loadExtension(extensionData); + await extension.startup(); + const extensionURL = `moz-extension://${extension.uuid}/page.html`; + + await doTest(async browser => { + const onLoad = BrowserTestUtils.browserLoaded(browser); + BrowserTestUtils.loadURIString(browser, extensionURL); + await onLoad; + await openPopup("x"); + + await trigger(); + await assert(); + }); + + await extension.unload(); +} diff --git a/browser/components/urlbar/tests/engagementTelemetry/browser/head-search_mode.js b/browser/components/urlbar/tests/engagementTelemetry/browser/head-search_mode.js new file mode 100644 index 0000000000..5c877da05f --- /dev/null +++ b/browser/components/urlbar/tests/engagementTelemetry/browser/head-search_mode.js @@ -0,0 +1,93 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/* import-globals-from head.js */ + +async function doNotSearchModeTest({ trigger, assert }) { + await doTest(async browser => { + await openPopup("x"); + + await trigger(); + await assert(); + }); +} + +async function doSearchEngineTest({ trigger, assert }) { + await doTest(async browser => { + await openPopup("x"); + await UrlbarTestUtils.enterSearchMode(window); + + await trigger(); + await assert(); + }); +} + +async function doBookmarksTest({ trigger, assert }) { + await doTest(async browser => { + await PlacesUtils.bookmarks.insert({ + parentGuid: PlacesUtils.bookmarks.unfiledGuid, + url: "https://example.com/bookmark", + title: "bookmark", + }); + await openPopup("bookmark"); + await UrlbarTestUtils.enterSearchMode(window, { + source: UrlbarUtils.RESULT_SOURCE.BOOKMARKS, + }); + await selectRowByURL("https://example.com/bookmark"); + + await trigger(); + await assert(); + }); +} + +async function doHistoryTest({ trigger, assert }) { + await SpecialPowers.pushPrefEnv({ + set: [["browser.urlbar.autoFill", false]], + }); + + await doTest(async browser => { + await PlacesTestUtils.addVisits("https://example.com/test"); + await openPopup("example"); + await UrlbarTestUtils.enterSearchMode(window, { + source: UrlbarUtils.RESULT_SOURCE.HISTORY, + }); + await selectRowByURL("https://example.com/test"); + + await trigger(); + await assert(); + }); + + await SpecialPowers.popPrefEnv(); +} + +async function doTabTest({ trigger, assert }) { + const tab = BrowserTestUtils.addTab(gBrowser, "https://example.com/"); + + await doTest(async browser => { + await openPopup("example"); + await UrlbarTestUtils.enterSearchMode(window, { + source: UrlbarUtils.RESULT_SOURCE.TABS, + }); + await selectRowByProvider("Places"); + + await trigger(); + await assert(); + }); + + BrowserTestUtils.removeTab(tab); +} + +async function doActionsTest({ trigger, assert }) { + await doTest(async browser => { + await openPopup("add"); + await UrlbarTestUtils.enterSearchMode(window, { + source: UrlbarUtils.RESULT_SOURCE.ACTIONS, + }); + await selectRowByProvider("quickactions"); + + await trigger(); + await assert(); + }); +} diff --git a/browser/components/urlbar/tests/engagementTelemetry/browser/head.js b/browser/components/urlbar/tests/engagementTelemetry/browser/head.js new file mode 100644 index 0000000000..fa623deb5c --- /dev/null +++ b/browser/components/urlbar/tests/engagementTelemetry/browser/head.js @@ -0,0 +1,458 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +Services.scriptloader.loadSubScript( + "chrome://mochitests/content/browser/browser/components/urlbar/tests/browser/head-common.js", + this +); + +ChromeUtils.defineESModuleGetters(this, { + QuickSuggest: "resource:///modules/QuickSuggest.sys.mjs", +}); + +const lazy = {}; + +XPCOMUtils.defineLazyGetter(lazy, "QuickSuggestTestUtils", () => { + const { QuickSuggestTestUtils: module } = ChromeUtils.importESModule( + "resource://testing-common/QuickSuggestTestUtils.sys.mjs" + ); + module.init(this); + return module; +}); + +XPCOMUtils.defineLazyGetter(this, "MerinoTestUtils", () => { + const { MerinoTestUtils: module } = ChromeUtils.importESModule( + "resource://testing-common/MerinoTestUtils.sys.mjs" + ); + module.init(this); + return module; +}); + +ChromeUtils.defineESModuleGetters(lazy, { + UrlbarTestUtils: "resource://testing-common/UrlbarTestUtils.sys.mjs", + sinon: "resource://testing-common/Sinon.sys.mjs", +}); + +async function addTopSites(url) { + for (let i = 0; i < 5; i++) { + await PlacesTestUtils.addVisits(url); + } + await updateTopSites(sites => { + return sites && sites[0] && sites[0].url == url; + }); +} + +function assertAbandonmentTelemetry(expectedExtraList) { + _assertGleanTelemetry("abandonment", expectedExtraList); +} + +function assertEngagementTelemetry(expectedExtraList) { + _assertGleanTelemetry("engagement", expectedExtraList); +} + +function assertImpressionTelemetry(expectedExtraList) { + _assertGleanTelemetry("impression", expectedExtraList); +} + +function assertExposureTelemetry(expectedExtraList) { + _assertGleanTelemetry("exposure", expectedExtraList); +} + +function _assertGleanTelemetry(telemetryName, expectedExtraList) { + const telemetries = Glean.urlbar[telemetryName].testGetValue() ?? []; + Assert.equal( + telemetries.length, + expectedExtraList.length, + "Telemetry event length matches expected event length." + ); + + for (let i = 0; i < telemetries.length; i++) { + const telemetry = telemetries[i]; + Assert.equal(telemetry.category, "urlbar"); + Assert.equal(telemetry.name, telemetryName); + + const expectedExtra = expectedExtraList[i]; + for (const key of Object.keys(expectedExtra)) { + Assert.equal( + telemetry.extra[key], + expectedExtra[key], + `${key} is correct` + ); + } + } +} + +async function ensureQuickSuggestInit({ + merinoSuggestions = undefined, + config = undefined, +} = {}) { + return lazy.QuickSuggestTestUtils.ensureQuickSuggestInit({ + config, + merinoSuggestions, + remoteSettingsResults: [ + { + type: "data", + attachment: [ + { + id: 1, + url: "https://example.com/sponsored", + title: "Sponsored suggestion", + keywords: ["sponsored"], + click_url: "https://example.com/click", + impression_url: "https://example.com/impression", + advertiser: "TestAdvertiser", + iab_category: "22 - Shopping", + }, + { + id: 2, + url: `https://example.com/nonsponsored`, + title: "Non-sponsored suggestion", + keywords: ["nonsponsored"], + click_url: "https://example.com/click", + impression_url: "https://example.com/impression", + advertiser: "TestAdvertiser", + iab_category: "5 - Education", + }, + ], + }, + { + type: "weather", + weather: MerinoTestUtils.WEATHER_RS_DATA, + }, + ], + }); +} + +async function doBlur() { + await UrlbarTestUtils.promisePopupClose(window, () => { + gURLBar.blur(); + }); +} + +async function doClick() { + const selected = UrlbarTestUtils.getSelectedRow(window); + const onLoad = BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser); + EventUtils.synthesizeMouseAtCenter(selected, {}); + await onLoad; +} + +async function doClickSubButton(selector) { + const selected = UrlbarTestUtils.getSelectedElement(window); + const button = selected.closest(".urlbarView-row").querySelector(selector); + EventUtils.synthesizeMouseAtCenter(button, {}); +} + +async function doDropAndGo(data) { + const onLoad = BrowserTestUtils.browserLoaded(browser); + EventUtils.synthesizeDrop( + document.getElementById("back-button"), + gURLBar.inputField, + [[{ type: "text/plain", data }]], + "copy", + window + ); + await onLoad; +} + +async function doEnter(modifier = {}) { + const onLoad = BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser); + EventUtils.synthesizeKey("KEY_Enter", modifier); + await onLoad; +} + +async function doPaste(data) { + await SimpleTest.promiseClipboardChange(data, () => { + clipboardHelper.copyString(data); + }); + + gURLBar.focus(); + gURLBar.select(); + document.commandDispatcher + .getControllerForCommand("cmd_paste") + .doCommand("cmd_paste"); + await UrlbarTestUtils.promiseSearchComplete(window); +} + +async function doPasteAndGo(data) { + await SimpleTest.promiseClipboardChange(data, () => { + clipboardHelper.copyString(data); + }); + const inputBox = gURLBar.querySelector("moz-input-box"); + const contextMenu = inputBox.menupopup; + const onPopup = BrowserTestUtils.waitForEvent(contextMenu, "popupshown"); + EventUtils.synthesizeMouseAtCenter(gURLBar.inputField, { + type: "contextmenu", + button: 2, + }); + await onPopup; + const onLoad = BrowserTestUtils.browserLoaded(browser); + const menuitem = inputBox.getMenuItem("paste-and-go"); + contextMenu.activateItem(menuitem); + await onLoad; +} + +async function doTest(testFn) { + await Services.fog.testFlushAllChildren(); + Services.fog.testResetFOG(); + // Enable recording telemetry for abandonment, engagement and impression. + Services.fog.setMetricsFeatureConfig( + JSON.stringify({ + "urlbar.abandonment": true, + "urlbar.engagement": true, + "urlbar.impression": true, + }) + ); + + gURLBar.controller.engagementEvent.reset(); + await PlacesUtils.history.clear(); + await PlacesUtils.bookmarks.eraseEverything(); + await PlacesTestUtils.clearHistoryVisits(); + await PlacesTestUtils.clearInputHistory(); + await UrlbarTestUtils.formHistory.clear(window); + await QuickSuggest.blockedSuggestions.clear(); + await QuickSuggest.blockedSuggestions._test_readyPromise; + await updateTopSites(() => true); + + try { + await BrowserTestUtils.withNewTab(gBrowser, testFn); + } finally { + Services.fog.setMetricsFeatureConfig("{}"); + } +} + +async function initGroupTest() { + /* import-globals-from head-groups.js */ + Services.scriptloader.loadSubScript( + "chrome://mochitests/content/browser/browser/components/urlbar/tests/engagementTelemetry/browser/head-groups.js", + this + ); + await setup(); +} + +async function initInteractionTest() { + /* import-globals-from head-interaction.js */ + Services.scriptloader.loadSubScript( + "chrome://mochitests/content/browser/browser/components/urlbar/tests/engagementTelemetry/browser/head-interaction.js", + this + ); + await setup(); +} + +async function initNCharsAndNWordsTest() { + /* import-globals-from head-n_chars_n_words.js */ + Services.scriptloader.loadSubScript( + "chrome://mochitests/content/browser/browser/components/urlbar/tests/engagementTelemetry/browser/head-n_chars_n_words.js", + this + ); + await setup(); +} + +async function initSapTest() { + /* import-globals-from head-sap.js */ + Services.scriptloader.loadSubScript( + "chrome://mochitests/content/browser/browser/components/urlbar/tests/engagementTelemetry/browser/head-sap.js", + this + ); + await setup(); +} + +async function initSearchModeTest() { + /* import-globals-from head-search_mode.js */ + Services.scriptloader.loadSubScript( + "chrome://mochitests/content/browser/browser/components/urlbar/tests/engagementTelemetry/browser/head-search_mode.js", + this + ); + await setup(); +} + +async function initExposureTest() { + /* import-globals-from head-exposure.js */ + Services.scriptloader.loadSubScript( + "chrome://mochitests/content/browser/browser/components/urlbar/tests/engagementTelemetry/browser/head-exposure.js", + this + ); + await setup(); +} + +function loadOmniboxAddon({ keyword }) { + return ExtensionTestUtils.loadExtension({ + manifest: { + permissions: ["tabs"], + omnibox: { + keyword, + }, + }, + background() { + /* global browser */ + browser.omnibox.setDefaultSuggestion({ + description: "doit", + }); + browser.omnibox.onInputEntered.addListener(() => { + browser.tabs.update({ url: "https://example.com/" }); + }); + browser.omnibox.onInputChanged.addListener((text, suggest) => { + suggest([]); + }); + }, + }); +} + +async function loadRemoteTab(url) { + await SpecialPowers.pushPrefEnv({ + set: [ + ["browser.urlbar.suggest.searches", false], + ["browser.urlbar.maxHistoricalSearchSuggestions", 0], + ["browser.urlbar.autoFill", false], + ["services.sync.username", "fake"], + ["services.sync.syncedTabs.showRemoteTabs", true], + ], + }); + + const REMOTE_TAB = { + id: "test", + type: "client", + lastModified: 1492201200, + name: "test", + clientType: "desktop", + tabs: [ + { + type: "tab", + title: "tesrt", + url, + icon: UrlbarUtils.ICON.DEFAULT, + client: "test", + lastUsed: Math.floor(Date.now() / 1000), + }, + ], + }; + + const sandbox = lazy.sinon.createSandbox(); + // eslint-disable-next-line no-undef + const syncedTabs = SyncedTabs; + const originalSyncedTabsInternal = syncedTabs._internal; + syncedTabs._internal = { + isConfiguredToSyncTabs: true, + hasSyncedThisSession: true, + getTabClients() { + return Promise.resolve([]); + }, + syncTabs() { + return Promise.resolve(); + }, + }; + const weaveXPCService = Cc["@mozilla.org/weave/service;1"].getService( + Ci.nsISupports + ).wrappedJSObject; + const oldWeaveServiceReady = weaveXPCService.ready; + weaveXPCService.ready = true; + sandbox + .stub(syncedTabs._internal, "getTabClients") + .callsFake(() => Promise.resolve(Cu.cloneInto([REMOTE_TAB], {}))); + + return { + async unload() { + sandbox.restore(); + weaveXPCService.ready = oldWeaveServiceReady; + syncedTabs._internal = originalSyncedTabsInternal; + // Reset internal cache in UrlbarProviderRemoteTabs. + Services.obs.notifyObservers(null, "weave:engine:sync:finish", "tabs"); + await SpecialPowers.popPrefEnv(); + }, + }; +} + +async function openPopup(input) { + await UrlbarTestUtils.promisePopupOpen(window, async () => { + await UrlbarTestUtils.inputIntoURLBar(window, input); + }); + await UrlbarTestUtils.promiseSearchComplete(window); +} + +async function selectRowByURL(url) { + for (let i = 0; i < UrlbarTestUtils.getResultCount(window); i++) { + const detail = await UrlbarTestUtils.getDetailsOfResultAt(window, i); + if (detail.url === url) { + UrlbarTestUtils.setSelectedRowIndex(window, i); + return; + } + } +} + +async function selectRowByProvider(provider) { + for (let i = 0; i < UrlbarTestUtils.getResultCount(window); i++) { + const detail = await UrlbarTestUtils.getDetailsOfResultAt(window, i); + if (detail.result.providerName === provider) { + UrlbarTestUtils.setSelectedRowIndex(window, i); + break; + } + } +} + +async function selectRowByType(type) { + for (let i = 0; i < UrlbarTestUtils.getResultCount(window); i++) { + const detail = await UrlbarTestUtils.getDetailsOfResultAt(window, i); + if (detail.result.payload.type === type) { + UrlbarTestUtils.setSelectedRowIndex(window, i); + return; + } + } +} + +async function setup() { + await SpecialPowers.pushPrefEnv({ + set: [ + ["browser.urlbar.searchEngagementTelemetry.enabled", true], + ["browser.urlbar.quickactions.enabled", true], + ["browser.urlbar.quickactions.minimumSearchString", 0], + ["browser.urlbar.suggest.quickactions", true], + ["browser.urlbar.shortcuts.quickactions", true], + [ + "browser.urlbar.searchEngagementTelemetry.pauseImpressionIntervalMs", + 100, + ], + ], + }); + + const engine = await SearchTestUtils.promiseNewSearchEngine({ + url: "chrome://mochitests/content/browser/browser/components/urlbar/tests/browser/searchSuggestionEngine.xml", + }); + const originalDefaultEngine = await Services.search.getDefault(); + await Services.search.setDefault( + engine, + Ci.nsISearchService.CHANGE_REASON_UNKNOWN + ); + await Services.search.moveEngine(engine, 0); + + registerCleanupFunction(async function () { + await SpecialPowers.popPrefEnv(); + await Services.search.setDefault( + originalDefaultEngine, + Ci.nsISearchService.CHANGE_REASON_UNKNOWN + ); + }); +} + +async function setupNimbus(variables) { + return lazy.UrlbarTestUtils.initNimbusFeature(variables); +} + +async function showResultByArrowDown() { + gURLBar.value = ""; + gURLBar.select(); + await UrlbarTestUtils.promisePopupOpen(window, () => { + EventUtils.synthesizeKey("KEY_ArrowDown"); + }); + await UrlbarTestUtils.promiseSearchComplete(window); +} + +async function waitForPauseImpression() { + await new Promise(r => + setTimeout( + r, + UrlbarPrefs.get("searchEngagementTelemetry.pauseImpressionIntervalMs") + ) + ); + await Services.fog.testFlushAllChildren(); +} |