summaryrefslogtreecommitdiffstats
path: root/browser/components/urlbar/tests/browser/browser_urlbar_telemetry_zeroPrefix.js
diff options
context:
space:
mode:
Diffstat (limited to 'browser/components/urlbar/tests/browser/browser_urlbar_telemetry_zeroPrefix.js')
-rw-r--r--browser/components/urlbar/tests/browser/browser_urlbar_telemetry_zeroPrefix.js266
1 files changed, 266 insertions, 0 deletions
diff --git a/browser/components/urlbar/tests/browser/browser_urlbar_telemetry_zeroPrefix.js b/browser/components/urlbar/tests/browser/browser_urlbar_telemetry_zeroPrefix.js
new file mode 100644
index 0000000000..b331921553
--- /dev/null
+++ b/browser/components/urlbar/tests/browser/browser_urlbar_telemetry_zeroPrefix.js
@@ -0,0 +1,266 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/**
+ * This file tests urlbar telemetry related to the zero-prefix view, i.e., when
+ * the search string is empty.
+ */
+
+"use strict";
+
+const HISTOGRAM_DWELL_TIME = "FX_URLBAR_ZERO_PREFIX_DWELL_TIME_MS";
+const SCALARS = {
+ ABANDONMENT: "urlbar.zeroprefix.abandonment",
+ ENGAGEMENT: "urlbar.zeroprefix.engagement",
+ EXPOSURE: "urlbar.zeroprefix.exposure",
+};
+
+add_setup(async function () {
+ await PlacesUtils.history.clear();
+ await PlacesUtils.bookmarks.eraseEverything();
+ Services.telemetry.clearScalars();
+
+ await SearchTestUtils.installSearchExtension({}, { setAsDefault: true });
+ await updateTopSitesAndAwaitChanged();
+});
+
+// zero prefix engagement
+add_task(async function engagement() {
+ let dwellHistogram =
+ TelemetryTestUtils.getAndClearHistogram(HISTOGRAM_DWELL_TIME);
+
+ await BrowserTestUtils.withNewTab("about:blank", async () => {
+ await showZeroPrefix();
+ checkScalars({
+ [SCALARS.EXPOSURE]: 1,
+ });
+ checkAndClearHistogram(dwellHistogram, false);
+
+ info("Finding row with result type URL");
+ let foundURLRow = false;
+ let count = UrlbarTestUtils.getResultCount(window);
+ for (let i = 0; i < count && !foundURLRow; i++) {
+ EventUtils.synthesizeKey("KEY_ArrowDown");
+ let index = UrlbarTestUtils.getSelectedRowIndex(window);
+ Assert.equal(index, i, "The expected row index should be selected");
+ let details = await UrlbarTestUtils.getDetailsOfResultAt(window, i);
+ info(`Checked row at index ${i}, result type is: ${details.type}`);
+ if (details.type == UrlbarUtils.RESULT_TYPE.URL) {
+ foundURLRow = true;
+ }
+ }
+ Assert.ok(foundURLRow, "Should have found a row with result type URL");
+
+ let loadPromise = BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
+ EventUtils.synthesizeKey("KEY_Enter");
+ await loadPromise;
+ });
+
+ checkScalars({
+ [SCALARS.ENGAGEMENT]: 1,
+ });
+ checkAndClearHistogram(dwellHistogram, true);
+});
+
+// zero prefix abandonment
+add_task(async function abandonment() {
+ let dwellHistogram =
+ TelemetryTestUtils.getAndClearHistogram(HISTOGRAM_DWELL_TIME);
+
+ // Open and close the view twice. The second time the view will used a cached
+ // query context and that shouldn't interfere with telemetry.
+ for (let i = 0; i < 2; i++) {
+ await showZeroPrefix();
+ checkScalars({
+ [SCALARS.EXPOSURE]: 1,
+ });
+ checkAndClearHistogram(dwellHistogram, false);
+
+ await UrlbarTestUtils.promisePopupClose(window, () => gURLBar.blur());
+ checkScalars({
+ [SCALARS.ABANDONMENT]: 1,
+ });
+ dwellHistogram = checkAndClearHistogram(dwellHistogram, true);
+ }
+});
+
+// Shows the zero-prefix view, does some searches, then shows it again by doing
+// a search for an empty string.
+add_task(async function searches() {
+ let dwellHistogram =
+ TelemetryTestUtils.getAndClearHistogram(HISTOGRAM_DWELL_TIME);
+
+ info("Show zero prefix");
+ await showZeroPrefix();
+ checkScalars({
+ [SCALARS.EXPOSURE]: 1,
+ });
+ checkAndClearHistogram(dwellHistogram, false);
+
+ info("Search for 't'");
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ window,
+ value: "t",
+ });
+ checkScalars({});
+ dwellHistogram = checkAndClearHistogram(dwellHistogram, true);
+
+ info("Search for 'te'");
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ window,
+ value: "te",
+ });
+ checkScalars({});
+ checkAndClearHistogram(dwellHistogram, false);
+
+ info("Search for 't'");
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ window,
+ value: "t",
+ });
+ checkScalars({});
+ checkAndClearHistogram(dwellHistogram, false);
+
+ info("Search for ''");
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ window,
+ value: "",
+ });
+ checkScalars({
+ [SCALARS.EXPOSURE]: 1,
+ });
+ checkAndClearHistogram(dwellHistogram, false);
+
+ info("Blur urlbar and close view");
+ await UrlbarTestUtils.promisePopupClose(window, () => gURLBar.blur());
+ checkScalars({
+ [SCALARS.ABANDONMENT]: 1,
+ });
+ checkAndClearHistogram(dwellHistogram, true);
+});
+
+// A zero prefix engagement should not be recorded when the view isn't showing
+// zero prefix.
+add_task(async function notZeroPrefix_engagement() {
+ let dwellHistogram =
+ TelemetryTestUtils.getAndClearHistogram(HISTOGRAM_DWELL_TIME);
+
+ await BrowserTestUtils.withNewTab("about:blank", async () => {
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ window,
+ value: "test",
+ });
+ let loadPromise = BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
+ EventUtils.synthesizeKey("KEY_Enter");
+ await loadPromise;
+ });
+
+ checkScalars({});
+ checkAndClearHistogram(dwellHistogram, false);
+});
+
+// A zero prefix abandonment should not be recorded when the view isn't showing
+// zero prefix.
+add_task(async function notZeroPrefix_abandonment() {
+ let dwellHistogram =
+ TelemetryTestUtils.getAndClearHistogram(HISTOGRAM_DWELL_TIME);
+
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ window,
+ value: "test",
+ });
+ await UrlbarTestUtils.promisePopupClose(window, () => gURLBar.blur());
+
+ checkScalars({});
+ checkAndClearHistogram(dwellHistogram, false);
+});
+
+function checkScalars(expected) {
+ let scalars = TelemetryTestUtils.getProcessScalars("parent", false, true);
+ for (let scalar of Object.values(SCALARS)) {
+ if (expected.hasOwnProperty(scalar)) {
+ TelemetryTestUtils.assertScalar(scalars, scalar, expected[scalar]);
+ } else {
+ Assert.ok(
+ !scalars.hasOwnProperty(scalar),
+ "Scalar should not be recorded: " + scalar
+ );
+ }
+ }
+}
+
+function checkAndClearHistogram(histogram, expected) {
+ if (expected) {
+ Assert.deepEqual(
+ Object.values(histogram.snapshot().values).filter(v => v > 0),
+ [1],
+ "Dwell histogram should be updated"
+ );
+ } else {
+ Assert.strictEqual(
+ histogram.snapshot().sum,
+ 0,
+ "Dwell histogram should not be updated"
+ );
+ }
+
+ return TelemetryTestUtils.getAndClearHistogram(histogram.name());
+}
+
+async function showZeroPrefix() {
+ let { promise, cleanup } = waitForQueryFinished();
+ await SimpleTest.promiseFocus(window);
+ await UrlbarTestUtils.promisePopupOpen(window, () =>
+ document.getElementById("Browser:OpenLocation").doCommand()
+ );
+ await promise;
+ cleanup();
+
+ Assert.greater(
+ UrlbarTestUtils.getResultCount(window),
+ 0,
+ "There should be at least one row in the zero prefix view"
+ );
+}
+
+/**
+ * Returns a promise that's resolved on the next `onQueryFinished()`. It's
+ * important to wait for `onQueryFinished()` because that's when the view checks
+ * whether it's showing zero prefix.
+ *
+ * @returns {object}
+ * An object with the following properties:
+ * {Promise} promise
+ * Resolved when `onQueryFinished()` is called.
+ * {Function} cleanup
+ * This should be called to remove the listener.
+ */
+function waitForQueryFinished() {
+ let deferred = PromiseUtils.defer();
+ let listener = {
+ onQueryFinished: () => deferred.resolve(),
+ };
+ gURLBar.controller.addQueryListener(listener);
+
+ return {
+ promise: deferred.promise,
+ cleanup() {
+ gURLBar.controller.removeQueryListener(listener);
+ },
+ };
+}
+
+async function updateTopSitesAndAwaitChanged() {
+ let url = "http://mochi.test:8888/topsite";
+ for (let i = 0; i < 5; i++) {
+ await PlacesTestUtils.addVisits(url);
+ }
+
+ info("Updating top sites and awaiting newtab-top-sites-changed");
+ let changedPromise = TestUtils.topicObserved("newtab-top-sites-changed").then(
+ () => info("Observed newtab-top-sites-changed")
+ );
+ await updateTopSites(sites => sites?.length);
+ await changedPromise;
+}