summaryrefslogtreecommitdiffstats
path: root/browser/components/urlbar/tests/browser/browser_urlbar_telemetry_tabtosearch.js
diff options
context:
space:
mode:
Diffstat (limited to 'browser/components/urlbar/tests/browser/browser_urlbar_telemetry_tabtosearch.js')
-rw-r--r--browser/components/urlbar/tests/browser/browser_urlbar_telemetry_tabtosearch.js416
1 files changed, 416 insertions, 0 deletions
diff --git a/browser/components/urlbar/tests/browser/browser_urlbar_telemetry_tabtosearch.js b/browser/components/urlbar/tests/browser/browser_urlbar_telemetry_tabtosearch.js
new file mode 100644
index 0000000000..35607d2f94
--- /dev/null
+++ b/browser/components/urlbar/tests/browser/browser_urlbar_telemetry_tabtosearch.js
@@ -0,0 +1,416 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/**
+ * This file tests telemetry for tabtosearch results.
+ * NB: This file does not test the search mode `entry` field for tab-to-search
+ * results. That is tested in browser_UsageTelemetry_urlbar_searchmode.js.
+ */
+
+"use strict";
+
+const ENGINE_NAME = "MozSearch";
+const ENGINE_DOMAIN = "example.com";
+
+ChromeUtils.defineESModuleGetters(this, {
+ UrlbarProviderTabToSearch:
+ "resource:///modules/UrlbarProviderTabToSearch.sys.mjs",
+});
+
+function snapshotHistograms() {
+ Services.telemetry.clearScalars();
+ Services.telemetry.clearEvents();
+ return {
+ resultMethodHist: TelemetryTestUtils.getAndClearHistogram(
+ "FX_URLBAR_SELECTED_RESULT_METHOD"
+ ),
+ search_hist: TelemetryTestUtils.getAndClearKeyedHistogram("SEARCH_COUNTS"),
+ };
+}
+
+function assertTelemetryResults(histograms, type, index, method) {
+ TelemetryTestUtils.assertHistogram(histograms.resultMethodHist, method, 1);
+
+ TelemetryTestUtils.assertKeyedScalar(
+ TelemetryTestUtils.getProcessScalars("parent", true, true),
+ `urlbar.picked.${type}`,
+ index,
+ 1
+ );
+}
+
+/**
+ * Checks to see if the second result in the Urlbar is a tab-to-search result
+ * with the correct engine.
+ *
+ * @param {string} engineName
+ * The expected engine name.
+ * @param {boolean} [isOnboarding]
+ * If true, expects the tab-to-search result to be an onbarding result.
+ */
+async function checkForTabToSearchResult(engineName, isOnboarding) {
+ Assert.ok(UrlbarTestUtils.isPopupOpen(window), "Popup should be open.");
+ let tabToSearchResult = (
+ await UrlbarTestUtils.waitForAutocompleteResultAt(window, 1)
+ ).result;
+ Assert.equal(
+ tabToSearchResult.providerName,
+ "TabToSearch",
+ "The second result is a tab-to-search result."
+ );
+ Assert.equal(
+ tabToSearchResult.payload.engine,
+ engineName,
+ "The tab-to-search result is for the first engine."
+ );
+ if (isOnboarding) {
+ Assert.equal(
+ tabToSearchResult.payload.dynamicType,
+ "onboardTabToSearch",
+ "The tab-to-search result is an onboarding result."
+ );
+ } else {
+ Assert.ok(
+ !tabToSearchResult.payload.dynamicType,
+ "The tab-to-search result should not be an onboarding result."
+ );
+ }
+}
+
+add_setup(async function () {
+ await SpecialPowers.pushPrefEnv({
+ set: [["browser.urlbar.tabToSearch.onboard.interactionsLeft", 0]],
+ });
+
+ await SearchTestUtils.installSearchExtension({
+ name: ENGINE_NAME,
+ search_url: `https://${ENGINE_DOMAIN}/`,
+ });
+
+ // Reset the enginesShown sets in case a previous test showed a tab-to-search
+ // result but did not end its engagement.
+ UrlbarProviderTabToSearch.enginesShown.regular.clear();
+ UrlbarProviderTabToSearch.enginesShown.onboarding.clear();
+
+ // Enable local telemetry recording for the duration of the tests.
+ let oldCanRecord = Services.telemetry.canRecordExtended;
+ Services.telemetry.canRecordExtended = true;
+
+ registerCleanupFunction(async () => {
+ Services.telemetry.canRecordExtended = oldCanRecord;
+ });
+});
+
+add_task(async function test() {
+ await BrowserTestUtils.withNewTab("about:blank", async () => {
+ const histograms = snapshotHistograms();
+
+ for (let i = 0; i < 3; i++) {
+ await PlacesTestUtils.addVisits([`https://${ENGINE_DOMAIN}/`]);
+ }
+
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ window,
+ value: ENGINE_DOMAIN.slice(0, 4),
+ fireInputEvent: true,
+ });
+
+ let tabToSearchResult = (
+ await UrlbarTestUtils.waitForAutocompleteResultAt(window, 1)
+ ).result;
+ Assert.equal(
+ tabToSearchResult.providerName,
+ "TabToSearch",
+ "The second result is a tab-to-search result."
+ );
+ Assert.equal(
+ tabToSearchResult.payload.engine,
+ ENGINE_NAME,
+ "The tab-to-search result is for the correct engine."
+ );
+ EventUtils.synthesizeKey("KEY_ArrowDown");
+ Assert.equal(
+ UrlbarTestUtils.getSelectedRowIndex(window),
+ 1,
+ "Sanity check: The second result is selected."
+ );
+
+ // Select the tab-to-search result.
+ let searchPromise = UrlbarTestUtils.promiseSearchComplete(window);
+ EventUtils.synthesizeKey("KEY_Enter");
+ await searchPromise;
+
+ await UrlbarTestUtils.assertSearchMode(window, {
+ engineName: ENGINE_NAME,
+ entry: "tabtosearch",
+ });
+
+ assertTelemetryResults(
+ histograms,
+ "tabtosearch",
+ 1,
+ UrlbarTestUtils.SELECTED_RESULT_METHODS.arrowEnterSelection
+ );
+
+ await UrlbarTestUtils.exitSearchMode(window);
+ await UrlbarTestUtils.promisePopupClose(window, () => {
+ gURLBar.blur();
+ });
+ await PlacesUtils.history.clear();
+ });
+
+ Services.telemetry.clearScalars();
+ Services.telemetry.clearEvents();
+});
+
+add_task(async function impressions() {
+ await SpecialPowers.pushPrefEnv({
+ set: [["browser.urlbar.tabToSearch.onboard.interactionsLeft", 0]],
+ });
+ await impressions_test(false);
+ await SpecialPowers.popPrefEnv();
+});
+
+add_task(async function onboarding_impressions() {
+ await SpecialPowers.pushPrefEnv({
+ set: [["browser.urlbar.tabToSearch.onboard.interactionsLeft", 3]],
+ });
+ await impressions_test(true);
+ await SpecialPowers.popPrefEnv();
+ delete UrlbarProviderTabToSearch.onboardingInteractionAtTime;
+});
+
+async function impressions_test(isOnboarding) {
+ await BrowserTestUtils.withNewTab("about:blank", async browser => {
+ const firstEngineHost = "example";
+ let extension = await SearchTestUtils.installSearchExtension(
+ {
+ name: `${ENGINE_NAME}2`,
+ search_url: `https://${firstEngineHost}-2.com/`,
+ },
+ { skipUnload: true }
+ );
+
+ for (let i = 0; i < 3; i++) {
+ await PlacesTestUtils.addVisits([`https://${firstEngineHost}-2.com`]);
+ await PlacesTestUtils.addVisits([`https://${ENGINE_DOMAIN}/`]);
+ }
+
+ // First do multiple searches for substrings of firstEngineHost. The view
+ // should show the same tab-to-search onboarding result the entire time, so
+ // we should not continue to increment urlbar.tips.
+ for (let i = 1; i < firstEngineHost.length; i++) {
+ info(
+ `Search for "${firstEngineHost.slice(
+ 0,
+ i
+ )}". Only record one impression for this sequence.`
+ );
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ window,
+ value: firstEngineHost.slice(0, i),
+ fireInputEvent: true,
+ });
+ await checkForTabToSearchResult(ENGINE_NAME, isOnboarding);
+ }
+
+ await UrlbarTestUtils.promisePopupClose(window, () => gURLBar.blur());
+ let scalars = TelemetryTestUtils.getProcessScalars("parent", true);
+ TelemetryTestUtils.assertKeyedScalar(
+ scalars,
+ "urlbar.tips",
+ isOnboarding ? "tabtosearch_onboard-shown" : "tabtosearch-shown",
+ 1
+ );
+ TelemetryTestUtils.assertKeyedScalar(
+ scalars,
+ isOnboarding
+ ? "urlbar.tabtosearch.impressions_onboarding"
+ : "urlbar.tabtosearch.impressions",
+ // "other" is recorded as the engine name because we're not using a built-in engine.
+ "other",
+ 1
+ );
+
+ info("Type through autofill to second engine hostname. Record impression.");
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ window,
+ value: firstEngineHost,
+ fireInputEvent: true,
+ });
+ await checkForTabToSearchResult(ENGINE_NAME, isOnboarding);
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ window,
+ value: `${firstEngineHost}-`,
+ fireInputEvent: true,
+ });
+ await checkForTabToSearchResult(`${ENGINE_NAME}2`, isOnboarding);
+ await UrlbarTestUtils.promisePopupClose(window, () => gURLBar.blur());
+ // Since the user typed past the autofill for the first engine, we showed a
+ // different onboarding result and now we increment
+ // tabtosearch_onboard-shown.
+ scalars = TelemetryTestUtils.getProcessScalars("parent", true);
+ TelemetryTestUtils.assertKeyedScalar(
+ scalars,
+ "urlbar.tips",
+ isOnboarding ? "tabtosearch_onboard-shown" : "tabtosearch-shown",
+ 3
+ );
+ TelemetryTestUtils.assertKeyedScalar(
+ scalars,
+ isOnboarding
+ ? "urlbar.tabtosearch.impressions_onboarding"
+ : "urlbar.tabtosearch.impressions",
+ "other",
+ 3
+ );
+
+ info("Make a typo and return to autofill. Do not record impression.");
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ window,
+ value: `${firstEngineHost}-`,
+ fireInputEvent: true,
+ });
+ await checkForTabToSearchResult(`${ENGINE_NAME}2`, isOnboarding);
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ window,
+ value: `${firstEngineHost}-3`,
+ fireInputEvent: true,
+ });
+ Assert.equal(
+ UrlbarTestUtils.getResultCount(window),
+ 1,
+ "We are not showing a tab-to-search result."
+ );
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ window,
+ value: `${firstEngineHost}-2`,
+ fireInputEvent: true,
+ });
+ await checkForTabToSearchResult(`${ENGINE_NAME}2`, isOnboarding);
+ await UrlbarTestUtils.promisePopupClose(window, () => gURLBar.blur());
+ scalars = TelemetryTestUtils.getProcessScalars("parent", true);
+ TelemetryTestUtils.assertKeyedScalar(
+ scalars,
+ "urlbar.tips",
+ isOnboarding ? "tabtosearch_onboard-shown" : "tabtosearch-shown",
+ 4
+ );
+ TelemetryTestUtils.assertKeyedScalar(
+ scalars,
+ isOnboarding
+ ? "urlbar.tabtosearch.impressions_onboarding"
+ : "urlbar.tabtosearch.impressions",
+ "other",
+ 4
+ );
+
+ info(
+ "Cancel then restart autofill. Continue to show the tab-to-search result."
+ );
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ window,
+ value: `${firstEngineHost}-2`,
+ fireInputEvent: true,
+ });
+ await checkForTabToSearchResult(`${ENGINE_NAME}2`, isOnboarding);
+ let searchPromise = UrlbarTestUtils.promiseSearchComplete(window);
+ EventUtils.synthesizeKey("KEY_Backspace");
+ await searchPromise;
+ await checkForTabToSearchResult(`${ENGINE_NAME}2`, isOnboarding);
+ searchPromise = UrlbarTestUtils.promiseSearchComplete(window);
+ // Type the "." from `example-2.com`.
+ EventUtils.synthesizeKey(".");
+ await searchPromise;
+ await checkForTabToSearchResult(`${ENGINE_NAME}2`, isOnboarding);
+ await UrlbarTestUtils.promisePopupClose(window, () => gURLBar.blur());
+ scalars = TelemetryTestUtils.getProcessScalars("parent", true);
+ TelemetryTestUtils.assertKeyedScalar(
+ scalars,
+ "urlbar.tips",
+ isOnboarding ? "tabtosearch_onboard-shown" : "tabtosearch-shown",
+ 5
+ );
+ TelemetryTestUtils.assertKeyedScalar(
+ scalars,
+ isOnboarding
+ ? "urlbar.tabtosearch.impressions_onboarding"
+ : "urlbar.tabtosearch.impressions",
+ // "other" is recorded as the engine name because we're not using a built-in engine.
+ "other",
+ 5
+ );
+
+ // See javadoc for UrlbarProviderTabToSearch.onEngagement for discussion
+ // about retained results.
+ info("Reopen the result set with retained results. Record impression.");
+ await UrlbarTestUtils.promisePopupOpen(window, () => {
+ EventUtils.synthesizeMouseAtCenter(gURLBar.inputField, {});
+ });
+ await checkForTabToSearchResult(`${ENGINE_NAME}2`, isOnboarding);
+ await UrlbarTestUtils.promisePopupClose(window, () => gURLBar.blur());
+ scalars = TelemetryTestUtils.getProcessScalars("parent", true);
+ TelemetryTestUtils.assertKeyedScalar(
+ scalars,
+ "urlbar.tips",
+ isOnboarding ? "tabtosearch_onboard-shown" : "tabtosearch-shown",
+ 6
+ );
+ TelemetryTestUtils.assertKeyedScalar(
+ scalars,
+ isOnboarding
+ ? "urlbar.tabtosearch.impressions_onboarding"
+ : "urlbar.tabtosearch.impressions",
+ "other",
+ 6
+ );
+
+ info(
+ "Open a result page and then autofill engine host. Record impression."
+ );
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ window,
+ value: firstEngineHost,
+ fireInputEvent: true,
+ });
+ await checkForTabToSearchResult(ENGINE_NAME, isOnboarding);
+ // Press enter on the heuristic result so we visit example.com without
+ // doing an additional search.
+ let loadPromise = BrowserTestUtils.browserLoaded(browser);
+ EventUtils.synthesizeKey("KEY_Enter");
+ await loadPromise;
+ // Click the Urlbar and type to simulate what a user would actually do. If
+ // we use promiseAutocompleteResultPopup, no query would be made between
+ // this one and the previous tab-to-search query. Thus
+ // `onboardingEnginesShown` would not be cleared. This would not happen
+ // in real-world usage.
+ await UrlbarTestUtils.promisePopupOpen(window, () => {
+ EventUtils.synthesizeMouseAtCenter(gURLBar.inputField, {});
+ });
+ searchPromise = UrlbarTestUtils.promiseSearchComplete(window);
+ EventUtils.synthesizeKey(firstEngineHost.slice(0, 4));
+ await searchPromise;
+ await checkForTabToSearchResult(ENGINE_NAME, isOnboarding);
+ await UrlbarTestUtils.promisePopupClose(window, () => gURLBar.blur());
+ // We clear the scalar this time.
+ scalars = TelemetryTestUtils.getProcessScalars("parent", true, true);
+ TelemetryTestUtils.assertKeyedScalar(
+ scalars,
+ "urlbar.tips",
+ isOnboarding ? "tabtosearch_onboard-shown" : "tabtosearch-shown",
+ 8
+ );
+ TelemetryTestUtils.assertKeyedScalar(
+ scalars,
+ isOnboarding
+ ? "urlbar.tabtosearch.impressions_onboarding"
+ : "urlbar.tabtosearch.impressions",
+ "other",
+ 8
+ );
+
+ await PlacesUtils.history.clear();
+ await extension.unload();
+ });
+}