summaryrefslogtreecommitdiffstats
path: root/toolkit/components/search/tests/xpcshell/test_telemetry_event_default.js
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/components/search/tests/xpcshell/test_telemetry_event_default.js')
-rw-r--r--toolkit/components/search/tests/xpcshell/test_telemetry_event_default.js517
1 files changed, 517 insertions, 0 deletions
diff --git a/toolkit/components/search/tests/xpcshell/test_telemetry_event_default.js b/toolkit/components/search/tests/xpcshell/test_telemetry_event_default.js
new file mode 100644
index 0000000000..84bd1be9cc
--- /dev/null
+++ b/toolkit/components/search/tests/xpcshell/test_telemetry_event_default.js
@@ -0,0 +1,517 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/*
+ * Tests for the default engine telemetry event that can be tested via xpcshell,
+ * related to changing or selecting a different configuration.
+ * Other tests are typically in browser mochitests.
+ */
+
+"use strict";
+
+ChromeUtils.defineESModuleGetters(this, {
+ NimbusFeatures: "resource://nimbus/ExperimentAPI.sys.mjs",
+ TelemetryTestUtils: "resource://testing-common/TelemetryTestUtils.sys.mjs",
+});
+
+const BASE_CONFIG = [
+ {
+ webExtension: {
+ id: "engine@search.mozilla.org",
+ name: "Test search engine",
+ search_url: "https://www.google.com/search",
+ params: [
+ {
+ name: "q",
+ value: "{searchTerms}",
+ },
+ {
+ name: "channel",
+ condition: "purpose",
+ purpose: "contextmenu",
+ value: "rcs",
+ },
+ {
+ name: "channel",
+ condition: "purpose",
+ purpose: "keyword",
+ value: "fflb",
+ },
+ ],
+ suggest_url:
+ "https://suggestqueries.google.com/complete/search?output=firefox&client=firefox&hl={moz:locale}&q={searchTerms}",
+ },
+ appliesTo: [{ included: { everywhere: true } }],
+ default: "yes",
+ },
+];
+const MAIN_CONFIG = [
+ {
+ webExtension: {
+ id: "engine@search.mozilla.org",
+ name: "Test search engine",
+ search_url: "https://www.google.com/search",
+ params: [
+ {
+ name: "q",
+ value: "{searchTerms}",
+ },
+ {
+ name: "channel",
+ condition: "purpose",
+ purpose: "contextmenu",
+ value: "rcs",
+ },
+ {
+ name: "channel",
+ condition: "purpose",
+ purpose: "keyword",
+ value: "fflb",
+ },
+ ],
+ suggest_url:
+ "https://suggestqueries.google.com/complete/search?output=firefox&client=firefox&hl={moz:locale}&q={searchTerms}",
+ },
+ appliesTo: [{ included: { everywhere: true } }],
+ default: "no",
+ },
+ {
+ webExtension: {
+ id: "engine-chromeicon@search.mozilla.org",
+
+ name: "engine-chromeicon",
+ search_url: "https://www.google.com/search",
+ params: [
+ {
+ name: "q",
+ value: "{searchTerms}",
+ },
+ ],
+ },
+ appliesTo: [{ included: { everywhere: true } }],
+ default: "yes-if-no-other",
+ },
+ {
+ webExtension: {
+ id: "engine-fr@search.mozilla.org",
+ name: "Test search engine (fr)",
+ search_url: "https://www.google.fr/search",
+ params: [
+ {
+ name: "q",
+ value: "{searchTerms}",
+ },
+ {
+ name: "ie",
+ value: "iso-8859-1",
+ },
+ {
+ name: "oe",
+ value: "iso-8859-1",
+ },
+ ],
+ },
+ appliesTo: [
+ { included: { everywhere: true } },
+ {
+ included: { locales: { matches: ["fr"] } },
+ excluded: { regions: ["DE"] },
+ default: "yes",
+ },
+ ],
+ default: "no",
+ },
+ {
+ webExtension: {
+ id: "engine-pref@search.mozilla.org",
+ name: "engine-pref",
+ search_url: "https://www.google.com/search",
+ params: [
+ {
+ name: "q",
+ value: "{searchTerms}",
+ },
+ {
+ name: "code",
+ condition: "pref",
+ pref: "code",
+ },
+ {
+ name: "test",
+ condition: "pref",
+ pref: "test",
+ },
+ ],
+ },
+ appliesTo: [
+ { included: { everywhere: true } },
+ { included: { regions: ["DE"] }, default: "yes" },
+ ],
+ default: "no",
+ },
+ {
+ webExtension: {
+ id: "engine2@search.mozilla.org",
+ name: "A second test engine",
+ search_url: "https://duckduckgo.com/?q={searchTerms}",
+ },
+ appliesTo: [
+ { included: { everywhere: true } },
+ { included: { everywhere: true }, experiment: "test1", default: "yes" },
+ ],
+ default: "no",
+ },
+];
+
+const testSearchEngine = {
+ id: "engine",
+ name: "Test search engine",
+ loadPath: SearchUtils.newSearchConfigEnabled
+ ? "[app]engine@search.mozilla.org"
+ : "[addon]engine@search.mozilla.org",
+ submissionURL: "https://www.google.com/search?q=",
+};
+const testChromeIconEngine = {
+ id: "engine-chromeicon",
+ name: "engine-chromeicon",
+ loadPath: SearchUtils.newSearchConfigEnabled
+ ? "[app]engine-chromeicon@search.mozilla.org"
+ : "[addon]engine-chromeicon@search.mozilla.org",
+
+ submissionURL: "https://www.google.com/search?q=",
+};
+const testFrEngine = {
+ id: "engine-fr",
+ name: "Test search engine (fr)",
+ loadPath: SearchUtils.newSearchConfigEnabled
+ ? "[app]engine-fr@search.mozilla.org"
+ : "[addon]engine-fr@search.mozilla.org",
+ submissionURL: "https://www.google.fr/search?q=&ie=iso-8859-1&oe=iso-8859-1",
+};
+const testPrefEngine = {
+ id: "engine-pref",
+ name: "engine-pref",
+ loadPath: SearchUtils.newSearchConfigEnabled
+ ? "[app]engine-pref@search.mozilla.org"
+ : "[addon]engine-pref@search.mozilla.org",
+ submissionURL: "https://www.google.com/search?q=",
+};
+const testEngine2 = {
+ id: "engine2",
+ name: "A second test engine",
+ loadPath: SearchUtils.newSearchConfigEnabled
+ ? "[app]engine2@search.mozilla.org"
+ : "[addon]engine2@search.mozilla.org",
+ submissionURL: "https://duckduckgo.com/?q=",
+};
+
+function clearTelemetry() {
+ Services.telemetry.clearEvents();
+ Services.fog.testResetFOG();
+}
+
+async function checkTelemetry(
+ source,
+ prevEngine,
+ newEngine,
+ checkPrivate = false,
+ additionalEventsExpected = false
+) {
+ // TODO Bug 1876178 - Improve engine change telemetry.
+ // When we reload engines due to a config change, we update the engines as
+ // they may have changed, we don't track if any attribute has actually changed
+ // from previous, and so we send out an update regardless. This is why in
+ // this test we test for the additional `engine-update` event that's recorded.
+ // In future, we should be more specific about when to record the event and
+ // so only one event is captured and not two.
+ let additionalEvent = [
+ {
+ object: checkPrivate ? "change_private" : "change_default",
+ value: "engine-update",
+ extra: {
+ prev_id: prevEngine?.id ?? "",
+ new_id: prevEngine?.id ?? "",
+ new_name: prevEngine?.name ?? "",
+ new_load_path: prevEngine?.loadPath ?? "",
+ // Telemetry has a limit of 80 characters.
+ new_sub_url: prevEngine?.submissionURL.slice(0, 80) ?? "",
+ },
+ },
+ ];
+
+ TelemetryTestUtils.assertEvents(
+ [
+ ...(additionalEventsExpected ? additionalEvent : []),
+ {
+ object: checkPrivate ? "change_private" : "change_default",
+ value: source,
+ extra: {
+ prev_id: prevEngine?.id ?? "",
+ new_id: newEngine?.id ?? "",
+ new_name: newEngine?.name ?? "",
+ new_load_path: newEngine?.loadPath ?? "",
+ // Telemetry has a limit of 80 characters.
+ new_sub_url: newEngine?.submissionURL.slice(0, 80) ?? "",
+ },
+ },
+ ],
+ { category: "search", method: "engine" }
+ );
+
+ let snapshot;
+ if (checkPrivate) {
+ snapshot = await Glean.searchEnginePrivate.changed.testGetValue();
+ } else {
+ snapshot = await Glean.searchEngineDefault.changed.testGetValue();
+ }
+
+ if (additionalEventsExpected) {
+ delete snapshot[0].timestamp;
+ Assert.deepEqual(
+ snapshot[0],
+ {
+ category: checkPrivate
+ ? "search.engine.private"
+ : "search.engine.default",
+ name: "changed",
+ extra: {
+ change_source: "engine-update",
+ previous_engine_id: prevEngine?.id ?? "",
+ new_engine_id: prevEngine?.id ?? "",
+ new_display_name: prevEngine?.name ?? "",
+ new_load_path: prevEngine?.loadPath ?? "",
+ new_submission_url: prevEngine?.submissionURL ?? "",
+ },
+ },
+ "Should have received the correct event details"
+ );
+ snapshot.shift();
+ }
+
+ delete snapshot[0].timestamp;
+ Assert.deepEqual(
+ snapshot[0],
+ {
+ category: checkPrivate
+ ? "search.engine.private"
+ : "search.engine.default",
+ name: "changed",
+ extra: {
+ change_source: source,
+ previous_engine_id: prevEngine?.id ?? "",
+ new_engine_id: newEngine?.id ?? "",
+ new_display_name: newEngine?.name ?? "",
+ new_load_path: newEngine?.loadPath ?? "",
+ new_submission_url: newEngine?.submissionURL ?? "",
+ },
+ },
+ "Should have received the correct event details"
+ );
+}
+
+let getVariableStub;
+
+add_setup(async () => {
+ Region._setHomeRegion("US", false);
+ Services.locale.availableLocales = [
+ ...Services.locale.availableLocales,
+ "en",
+ "fr",
+ ];
+ Services.locale.requestedLocales = ["en"];
+
+ sinon.spy(NimbusFeatures.searchConfiguration, "onUpdate");
+ sinon.stub(NimbusFeatures.searchConfiguration, "ready").resolves();
+ getVariableStub = sinon.stub(
+ NimbusFeatures.searchConfiguration,
+ "getVariable"
+ );
+ getVariableStub.returns(null);
+
+ SearchTestUtils.useMockIdleService();
+ Services.fog.initializeFOG();
+ sinon.stub(
+ Services.search.wrappedJSObject,
+ "_showRemovalOfSearchEngineNotificationBox"
+ );
+
+ await SearchTestUtils.useTestEngines("data", null, BASE_CONFIG);
+ await AddonTestUtils.promiseStartupManager();
+
+ await Services.search.init();
+});
+
+add_task(async function test_configuration_changes_default() {
+ clearTelemetry();
+
+ await SearchTestUtils.updateRemoteSettingsConfig(MAIN_CONFIG);
+
+ await checkTelemetry(
+ "config",
+ testSearchEngine,
+ testChromeIconEngine,
+ false,
+ true
+ );
+});
+
+add_task(async function test_experiment_changes_default() {
+ clearTelemetry();
+
+ let reloadObserved =
+ SearchTestUtils.promiseSearchNotification("engines-reloaded");
+ getVariableStub.callsFake(name => (name == "experiment" ? "test1" : null));
+ NimbusFeatures.searchConfiguration.onUpdate.firstCall.args[0]();
+ await reloadObserved;
+
+ await checkTelemetry(
+ "experiment",
+ testChromeIconEngine,
+ testEngine2,
+ false,
+ true
+ );
+
+ // Reset the stub so that we are no longer in an experiment.
+ getVariableStub.returns(null);
+});
+
+add_task(async function test_locale_changes_default() {
+ clearTelemetry();
+
+ let reloadObserved =
+ SearchTestUtils.promiseSearchNotification("engines-reloaded");
+ Services.locale.requestedLocales = ["fr"];
+ await reloadObserved;
+
+ await checkTelemetry("locale", testEngine2, testFrEngine, false, true);
+});
+
+add_task(async function test_region_changes_default() {
+ clearTelemetry();
+
+ let reloadObserved =
+ SearchTestUtils.promiseSearchNotification("engines-reloaded");
+ Region._setHomeRegion("DE", true);
+ await reloadObserved;
+
+ await checkTelemetry("region", testFrEngine, testPrefEngine, false, true);
+});
+
+add_task(async function test_user_changes_separate_private_pref() {
+ Services.prefs.setBoolPref(
+ SearchUtils.BROWSER_SEARCH_PREF + "separatePrivateDefault.ui.enabled",
+ true
+ );
+ Services.prefs.setBoolPref(
+ SearchUtils.BROWSER_SEARCH_PREF + "separatePrivateDefault",
+ true
+ );
+
+ await Services.search.setDefaultPrivate(
+ Services.search.getEngineByName("engine-chromeicon"),
+ Ci.nsISearchService.CHANGE_REASON_UNKNOWN
+ );
+
+ Assert.notEqual(
+ await Services.search.getDefault(),
+ await Services.search.getDefaultPrivate(),
+ "Should have different engines for the pre-condition"
+ );
+
+ Services.prefs.setBoolPref(
+ SearchUtils.BROWSER_SEARCH_PREF + "separatePrivateDefault.ui.enabled",
+ false
+ );
+
+ clearTelemetry();
+
+ Services.prefs.setBoolPref(
+ SearchUtils.BROWSER_SEARCH_PREF + "separatePrivateDefault",
+ false
+ );
+
+ await checkTelemetry("user_private_split", testChromeIconEngine, null, true);
+
+ getVariableStub.returns(null);
+});
+
+add_task(async function test_experiment_with_separate_default_notifies() {
+ Services.prefs.setBoolPref(
+ SearchUtils.BROWSER_SEARCH_PREF + "separatePrivateDefault.ui.enabled",
+ false
+ );
+ Services.prefs.setBoolPref(
+ SearchUtils.BROWSER_SEARCH_PREF + "separatePrivateDefault",
+ true
+ );
+
+ clearTelemetry();
+
+ getVariableStub.callsFake(name =>
+ name == "seperatePrivateDefaultUIEnabled" ? true : null
+ );
+ NimbusFeatures.searchConfiguration.onUpdate.firstCall.args[0]();
+
+ await checkTelemetry("experiment", null, testChromeIconEngine, true);
+
+ clearTelemetry();
+
+ // Reset the stub so that we are no longer in an experiment.
+ getVariableStub.returns(null);
+ NimbusFeatures.searchConfiguration.onUpdate.firstCall.args[0]();
+
+ await checkTelemetry("experiment", testChromeIconEngine, null, true);
+});
+
+add_task(async function test_default_engine_update() {
+ clearTelemetry();
+ let extension = await SearchTestUtils.installSearchExtension(
+ {
+ name: "engine",
+ id: "engine@tests.mozilla.org",
+ search_url_get_params: `q={searchTerms}&version=1.0`,
+ search_url: "https://www.google.com/search",
+ version: "1.0",
+ },
+ { skipUnload: true }
+ );
+ let engine = Services.search.getEngineByName("engine");
+
+ Assert.ok(!!engine, "Should have loaded the engine");
+
+ await Services.search.setDefault(
+ engine,
+ Ci.nsISearchService.CHANGE_REASON_UNKNOWN
+ );
+
+ clearTelemetry();
+
+ let promiseChanged = TestUtils.topicObserved(
+ "browser-search-engine-modified",
+ (eng, verb) => verb == "engine-changed"
+ );
+ let manifest = SearchTestUtils.createEngineManifest({
+ name: "Bar",
+ id: "engine@tests.mozilla.org",
+ search_url_get_params: `q={searchTerms}&version=2.0`,
+ search_url: "https://www.google.com/search",
+ version: "2.0",
+ });
+
+ await extension.upgrade({
+ useAddonManager: "permanent",
+ manifest,
+ });
+ await AddonTestUtils.waitForSearchProviderStartup(extension);
+ await promiseChanged;
+
+ const defaultEngineData = {
+ id: engine.telemetryId,
+ name: "Bar",
+ loadPath: engine.wrappedJSObject._loadPath,
+ submissionURL: "https://www.google.com/search?q=&version=2.0",
+ };
+ await checkTelemetry("engine-update", defaultEngineData, defaultEngineData);
+ await extension.unload();
+});