summaryrefslogtreecommitdiffstats
path: root/browser/base/content/test/urlbar/browser_urlbarSearchTelemetry.js
diff options
context:
space:
mode:
Diffstat (limited to 'browser/base/content/test/urlbar/browser_urlbarSearchTelemetry.js')
-rw-r--r--browser/base/content/test/urlbar/browser_urlbarSearchTelemetry.js216
1 files changed, 216 insertions, 0 deletions
diff --git a/browser/base/content/test/urlbar/browser_urlbarSearchTelemetry.js b/browser/base/content/test/urlbar/browser_urlbarSearchTelemetry.js
new file mode 100644
index 000000000..8c28401ea
--- /dev/null
+++ b/browser/base/content/test/urlbar/browser_urlbarSearchTelemetry.js
@@ -0,0 +1,216 @@
+"use strict";
+
+Cu.import("resource:///modules/BrowserUITelemetry.jsm");
+
+const SUGGEST_URLBAR_PREF = "browser.urlbar.suggest.searches";
+const TEST_ENGINE_BASENAME = "searchSuggestionEngine.xml";
+
+// Must run first.
+add_task(function* prepare() {
+ Services.prefs.setBoolPref(SUGGEST_URLBAR_PREF, true);
+ let engine = yield promiseNewSearchEngine(TEST_ENGINE_BASENAME);
+ let oldCurrentEngine = Services.search.currentEngine;
+ Services.search.currentEngine = engine;
+
+ registerCleanupFunction(function* () {
+ Services.prefs.clearUserPref(SUGGEST_URLBAR_PREF);
+ Services.search.currentEngine = oldCurrentEngine;
+
+ // Clicking urlbar results causes visits to their associated pages, so clear
+ // that history now.
+ yield PlacesTestUtils.clearHistory();
+
+ // Make sure the popup is closed for the next test.
+ gURLBar.blur();
+ Assert.ok(!gURLBar.popup.popupOpen, "popup should be closed");
+ });
+
+ // Move the mouse away from the urlbar one-offs so that a one-off engine is
+ // not inadvertently selected.
+ yield new Promise(resolve => {
+ EventUtils.synthesizeNativeMouseMove(window.document.documentElement, 0, 0,
+ resolve);
+ });
+});
+
+add_task(function* heuristicResultMouse() {
+ yield compareCounts(function* () {
+ let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser);
+ gURLBar.focus();
+ yield promiseAutocompleteResultPopup("heuristicResult");
+ let action = getActionAtIndex(0);
+ Assert.ok(!!action, "there should be an action at index 0");
+ Assert.equal(action.type, "searchengine", "type should be searchengine");
+ let loadPromise = BrowserTestUtils.browserLoaded(tab.linkedBrowser);
+ gURLBar.popup.richlistbox.getItemAtIndex(0).click();
+ yield loadPromise;
+ yield BrowserTestUtils.removeTab(tab);
+ });
+});
+
+add_task(function* heuristicResultKeyboard() {
+ yield compareCounts(function* () {
+ let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser);
+ gURLBar.focus();
+ yield promiseAutocompleteResultPopup("heuristicResult");
+ let action = getActionAtIndex(0);
+ Assert.ok(!!action, "there should be an action at index 0");
+ Assert.equal(action.type, "searchengine", "type should be searchengine");
+ let loadPromise = BrowserTestUtils.browserLoaded(tab.linkedBrowser);
+ EventUtils.sendKey("return");
+ yield loadPromise;
+ yield BrowserTestUtils.removeTab(tab);
+ });
+});
+
+add_task(function* searchSuggestionMouse() {
+ yield compareCounts(function* () {
+ let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser);
+ gURLBar.focus();
+ yield promiseAutocompleteResultPopup("searchSuggestion");
+ let idx = getFirstSuggestionIndex();
+ Assert.ok(idx >= 0, "there should be a first suggestion");
+ let loadPromise = BrowserTestUtils.browserLoaded(tab.linkedBrowser);
+ gURLBar.popup.richlistbox.getItemAtIndex(idx).click();
+ yield loadPromise;
+ yield BrowserTestUtils.removeTab(tab);
+ });
+});
+
+add_task(function* searchSuggestionKeyboard() {
+ yield compareCounts(function* () {
+ let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser);
+ gURLBar.focus();
+ yield promiseAutocompleteResultPopup("searchSuggestion");
+ let idx = getFirstSuggestionIndex();
+ Assert.ok(idx >= 0, "there should be a first suggestion");
+ let loadPromise = BrowserTestUtils.browserLoaded(tab.linkedBrowser);
+ while (idx--) {
+ EventUtils.sendKey("down");
+ }
+ EventUtils.sendKey("return");
+ yield loadPromise;
+ yield BrowserTestUtils.removeTab(tab);
+ });
+});
+
+/**
+ * This does three things: gets current telemetry/FHR counts, calls
+ * clickCallback, gets telemetry/FHR counts again to compare them to the old
+ * counts.
+ *
+ * @param clickCallback Use this to open the urlbar popup and choose and click a
+ * result.
+ */
+function* compareCounts(clickCallback) {
+ // Search events triggered by clicks (not the Return key in the urlbar) are
+ // recorded in three places:
+ // * BrowserUITelemetry
+ // * Telemetry histogram named "SEARCH_COUNTS"
+ // * FHR
+
+ let engine = Services.search.currentEngine;
+ let engineID = "org.mozilla.testsearchsuggestions";
+
+ // First, get the current counts.
+
+ // BrowserUITelemetry
+ let uiTelemCount = 0;
+ let bucket = BrowserUITelemetry.currentBucket;
+ let events = BrowserUITelemetry.getToolbarMeasures().countableEvents;
+ if (events[bucket] &&
+ events[bucket].search &&
+ events[bucket].search.urlbar) {
+ uiTelemCount = events[bucket].search.urlbar;
+ }
+
+ // telemetry histogram SEARCH_COUNTS
+ let histogramCount = 0;
+ let histogramKey = engineID + ".urlbar";
+ let histogram;
+ try {
+ histogram = Services.telemetry.getKeyedHistogramById("SEARCH_COUNTS");
+ } catch (ex) {
+ // No searches performed yet, not a problem.
+ }
+ if (histogram) {
+ let snapshot = histogram.snapshot();
+ if (histogramKey in snapshot) {
+ histogramCount = snapshot[histogramKey].sum;
+ }
+ }
+
+ // FHR -- first make sure the engine has an identifier so that FHR is happy.
+ Object.defineProperty(engine.wrappedJSObject, "identifier",
+ { value: engineID });
+
+ gURLBar.focus();
+ yield clickCallback();
+
+ // Now get the new counts and compare them to the old.
+
+ // BrowserUITelemetry
+ events = BrowserUITelemetry.getToolbarMeasures().countableEvents;
+ Assert.ok(bucket in events, "bucket should be recorded");
+ events = events[bucket];
+ Assert.ok("search" in events, "search should be recorded");
+ events = events.search;
+ Assert.ok("urlbar" in events, "urlbar should be recorded");
+ Assert.equal(events.urlbar, uiTelemCount + 1,
+ "clicked suggestion should be recorded");
+
+ // telemetry histogram SEARCH_COUNTS
+ histogram = Services.telemetry.getKeyedHistogramById("SEARCH_COUNTS");
+ let snapshot = histogram.snapshot();
+ Assert.ok(histogramKey in snapshot, "histogram with key should be recorded");
+ Assert.equal(snapshot[histogramKey].sum, histogramCount + 1,
+ "histogram sum should be incremented");
+}
+
+/**
+ * Returns the "action" object at the given index in the urlbar results:
+ * { type, params: {}}
+ *
+ * @param index The index in the urlbar results.
+ * @return An action object, or null if index >= number of results.
+ */
+function getActionAtIndex(index) {
+ let controller = gURLBar.popup.input.controller;
+ if (controller.matchCount <= index) {
+ return null;
+ }
+ let url = controller.getValueAt(index);
+ let mozActionMatch = url.match(/^moz-action:([^,]+),(.*)$/);
+ if (!mozActionMatch) {
+ let msg = "result at index " + index + " is not a moz-action: " + url;
+ Assert.ok(false, msg);
+ throw new Error(msg);
+ }
+ let [, type, paramStr] = mozActionMatch;
+ return {
+ type: type,
+ params: JSON.parse(paramStr),
+ };
+}
+
+/**
+ * Returns the index of the first search suggestion in the urlbar results.
+ *
+ * @return An index, or -1 if there are no search suggestions.
+ */
+function getFirstSuggestionIndex() {
+ let controller = gURLBar.popup.input.controller;
+ let matchCount = controller.matchCount;
+ for (let i = 0; i < matchCount; i++) {
+ let url = controller.getValueAt(i);
+ let mozActionMatch = url.match(/^moz-action:([^,]+),(.*)$/);
+ if (mozActionMatch) {
+ let [, type, paramStr] = mozActionMatch;
+ let params = JSON.parse(paramStr);
+ if (type == "searchengine" && "searchSuggestion" in params) {
+ return i;
+ }
+ }
+ }
+ return -1;
+}