зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1709741 - Rework quick suggest initialization and remote settings sync. r=nanj
I ran into problems while working on tests for bug 1737923 and bug 1737928, where pref changes were causing UrlbarQuickSuggest to recreate its results map and keyword tree and they both ended up becoming empty at random points during the tests. With the way UrlbarQuickSuggest is currently written, there's no way for tests to prevent that from happening, so I came back to this bug and finally finished it. This revision makes these changes: * Serialize all access to RS, so for example we're not trying to handle a sync listener callback from RS at the same time we're initializing the UrlbarQuickSuggest instance, or we're not trying to do two concurrent syncs from RS (`onEnabledUpdate`) when quick suggest prefs change back to back. This also allows tests to safely access/test the UrlbarQuickSuggest instance (combined with `readyPromise`) without the possibility of the data changing at the same time * Add a `readyPromise` that consumers (tests) can use so they can be certain that no updates are currently ongoing * Simplify the methods related to syncing from RS. Now there's only one method, `_queueSettingsSync` * Don't unnecessarily recreate the RS client (`_rs`) and sync from it every time a quick suggest pref changes or something else causes `onEnabledUpdate` to be called * Tear down the RS client (`_rs`) when quick suggest is disabled so that it's not taking up memory (hopefully?) and it's not listening for syncs that are unnecessary when the user turns off suggestions * Move `SUGGESTION_SCORE` from KeywordTree to UrlbarQuickSuggest. It was supposed to be there all along but I added it to the wrong class in D124132! :-/ * Make a few other style/cosmetic tweaks to UrlbarQuickSuggest * Make some unrelated trivial improvements to test_quicksuggest_merino.js Depends on D128970 Differential Revision: https://phabricator.services.mozilla.com/D130429
This commit is contained in:
Родитель
f4a5ea4479
Коммит
862f58627b
|
@ -14,16 +14,15 @@ const { XPCOMUtils } = ChromeUtils.import(
|
|||
"resource://gre/modules/XPCOMUtils.jsm"
|
||||
);
|
||||
|
||||
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetters(this, {
|
||||
BrowserWindowTracker: "resource:///modules/BrowserWindowTracker.jsm",
|
||||
RemoteSettings: "resource://services-settings/remote-settings.js",
|
||||
UrlbarPrefs: "resource:///modules/UrlbarPrefs.jsm",
|
||||
NimbusFeatures: "resource://nimbus/ExperimentAPI.jsm",
|
||||
QUICK_SUGGEST_SOURCE: "resource:///modules/UrlbarProviderQuickSuggest.jsm",
|
||||
RemoteSettings: "resource://services-settings/remote-settings.js",
|
||||
Services: "resource://gre/modules/Services.jsm",
|
||||
UrlbarPrefs: "resource:///modules/UrlbarPrefs.jsm",
|
||||
UrlbarProviderQuickSuggest:
|
||||
"resource:///modules/UrlbarProviderQuickSuggest.jsm",
|
||||
QUICK_SUGGEST_SOURCE: "resource:///modules/UrlbarProviderQuickSuggest.jsm",
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyGlobalGetters(this, ["TextDecoder"]);
|
||||
|
@ -67,34 +66,48 @@ const SUGGESTION_SCORE = 0.2;
|
|||
* to provide suggestions for UrlbarProviderQuickSuggest.
|
||||
*/
|
||||
class Suggestions {
|
||||
// The RemoteSettings client.
|
||||
_rs = null;
|
||||
// Let tests wait for init to complete.
|
||||
_initPromise = null;
|
||||
// Resolver function stored to call when init is complete.
|
||||
_initResolve = null;
|
||||
// A tree that maps keywords to a result.
|
||||
_tree = new KeywordTree();
|
||||
// A map of the result data.
|
||||
_results = new Map();
|
||||
|
||||
async init() {
|
||||
if (this._initPromise) {
|
||||
return this._initPromise;
|
||||
}
|
||||
this._initPromise = Promise.resolve();
|
||||
if (UrlbarPrefs.get(FEATURE_AVAILABLE)) {
|
||||
this._initPromise = new Promise(resolve => (this._initResolve = resolve));
|
||||
Services.tm.idleDispatchToMainThread(this.onEnabledUpdate.bind(this));
|
||||
} else {
|
||||
NimbusFeatures.urlbar.onUpdate(this.onEnabledUpdate.bind(this));
|
||||
}
|
||||
constructor() {
|
||||
UrlbarPrefs.addObserver(this);
|
||||
return this._initPromise;
|
||||
NimbusFeatures.urlbar.onUpdate(() => this._queueSettingsSetup());
|
||||
|
||||
this._queueSettingsTask(() => {
|
||||
return new Promise(resolve => {
|
||||
Services.tm.idleDispatchToMainThread(() => {
|
||||
this._queueSettingsSetup();
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* @returns {number}
|
||||
* A score in the range [0, 1] that can be used to compare suggestions from
|
||||
* remote settings to suggestions from Merino. Remote settings suggestions
|
||||
* don't have a natural score so we hardcode a value.
|
||||
*/
|
||||
get SUGGESTION_SCORE() {
|
||||
return SUGGESTION_SCORE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {Promise}
|
||||
* Resolves when any ongoing updates to the suggestions data are done.
|
||||
*/
|
||||
get readyPromise() {
|
||||
if (!this._settingsTaskQueue.length) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
return new Promise(resolve => {
|
||||
this._emptySettingsTaskQueueCallbacks.push(resolve);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle queries from the Urlbar.
|
||||
*
|
||||
* @param {string} phrase
|
||||
* The search string.
|
||||
*/
|
||||
async query(phrase) {
|
||||
log.info("Handling query for", phrase);
|
||||
|
@ -112,7 +125,7 @@ class Suggestions {
|
|||
let date =
|
||||
`${d.getFullYear()}${pad(d.getMonth() + 1)}` +
|
||||
`${pad(d.getDate())}${pad(d.getHours())}`;
|
||||
let icon = await this.fetchIcon(result.icon);
|
||||
let icon = await this._fetchIcon(result.icon);
|
||||
return {
|
||||
full_keyword: this.getFullKeyword(phrase, result.keywords),
|
||||
title: result.title,
|
||||
|
@ -181,55 +194,6 @@ class Suggestions {
|
|||
return longerPhrase || trimmedQuery;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a urlbar pref changes. The onboarding dialog will set the
|
||||
* `browser.urlbar.suggest.quicksuggest` prefs if the user has opted in, at
|
||||
* which point we can start showing results.
|
||||
*
|
||||
* @param {string} pref
|
||||
* The name of the pref relative to `browser.urlbar`.
|
||||
*/
|
||||
onPrefChanged(pref) {
|
||||
switch (pref) {
|
||||
// Both sponsored and non-sponsored results come from the same remote
|
||||
// settings dataset, so we only need to listen for `suggest.quicksuggest`
|
||||
// and not also `suggest.quicksuggest.sponsored`.
|
||||
case "suggest.quicksuggest":
|
||||
this.onEnabledUpdate();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Called when an update that may change whether this feature is enabled
|
||||
* or not has occured.
|
||||
*
|
||||
* Quick suggest is controlled by the following preferences. All three must be
|
||||
* enabled to show sponsored results. The first two must be enabled to show
|
||||
* non-sponsored results.
|
||||
*
|
||||
* * `quicksuggest.enabled`: The global toggle for the entire quick suggest
|
||||
* feature. This pref can be overridden by the `quickSuggestEnabled` Nimbus
|
||||
* variable. If false, neither sponsored nor non-sponsored suggestions will
|
||||
* be shown. If true, then we look at the individual prefs
|
||||
* `suggest.quicksuggest` and `suggest.quicksuggest.sponsored`.
|
||||
*
|
||||
* * `suggest.quicksuggest`: Whether any quick suggest results are shown. This
|
||||
* must be true to show both non-sponsored and sponsored results.
|
||||
*
|
||||
* * `suggest.quicksuggest.sponsored`: Whether sponsored quick suggest results
|
||||
* are shown. Both this pref and `suggest.quicksuggest` must be true to
|
||||
* show sponsored results.
|
||||
*/
|
||||
onEnabledUpdate() {
|
||||
if (
|
||||
UrlbarPrefs.get(FEATURE_AVAILABLE) &&
|
||||
UrlbarPrefs.get("suggest.quicksuggest")
|
||||
) {
|
||||
this._setupRemoteSettings();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* An onboarding dialog can be shown to the users who are enrolled into
|
||||
* the QuickSuggest experiments or rollouts. This behavior is controlled
|
||||
|
@ -336,102 +300,108 @@ class Suggestions {
|
|||
);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set up RemoteSettings listeners.
|
||||
/**
|
||||
* Called when a urlbar pref changes. The onboarding dialog will set the
|
||||
* `browser.urlbar.suggest.quicksuggest` prefs if the user has opted in, at
|
||||
* which point we can start showing results.
|
||||
*
|
||||
* @param {string} pref
|
||||
* The name of the pref relative to `browser.urlbar`.
|
||||
*/
|
||||
async _setupRemoteSettings() {
|
||||
this._rs = RemoteSettings(RS_COLLECTION);
|
||||
this._rs.on("sync", this._onSettingsSync.bind(this));
|
||||
await this._ensureAttachmentsDownloaded();
|
||||
if (this._initResolve) {
|
||||
this._initResolve();
|
||||
this._initResolve = null;
|
||||
onPrefChanged(pref) {
|
||||
switch (pref) {
|
||||
// Both sponsored and non-sponsored results come from the same remote
|
||||
// settings dataset, so we only need to listen for `suggest.quicksuggest`
|
||||
// and not also `suggest.quicksuggest.sponsored`.
|
||||
case "suggest.quicksuggest":
|
||||
this._queueSettingsSetup();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Called when RemoteSettings updates are received.
|
||||
// The RemoteSettings client.
|
||||
_rs = null;
|
||||
|
||||
// Queue of callback functions for serializing access to remote settings and
|
||||
// related data. See _queueSettingsTask().
|
||||
_settingsTaskQueue = [];
|
||||
|
||||
// Functions to call when the settings task queue becomes empty.
|
||||
_emptySettingsTaskQueueCallbacks = [];
|
||||
|
||||
// Maps from result IDs to the corresponding results.
|
||||
_results = new Map();
|
||||
|
||||
// A tree that maps keywords to a result.
|
||||
_tree = new KeywordTree();
|
||||
|
||||
/**
|
||||
* Queues a task to ensure our remote settings client is initialized or torn
|
||||
* down as appropriate.
|
||||
*/
|
||||
async _onSettingsSync({ data: { deleted } }) {
|
||||
const toDelete = deleted?.filter(d => d.attachment);
|
||||
// Remove local files of deleted records
|
||||
if (toDelete) {
|
||||
await Promise.all(
|
||||
toDelete.map(entry => this._rs.attachments.delete(entry))
|
||||
);
|
||||
}
|
||||
await this._ensureAttachmentsDownloaded();
|
||||
_queueSettingsSetup() {
|
||||
this._queueSettingsTask(() => {
|
||||
let enabled =
|
||||
UrlbarPrefs.get(FEATURE_AVAILABLE) &&
|
||||
UrlbarPrefs.get("suggest.quicksuggest");
|
||||
if (enabled && !this._rs) {
|
||||
this._onSettingsSync = (...args) => this._queueSettingsSync(...args);
|
||||
this._rs = RemoteSettings(RS_COLLECTION);
|
||||
this._rs.on("sync", this._onSettingsSync);
|
||||
this._queueSettingsSync();
|
||||
} else if (!enabled && this._rs) {
|
||||
this._rs.off("sync", this._onSettingsSync);
|
||||
this._rs = null;
|
||||
this._onSettingsSync = null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* We store our RemoteSettings data in attachments, ensure the attachments
|
||||
* are saved locally.
|
||||
/**
|
||||
* Queues a task to (re)create the results map and keyword tree from the
|
||||
* remote settings data plus any other work that needs to be done on sync.
|
||||
*
|
||||
* @param {object} [event]
|
||||
* The event object passed to the "sync" event listener if you're calling
|
||||
* this from the listener.
|
||||
*/
|
||||
async _ensureAttachmentsDownloaded() {
|
||||
// Make sure we don't re-enter this method, which can happen due to a cycle
|
||||
// created by our remote settings sync listener as follows:
|
||||
//
|
||||
// Pref change -> onPrefChanged -> onEnabledUpdate -> _setupRemoteSettings
|
||||
// -> _ensureAttachmentsDownloaded -> this._rs.get -> RemoteSettingsClient
|
||||
// calls sync on itself -> RemoteSettingsClient emits a sync event ->
|
||||
// _onSettingsSync -> _ensureAttachmentsDownloaded
|
||||
//
|
||||
// Because RemoteSettingsClient awaits when it emits its sync event, we get
|
||||
// a deadlock in that call stack. Quick suggest will not be able to complete
|
||||
// initialization and return suggestions until something else causes it to
|
||||
// fetch the data again. Restarting the app also fixes it because it seems
|
||||
// RemoteSettingsClient takes a different code path on initialization after
|
||||
// restart, presumably because the data was successfully downloaded and
|
||||
// cached before the deadlock.
|
||||
if (this._ensureAttachmentsDownloadedRunning) {
|
||||
return;
|
||||
}
|
||||
this._ensureAttachmentsDownloadedRunning = true;
|
||||
try {
|
||||
await this._ensureAttachmentsDownloadedHelper();
|
||||
} finally {
|
||||
this._ensureAttachmentsDownloadedRunning = false;
|
||||
}
|
||||
_queueSettingsSync(event = null) {
|
||||
this._queueSettingsTask(async () => {
|
||||
// Remove local files of deleted records
|
||||
if (event?.data?.deleted) {
|
||||
await Promise.all(
|
||||
event.data.deleted
|
||||
.filter(d => d.attachment)
|
||||
.map(entry => this._rs.attachments.delete(entry))
|
||||
);
|
||||
}
|
||||
|
||||
let data = await this._rs.get({ filters: { type: "data" } });
|
||||
let icons = await this._rs.get({ filters: { type: "icon" } });
|
||||
await Promise.all(icons.map(r => this._rs.attachments.download(r)));
|
||||
|
||||
this._results = new Map();
|
||||
this._tree = new KeywordTree();
|
||||
|
||||
for (let record of data) {
|
||||
let { buffer } = await this._rs.attachments.download(record, {
|
||||
useCache: true,
|
||||
});
|
||||
let results = JSON.parse(new TextDecoder("utf-8").decode(buffer));
|
||||
this._addResults(results);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async _ensureAttachmentsDownloadedHelper() {
|
||||
log.info("_ensureAttachmentsDownloaded started");
|
||||
let dataOpts = { useCache: true };
|
||||
let data = await this._rs.get({ filters: { type: "data" } });
|
||||
await Promise.all(
|
||||
data.map(r => this._rs.attachments.download(r, dataOpts))
|
||||
);
|
||||
|
||||
let icons = await this._rs.get({ filters: { type: "icon" } });
|
||||
await Promise.all(icons.map(r => this._rs.attachments.download(r)));
|
||||
|
||||
await this._createTree();
|
||||
log.info("_ensureAttachmentsDownloaded complete");
|
||||
}
|
||||
|
||||
/*
|
||||
* Recreate the KeywordTree on startup or with RemoteSettings updates.
|
||||
/**
|
||||
* Adds a list of result objects to the results map and keyword tree. This
|
||||
* method is also used by tests to set up mock suggestions.
|
||||
*
|
||||
* @param {array} results
|
||||
* Array of result objects.
|
||||
*/
|
||||
async _createTree() {
|
||||
log.info("Building new KeywordTree");
|
||||
this._results = new Map();
|
||||
this._tree = new KeywordTree();
|
||||
let data = await this._rs.get({ filters: { type: "data" } });
|
||||
|
||||
for (let record of data) {
|
||||
let { buffer } = await this._rs.attachments.download(record, {
|
||||
useCache: true,
|
||||
});
|
||||
let json = JSON.parse(new TextDecoder("utf-8").decode(buffer));
|
||||
this._processSuggestionsJSON(json);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle incoming suggestions data and add to local data.
|
||||
*/
|
||||
async _processSuggestionsJSON(json) {
|
||||
for (let result of json) {
|
||||
_addResults(results) {
|
||||
for (let result of results) {
|
||||
this._results.set(result.id, result);
|
||||
for (let keyword of result.keywords) {
|
||||
this._tree.set(keyword, result.id);
|
||||
|
@ -439,10 +409,54 @@ class Suggestions {
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Fetch the icon from RemoteSettings attachments.
|
||||
/**
|
||||
* Adds a function to the remote settings task queue. Methods in this class
|
||||
* should call this when they need to to modify or access the settings client.
|
||||
* It ensures settings accesses are serialized, do not overlap, and happen
|
||||
* only one at a time. It also lets clients, especially tests, use this class
|
||||
* without having to worry about whether a settings sync or initialization is
|
||||
* ongoing; see `readyPromise`.
|
||||
*
|
||||
* @param {function} callback
|
||||
* The function to queue.
|
||||
*/
|
||||
async fetchIcon(path) {
|
||||
_queueSettingsTask(callback) {
|
||||
this._settingsTaskQueue.push(callback);
|
||||
if (this._settingsTaskQueue.length == 1) {
|
||||
this._doNextSettingsTask();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls the next function in the settings task queue and recurses until the
|
||||
* queue is empty. Once empty, all empty-queue callback functions are called.
|
||||
*/
|
||||
async _doNextSettingsTask() {
|
||||
if (!this._settingsTaskQueue.length) {
|
||||
while (this._emptySettingsTaskQueueCallbacks.length) {
|
||||
let callback = this._emptySettingsTaskQueueCallbacks.shift();
|
||||
callback();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
let task = this._settingsTaskQueue[0];
|
||||
try {
|
||||
await task();
|
||||
} catch (error) {
|
||||
log.error(error);
|
||||
}
|
||||
this._settingsTaskQueue.shift();
|
||||
this._doNextSettingsTask();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the icon from RemoteSettings attachments.
|
||||
*
|
||||
* @param {string} path
|
||||
* The icon's remote settings path.
|
||||
*/
|
||||
async _fetchIcon(path) {
|
||||
if (!path) {
|
||||
return null;
|
||||
}
|
||||
|
@ -487,10 +501,6 @@ class KeywordTree {
|
|||
this.tree = new Map();
|
||||
}
|
||||
|
||||
static get SUGGESTION_SCORE() {
|
||||
return SUGGESTION_SCORE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set a keyword for a result.
|
||||
*/
|
||||
|
@ -625,4 +635,3 @@ class KeywordTree {
|
|||
}
|
||||
|
||||
let UrlbarQuickSuggest = new Suggestions();
|
||||
UrlbarQuickSuggest.init();
|
||||
|
|
|
@ -107,27 +107,34 @@ class TestUtils {
|
|||
* be updated again during the test, and also optionally sets it up with mock
|
||||
* data.
|
||||
*
|
||||
* @param {array} [data]
|
||||
* Array of quick suggest data objects. If not given, then this function
|
||||
* @param {array} [results]
|
||||
* Array of quick suggest result objects. If not given, then this function
|
||||
* won't set up any mock data.
|
||||
* @returns {function}
|
||||
* A cleanup function. You only need to call this function if you're in a
|
||||
* browser chrome test and you did not also call `init`. You can ignore it
|
||||
* otherwise.
|
||||
*/
|
||||
async ensureQuickSuggestInit(data = null) {
|
||||
this.info?.("Awaiting UrlbarQuickSuggest.init");
|
||||
await UrlbarQuickSuggest.init();
|
||||
this.info?.("Done awaiting UrlbarQuickSuggest.init");
|
||||
async ensureQuickSuggestInit(results = null) {
|
||||
this.info?.(
|
||||
"ensureQuickSuggestInit awaiting UrlbarQuickSuggest.readyPromise"
|
||||
);
|
||||
await UrlbarQuickSuggest.readyPromise;
|
||||
this.info?.(
|
||||
"ensureQuickSuggestInit done awaiting UrlbarQuickSuggest.readyPromise"
|
||||
);
|
||||
|
||||
// Stub _queueSettingsSync() so any actual remote settings syncs that happen
|
||||
// during the test are ignored.
|
||||
let sandbox = sinon.createSandbox();
|
||||
sandbox.stub(UrlbarQuickSuggest, "_ensureAttachmentsDownloadedHelper");
|
||||
sandbox.stub(UrlbarQuickSuggest, "_queueSettingsSync");
|
||||
let cleanup = () => sandbox.restore();
|
||||
this.registerCleanupFunction?.(cleanup);
|
||||
if (data) {
|
||||
this.info?.("Awaiting UrlbarQuickSuggest._processSuggestionsJSON");
|
||||
await UrlbarQuickSuggest._processSuggestionsJSON(data);
|
||||
this.info?.("Done awaiting UrlbarQuickSuggest._processSuggestionsJSON");
|
||||
|
||||
if (results) {
|
||||
UrlbarQuickSuggest._addResults(results);
|
||||
}
|
||||
|
||||
return cleanup;
|
||||
}
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
XPCOMUtils.defineLazyModuleGetters(this, {
|
||||
UrlbarProviderQuickSuggest:
|
||||
"resource:///modules/UrlbarProviderQuickSuggest.jsm",
|
||||
UrlbarQuickSuggest: "resource:///modules/UrlbarQuickSuggest.jsm",
|
||||
});
|
||||
|
||||
const SPONSORED_SEARCH_STRING = "frab";
|
||||
|
@ -152,9 +153,6 @@ add_task(async function init() {
|
|||
let engine = await addTestSuggestionsEngine();
|
||||
await Services.search.setDefault(engine);
|
||||
|
||||
// Set up the remote settings client with the test data. Need to set the
|
||||
// `suggest.quicksuggest` pref to make `init` finish.
|
||||
UrlbarPrefs.set("suggest.quicksuggest", true);
|
||||
await QuickSuggestTestUtils.ensureQuickSuggestInit(REMOTE_SETTINGS_DATA);
|
||||
});
|
||||
|
||||
|
@ -660,3 +658,72 @@ async function doDedupeAgainstURLTest({
|
|||
UrlbarPrefs.clear("suggest.searches");
|
||||
await PlacesUtils.history.clear();
|
||||
}
|
||||
|
||||
// Tests setup and teardown of the remote settings client depending on whether
|
||||
// quick suggest is enabled.
|
||||
add_task(async function setupAndTeardown() {
|
||||
// Sanity check pref values at the start of this task. We assume all previous
|
||||
// tasks have left `quicksuggest.enabled` set to true (from the init task) and
|
||||
// the `suggest.quicksuggest` prefs set to false.
|
||||
let initialPrefs = {
|
||||
"quicksuggest.enabled": true,
|
||||
"suggest.quicksuggest": false,
|
||||
"suggest.quicksuggest.sponsored": false,
|
||||
};
|
||||
for (let [name, expectedValue] of Object.entries(initialPrefs)) {
|
||||
Assert.equal(
|
||||
UrlbarPrefs.get(name),
|
||||
expectedValue,
|
||||
`Initial value of ${name} is ${expectedValue}`
|
||||
);
|
||||
}
|
||||
|
||||
// Initially the settings client will be null since both
|
||||
// `suggest.quicksuggest` prefs are false.
|
||||
Assert.ok(!UrlbarQuickSuggest._rs, "Settings client is null initially");
|
||||
|
||||
UrlbarPrefs.set("suggest.quicksuggest", true);
|
||||
await UrlbarQuickSuggest.readyPromise;
|
||||
Assert.ok(
|
||||
UrlbarQuickSuggest._rs,
|
||||
"Settings client is non-null after enabling suggest.quicksuggest"
|
||||
);
|
||||
|
||||
UrlbarPrefs.set("suggest.quicksuggest", false);
|
||||
await UrlbarQuickSuggest.readyPromise;
|
||||
Assert.ok(
|
||||
!UrlbarQuickSuggest._rs,
|
||||
"Settings client is null after disabling suggest.quicksuggest"
|
||||
);
|
||||
|
||||
UrlbarPrefs.set("suggest.quicksuggest.sponsored", true);
|
||||
await UrlbarQuickSuggest.readyPromise;
|
||||
Assert.ok(
|
||||
!UrlbarQuickSuggest._rs,
|
||||
"Settings client remains null after enabling suggest.quicksuggest.sponsored"
|
||||
);
|
||||
|
||||
UrlbarPrefs.set("suggest.quicksuggest", true);
|
||||
await UrlbarQuickSuggest.readyPromise;
|
||||
Assert.ok(
|
||||
UrlbarQuickSuggest._rs,
|
||||
"Settings client is non-null after enabling suggest.quicksuggest"
|
||||
);
|
||||
|
||||
UrlbarPrefs.set("quicksuggest.enabled", false);
|
||||
await UrlbarQuickSuggest.readyPromise;
|
||||
Assert.ok(
|
||||
!UrlbarQuickSuggest._rs,
|
||||
"Settings client is null after disabling quicksuggest.enabled"
|
||||
);
|
||||
|
||||
// Leave the prefs in the same state as when the task started.
|
||||
UrlbarPrefs.clear("suggest.quicksuggest");
|
||||
UrlbarPrefs.clear("suggest.quicksuggest.sponsored");
|
||||
UrlbarPrefs.set("quicksuggest.enabled", true);
|
||||
await UrlbarQuickSuggest.readyPromise;
|
||||
Assert.ok(
|
||||
!UrlbarQuickSuggest._rs,
|
||||
"Settings client remains null at end of task"
|
||||
);
|
||||
});
|
||||
|
|
|
@ -19,18 +19,41 @@ const PREF_MERINO_ENDPOINT_URL = "merino.endpointURL";
|
|||
|
||||
const TELEMETRY_MERINO_LATENCY = "FX_URLBAR_MERINO_LATENCY_MS";
|
||||
|
||||
const REMOTE_SETTINGS_SEARCH_STRING = "frab";
|
||||
|
||||
const REMOTE_SETTINGS_DATA = [
|
||||
{
|
||||
id: 1,
|
||||
url: "http://test.com/q=frabbits",
|
||||
title: "frabbits",
|
||||
keywords: ["fra", "frab"],
|
||||
keywords: [REMOTE_SETTINGS_SEARCH_STRING],
|
||||
click_url: "http://click.reporting.test.com/",
|
||||
impression_url: "http://impression.reporting.test.com/",
|
||||
advertiser: "TestAdvertiser",
|
||||
},
|
||||
];
|
||||
|
||||
const EXPECTED_REMOTE_SETTINGS_RESULT = {
|
||||
type: UrlbarUtils.RESULT_TYPE.URL,
|
||||
source: UrlbarUtils.RESULT_SOURCE.SEARCH,
|
||||
heuristic: false,
|
||||
payload: {
|
||||
qsSuggestion: REMOTE_SETTINGS_SEARCH_STRING,
|
||||
title: "frabbits",
|
||||
url: "http://test.com/q=frabbits",
|
||||
icon: null,
|
||||
sponsoredImpressionUrl: "http://impression.reporting.test.com/",
|
||||
sponsoredClickUrl: "http://click.reporting.test.com/",
|
||||
sponsoredBlockId: 1,
|
||||
sponsoredAdvertiser: "testadvertiser",
|
||||
isSponsored: true,
|
||||
helpUrl: UrlbarProviderQuickSuggest.helpUrl,
|
||||
helpL10nId: "firefox-suggest-urlbar-learn-more",
|
||||
displayUrl: "http://test.com/q=frabbits",
|
||||
source: "remote-settings",
|
||||
},
|
||||
};
|
||||
|
||||
let gMerinoResponse;
|
||||
|
||||
add_task(async function init() {
|
||||
|
@ -49,6 +72,12 @@ add_task(async function init() {
|
|||
|
||||
// Set up the remote settings client with the test data.
|
||||
await QuickSuggestTestUtils.ensureQuickSuggestInit(REMOTE_SETTINGS_DATA);
|
||||
|
||||
Assert.equal(
|
||||
typeof UrlbarQuickSuggest.SUGGESTION_SCORE,
|
||||
"number",
|
||||
"Sanity check: UrlbarQuickSuggest.SUGGESTION_SCORE is defined"
|
||||
);
|
||||
});
|
||||
|
||||
// Tests with Merino enabled and remote settings disabled.
|
||||
|
@ -78,7 +107,7 @@ add_task(async function oneEnabled_merino() {
|
|||
},
|
||||
});
|
||||
|
||||
let context = createContext("frab", {
|
||||
let context = createContext(REMOTE_SETTINGS_SEARCH_STRING, {
|
||||
providers: [UrlbarProviderQuickSuggest.name],
|
||||
isPrivate: false,
|
||||
});
|
||||
|
@ -137,34 +166,13 @@ add_task(async function oneEnabled_remoteSettings() {
|
|||
},
|
||||
});
|
||||
|
||||
let context = createContext("frab", {
|
||||
let context = createContext(REMOTE_SETTINGS_SEARCH_STRING, {
|
||||
providers: [UrlbarProviderQuickSuggest.name],
|
||||
isPrivate: false,
|
||||
});
|
||||
await check_results({
|
||||
context,
|
||||
matches: [
|
||||
{
|
||||
type: UrlbarUtils.RESULT_TYPE.URL,
|
||||
source: UrlbarUtils.RESULT_SOURCE.SEARCH,
|
||||
heuristic: false,
|
||||
payload: {
|
||||
qsSuggestion: "frab",
|
||||
title: "frabbits",
|
||||
url: "http://test.com/q=frabbits",
|
||||
icon: null,
|
||||
sponsoredImpressionUrl: "http://impression.reporting.test.com/",
|
||||
sponsoredClickUrl: "http://click.reporting.test.com/",
|
||||
sponsoredBlockId: 1,
|
||||
sponsoredAdvertiser: "testadvertiser",
|
||||
isSponsored: true,
|
||||
helpUrl: UrlbarProviderQuickSuggest.helpUrl,
|
||||
helpL10nId: "firefox-suggest-urlbar-learn-more",
|
||||
displayUrl: "http://test.com/q=frabbits",
|
||||
source: "remote-settings",
|
||||
},
|
||||
},
|
||||
],
|
||||
matches: [EXPECTED_REMOTE_SETTINGS_RESULT],
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -194,7 +202,7 @@ add_task(async function higherScore_merino() {
|
|||
},
|
||||
});
|
||||
|
||||
let context = createContext("frab", {
|
||||
let context = createContext(REMOTE_SETTINGS_SEARCH_STRING, {
|
||||
providers: [UrlbarProviderQuickSuggest.name],
|
||||
isPrivate: false,
|
||||
});
|
||||
|
@ -252,34 +260,13 @@ add_task(async function higherScore_remoteSettings() {
|
|||
},
|
||||
});
|
||||
|
||||
let context = createContext("frab", {
|
||||
let context = createContext(REMOTE_SETTINGS_SEARCH_STRING, {
|
||||
providers: [UrlbarProviderQuickSuggest.name],
|
||||
isPrivate: false,
|
||||
});
|
||||
await check_results({
|
||||
context,
|
||||
matches: [
|
||||
{
|
||||
type: UrlbarUtils.RESULT_TYPE.URL,
|
||||
source: UrlbarUtils.RESULT_SOURCE.SEARCH,
|
||||
heuristic: false,
|
||||
payload: {
|
||||
qsSuggestion: "frab",
|
||||
title: "frabbits",
|
||||
url: "http://test.com/q=frabbits",
|
||||
icon: null,
|
||||
sponsoredImpressionUrl: "http://impression.reporting.test.com/",
|
||||
sponsoredClickUrl: "http://click.reporting.test.com/",
|
||||
sponsoredBlockId: 1,
|
||||
sponsoredAdvertiser: "testadvertiser",
|
||||
isSponsored: true,
|
||||
helpUrl: UrlbarProviderQuickSuggest.helpUrl,
|
||||
helpL10nId: "firefox-suggest-urlbar-learn-more",
|
||||
displayUrl: "http://test.com/q=frabbits",
|
||||
source: "remote-settings",
|
||||
},
|
||||
},
|
||||
],
|
||||
matches: [EXPECTED_REMOTE_SETTINGS_RESULT],
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -309,34 +296,13 @@ add_task(async function sameScore() {
|
|||
},
|
||||
});
|
||||
|
||||
let context = createContext("frab", {
|
||||
let context = createContext(REMOTE_SETTINGS_SEARCH_STRING, {
|
||||
providers: [UrlbarProviderQuickSuggest.name],
|
||||
isPrivate: false,
|
||||
});
|
||||
await check_results({
|
||||
context,
|
||||
matches: [
|
||||
{
|
||||
type: UrlbarUtils.RESULT_TYPE.URL,
|
||||
source: UrlbarUtils.RESULT_SOURCE.SEARCH,
|
||||
heuristic: false,
|
||||
payload: {
|
||||
qsSuggestion: "frab",
|
||||
title: "frabbits",
|
||||
url: "http://test.com/q=frabbits",
|
||||
icon: null,
|
||||
sponsoredImpressionUrl: "http://impression.reporting.test.com/",
|
||||
sponsoredClickUrl: "http://click.reporting.test.com/",
|
||||
sponsoredBlockId: 1,
|
||||
sponsoredAdvertiser: "testadvertiser",
|
||||
isSponsored: true,
|
||||
helpUrl: UrlbarProviderQuickSuggest.helpUrl,
|
||||
helpL10nId: "firefox-suggest-urlbar-learn-more",
|
||||
displayUrl: "http://test.com/q=frabbits",
|
||||
source: "remote-settings",
|
||||
},
|
||||
},
|
||||
],
|
||||
matches: [EXPECTED_REMOTE_SETTINGS_RESULT],
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -366,34 +332,13 @@ add_task(async function noMerinoScore() {
|
|||
},
|
||||
});
|
||||
|
||||
let context = createContext("frab", {
|
||||
let context = createContext(REMOTE_SETTINGS_SEARCH_STRING, {
|
||||
providers: [UrlbarProviderQuickSuggest.name],
|
||||
isPrivate: false,
|
||||
});
|
||||
await check_results({
|
||||
context,
|
||||
matches: [
|
||||
{
|
||||
type: UrlbarUtils.RESULT_TYPE.URL,
|
||||
source: UrlbarUtils.RESULT_SOURCE.SEARCH,
|
||||
heuristic: false,
|
||||
payload: {
|
||||
qsSuggestion: "frab",
|
||||
title: "frabbits",
|
||||
url: "http://test.com/q=frabbits",
|
||||
icon: null,
|
||||
sponsoredImpressionUrl: "http://impression.reporting.test.com/",
|
||||
sponsoredClickUrl: "http://click.reporting.test.com/",
|
||||
sponsoredBlockId: 1,
|
||||
sponsoredAdvertiser: "testadvertiser",
|
||||
isSponsored: true,
|
||||
helpUrl: UrlbarProviderQuickSuggest.helpUrl,
|
||||
helpL10nId: "firefox-suggest-urlbar-learn-more",
|
||||
displayUrl: "http://test.com/q=frabbits",
|
||||
source: "remote-settings",
|
||||
},
|
||||
},
|
||||
],
|
||||
matches: [EXPECTED_REMOTE_SETTINGS_RESULT],
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -468,34 +413,13 @@ add_task(async function noSuggestion_merino() {
|
|||
},
|
||||
});
|
||||
|
||||
let context = createContext("frab", {
|
||||
let context = createContext(REMOTE_SETTINGS_SEARCH_STRING, {
|
||||
providers: [UrlbarProviderQuickSuggest.name],
|
||||
isPrivate: false,
|
||||
});
|
||||
await check_results({
|
||||
context,
|
||||
matches: [
|
||||
{
|
||||
type: UrlbarUtils.RESULT_TYPE.URL,
|
||||
source: UrlbarUtils.RESULT_SOURCE.SEARCH,
|
||||
heuristic: false,
|
||||
payload: {
|
||||
qsSuggestion: "frab",
|
||||
title: "frabbits",
|
||||
url: "http://test.com/q=frabbits",
|
||||
icon: null,
|
||||
sponsoredImpressionUrl: "http://impression.reporting.test.com/",
|
||||
sponsoredClickUrl: "http://click.reporting.test.com/",
|
||||
sponsoredBlockId: 1,
|
||||
sponsoredAdvertiser: "testadvertiser",
|
||||
isSponsored: true,
|
||||
helpUrl: UrlbarProviderQuickSuggest.helpUrl,
|
||||
helpL10nId: "firefox-suggest-urlbar-learn-more",
|
||||
displayUrl: "http://test.com/q=frabbits",
|
||||
source: "remote-settings",
|
||||
},
|
||||
},
|
||||
],
|
||||
matches: [EXPECTED_REMOTE_SETTINGS_RESULT],
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -526,7 +450,7 @@ add_task(async function bothDisabled() {
|
|||
},
|
||||
});
|
||||
|
||||
let context = createContext("frab", {
|
||||
let context = createContext(REMOTE_SETTINGS_SEARCH_STRING, {
|
||||
providers: [UrlbarProviderQuickSuggest.name],
|
||||
isPrivate: false,
|
||||
});
|
||||
|
|
Загрузка…
Ссылка в новой задаче