"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; }