зеркало из https://github.com/mozilla/gecko-dev.git
Backed out 2 changesets (bug 1861540) for causing xpc shell failures in test_quicksuggest_addons.js CLOSED TREE
Backed out changeset 2a0da4c5f326 (bug 1861540) Backed out changeset b67e855993cb (bug 1861540)
This commit is contained in:
Родитель
1e7df617ec
Коммит
90898c25ea
|
@ -73,7 +73,6 @@ EXTRA_JS_MODULES["urlbar/private"] += [
|
|||
TESTING_JS_MODULES += [
|
||||
"tests/quicksuggest/MerinoTestUtils.sys.mjs",
|
||||
"tests/quicksuggest/QuickSuggestTestUtils.sys.mjs",
|
||||
"tests/quicksuggest/RemoteSettingsServer.sys.mjs",
|
||||
"tests/UrlbarTestUtils.sys.mjs",
|
||||
]
|
||||
BROWSER_CHROME_MANIFESTS += [
|
||||
|
|
|
@ -258,15 +258,6 @@ export class SuggestBackendJs extends BaseFeature {
|
|||
this.#emitter.emit("config-set");
|
||||
}
|
||||
|
||||
async _test_syncAll() {
|
||||
if (this.#rs) {
|
||||
// `RemoteSettingsClient` won't start another import if it's already
|
||||
// importing. Wait for it to finish before starting the new one.
|
||||
await this.#rs._importingPromise;
|
||||
await this.#syncAll();
|
||||
}
|
||||
}
|
||||
|
||||
// The `RemoteSettings` client.
|
||||
#rs = null;
|
||||
|
||||
|
|
|
@ -147,7 +147,7 @@ export class SuggestBackendRust extends BaseFeature {
|
|||
/**
|
||||
* nsITimerCallback
|
||||
*/
|
||||
notify() {
|
||||
async notify() {
|
||||
this.logger.info("Ingest timer fired");
|
||||
this.#ingest();
|
||||
}
|
||||
|
@ -164,10 +164,7 @@ export class SuggestBackendRust extends BaseFeature {
|
|||
let path = this.#storePath;
|
||||
this.logger.info("Initializing SuggestStore: " + path);
|
||||
try {
|
||||
this.#store = lazy.SuggestStore.init(
|
||||
path,
|
||||
SuggestBackendRust._test_remoteSettingsConfig
|
||||
);
|
||||
this.#store = lazy.SuggestStore.init(path);
|
||||
} catch (error) {
|
||||
this.logger.error("Error initializing SuggestStore:");
|
||||
this.logger.error(error);
|
||||
|
@ -213,21 +210,10 @@ export class SuggestBackendRust extends BaseFeature {
|
|||
this.#ingestPromise = this.#store.ingest(
|
||||
new lazy.SuggestIngestionConstraints()
|
||||
);
|
||||
try {
|
||||
await this.#ingestPromise;
|
||||
} catch (error) {
|
||||
// Ingest can throw a `SuggestApiError` subclass called `Other` that has a
|
||||
// custom `reason` message, which is very helpful for diagnosing problems
|
||||
// with remote settings data in tests in particular.
|
||||
this.logger.error("Ingest error: " + (error.reason ?? error));
|
||||
}
|
||||
await this.#ingestPromise;
|
||||
this.logger.info("Finished ingest");
|
||||
}
|
||||
|
||||
async _test_ingest() {
|
||||
await this.#ingest();
|
||||
}
|
||||
|
||||
// The `SuggestStore` instance.
|
||||
#store;
|
||||
|
||||
|
|
|
@ -992,7 +992,7 @@ add_task(async function selected_result_addons() {
|
|||
|
||||
add_task(async function selected_result_rust_adm_sponsored() {
|
||||
const cleanupQuickSuggest = await ensureQuickSuggestInit({
|
||||
prefs: [["quicksuggest.rustEnabled", true]],
|
||||
rustEnabled: true,
|
||||
});
|
||||
|
||||
await doTest(async browser => {
|
||||
|
@ -1016,7 +1016,7 @@ add_task(async function selected_result_rust_adm_sponsored() {
|
|||
|
||||
add_task(async function selected_result_rust_adm_nonsponsored() {
|
||||
const cleanupQuickSuggest = await ensureQuickSuggestInit({
|
||||
prefs: [["quicksuggest.rustEnabled", true]],
|
||||
rustEnabled: true,
|
||||
});
|
||||
|
||||
await doTest(async browser => {
|
||||
|
|
|
@ -234,6 +234,9 @@ async function doGeneralHistoryTest({ trigger, assert }) {
|
|||
|
||||
async function doSuggestTest({ trigger, assert }) {
|
||||
const cleanupQuickSuggest = await ensureQuickSuggestInit();
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [["browser.urlbar.bestMatch.enabled", false]],
|
||||
});
|
||||
|
||||
await doTest(async browser => {
|
||||
await openPopup("nonsponsored");
|
||||
|
@ -243,6 +246,7 @@ async function doSuggestTest({ trigger, assert }) {
|
|||
await assert();
|
||||
});
|
||||
|
||||
await SpecialPowers.popPrefEnv();
|
||||
await cleanupQuickSuggest();
|
||||
}
|
||||
|
||||
|
|
|
@ -93,7 +93,7 @@ function _assertGleanTelemetry(telemetryName, expectedExtraList) {
|
|||
async function ensureQuickSuggestInit({ ...args } = {}) {
|
||||
return lazy.QuickSuggestTestUtils.ensureQuickSuggestInit({
|
||||
...args,
|
||||
remoteSettingsRecords: [
|
||||
remoteSettingsResults: [
|
||||
{
|
||||
type: "data",
|
||||
attachment: [
|
||||
|
@ -106,7 +106,6 @@ async function ensureQuickSuggestInit({ ...args } = {}) {
|
|||
impression_url: "https://example.com/impression",
|
||||
advertiser: "TestAdvertiser",
|
||||
iab_category: "22 - Shopping",
|
||||
icon: "1234",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
|
@ -115,9 +114,8 @@ async function ensureQuickSuggestInit({ ...args } = {}) {
|
|||
keywords: ["nonsponsored"],
|
||||
click_url: "https://example.com/click",
|
||||
impression_url: "https://example.com/impression",
|
||||
advertiser: "Wikipedia",
|
||||
advertiser: "TestAdvertiser",
|
||||
iab_category: "5 - Education",
|
||||
icon: "1234",
|
||||
},
|
||||
],
|
||||
},
|
||||
|
|
|
@ -16,12 +16,7 @@ ChromeUtils.defineESModuleGetters(lazy, {
|
|||
NimbusFeatures: "resource://nimbus/ExperimentAPI.sys.mjs",
|
||||
QuickSuggest: "resource:///modules/QuickSuggest.sys.mjs",
|
||||
RemoteSettings: "resource://services-settings/remote-settings.sys.mjs",
|
||||
RemoteSettingsConfig: "resource://gre/modules/RustRemoteSettings.sys.mjs",
|
||||
RemoteSettingsServer:
|
||||
"resource://testing-common/RemoteSettingsServer.sys.mjs",
|
||||
SearchUtils: "resource://gre/modules/SearchUtils.sys.mjs",
|
||||
SuggestBackendRust:
|
||||
"resource:///modules/urlbar/private/SuggestBackendRust.sys.mjs",
|
||||
Suggestion: "resource://gre/modules/RustSuggest.sys.mjs",
|
||||
SuggestionProvider: "resource://gre/modules/RustSuggest.sys.mjs",
|
||||
SuggestStore: "resource://gre/modules/RustSuggest.sys.mjs",
|
||||
|
@ -129,6 +124,257 @@ const TEST_SCOPE_PROPERTIES = [
|
|||
"registerCleanupFunction",
|
||||
];
|
||||
|
||||
/**
|
||||
* Mock RemoteSettings.
|
||||
*
|
||||
* @param {object} options
|
||||
* Options object
|
||||
* @param {object} options.config
|
||||
* Dummy config in the RemoteSettings.
|
||||
* @param {Array} options.data
|
||||
* Dummy data in the RemoteSettings.
|
||||
*/
|
||||
class MockRemoteSettings {
|
||||
constructor({ config = DEFAULT_CONFIG, data = [] }) {
|
||||
this.#config = config;
|
||||
this.#data = data;
|
||||
|
||||
// Make a stub for "get" function to return dummy data.
|
||||
const rs = lazy.RemoteSettings("quicksuggest");
|
||||
this.#sandbox = lazy.sinon.createSandbox();
|
||||
this.#sandbox.stub(rs, "get").callsFake(async query => {
|
||||
return query.filters.type === "configuration"
|
||||
? [{ configuration: this.#config }]
|
||||
: this.#data.filter(r => r.type === query.filters.type);
|
||||
});
|
||||
|
||||
// Make a stub for "download" in attachments.
|
||||
this.#sandbox.stub(rs.attachments, "download").callsFake(async record => {
|
||||
if (!record.attachment) {
|
||||
throw new Error("No attachmet in the record");
|
||||
}
|
||||
const encoder = new TextEncoder();
|
||||
return {
|
||||
buffer: encoder.encode(JSON.stringify(record.attachment)),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
async sync() {
|
||||
if (!lazy.QuickSuggest.jsBackend.rs) {
|
||||
// There are no registered features that use remote settings.
|
||||
return;
|
||||
}
|
||||
|
||||
// Observe config-set event to recognize that the config is synced.
|
||||
const onConfigSync = new Promise(resolve => {
|
||||
lazy.QuickSuggest.jsBackend.emitter.once("config-set", resolve);
|
||||
});
|
||||
|
||||
// Make a stub for each feature to recognize that the features are synced.
|
||||
const features = lazy.QuickSuggest.jsBackend.features;
|
||||
const onFeatureSyncs = features.map(feature => {
|
||||
return new Promise(resolve => {
|
||||
const stub = this.#sandbox
|
||||
.stub(feature, "onRemoteSettingsSync")
|
||||
.callsFake(async (...args) => {
|
||||
// Call and wait for the original function.
|
||||
await stub.wrappedMethod.apply(feature, args);
|
||||
stub.restore();
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Force to sync.
|
||||
const rs = lazy.RemoteSettings("quicksuggest");
|
||||
rs.emit("sync");
|
||||
|
||||
// Wait for sync.
|
||||
await Promise.all([onConfigSync, ...onFeatureSyncs]);
|
||||
}
|
||||
|
||||
/*
|
||||
* Update the config and data in RemoteSettings. If the config or the data are
|
||||
* undefined, use the current one.
|
||||
*
|
||||
* @param {object} options
|
||||
* Options object
|
||||
* @param {object} options.config
|
||||
* Dummy config in the RemoteSettings.
|
||||
* @param {Array} options.data
|
||||
* Dummy data in the RemoteSettings.
|
||||
*/
|
||||
async update({ config = this.#config, data = this.#data }) {
|
||||
this.#config = config;
|
||||
this.#data = data;
|
||||
|
||||
await this.sync();
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
this.#sandbox.restore();
|
||||
}
|
||||
|
||||
#config = null;
|
||||
#data = null;
|
||||
#sandbox = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mock `RustSuggest` implementation.
|
||||
*
|
||||
* @param {object} options
|
||||
* Options object
|
||||
* @param {Array} options.data
|
||||
* Mock remote settings records.
|
||||
*/
|
||||
export class MockRustSuggest {
|
||||
constructor({ data = [] }) {
|
||||
this.#data = data;
|
||||
|
||||
this.#sandbox = lazy.sinon.createSandbox();
|
||||
this.#sandbox.stub(lazy.SuggestStore, "init").returns(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the mock data.
|
||||
*
|
||||
* @param {object} options
|
||||
* Options object
|
||||
* @param {Array} options.data
|
||||
* Mock remote settings records.
|
||||
*/
|
||||
async update({ data }) {
|
||||
this.#data = data;
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
this.#sandbox.restore();
|
||||
}
|
||||
|
||||
// `RustSuggest` methods below.
|
||||
|
||||
ingest() {
|
||||
// Unlike the real Rust component, ingest isn't necessary here since our
|
||||
// `query()` implementation has immediate access to the mock data. This
|
||||
// makes it easier for tests because they don't need to wait for ingest
|
||||
// every time they change the data.
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
interrupt() {}
|
||||
|
||||
clear() {}
|
||||
|
||||
async query(query) {
|
||||
let { keyword, providers } = query;
|
||||
return this.#data.reduce((matchedSuggestions, record) => {
|
||||
if (!record.attachment) {
|
||||
return matchedSuggestions;
|
||||
}
|
||||
for (let suggestion of record.attachment) {
|
||||
let provider = this.#getProvider(record, suggestion);
|
||||
if (!providers.includes(provider)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (provider) {
|
||||
case lazy.SuggestionProvider.POCKET:
|
||||
if (
|
||||
!suggestion.lowConfidenceKeywords.includes(keyword) &&
|
||||
!suggestion.highConfidenceKeywords.includes(keyword)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (!suggestion.keywords.includes(keyword)) {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
matchedSuggestions.push(
|
||||
this.#makeSuggestion(provider, suggestion, query)
|
||||
);
|
||||
}
|
||||
|
||||
return matchedSuggestions;
|
||||
}, []);
|
||||
}
|
||||
|
||||
#getProvider(record, suggestion) {
|
||||
switch (record.type) {
|
||||
case "data":
|
||||
case "test-data-type": {
|
||||
let isSponsored = suggestion.hasOwnProperty("is_sponsored")
|
||||
? suggestion.is_sponsored
|
||||
: suggestion.iab_category == "22 - Shopping";
|
||||
return isSponsored
|
||||
? lazy.SuggestionProvider.AMP
|
||||
: lazy.SuggestionProvider.WIKIPEDIA;
|
||||
}
|
||||
case "amo-suggestions":
|
||||
return lazy.SuggestionProvider.AMO;
|
||||
case "pocket-suggestions":
|
||||
return lazy.SuggestionProvider.POCKET;
|
||||
}
|
||||
throw new Error(
|
||||
"Unrecognized record-suggestion: " +
|
||||
JSON.stringify({ record, suggestion })
|
||||
);
|
||||
}
|
||||
|
||||
#makeSuggestion(provider, suggestion, query) {
|
||||
switch (provider) {
|
||||
case lazy.SuggestionProvider.AMP:
|
||||
return new lazy.Suggestion.Amp(
|
||||
suggestion.title,
|
||||
suggestion.url,
|
||||
suggestion.url, // rawUrl
|
||||
[], // icon
|
||||
query.keyword, // fullKeyword
|
||||
suggestion.id, // blockId
|
||||
suggestion.advertiser,
|
||||
suggestion.iab_category,
|
||||
suggestion.impression_url,
|
||||
suggestion.click_url,
|
||||
suggestion.click_url // rawClickUrl
|
||||
);
|
||||
case lazy.SuggestionProvider.AMO:
|
||||
return new lazy.Suggestion.Amo(
|
||||
suggestion.title,
|
||||
suggestion.url,
|
||||
suggestion.icon,
|
||||
suggestion.description,
|
||||
suggestion.rating,
|
||||
suggestion.number_of_ratings,
|
||||
suggestion.guid,
|
||||
suggestion.score
|
||||
);
|
||||
case lazy.SuggestionProvider.POCKET:
|
||||
return new lazy.Suggestion.Pocket(
|
||||
suggestion.title,
|
||||
suggestion.url,
|
||||
suggestion.score,
|
||||
suggestion.highConfidenceKeywords.includes(query.keyword) // isTopPick
|
||||
);
|
||||
case lazy.SuggestionProvider.WIKIPEDIA:
|
||||
return new lazy.Suggestion.Wikipedia(
|
||||
suggestion.title,
|
||||
suggestion.url,
|
||||
[], // icon
|
||||
query.keyword // fullKeyword
|
||||
);
|
||||
}
|
||||
throw new Error("Unrecognized provider: " + provider);
|
||||
}
|
||||
|
||||
#data = null;
|
||||
#sandbox = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test utils for quick suggest.
|
||||
*/
|
||||
|
@ -175,16 +421,15 @@ class _QuickSuggestTestUtils {
|
|||
}
|
||||
|
||||
/**
|
||||
* Sets up local remote settings and Merino servers, registers test
|
||||
* suggestions, and initializes Suggest.
|
||||
* Waits for quick suggest initialization to finish, ensures its data will not
|
||||
* be updated again during the test, and also optionally sets it up with mock
|
||||
* suggestions.
|
||||
*
|
||||
* @param {object} options
|
||||
* Options object
|
||||
* @param {Array} options.remoteSettingsRecords
|
||||
* Array of remote settings records. Each item in this array should be a
|
||||
* realistic remote settings record with some exceptions, e.g.,
|
||||
* `record.attachment`, if defined, should be the attachment itself and not
|
||||
* its metadata. For details see `RemoteSettingsServer.addRecords()`.
|
||||
* @param {Array} options.remoteSettingsResults
|
||||
* Array of remote settings result objects. If not given, no suggestions
|
||||
* will be present in remote settings.
|
||||
* @param {Array} options.merinoSuggestions
|
||||
* Array of Merino suggestion objects. If given, this function will start
|
||||
* the mock Merino server and set `quicksuggest.dataCollection.enabled` to
|
||||
|
@ -192,152 +437,105 @@ class _QuickSuggestTestUtils {
|
|||
* Otherwise Merino will not serve suggestions, but you can still set up
|
||||
* Merino without using this function by using `MerinoTestUtils` directly.
|
||||
* @param {object} options.config
|
||||
* The Suggest configuration object. This should not be the full remote
|
||||
* settings record; only pass the object that should be set to the nested
|
||||
* `configuration` object inside the record.
|
||||
* @param {Array} options.prefs
|
||||
* An array of Suggest-related prefs to set. This is useful because setting
|
||||
* some prefs, like feature gates, can cause Suggest to sync from remote
|
||||
* settings; this function will set them, wait for sync to finish, and clear
|
||||
* them when the cleanup function is called. Each item in this array should
|
||||
* itself be a two-element array `[prefName, prefValue]` similar to the
|
||||
* `set` array passed to `SpecialPowers.pushPrefEnv()`, except here pref
|
||||
* names are relative to `browser.urlbar`.
|
||||
* The quick suggest configuration object.
|
||||
* @param {object} options.rustEnabled
|
||||
* Whether the Rust backend should be enabled. If false, the JS backend will
|
||||
* be used. (There's no way to tell this function not to change the backend.
|
||||
* If you need that, please modify this function to support it!)
|
||||
* @returns {Function}
|
||||
* An async cleanup function. This function is automatically registered as a
|
||||
* cleanup function, so you only need to call it if your test needs to clean
|
||||
* up Suggest before it ends, for example if you have a small number of
|
||||
* tasks that need Suggest and it's not enabled throughout your test. The
|
||||
* cleanup function is idempotent so there's no harm in calling it more than
|
||||
* once. Be sure to `await` it.
|
||||
* An async cleanup function. This function is automatically registered as
|
||||
* a cleanup function, so you only need to call it if your test needs to
|
||||
* clean up quick suggest before it ends, for example if you have a small
|
||||
* number of tasks that need quick suggest and it's not enabled throughout
|
||||
* your test. The cleanup function is idempotent so there's no harm in
|
||||
* calling it more than once. Be sure to `await` it.
|
||||
*/
|
||||
async ensureQuickSuggestInit({
|
||||
remoteSettingsRecords = [],
|
||||
remoteSettingsResults,
|
||||
merinoSuggestions = null,
|
||||
config = DEFAULT_CONFIG,
|
||||
prefs = [],
|
||||
rustEnabled = false,
|
||||
} = {}) {
|
||||
prefs.push(["quicksuggest.enabled", true]);
|
||||
|
||||
// Set up the local remote settings server. We do this even for tests that
|
||||
// aren't specifically related to remote settings so Suggest doesn't hit the
|
||||
// real remote settings server during testing.
|
||||
this.#log(
|
||||
"ensureQuickSuggestInit",
|
||||
"Started, preparing remote settings server"
|
||||
);
|
||||
if (!this.#remoteSettingsServer) {
|
||||
this.#remoteSettingsServer = new lazy.RemoteSettingsServer();
|
||||
}
|
||||
await this.#remoteSettingsServer.setRecords({
|
||||
collection: "quicksuggest",
|
||||
records: [
|
||||
...remoteSettingsRecords,
|
||||
{ type: "configuration", configuration: config },
|
||||
],
|
||||
this.#mockRemoteSettings = new MockRemoteSettings({
|
||||
config,
|
||||
data: remoteSettingsResults,
|
||||
});
|
||||
this.#mockRustSuggest = new MockRustSuggest({
|
||||
data: remoteSettingsResults,
|
||||
});
|
||||
this.#log("ensureQuickSuggestInit", "Starting remote settings server");
|
||||
await this.#remoteSettingsServer.start();
|
||||
|
||||
// Get the cached `RemoteSettings` client used by the JS backend and tell it
|
||||
// to ignore signatures and to always force sync. Otherwise it won't sync if
|
||||
// the previous sync was recent enough, which is incompatible with testing.
|
||||
let rs = lazy.RemoteSettings("quicksuggest");
|
||||
let { get, verifySignature } = rs;
|
||||
rs.verifySignature = false;
|
||||
rs.get = opts => get.call(rs, { forceSync: true, ...opts });
|
||||
this.#restoreRemoteSettings = () => {
|
||||
rs.verifySignature = verifySignature;
|
||||
rs.get = get;
|
||||
};
|
||||
|
||||
// Tell the Rust backend to use the local remote setting server.
|
||||
lazy.SuggestBackendRust._test_remoteSettingsConfig =
|
||||
new lazy.RemoteSettingsConfig(
|
||||
this.#remoteSettingsServer.url.toString(),
|
||||
"main",
|
||||
"quicksuggest"
|
||||
);
|
||||
|
||||
// Finally, init Suggest and set prefs. Do this after setting up remote
|
||||
// settings because the current backend will immediately try to sync.
|
||||
this.#log(
|
||||
"ensureQuickSuggestInit",
|
||||
"Calling QuickSuggest.init() and setting prefs"
|
||||
);
|
||||
this.info?.("ensureQuickSuggestInit calling QuickSuggest.init()");
|
||||
lazy.QuickSuggest.init();
|
||||
for (let [name, value] of prefs) {
|
||||
lazy.UrlbarPrefs.set(name, value);
|
||||
|
||||
// Set the Rust pref. This must happen after setting up `MockRustSuggest`.
|
||||
// Otherwise the real Rust component will be used and the Rust remote
|
||||
// settings client will try to access the real remote settings server on
|
||||
// ingestion.
|
||||
lazy.UrlbarPrefs.set("quicksuggest.rustEnabled", rustEnabled);
|
||||
if (!rustEnabled) {
|
||||
// Sync with current data.
|
||||
this.info?.("ensureQuickSuggestInit syncing MockRemoteSettings");
|
||||
await this.#mockRemoteSettings.sync();
|
||||
this.info?.("ensureQuickSuggestInit done syncing MockRemoteSettings");
|
||||
}
|
||||
|
||||
// Wait for the current backend to finish syncing.
|
||||
await this.forceSync();
|
||||
|
||||
// Set up Merino. This can happen any time relative to Suggest init.
|
||||
// Set up Merino.
|
||||
if (merinoSuggestions) {
|
||||
this.#log("ensureQuickSuggestInit", "Setting up Merino server");
|
||||
this.info?.("ensureQuickSuggestInit setting up Merino server");
|
||||
await lazy.MerinoTestUtils.server.start();
|
||||
lazy.MerinoTestUtils.server.response.body.suggestions = merinoSuggestions;
|
||||
lazy.UrlbarPrefs.set("quicksuggest.dataCollection.enabled", true);
|
||||
this.#log("ensureQuickSuggestInit", "Done setting up Merino server");
|
||||
this.info?.("ensureQuickSuggestInit done setting up Merino server");
|
||||
}
|
||||
|
||||
let cleanupCalled = false;
|
||||
let cleanup = async () => {
|
||||
if (!cleanupCalled) {
|
||||
cleanupCalled = true;
|
||||
await this.#uninitQuickSuggest(prefs, !!merinoSuggestions);
|
||||
await this.#uninitQuickSuggest(!!merinoSuggestions);
|
||||
}
|
||||
};
|
||||
this.registerCleanupFunction?.(cleanup);
|
||||
|
||||
this.#log("ensureQuickSuggestInit", "Done");
|
||||
return cleanup;
|
||||
}
|
||||
|
||||
async #uninitQuickSuggest(prefs, clearDataCollectionEnabled) {
|
||||
this.#log("#uninitQuickSuggest", "Started");
|
||||
async #uninitQuickSuggest(clearDataCollectionEnabled) {
|
||||
this.info?.("uninitQuickSuggest started");
|
||||
|
||||
// Reset prefs, which can cause the current backend to start syncing. Wait
|
||||
// for it to finish.
|
||||
for (let [name] of prefs) {
|
||||
lazy.UrlbarPrefs.clear(name);
|
||||
// Reset the Rust enabled status. If the JS backend becomes enabled now, it
|
||||
// will re-sync all features. Wait for that to finish *before* cleaning up
|
||||
// MockRemoteSettings. This will ensure that all activity has stopped before
|
||||
// this function returns.
|
||||
let rustEnabled = lazy.UrlbarPrefs.get("quicksuggest.rustEnabled");
|
||||
lazy.UrlbarPrefs.clear("quicksuggest.rustEnabled");
|
||||
if (rustEnabled && !lazy.UrlbarPrefs.get("quicksuggest.rustEnabled")) {
|
||||
this.info?.("uninitQuickSuggest syncing MockRemoteSettings");
|
||||
await this.#mockRemoteSettings.sync();
|
||||
this.info?.("uninitQuickSuggest done syncing MockRemoteSettings");
|
||||
}
|
||||
await this.forceSync();
|
||||
|
||||
this.#log("#uninitQuickSuggest", "Stopping remote settings server");
|
||||
await this.#remoteSettingsServer.stop();
|
||||
this.#restoreRemoteSettings();
|
||||
this.#mockRemoteSettings.cleanup();
|
||||
this.#mockRustSuggest.cleanup();
|
||||
|
||||
if (clearDataCollectionEnabled) {
|
||||
lazy.UrlbarPrefs.clear("quicksuggest.dataCollection.enabled");
|
||||
}
|
||||
|
||||
this.#log("#uninitQuickSuggest", "Done");
|
||||
this.info?.("uninitQuickSuggest done");
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all records from the local remote settings server and adds a new
|
||||
* batch of records.
|
||||
* Clears the current remote settings data and adds a new set of data.
|
||||
* This can be used to add remote settings data after
|
||||
* `ensureQuickSuggestInit()` has been called.
|
||||
*
|
||||
* @param {Array} records
|
||||
* Array of remote settings records. See `ensureQuickSuggestInit()`.
|
||||
* @param {object} options
|
||||
* Options object.
|
||||
* @param {boolean} options.forceSync
|
||||
* Whether to force Suggest to sync after updating the records.
|
||||
* @param {Array} data
|
||||
* Array of remote settings data objects.
|
||||
*/
|
||||
async setRemoteSettingsRecords(records, { forceSync = true } = {}) {
|
||||
this.#log("setRemoteSettingsRecords", "Started");
|
||||
await this.#remoteSettingsServer.setRecords({
|
||||
collection: "quicksuggest",
|
||||
records,
|
||||
});
|
||||
if (forceSync) {
|
||||
this.#log("setRemoteSettingsRecords", "Forcing sync");
|
||||
await this.forceSync();
|
||||
}
|
||||
this.#log("setRemoteSettingsRecords", "Done");
|
||||
async setRemoteSettingsResults(data) {
|
||||
await this.#mockRemoteSettings.update({ data });
|
||||
this.#mockRustSuggest.update({ data });
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -345,40 +543,10 @@ class _QuickSuggestTestUtils {
|
|||
* `DEFAULT_CONFIG` before your test finishes. See also `withConfig()`.
|
||||
*
|
||||
* @param {object} config
|
||||
* The quick suggest configuration object. This should not be the full
|
||||
* remote settings record; only pass the object that should be set to the
|
||||
* `configuration` nested object inside the record.
|
||||
* The config to be applied. See
|
||||
*/
|
||||
async setConfig(config) {
|
||||
this.#log("setConfig", "Started");
|
||||
let type = "configuration";
|
||||
this.#remoteSettingsServer.removeRecords({ type });
|
||||
await this.#remoteSettingsServer.addRecords({
|
||||
collection: "quicksuggest",
|
||||
records: [{ type, configuration: config }],
|
||||
});
|
||||
this.#log("setConfig", "Forcing sync");
|
||||
await this.forceSync();
|
||||
this.#log("setConfig", "Done");
|
||||
}
|
||||
|
||||
/**
|
||||
* Forces Suggest to sync with remote settings. This can be used to ensure
|
||||
* Suggest has finished all sync activity.
|
||||
*/
|
||||
async forceSync() {
|
||||
this.#log("forceSync", "Started");
|
||||
if (lazy.QuickSuggest.rustBackend.isEnabled) {
|
||||
this.#log("forceSync", "Syncing Rust backend");
|
||||
await lazy.QuickSuggest.rustBackend._test_ingest();
|
||||
this.#log("forceSync", "Done syncing Rust backend");
|
||||
}
|
||||
if (lazy.QuickSuggest.jsBackend.isEnabled) {
|
||||
this.#log("forceSync", "Syncing JS backend");
|
||||
await lazy.QuickSuggest.jsBackend._test_syncAll();
|
||||
this.#log("forceSync", "Done syncing JS backend");
|
||||
}
|
||||
this.#log("forceSync", "Done");
|
||||
await this.#mockRemoteSettings.update({ config });
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -484,8 +652,7 @@ class _QuickSuggestTestUtils {
|
|||
);
|
||||
let { result } = details;
|
||||
|
||||
this.#log(
|
||||
"assertIsQuickSuggest",
|
||||
this.info?.(
|
||||
`Checking actual result at index ${index}: ` + JSON.stringify(result)
|
||||
);
|
||||
|
||||
|
@ -694,8 +861,7 @@ class _QuickSuggestTestUtils {
|
|||
|
||||
for (let i = 0; i < pings.length; i++) {
|
||||
let ping = pings[i];
|
||||
this.#log(
|
||||
"assertPings",
|
||||
this.info?.(
|
||||
`Checking ping at index ${i}, expected is: ` + JSON.stringify(ping)
|
||||
);
|
||||
|
||||
|
@ -719,7 +885,7 @@ class _QuickSuggestTestUtils {
|
|||
|
||||
// Check the payload.
|
||||
let actualPayload = call.args[0];
|
||||
this.#assertPingPayload(actualPayload, payload);
|
||||
this._assertPingPayload(actualPayload, payload);
|
||||
}
|
||||
|
||||
spy.resetHistory();
|
||||
|
@ -736,9 +902,8 @@ class _QuickSuggestTestUtils {
|
|||
* values. Function values are called and passed the corresponding actual
|
||||
* values and should return true if the actual values are correct.
|
||||
*/
|
||||
#assertPingPayload(actualPayload, expectedPayload) {
|
||||
this.#log(
|
||||
"#assertPingPayload",
|
||||
_assertPingPayload(actualPayload, expectedPayload) {
|
||||
this.info?.(
|
||||
"Checking ping payload. Actual: " +
|
||||
JSON.stringify(actualPayload) +
|
||||
" -- Expected (excluding function properties): " +
|
||||
|
@ -801,10 +966,7 @@ class _QuickSuggestTestUtils {
|
|||
urls[key] = { url, value, timestamp };
|
||||
}
|
||||
|
||||
this.#log(
|
||||
"assertTimestampsReplaced",
|
||||
"Parsed timestamps: " + JSON.stringify(urls)
|
||||
);
|
||||
this.info?.("Parsed timestamps: " + JSON.stringify(urls));
|
||||
|
||||
// Make a set of unique timestamp strings. There should only be one.
|
||||
let { timestamp } = Object.values(urls)[0];
|
||||
|
@ -860,7 +1022,7 @@ class _QuickSuggestTestUtils {
|
|||
* The experiment cleanup function (async).
|
||||
*/
|
||||
async enrollExperiment({ valueOverrides = {} }) {
|
||||
this.#log("enrollExperiment", "Awaiting ExperimentAPI.ready");
|
||||
this.info?.("Awaiting ExperimentAPI.ready");
|
||||
await lazy.ExperimentAPI.ready();
|
||||
|
||||
// Wait for any prior scenario updates to finish. If updates are ongoing,
|
||||
|
@ -878,22 +1040,16 @@ class _QuickSuggestTestUtils {
|
|||
});
|
||||
|
||||
// Wait for the pref updates triggered by the experiment enrollment.
|
||||
this.#log(
|
||||
"enrollExperiment",
|
||||
"Awaiting update after enrolling in experiment"
|
||||
);
|
||||
this.info?.("Awaiting update after enrolling in experiment");
|
||||
await this.waitForScenarioUpdated();
|
||||
|
||||
return async () => {
|
||||
this.#log("enrollExperiment.cleanup", "Awaiting experiment cleanup");
|
||||
this.info?.("Awaiting experiment cleanup");
|
||||
await doExperimentCleanup();
|
||||
|
||||
// The same pref updates will be triggered by unenrollment, so wait for
|
||||
// them again.
|
||||
this.#log(
|
||||
"enrollExperiment.cleanup",
|
||||
"Awaiting update after unenrolling in experiment"
|
||||
);
|
||||
this.info?.("Awaiting update after unenrolling in experiment");
|
||||
await this.waitForScenarioUpdated();
|
||||
};
|
||||
}
|
||||
|
@ -965,8 +1121,7 @@ class _QuickSuggestTestUtils {
|
|||
*/
|
||||
async withLocales(locales, callback) {
|
||||
let promiseChanges = async desiredLocales => {
|
||||
this.#log(
|
||||
"withLocales",
|
||||
this.info?.(
|
||||
"Changing locales from " +
|
||||
JSON.stringify(Services.locale.requestedLocales) +
|
||||
" to " +
|
||||
|
@ -978,38 +1133,32 @@ class _QuickSuggestTestUtils {
|
|||
return;
|
||||
}
|
||||
|
||||
this.#log("withLocales", "Waiting for intl:requested-locales-changed");
|
||||
this.info?.("Waiting for intl:requested-locales-changed");
|
||||
await lazy.TestUtils.topicObserved("intl:requested-locales-changed");
|
||||
this.#log("withLocales", "Observed intl:requested-locales-changed");
|
||||
this.info?.("Observed intl:requested-locales-changed");
|
||||
|
||||
// Wait for the search service to reload engines. Otherwise tests can fail
|
||||
// in strange ways due to internal search service state during shutdown.
|
||||
// It won't always reload engines but it's hard to tell in advance when it
|
||||
// won't, so also set a timeout.
|
||||
this.#log("withLocales", "Waiting for TOPIC_SEARCH_SERVICE");
|
||||
this.info?.("Waiting for TOPIC_SEARCH_SERVICE");
|
||||
await Promise.race([
|
||||
lazy.TestUtils.topicObserved(
|
||||
lazy.SearchUtils.TOPIC_SEARCH_SERVICE,
|
||||
(subject, data) => {
|
||||
this.#log(
|
||||
"withLocales",
|
||||
"Observed TOPIC_SEARCH_SERVICE with data: " + data
|
||||
);
|
||||
this.info?.("Observed TOPIC_SEARCH_SERVICE with data: " + data);
|
||||
return data == "engines-reloaded";
|
||||
}
|
||||
),
|
||||
new Promise(resolve => {
|
||||
lazy.setTimeout(() => {
|
||||
this.#log(
|
||||
"withLocales",
|
||||
"Timed out waiting for TOPIC_SEARCH_SERVICE"
|
||||
);
|
||||
this.info?.("Timed out waiting for TOPIC_SEARCH_SERVICE");
|
||||
resolve();
|
||||
}, 2000);
|
||||
}),
|
||||
]);
|
||||
|
||||
this.#log("withLocales", "Done waiting for locale changes");
|
||||
this.info?.("Done waiting for locale changes");
|
||||
};
|
||||
|
||||
let available = Services.locale.availableLocales;
|
||||
|
@ -1035,12 +1184,8 @@ class _QuickSuggestTestUtils {
|
|||
await promise;
|
||||
}
|
||||
|
||||
#log(fnName, msg) {
|
||||
this.info?.(`QuickSuggestTestUtils.${fnName} ${msg}`);
|
||||
}
|
||||
|
||||
#remoteSettingsServer;
|
||||
#restoreRemoteSettings;
|
||||
#mockRemoteSettings = null;
|
||||
#mockRustSuggest = null;
|
||||
}
|
||||
|
||||
export var QuickSuggestTestUtils = new _QuickSuggestTestUtils();
|
||||
|
|
|
@ -1,619 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/* eslint-disable jsdoc/require-param-description */
|
||||
|
||||
const lazy = {};
|
||||
|
||||
ChromeUtils.defineESModuleGetters(lazy, {
|
||||
HttpError: "resource://testing-common/httpd.sys.mjs",
|
||||
HttpServer: "resource://testing-common/httpd.sys.mjs",
|
||||
HTTP_404: "resource://testing-common/httpd.sys.mjs",
|
||||
Log: "resource://gre/modules/Log.sys.mjs",
|
||||
});
|
||||
|
||||
const SERVER_PREF = "services.settings.server";
|
||||
|
||||
/**
|
||||
* A remote settings server. Tested with the desktop and Rust remote settings
|
||||
* clients.
|
||||
*/
|
||||
export class RemoteSettingsServer {
|
||||
/**
|
||||
* The server must be started by calling `start()`.
|
||||
*
|
||||
* @param {object} options
|
||||
* @param {number} options.logLevel
|
||||
* A `Log.Level` value from `Log.sys.mjs`. `Log.Level.Info` logs basic info
|
||||
* on requests and responses like paths and status codes. Pass
|
||||
* `Log.Level.Debug` to log more info like headers, response bodies, and
|
||||
* added and removed records.
|
||||
*/
|
||||
constructor({ logLevel = lazy.Log.Level.Info } = {}) {
|
||||
this.#log = lazy.Log.repository.getLogger("RemoteSettingsServer");
|
||||
this.#log.level = logLevel;
|
||||
|
||||
// Use `DumpAppender` instead of `ConsoleAppender`. The xpcshell and browser
|
||||
// test harnesses buffer console messages and log them later, which makes it
|
||||
// really hard to debug problems. `DumpAppender` logs to stdout, which the
|
||||
// harnesses log immediately.
|
||||
this.#log.addAppender(
|
||||
new lazy.Log.DumpAppender(new lazy.Log.BasicFormatter())
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {URL}
|
||||
* The server's URL. Null when the server is stopped.
|
||||
*/
|
||||
get url() {
|
||||
return this.#url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the server and sets the `services.settings.server` pref to its
|
||||
* URL. The server's `url` property will be non-null on return.
|
||||
*/
|
||||
async start() {
|
||||
this.#log.info("Starting");
|
||||
|
||||
if (this.#url) {
|
||||
this.#log.info("Already started at " + this.#url);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.#server) {
|
||||
this.#server = new lazy.HttpServer();
|
||||
this.#server.registerPrefixHandler("/", this);
|
||||
}
|
||||
this.#server.start(-1);
|
||||
|
||||
this.#url = new URL("http://localhost/v1");
|
||||
this.#url.port = this.#server.identity.primaryPort;
|
||||
|
||||
this.#originalServerPrefValue = Services.prefs.getCharPref(
|
||||
SERVER_PREF,
|
||||
null
|
||||
);
|
||||
Services.prefs.setCharPref(SERVER_PREF, this.#url.toString());
|
||||
|
||||
this.#log.info("Server is now started at " + this.#url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the server and clears the `services.settings.server` pref. The
|
||||
* server's `url` property will be null on return.
|
||||
*/
|
||||
async stop() {
|
||||
this.#log.info("Stopping");
|
||||
|
||||
if (!this.#url) {
|
||||
this.#log.info("Already stopped");
|
||||
return;
|
||||
}
|
||||
|
||||
await this.#server.stop();
|
||||
this.#url = null;
|
||||
|
||||
if (this.#originalServerPrefValue === null) {
|
||||
Services.prefs.clearUserPref(SERVER_PREF);
|
||||
} else {
|
||||
Services.prefs.setCharPref(SERVER_PREF, this.#originalServerPrefValue);
|
||||
}
|
||||
|
||||
this.#log.info("Server is now stopped");
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds remote settings records to the server. Records may have attachments;
|
||||
* see the param doc below.
|
||||
*
|
||||
* @param {object} options
|
||||
* @param {string} options.bucket
|
||||
* @param {string} options.collection
|
||||
* @param {Array} options.records
|
||||
* Each object in this array should be a realistic remote settings record
|
||||
* with the following exceptions:
|
||||
*
|
||||
* - `record.id` will be generated if it's undefined.
|
||||
* - `record.last_modified` will be set to the `#lastModified` property of
|
||||
* the server if it's undefined.
|
||||
* - `record.attachment`, if defined, should be the attachment itself and
|
||||
* not its metadata. The server will automatically create some dummy
|
||||
* metadata. Currently the only supported attachment type is plain
|
||||
* JSON'able objects that the server will convert to JSON in responses.
|
||||
*/
|
||||
async addRecords({ bucket = "main", collection = "test", records }) {
|
||||
this.#log.debug(
|
||||
"Adding records: " +
|
||||
JSON.stringify({ bucket, collection, records }, null, 2)
|
||||
);
|
||||
|
||||
this.#lastModified++;
|
||||
|
||||
let key = this.#recordsKey(bucket, collection);
|
||||
let allRecords = this.#records.get(key);
|
||||
if (!allRecords) {
|
||||
allRecords = [];
|
||||
this.#records.set(key, allRecords);
|
||||
}
|
||||
|
||||
for (let record of records) {
|
||||
let copy = { ...record };
|
||||
if (!copy.hasOwnProperty("id")) {
|
||||
copy.id = String(this.#nextRecordId++);
|
||||
}
|
||||
if (!copy.hasOwnProperty("last_modified")) {
|
||||
copy.last_modified = this.#lastModified;
|
||||
}
|
||||
if (copy.attachment) {
|
||||
await this.#addAttachment({ bucket, collection, record: copy });
|
||||
}
|
||||
allRecords.push(copy);
|
||||
}
|
||||
|
||||
this.#log.debug(
|
||||
"Done adding records. All records are now: " +
|
||||
JSON.stringify([...this.#records.entries()], null, 2)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks records as deleted. Deleted records will still be returned in
|
||||
* responses, but they'll have a `deleted = true` property. Their attachments
|
||||
* will be deleted immediately, however.
|
||||
*
|
||||
* @param {object} filter
|
||||
* If null, all records will be marked as deleted. Otherwise only records
|
||||
* that match the filter will be marked as deleted. For a given record, each
|
||||
* value in the filter object will be compared to the value with the same
|
||||
* key in the record. If all values are the same, the record will be
|
||||
* removed. Examples:
|
||||
*
|
||||
* To remove remove records whose `type` key has the value "data":
|
||||
* `{ type: "data" }
|
||||
*
|
||||
* To remove remove records whose `type` key has the value "data" and whose
|
||||
* `last_modified` key has the value 1234:
|
||||
* `{ type: "data", last_modified: 1234 }
|
||||
*/
|
||||
removeRecords(filter = null) {
|
||||
this.#log.debug("Removing records: " + JSON.stringify({ filter }));
|
||||
|
||||
this.#lastModified++;
|
||||
|
||||
for (let [recordsKey, records] of this.#records.entries()) {
|
||||
for (let record of records) {
|
||||
if (
|
||||
!filter ||
|
||||
Object.entries(filter).every(
|
||||
([filterKey, filterValue]) =>
|
||||
record.hasOwnProperty(filterKey) &&
|
||||
record[filterKey] == filterValue
|
||||
)
|
||||
) {
|
||||
if (record.attachment) {
|
||||
let attachmentKey = `${recordsKey}/${record.attachment.filename}`;
|
||||
this.#attachments.delete(attachmentKey);
|
||||
}
|
||||
record.deleted = true;
|
||||
record.last_modified = this.#lastModified;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.#log.debug(
|
||||
"Done removing records. All records are now: " +
|
||||
JSON.stringify([...this.#records.entries()], null, 2)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all existing records and adds the given records to the server.
|
||||
*
|
||||
* @param {object} options
|
||||
* @param {string} options.bucket
|
||||
* @param {string} options.collection
|
||||
* @param {Array} options.records
|
||||
* See `addRecords()`.
|
||||
*/
|
||||
async setRecords({ bucket = "main", collection = "test", records }) {
|
||||
this.#log.debug("Setting records");
|
||||
|
||||
this.removeRecords();
|
||||
await this.addRecords({ bucket, collection, records });
|
||||
|
||||
this.#log.debug("Done setting records");
|
||||
}
|
||||
|
||||
/**
|
||||
* `nsIHttpRequestHandler` callback from the backing server. Handles a
|
||||
* request.
|
||||
*
|
||||
* @param {nsIHttpRequest} request
|
||||
* @param {nsIHttpResponse} response
|
||||
*/
|
||||
handle(request, response) {
|
||||
this.#logRequest(request);
|
||||
|
||||
// Get the route that matches the request path.
|
||||
let { match, route } = this.#getRoute(request.path) || {};
|
||||
if (!route) {
|
||||
this.#prepareError({ request, response, error: lazy.HTTP_404 });
|
||||
return;
|
||||
}
|
||||
|
||||
let respInfo = route.response(match, request, response);
|
||||
if (respInfo instanceof lazy.HttpError) {
|
||||
this.#prepareError({ request, response, error: respInfo });
|
||||
} else {
|
||||
this.#prepareResponse({ ...respInfo, request, response });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {Array}
|
||||
* The routes handled by the server. Each item in this array is an object
|
||||
* with the following properties that describes one or more paths and the
|
||||
* response that should be sent when a request is made on those paths:
|
||||
*
|
||||
* {string} spec
|
||||
* A path spec. This is required unless `specs` is defined. To determine
|
||||
* which route should be used for a given request, the server will check
|
||||
* each route's spec(s) until it finds the first that matches the
|
||||
* request's path. A spec is just a path whose components can be variables
|
||||
* that start with "$". When a spec with variables matches a request path,
|
||||
* the `match` object passed to the route's `response` function will map
|
||||
* from variable names to the corresponding components in the path.
|
||||
* {Array} specs
|
||||
* An array of path spec strings. Use this instead of `spec` if the route
|
||||
* handles more than one.
|
||||
* {function} response
|
||||
* A function that will be called when the route matches a request. It is
|
||||
* called as: `response(match, request, response)`
|
||||
*
|
||||
* {object} match
|
||||
* An object mapping variable names in the spec to their matched
|
||||
* components in the path. See `#match()` for details.
|
||||
* {nsIHttpRequest} request
|
||||
* {nsIHttpResponse} response
|
||||
*
|
||||
* The function must return one of the following:
|
||||
*
|
||||
* {object}
|
||||
* An object that describes the response with the following properties:
|
||||
* {object} body
|
||||
* A plain JSON'able object. The server will convert this to JSON and
|
||||
* set it to the response body.
|
||||
* {HttpError}
|
||||
* An `HttpError` instance defined in `httpd.sys.mjs`.
|
||||
*/
|
||||
get #routes() {
|
||||
return [
|
||||
{
|
||||
spec: "/v1",
|
||||
response: () => ({
|
||||
body: {
|
||||
capabilities: {
|
||||
attachments: {
|
||||
base_url: this.#url.toString(),
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
|
||||
{
|
||||
spec: "/v1/buckets/monitor/collections/changes/changeset",
|
||||
response: () => ({
|
||||
body: {
|
||||
timestamp: this.#lastModified,
|
||||
changes: [
|
||||
{
|
||||
last_modified: this.#lastModified,
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
},
|
||||
|
||||
{
|
||||
spec: "/v1/buckets/$bucket/collections/$collection/changeset",
|
||||
response: ({ bucket, collection }) => {
|
||||
let records = this.#getRecords(bucket, collection);
|
||||
return !records
|
||||
? lazy.HTTP_404
|
||||
: {
|
||||
body: {
|
||||
metadata: null,
|
||||
timestamp: this.#lastModified,
|
||||
changes: records,
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
spec: "/v1/buckets/$bucket/collections/$collection/records",
|
||||
response: ({ bucket, collection }) => {
|
||||
let records = this.#getRecords(bucket, collection);
|
||||
return !records
|
||||
? lazy.HTTP_404
|
||||
: {
|
||||
body: {
|
||||
data: records,
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
specs: [
|
||||
// The Rust remote settings client doesn't include "v1" in attachment
|
||||
// URLs, but the JS client does.
|
||||
"/attachments/$bucket/$collection/$filename",
|
||||
"/v1/attachments/$bucket/$collection/$filename",
|
||||
],
|
||||
response: ({ bucket, collection, filename }) => {
|
||||
return {
|
||||
body: this.#getAttachment(bucket, collection, filename),
|
||||
};
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {object}
|
||||
* Default response headers.
|
||||
*/
|
||||
get #responseHeaders() {
|
||||
return {
|
||||
"Access-Control-Allow-Origin": "*",
|
||||
"Access-Control-Expose-Headers":
|
||||
"Retry-After, Content-Length, Alert, Backoff",
|
||||
Server: "waitress",
|
||||
Etag: `"${this.#lastModified}"`,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the route that matches a request path.
|
||||
*
|
||||
* @param {string} path
|
||||
* A request path.
|
||||
* @returns {object}
|
||||
* If no route matches the path, returns an empty object. Otherwise returns
|
||||
* an object with the following properties:
|
||||
*
|
||||
* {object} match
|
||||
* An object describing the matched variables in the route spec. See
|
||||
* `#match()` for details.
|
||||
* {object} route
|
||||
* The matched route. See `#routes` for details.
|
||||
*/
|
||||
#getRoute(path) {
|
||||
for (let route of this.#routes) {
|
||||
let specs = route.specs || [route.spec];
|
||||
for (let spec of specs) {
|
||||
let match = this.#match(path, spec);
|
||||
if (match) {
|
||||
return { match, route };
|
||||
}
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Matches a request path to a route spec.
|
||||
*
|
||||
* @param {string} path
|
||||
* A request path.
|
||||
* @param {string} spec
|
||||
* A route spec. See `#routes` for details.
|
||||
* @returns {object|null}
|
||||
* If the spec doesn't match the path, returns null. Otherwise returns an
|
||||
* object mapping variable names in the spec to their matched components in
|
||||
* the path. Example:
|
||||
*
|
||||
* path : "/main/myfeature/foo"
|
||||
* spec : "/$bucket/$collection/foo"
|
||||
* returns: `{ bucket: "main", collection: "myfeature" }`
|
||||
*/
|
||||
#match(path, spec) {
|
||||
let pathParts = path.split("/");
|
||||
let specParts = spec.split("/");
|
||||
|
||||
if (pathParts.length != specParts.length) {
|
||||
// If the path has only one more part than the spec and its last part is
|
||||
// empty, then the path ends in a trailing slash but the spec does not.
|
||||
// Consider that a match. Otherwise return null for no match.
|
||||
if (
|
||||
pathParts[pathParts.length - 1] ||
|
||||
pathParts.length != specParts.length + 1
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
pathParts.pop();
|
||||
}
|
||||
|
||||
let match = {};
|
||||
for (let i = 0; i < pathParts.length; i++) {
|
||||
let pathPart = pathParts[i];
|
||||
let specPart = specParts[i];
|
||||
if (specPart.startsWith("$")) {
|
||||
match[specPart.substring(1)] = pathPart;
|
||||
} else if (pathPart != specPart) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return match;
|
||||
}
|
||||
|
||||
#getRecords(bucket, collection) {
|
||||
return this.#records.get(this.#recordsKey(bucket, collection));
|
||||
}
|
||||
|
||||
#recordsKey(bucket, collection) {
|
||||
return `${bucket}/${collection}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers an attachment for a record.
|
||||
*
|
||||
* @param {object} options
|
||||
* @param {string} options.bucket
|
||||
* @param {string} options.collection
|
||||
* @param {object} options.record
|
||||
* The record should have an `attachment` property as described in
|
||||
* `addRecords()`.
|
||||
*/
|
||||
async #addAttachment({ bucket, collection, record }) {
|
||||
let { attachment } = record;
|
||||
let filename = record.id;
|
||||
|
||||
this.#attachments.set(
|
||||
this.#attachmentsKey(bucket, collection, filename),
|
||||
attachment
|
||||
);
|
||||
|
||||
let encoder = new TextEncoder();
|
||||
let bytes = encoder.encode(JSON.stringify(attachment));
|
||||
|
||||
let hashBuffer = await crypto.subtle.digest("SHA-256", bytes);
|
||||
let hashBytes = new Uint8Array(hashBuffer);
|
||||
let toHex = b => b.toString(16).padStart(2, "0");
|
||||
let hash = Array.from(hashBytes, toHex).join("");
|
||||
|
||||
// Replace `record.attachment` with appropriate metadata in order to conform
|
||||
// with the remote settings API.
|
||||
record.attachment = {
|
||||
hash,
|
||||
filename,
|
||||
mimetype: "application/json; charset=UTF-8",
|
||||
size: bytes.length,
|
||||
location: `attachments/${bucket}/${collection}/${filename}`,
|
||||
};
|
||||
}
|
||||
|
||||
#attachmentsKey(bucket, collection, filename) {
|
||||
return `${bucket}/${collection}/${filename}`;
|
||||
}
|
||||
|
||||
#getAttachment(bucket, collection, filename) {
|
||||
return this.#attachments.get(
|
||||
this.#attachmentsKey(bucket, collection, filename)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares an HTTP response.
|
||||
*
|
||||
* @param {object} options
|
||||
* @param {nsIHttpRequest} options.request
|
||||
* @param {nsIHttpResponse} options.response
|
||||
* @param {object|null} options.body
|
||||
* Currently only JSON'able objects are supported. They will be converted to
|
||||
* JSON in the response.
|
||||
* @param {integer} options.status
|
||||
* @param {string} options.statusText
|
||||
*/
|
||||
#prepareResponse({
|
||||
request,
|
||||
response,
|
||||
body = null,
|
||||
status = 200,
|
||||
statusText = "OK",
|
||||
}) {
|
||||
let headers = { ...this.#responseHeaders };
|
||||
if (body) {
|
||||
headers["Content-Type"] = "application/json; charset=UTF-8";
|
||||
}
|
||||
|
||||
this.#logResponse({ request, status, statusText, headers, body });
|
||||
|
||||
for (let [name, value] of Object.entries(headers)) {
|
||||
response.setHeader(name, value, false);
|
||||
}
|
||||
if (body) {
|
||||
response.write(JSON.stringify(body));
|
||||
}
|
||||
response.setStatusLine(request.httpVersion, status, statusText);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares an HTTP error response.
|
||||
*
|
||||
* @param {object} options
|
||||
* @param {nsIHttpRequest} options.request
|
||||
* @param {nsIHttpResponse} options.response
|
||||
* @param {HttpError} options.error
|
||||
* An `HttpError` instance defined in `httpd.sys.mjs`.
|
||||
*/
|
||||
#prepareError({ request, response, error }) {
|
||||
this.#prepareResponse({
|
||||
request,
|
||||
response,
|
||||
status: error.code,
|
||||
statusText: error.description,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs a request.
|
||||
*
|
||||
* @param {nsIHttpRequest} request
|
||||
*/
|
||||
#logRequest(request) {
|
||||
let pathAndQuery = request.path;
|
||||
if (request.queryString) {
|
||||
pathAndQuery += "?" + request.queryString;
|
||||
}
|
||||
this.#log.info(
|
||||
`< HTTP ${request.httpVersion} ${request.method} ${pathAndQuery}`
|
||||
);
|
||||
for (let name of request.headers) {
|
||||
this.#log.debug(`${name}: ${request.getHeader(name.toString())}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs a response.
|
||||
*
|
||||
* @param {object} options
|
||||
* @param {nsIHttpRequest} options.request
|
||||
* The associated request.
|
||||
* @param {integer} options.status
|
||||
* The HTTP status code of the response.
|
||||
* @param {string} options.statusText
|
||||
* The description of the status code.
|
||||
* @param {object} options.headers
|
||||
* An object mapping from response header names to values.
|
||||
* @param {object} options.body
|
||||
* The response body, if any.
|
||||
*/
|
||||
#logResponse({ request, status, statusText, headers, body }) {
|
||||
this.#log.info(`> ${status} ${request.path}`);
|
||||
for (let [name, value] of Object.entries(headers)) {
|
||||
this.#log.debug(`${name}: ${value}`);
|
||||
}
|
||||
if (body) {
|
||||
this.#log.debug("Response body: " + JSON.stringify(body, null, 2));
|
||||
}
|
||||
}
|
||||
|
||||
// records key (see `#recordsKey()`) -> array of record objects
|
||||
#records = new Map();
|
||||
|
||||
// attachments key (see `#attachmentsKey()`) -> attachment object
|
||||
#attachments = new Map();
|
||||
|
||||
#log;
|
||||
#server;
|
||||
#originalServerPrefValue;
|
||||
#url = null;
|
||||
#lastModified = 1368273600000;
|
||||
#nextRecordId = 1;
|
||||
}
|
|
@ -18,8 +18,6 @@ const REMOTE_SETTINGS_RESULTS = [
|
|||
click_url: "http://click.reporting.test.com/",
|
||||
impression_url: "http://impression.reporting.test.com/",
|
||||
advertiser: "TestAdvertiser",
|
||||
iab_category: "22 - Shopping",
|
||||
icon: "1234",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
|
@ -28,9 +26,8 @@ const REMOTE_SETTINGS_RESULTS = [
|
|||
keywords: ["nonspon"],
|
||||
click_url: "http://click.reporting.test.com/nonsponsored",
|
||||
impression_url: "http://impression.reporting.test.com/nonsponsored",
|
||||
advertiser: "Wikipedia",
|
||||
advertiser: "TestAdvertiserNonSponsored",
|
||||
iab_category: "5 - Education",
|
||||
icon: "1234",
|
||||
},
|
||||
];
|
||||
|
||||
|
@ -40,7 +37,7 @@ add_setup(async function () {
|
|||
await UrlbarTestUtils.formHistory.clear();
|
||||
|
||||
await QuickSuggestTestUtils.ensureQuickSuggestInit({
|
||||
remoteSettingsRecords: [
|
||||
remoteSettingsResults: [
|
||||
{
|
||||
type: "data",
|
||||
attachment: REMOTE_SETTINGS_RESULTS,
|
||||
|
@ -50,7 +47,7 @@ add_setup(async function () {
|
|||
});
|
||||
|
||||
// Tests a sponsored result and keyword highlighting.
|
||||
add_tasks_with_rust(async function sponsored() {
|
||||
add_task(async function sponsored() {
|
||||
await UrlbarTestUtils.promiseAutocompleteResultPopup({
|
||||
window,
|
||||
value: "fra",
|
||||
|
@ -76,7 +73,7 @@ add_tasks_with_rust(async function sponsored() {
|
|||
});
|
||||
|
||||
// Tests a non-sponsored result.
|
||||
add_tasks_with_rust(async function nonSponsored() {
|
||||
add_task(async function nonSponsored() {
|
||||
await UrlbarTestUtils.promiseAutocompleteResultPopup({
|
||||
window,
|
||||
value: "nonspon",
|
||||
|
@ -91,7 +88,7 @@ add_tasks_with_rust(async function nonSponsored() {
|
|||
});
|
||||
|
||||
// Tests sponsored priority feature.
|
||||
add_tasks_with_rust(async function sponsoredPriority() {
|
||||
add_task(async function sponsoredPriority() {
|
||||
const cleanUpNimbus = await UrlbarTestUtils.initNimbusFeature({
|
||||
quickSuggestSponsoredPriority: true,
|
||||
});
|
||||
|
@ -134,33 +131,31 @@ add_tasks_with_rust(async function sponsoredPriority() {
|
|||
});
|
||||
|
||||
// Tests sponsored priority feature does not affect to non-sponsored suggestion.
|
||||
add_tasks_with_rust(
|
||||
async function sponsoredPriorityButNotSponsoredSuggestion() {
|
||||
const cleanUpNimbus = await UrlbarTestUtils.initNimbusFeature({
|
||||
quickSuggestSponsoredPriority: true,
|
||||
});
|
||||
add_task(async function sponsoredPriorityButNotSponsoredSuggestion() {
|
||||
const cleanUpNimbus = await UrlbarTestUtils.initNimbusFeature({
|
||||
quickSuggestSponsoredPriority: true,
|
||||
});
|
||||
|
||||
await UrlbarTestUtils.promiseAutocompleteResultPopup({
|
||||
window,
|
||||
value: "nonspon",
|
||||
});
|
||||
await QuickSuggestTestUtils.assertIsQuickSuggest({
|
||||
window,
|
||||
index: 1,
|
||||
isSponsored: false,
|
||||
url: `${TEST_URL}?q=nonsponsored`,
|
||||
});
|
||||
await UrlbarTestUtils.promiseAutocompleteResultPopup({
|
||||
window,
|
||||
value: "nonspon",
|
||||
});
|
||||
await QuickSuggestTestUtils.assertIsQuickSuggest({
|
||||
window,
|
||||
index: 1,
|
||||
isSponsored: false,
|
||||
url: `${TEST_URL}?q=nonsponsored`,
|
||||
});
|
||||
|
||||
let row = await UrlbarTestUtils.waitForAutocompleteResultAt(window, 1);
|
||||
let before = window.getComputedStyle(row, "::before");
|
||||
Assert.equal(before.content, "attr(label)", "::before.content is enabled");
|
||||
Assert.equal(
|
||||
row.getAttribute("label"),
|
||||
"Firefox Suggest",
|
||||
"Row has general group label for quick suggest"
|
||||
);
|
||||
let row = await UrlbarTestUtils.waitForAutocompleteResultAt(window, 1);
|
||||
let before = window.getComputedStyle(row, "::before");
|
||||
Assert.equal(before.content, "attr(label)", "::before.content is enabled");
|
||||
Assert.equal(
|
||||
row.getAttribute("label"),
|
||||
"Firefox Suggest",
|
||||
"Row has general group label for quick suggest"
|
||||
);
|
||||
|
||||
await UrlbarTestUtils.promisePopupClose(window);
|
||||
await cleanUpNimbus();
|
||||
}
|
||||
);
|
||||
await UrlbarTestUtils.promisePopupClose(window);
|
||||
await cleanUpNimbus();
|
||||
});
|
||||
|
|
|
@ -79,12 +79,11 @@ const TEST_MERINO_SUGGESTIONS = [
|
|||
|
||||
add_setup(async function () {
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [
|
||||
// Disable search suggestions so we don't hit the network.
|
||||
["browser.search.suggest.enabled", false],
|
||||
],
|
||||
set: [["browser.urlbar.quicksuggest.enabled", true]],
|
||||
});
|
||||
|
||||
await SearchTestUtils.installSearchExtension({}, { setAsDefault: true });
|
||||
|
||||
await QuickSuggestTestUtils.ensureQuickSuggestInit({
|
||||
merinoSuggestions: TEST_MERINO_SUGGESTIONS,
|
||||
});
|
||||
|
|
|
@ -2,9 +2,6 @@
|
|||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Tests quick suggest dismissals ("blocks").
|
||||
//
|
||||
// TODO (bug 1859389): Make this work with Rust enabled once timestamp templates
|
||||
// are handled.
|
||||
|
||||
"use strict";
|
||||
|
||||
|
@ -59,7 +56,7 @@ add_setup(async function () {
|
|||
Services.telemetry.clearEvents();
|
||||
|
||||
await QuickSuggestTestUtils.ensureQuickSuggestInit({
|
||||
remoteSettingsRecords: [
|
||||
remoteSettingsResults: [
|
||||
{
|
||||
type: "data",
|
||||
attachment: REMOTE_SETTINGS_RESULTS,
|
||||
|
|
|
@ -43,10 +43,12 @@ const REMOTE_SETTINGS_RESULTS = [
|
|||
},
|
||||
];
|
||||
|
||||
// Trying to avoid timeouts.
|
||||
requestLongerTimeout(3);
|
||||
|
||||
add_setup(async function () {
|
||||
// This test intermittently times out on Mac TV WebRender.
|
||||
if (AppConstants.platform == "macosx") {
|
||||
requestLongerTimeout(3);
|
||||
}
|
||||
|
||||
await PlacesUtils.history.clear();
|
||||
await PlacesUtils.bookmarks.eraseEverything();
|
||||
await UrlbarTestUtils.formHistory.clear();
|
||||
|
@ -55,7 +57,7 @@ add_setup(async function () {
|
|||
await SearchTestUtils.installSearchExtension({}, { setAsDefault: true });
|
||||
|
||||
await QuickSuggestTestUtils.ensureQuickSuggestInit({
|
||||
remoteSettingsRecords: [
|
||||
remoteSettingsResults: [
|
||||
{
|
||||
type: "data",
|
||||
attachment: REMOTE_SETTINGS_RESULTS,
|
||||
|
|
|
@ -21,13 +21,25 @@ const REMOTE_SETTINGS_DATA = [
|
|||
];
|
||||
|
||||
add_setup(async function () {
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [
|
||||
["browser.urlbar.quicksuggest.enabled", true],
|
||||
["browser.urlbar.quicksuggest.nonsponsored", true],
|
||||
["browser.urlbar.suggest.mdn", true],
|
||||
],
|
||||
});
|
||||
|
||||
await QuickSuggestTestUtils.ensureQuickSuggestInit({
|
||||
remoteSettingsRecords: REMOTE_SETTINGS_DATA,
|
||||
prefs: [["mdn.featureGate", true]],
|
||||
remoteSettingsResults: REMOTE_SETTINGS_DATA,
|
||||
});
|
||||
});
|
||||
|
||||
add_task(async function basic() {
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [["browser.urlbar.mdn.featureGate", true]],
|
||||
});
|
||||
await waitForSuggestions();
|
||||
|
||||
const suggestion = REMOTE_SETTINGS_DATA[0].attachment[0];
|
||||
await UrlbarTestUtils.promiseAutocompleteResultPopup({
|
||||
window,
|
||||
|
@ -56,10 +68,15 @@ add_task(async function basic() {
|
|||
Assert.ok(true, "Expected page is loaded");
|
||||
|
||||
await PlacesUtils.history.clear();
|
||||
await SpecialPowers.popPrefEnv();
|
||||
});
|
||||
|
||||
// Tests the row/group label.
|
||||
add_task(async function rowLabel() {
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [["browser.urlbar.mdn.featureGate", true]],
|
||||
});
|
||||
|
||||
await UrlbarTestUtils.promiseAutocompleteResultPopup({
|
||||
window,
|
||||
value: REMOTE_SETTINGS_DATA[0].attachment[0].keywords[0],
|
||||
|
@ -71,6 +88,7 @@ add_task(async function rowLabel() {
|
|||
Assert.equal(row.getAttribute("label"), "Recommended resource");
|
||||
|
||||
await UrlbarTestUtils.promisePopupClose(window);
|
||||
await SpecialPowers.popPrefEnv();
|
||||
});
|
||||
|
||||
add_task(async function disable() {
|
||||
|
@ -103,7 +121,7 @@ add_task(async function resultMenu_notInterested() {
|
|||
// Re-enable suggestions and wait until MDNSuggestions syncs them from
|
||||
// remote settings again.
|
||||
UrlbarPrefs.set("suggest.mdn", true);
|
||||
await QuickSuggestTestUtils.forceSync();
|
||||
await waitForSuggestions();
|
||||
});
|
||||
|
||||
// Tests the "Not relevant" result menu dismissal command.
|
||||
|
@ -120,6 +138,11 @@ add_task(async function notRelevant() {
|
|||
});
|
||||
|
||||
async function doDismissTest(command) {
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [["browser.urlbar.mdn.featureGate", true]],
|
||||
});
|
||||
await waitForSuggestions();
|
||||
|
||||
const keyword = REMOTE_SETTINGS_DATA[0].attachment[0].keywords[0];
|
||||
// Do a search.
|
||||
await UrlbarTestUtils.promiseAutocompleteResultPopup({
|
||||
|
@ -223,4 +246,14 @@ async function doDismissTest(command) {
|
|||
}
|
||||
|
||||
await UrlbarTestUtils.promisePopupClose(window);
|
||||
await SpecialPowers.popPrefEnv();
|
||||
}
|
||||
|
||||
async function waitForSuggestions() {
|
||||
const keyword = REMOTE_SETTINGS_DATA[0].attachment[0].keywords[0];
|
||||
const feature = QuickSuggest.getFeature("MDNSuggestions");
|
||||
await TestUtils.waitForCondition(async () => {
|
||||
const suggestions = await feature.queryRemoteSettings(keyword);
|
||||
return !!suggestions.length;
|
||||
}, "Waiting for MDNSuggestions to serve remote settings suggestions");
|
||||
}
|
||||
|
|
|
@ -35,11 +35,6 @@ if (AppConstants.platform === "macosx") {
|
|||
let gCanTabMoveFocus;
|
||||
add_setup(async function () {
|
||||
gCanTabMoveFocus = await canTabMoveFocus();
|
||||
|
||||
// Ensure the test remote settings server is set up. This test doesn't trigger
|
||||
// any suggestions but it enables Suggest, which will attempt to sync from
|
||||
// remote settings.
|
||||
await QuickSuggestTestUtils.ensureQuickSuggestInit();
|
||||
});
|
||||
|
||||
// When the user has already enabled the data-collection pref, the dialog should
|
||||
|
|
|
@ -4,12 +4,6 @@
|
|||
"use strict";
|
||||
|
||||
// Browser tests for Pocket suggestions.
|
||||
//
|
||||
// TODO: Make this work with Rust enabled. Right now, running this test with
|
||||
// Rust hits the following error on ingest, which prevents ingest from finishing
|
||||
// successfully:
|
||||
//
|
||||
// 0:03.17 INFO Console message: [JavaScript Error: "1698289045697 urlbar ERROR QuickSuggest.SuggestBackendRust :: Ingest error: Error executing SQL: FOREIGN KEY constraint failed" {file: "resource://gre/modules/Log.sys.mjs" line: 722}]
|
||||
|
||||
// The expected index of the Pocket suggestion.
|
||||
const EXPECTED_RESULT_INDEX = 1;
|
||||
|
@ -30,20 +24,24 @@ const REMOTE_SETTINGS_DATA = [
|
|||
];
|
||||
|
||||
add_setup(async function () {
|
||||
// This must be done before enabling the feature (using the `featureGate`
|
||||
// pref) so that the mock remote settings are set up first. Also, don't pass
|
||||
// in the remote settings data yet; see below.
|
||||
await QuickSuggestTestUtils.ensureQuickSuggestInit();
|
||||
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [
|
||||
["browser.urlbar.quicksuggest.enabled", true],
|
||||
["browser.urlbar.suggest.quicksuggest.nonsponsored", true],
|
||||
["browser.urlbar.pocket.featureGate", true],
|
||||
// Disable search suggestions so we don't hit the network.
|
||||
["browser.search.suggest.enabled", false],
|
||||
],
|
||||
});
|
||||
|
||||
await QuickSuggestTestUtils.ensureQuickSuggestInit({
|
||||
remoteSettingsRecords: REMOTE_SETTINGS_DATA,
|
||||
prefs: [
|
||||
["suggest.quicksuggest.nonsponsored", true],
|
||||
["pocket.featureGate", true],
|
||||
],
|
||||
});
|
||||
// Now that the feature is enabled, set the remote settings data to force the
|
||||
// feature to sync so we can be sure syncing is done before starting the test.
|
||||
await QuickSuggestTestUtils.setRemoteSettingsResults(REMOTE_SETTINGS_DATA);
|
||||
});
|
||||
|
||||
add_task(async function basic() {
|
||||
|
@ -95,9 +93,6 @@ add_task(async function basic() {
|
|||
});
|
||||
|
||||
// Tests the "Show less frequently" command.
|
||||
//
|
||||
// TODO (bug 1861220, bug 1861228): The Rust implementation doesn't support
|
||||
// prefix matching or the remote settings config.
|
||||
add_task(async function resultMenu_showLessFrequently() {
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [
|
||||
|
@ -241,7 +236,11 @@ add_task(async function resultMenu_notInterested() {
|
|||
// Re-enable suggestions and wait until PocketSuggestions syncs them from
|
||||
// remote settings again.
|
||||
UrlbarPrefs.set("suggest.pocket", true);
|
||||
await QuickSuggestTestUtils.forceSync();
|
||||
let feature = QuickSuggest.getFeature("PocketSuggestions");
|
||||
await TestUtils.waitForCondition(async () => {
|
||||
let suggestions = await feature.queryRemoteSettings("pocket suggestion");
|
||||
return !!suggestions.length;
|
||||
}, "Waiting for PocketSuggestions to serve remote settings suggestions");
|
||||
});
|
||||
|
||||
// Tests the "Not relevant" result menu dismissal command.
|
||||
|
@ -389,9 +388,6 @@ add_task(async function rowLabel() {
|
|||
});
|
||||
|
||||
// Tests visibility of "Show less frequently" menu.
|
||||
//
|
||||
// TODO (bug 1861220, bug 1861228): The Rust implementation doesn't support
|
||||
// prefix matching or the remote settings config.
|
||||
add_task(async function showLessFrequentlyMenuVisibility() {
|
||||
const testCases = [
|
||||
// high confidence keyword best match
|
||||
|
|
|
@ -57,7 +57,7 @@ add_setup(async function () {
|
|||
await SearchTestUtils.installSearchExtension({}, { setAsDefault: true });
|
||||
|
||||
await QuickSuggestTestUtils.ensureQuickSuggestInit({
|
||||
remoteSettingsRecords: [
|
||||
remoteSettingsResults: [
|
||||
{
|
||||
type: "data",
|
||||
attachment: REMOTE_SETTINGS_RESULTS,
|
||||
|
|
|
@ -21,19 +21,15 @@ const REMOTE_SETTINGS_RESULT = {
|
|||
keywords: ["nonsponsored"],
|
||||
advertiser: "Wikipedia",
|
||||
iab_category: "5 - Education",
|
||||
icon: "1234",
|
||||
};
|
||||
|
||||
const suggestion_type = "nonsponsored";
|
||||
const index = 1;
|
||||
const position = index + 1;
|
||||
|
||||
// Trying to avoid timeouts in TV mode.
|
||||
requestLongerTimeout(3);
|
||||
|
||||
add_setup(async function () {
|
||||
await setUpTelemetryTest({
|
||||
remoteSettingsRecords: [
|
||||
remoteSettingsResults: [
|
||||
{
|
||||
type: "data",
|
||||
attachment: [REMOTE_SETTINGS_RESULT],
|
||||
|
|
|
@ -36,7 +36,7 @@ add_setup(async function () {
|
|||
await SearchTestUtils.installSearchExtension({}, { setAsDefault: true });
|
||||
|
||||
await QuickSuggestTestUtils.ensureQuickSuggestInit({
|
||||
remoteSettingsRecords: [
|
||||
remoteSettingsResults: [
|
||||
{
|
||||
type: "data",
|
||||
attachment: REMOTE_SETTINGS_RESULTS,
|
||||
|
|
|
@ -23,19 +23,15 @@ const REMOTE_SETTINGS_RESULT = {
|
|||
impression_url: "https://example.com/impression",
|
||||
advertiser: "testadvertiser",
|
||||
iab_category: "22 - Shopping",
|
||||
icon: "1234",
|
||||
};
|
||||
|
||||
const suggestion_type = "sponsored";
|
||||
const index = 1;
|
||||
const position = index + 1;
|
||||
|
||||
// Trying to avoid timeouts in TV mode.
|
||||
requestLongerTimeout(3);
|
||||
|
||||
add_setup(async function () {
|
||||
await setUpTelemetryTest({
|
||||
remoteSettingsRecords: [
|
||||
remoteSettingsResults: [
|
||||
{
|
||||
type: "data",
|
||||
attachment: [REMOTE_SETTINGS_RESULT],
|
||||
|
|
|
@ -29,7 +29,8 @@ add_setup(async function () {
|
|||
});
|
||||
|
||||
await setUpTelemetryTest({
|
||||
remoteSettingsRecords: [
|
||||
suggestions: [],
|
||||
remoteSettingsResults: [
|
||||
{
|
||||
type: "weather",
|
||||
weather: WEATHER_RS_DATA,
|
||||
|
|
|
@ -13,7 +13,7 @@ ChromeUtils.defineESModuleGetters(this, {
|
|||
|
||||
add_setup(async function () {
|
||||
await QuickSuggestTestUtils.ensureQuickSuggestInit({
|
||||
remoteSettingsRecords: [
|
||||
remoteSettingsResults: [
|
||||
{
|
||||
type: "weather",
|
||||
weather: MerinoTestUtils.WEATHER_RS_DATA,
|
||||
|
@ -85,7 +85,7 @@ add_task(async function test_weather_result_selection() {
|
|||
// repeats both steps until the min keyword length cap is reached.
|
||||
add_task(async function showLessFrequentlyCapReached_manySearches() {
|
||||
// Set up a min keyword length and cap.
|
||||
await QuickSuggestTestUtils.setRemoteSettingsRecords([
|
||||
await QuickSuggestTestUtils.setRemoteSettingsResults([
|
||||
{
|
||||
type: "weather",
|
||||
weather: {
|
||||
|
@ -172,7 +172,7 @@ add_task(async function showLessFrequentlyCapReached_manySearches() {
|
|||
gURLBar.view.resultMenu.hidePopup(true);
|
||||
|
||||
await UrlbarTestUtils.promisePopupClose(window);
|
||||
await QuickSuggestTestUtils.setRemoteSettingsRecords([
|
||||
await QuickSuggestTestUtils.setRemoteSettingsResults([
|
||||
{
|
||||
type: "weather",
|
||||
weather: MerinoTestUtils.WEATHER_RS_DATA,
|
||||
|
@ -185,7 +185,7 @@ add_task(async function showLessFrequentlyCapReached_manySearches() {
|
|||
// a single search until the min keyword length cap is reached.
|
||||
add_task(async function showLessFrequentlyCapReached_oneSearch() {
|
||||
// Set up a min keyword length and cap.
|
||||
await QuickSuggestTestUtils.setRemoteSettingsRecords([
|
||||
await QuickSuggestTestUtils.setRemoteSettingsResults([
|
||||
{
|
||||
type: "weather",
|
||||
weather: {
|
||||
|
@ -245,7 +245,7 @@ add_task(async function showLessFrequentlyCapReached_oneSearch() {
|
|||
|
||||
gURLBar.view.resultMenu.hidePopup(true);
|
||||
await UrlbarTestUtils.promisePopupClose(window);
|
||||
await QuickSuggestTestUtils.setRemoteSettingsRecords([
|
||||
await QuickSuggestTestUtils.setRemoteSettingsResults([
|
||||
{
|
||||
type: "weather",
|
||||
weather: MerinoTestUtils.WEATHER_RS_DATA,
|
||||
|
|
|
@ -84,15 +84,21 @@ async function updateTopSites(condition, searchShortcuts = false) {
|
|||
*
|
||||
* @param {object} options
|
||||
* Options
|
||||
* @param {Array} options.remoteSettingsRecords
|
||||
* See `QuickSuggestTestUtils.ensureQuickSuggestInit()`.
|
||||
* @param {Array} options.remoteSettingsResults
|
||||
* Array of remote settings result objects. If not given, no suggestions
|
||||
* will be present in remote settings.
|
||||
* @param {Array} options.merinoSuggestions
|
||||
* See `QuickSuggestTestUtils.ensureQuickSuggestInit()`.
|
||||
* Array of Merino suggestion objects. If given, this function will start
|
||||
* the mock Merino server and set `quicksuggest.dataCollection.enabled` to
|
||||
* true so that `UrlbarProviderQuickSuggest` will fetch suggestions from it.
|
||||
* Otherwise Merino will not serve suggestions, but you can still set up
|
||||
* Merino without using this function by using `MerinoTestUtils` directly.
|
||||
* @param {Array} options.config
|
||||
* See `QuickSuggestTestUtils.ensureQuickSuggestInit()`.
|
||||
* Quick suggest will be initialized with this config. Leave undefined to use
|
||||
* the default config. See `QuickSuggestTestUtils` for details.
|
||||
*/
|
||||
async function setUpTelemetryTest({
|
||||
remoteSettingsRecords,
|
||||
remoteSettingsResults,
|
||||
merinoSuggestions = null,
|
||||
config = QuickSuggestTestUtils.DEFAULT_CONFIG,
|
||||
}) {
|
||||
|
@ -115,7 +121,7 @@ async function setUpTelemetryTest({
|
|||
await SearchTestUtils.installSearchExtension({}, { setAsDefault: true });
|
||||
|
||||
await QuickSuggestTestUtils.ensureQuickSuggestInit({
|
||||
remoteSettingsRecords,
|
||||
remoteSettingsResults,
|
||||
merinoSuggestions,
|
||||
config,
|
||||
});
|
||||
|
@ -663,34 +669,19 @@ function add_tasks_with_rust(...args) {
|
|||
|
||||
for (let rustEnabled of [false, true]) {
|
||||
let newTaskFn = async (...taskFnArgs) => {
|
||||
info("add_tasks_with_rust: Setting rustEnabled: " + rustEnabled);
|
||||
info("Setting rustEnabled: " + rustEnabled);
|
||||
UrlbarPrefs.set("quicksuggest.rustEnabled", rustEnabled);
|
||||
info("add_tasks_with_rust: Done setting rustEnabled: " + rustEnabled);
|
||||
|
||||
// The current backend may now start syncing, so wait for it to finish.
|
||||
info("add_tasks_with_rust: Forcing sync");
|
||||
await QuickSuggestTestUtils.forceSync();
|
||||
info("add_tasks_with_rust: Done forcing sync");
|
||||
info("Done setting rustEnabled: " + rustEnabled);
|
||||
|
||||
let rv;
|
||||
try {
|
||||
info(
|
||||
"add_tasks_with_rust: Calling original task function: " + taskFn.name
|
||||
);
|
||||
info("Calling original task function: " + taskFn.name);
|
||||
rv = await taskFn(...taskFnArgs);
|
||||
} finally {
|
||||
info(
|
||||
"add_tasks_with_rust: Done calling original task function: " +
|
||||
taskFn.name
|
||||
);
|
||||
info("add_tasks_with_rust: Clearing rustEnabled");
|
||||
info("Done calling original task function: " + taskFn.name);
|
||||
info("Clearing rustEnabled");
|
||||
UrlbarPrefs.clear("quicksuggest.rustEnabled");
|
||||
info("add_tasks_with_rust: Done clearing rustEnabled");
|
||||
|
||||
// The current backend may now start syncing, so wait for it to finish.
|
||||
info("add_tasks_with_rust: Forcing sync");
|
||||
await QuickSuggestTestUtils.forceSync();
|
||||
info("add_tasks_with_rust: Done forcing sync");
|
||||
info("Done clearing rustEnabled");
|
||||
}
|
||||
return rv;
|
||||
};
|
||||
|
|
|
@ -32,46 +32,19 @@ function add_tasks_with_rust(...args) {
|
|||
|
||||
for (let rustEnabled of [false, true]) {
|
||||
let newTaskFn = async (...taskFnArgs) => {
|
||||
info("add_tasks_with_rust: Setting rustEnabled: " + rustEnabled);
|
||||
info("Setting rustEnabled: " + rustEnabled);
|
||||
UrlbarPrefs.set("quicksuggest.rustEnabled", rustEnabled);
|
||||
info("add_tasks_with_rust: Done setting rustEnabled: " + rustEnabled);
|
||||
|
||||
// The current backend may now start syncing, so wait for it to finish.
|
||||
info("add_tasks_with_rust: Forcing sync");
|
||||
await QuickSuggestTestUtils.forceSync();
|
||||
info("add_tasks_with_rust: Done forcing sync");
|
||||
info("Done setting rustEnabled: " + rustEnabled);
|
||||
|
||||
let rv;
|
||||
try {
|
||||
info(
|
||||
"add_tasks_with_rust: Calling original task function: " + taskFn.name
|
||||
);
|
||||
info("Calling original task function: " + taskFn.name);
|
||||
rv = await taskFn(...taskFnArgs);
|
||||
} catch (e) {
|
||||
// Clearly report any unusual errors to make them easier to spot and to
|
||||
// make the flow of the test clearer. The harness throws NS_ERROR_ABORT
|
||||
// when a normal assertion fails, so don't report that.
|
||||
if (e.result != Cr.NS_ERROR_ABORT) {
|
||||
Assert.ok(
|
||||
false,
|
||||
"add_tasks_with_rust: The original task function threw an error: " +
|
||||
e
|
||||
);
|
||||
}
|
||||
throw e;
|
||||
} finally {
|
||||
info(
|
||||
"add_tasks_with_rust: Done calling original task function: " +
|
||||
taskFn.name
|
||||
);
|
||||
info("add_tasks_with_rust: Clearing rustEnabled");
|
||||
info("Done calling original task function: " + taskFn.name);
|
||||
info("Clearing rustEnabled");
|
||||
UrlbarPrefs.clear("quicksuggest.rustEnabled");
|
||||
info("add_tasks_with_rust: Done clearing rustEnabled");
|
||||
|
||||
// The current backend may now start syncing, so wait for it to finish.
|
||||
info("add_tasks_with_rust: Forcing sync");
|
||||
await QuickSuggestTestUtils.forceSync();
|
||||
info("add_tasks_with_rust: Done forcing sync");
|
||||
info("Done clearing rustEnabled");
|
||||
}
|
||||
return rv;
|
||||
};
|
||||
|
@ -460,7 +433,7 @@ async function doShowLessFrequentlyTests({
|
|||
keyword,
|
||||
}) {
|
||||
// Do some sanity checks on the keyword. Any checks that fail are errors in
|
||||
// the test.
|
||||
// the test. This function assumes
|
||||
let spaceIndex = keyword.indexOf(" ");
|
||||
if (spaceIndex < 0) {
|
||||
throw new Error("keyword must contain a space");
|
||||
|
@ -512,19 +485,16 @@ async function doShowLessFrequentlyTests({
|
|||
},
|
||||
];
|
||||
|
||||
// The Rust implementation doesn't support the remote settings config.
|
||||
if (!UrlbarPrefs.get("quicksuggest.rustEnabled")) {
|
||||
info("Testing 'show less frequently' with cap in remote settings");
|
||||
await doOneShowLessFrequentlyTest({
|
||||
tests,
|
||||
feature,
|
||||
expectedResult,
|
||||
showLessFrequentlyCountPref,
|
||||
rs: {
|
||||
show_less_frequently_cap: 3,
|
||||
},
|
||||
});
|
||||
}
|
||||
info("Testing 'show less frequently' with cap in remote settings");
|
||||
await doOneShowLessFrequentlyTest({
|
||||
tests,
|
||||
feature,
|
||||
expectedResult,
|
||||
showLessFrequentlyCountPref,
|
||||
rs: {
|
||||
show_less_frequently_cap: 3,
|
||||
},
|
||||
});
|
||||
|
||||
// Nimbus should override remote settings.
|
||||
info("Testing 'show less frequently' with cap in Nimbus and remote settings");
|
||||
|
|
|
@ -32,7 +32,6 @@ const REMOTE_SETTINGS_RESULTS = [
|
|||
impression_url: "http://example.com/amp-impression",
|
||||
advertiser: "Amp",
|
||||
iab_category: "22 - Shopping",
|
||||
icon: "1234",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
|
@ -43,7 +42,6 @@ const REMOTE_SETTINGS_RESULTS = [
|
|||
impression_url: "http://example.com/wikipedia-impression",
|
||||
advertiser: "Wikipedia",
|
||||
iab_category: "5 - Education",
|
||||
icon: "1234",
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
|
@ -54,7 +52,6 @@ const REMOTE_SETTINGS_RESULTS = [
|
|||
impression_url: "http://example.com/http-impression",
|
||||
advertiser: "HttpAdvertiser",
|
||||
iab_category: "22 - Shopping",
|
||||
icon: "1234",
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
|
@ -65,7 +62,6 @@ const REMOTE_SETTINGS_RESULTS = [
|
|||
impression_url: "http://impression.reporting.test.com/prefix",
|
||||
advertiser: "TestAdvertiserPrefix",
|
||||
iab_category: "22 - Shopping",
|
||||
icon: "1234",
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
|
@ -76,7 +72,6 @@ const REMOTE_SETTINGS_RESULTS = [
|
|||
impression_url: "http://impression.reporting.test.com/timestamp",
|
||||
advertiser: "TestAdvertiserTimestamp",
|
||||
iab_category: "22 - Shopping",
|
||||
icon: "1234",
|
||||
},
|
||||
];
|
||||
|
||||
|
@ -128,6 +123,9 @@ function expectedHttpsResult() {
|
|||
}
|
||||
|
||||
add_setup(async function init() {
|
||||
UrlbarPrefs.set("quicksuggest.enabled", true);
|
||||
UrlbarPrefs.set("quicksuggest.shouldShowOnboardingDialog", false);
|
||||
|
||||
// Install a default test engine.
|
||||
let engine = await addTestSuggestionsEngine();
|
||||
await Services.search.setDefault(
|
||||
|
@ -140,7 +138,7 @@ add_setup(async function init() {
|
|||
];
|
||||
|
||||
await QuickSuggestTestUtils.ensureQuickSuggestInit({
|
||||
remoteSettingsRecords: [
|
||||
remoteSettingsResults: [
|
||||
{
|
||||
type: "data",
|
||||
attachment: REMOTE_SETTINGS_RESULTS,
|
||||
|
@ -183,7 +181,6 @@ add_task(async function telemetryType_nonsponsored() {
|
|||
add_tasks_with_rust(async function nonsponsoredOnly_match() {
|
||||
UrlbarPrefs.set("suggest.quicksuggest.nonsponsored", true);
|
||||
UrlbarPrefs.set("suggest.quicksuggest.sponsored", false);
|
||||
await QuickSuggestTestUtils.forceSync();
|
||||
|
||||
let context = createContext(NONSPONSORED_SEARCH_STRING, {
|
||||
providers: [UrlbarProviderQuickSuggest.name],
|
||||
|
@ -214,7 +211,6 @@ add_tasks_with_rust(async function nonsponsoredOnly_match() {
|
|||
add_tasks_with_rust(async function nonsponsoredOnly_noMatch() {
|
||||
UrlbarPrefs.set("suggest.quicksuggest.nonsponsored", true);
|
||||
UrlbarPrefs.set("suggest.quicksuggest.sponsored", false);
|
||||
await QuickSuggestTestUtils.forceSync();
|
||||
|
||||
let context = createContext(SPONSORED_SEARCH_STRING, {
|
||||
providers: [UrlbarProviderQuickSuggest.name],
|
||||
|
@ -227,7 +223,6 @@ add_tasks_with_rust(async function nonsponsoredOnly_noMatch() {
|
|||
add_tasks_with_rust(async function sponsoredOnly_sponsored() {
|
||||
UrlbarPrefs.set("suggest.quicksuggest.nonsponsored", false);
|
||||
UrlbarPrefs.set("suggest.quicksuggest.sponsored", true);
|
||||
await QuickSuggestTestUtils.forceSync();
|
||||
|
||||
let context = createContext(SPONSORED_SEARCH_STRING, {
|
||||
providers: [UrlbarProviderQuickSuggest.name],
|
||||
|
@ -258,7 +253,6 @@ add_tasks_with_rust(async function sponsoredOnly_sponsored() {
|
|||
add_tasks_with_rust(async function sponsoredOnly_nonsponsored() {
|
||||
UrlbarPrefs.set("suggest.quicksuggest.nonsponsored", false);
|
||||
UrlbarPrefs.set("suggest.quicksuggest.sponsored", true);
|
||||
await QuickSuggestTestUtils.forceSync();
|
||||
|
||||
let context = createContext(NONSPONSORED_SEARCH_STRING, {
|
||||
providers: [UrlbarProviderQuickSuggest.name],
|
||||
|
@ -272,7 +266,6 @@ add_tasks_with_rust(async function sponsoredOnly_nonsponsored() {
|
|||
add_tasks_with_rust(async function both_sponsored() {
|
||||
UrlbarPrefs.set("suggest.quicksuggest.nonsponsored", true);
|
||||
UrlbarPrefs.set("suggest.quicksuggest.sponsored", true);
|
||||
await QuickSuggestTestUtils.forceSync();
|
||||
|
||||
let context = createContext(SPONSORED_SEARCH_STRING, {
|
||||
providers: [UrlbarProviderQuickSuggest.name],
|
||||
|
@ -289,7 +282,6 @@ add_tasks_with_rust(async function both_sponsored() {
|
|||
add_tasks_with_rust(async function both_nonsponsored() {
|
||||
UrlbarPrefs.set("suggest.quicksuggest.nonsponsored", true);
|
||||
UrlbarPrefs.set("suggest.quicksuggest.sponsored", true);
|
||||
await QuickSuggestTestUtils.forceSync();
|
||||
|
||||
let context = createContext(NONSPONSORED_SEARCH_STRING, {
|
||||
providers: [UrlbarProviderQuickSuggest.name],
|
||||
|
@ -306,7 +298,6 @@ add_tasks_with_rust(async function both_nonsponsored() {
|
|||
add_tasks_with_rust(async function both_noMatch() {
|
||||
UrlbarPrefs.set("suggest.quicksuggest.nonsponsored", true);
|
||||
UrlbarPrefs.set("suggest.quicksuggest.sponsored", true);
|
||||
await QuickSuggestTestUtils.forceSync();
|
||||
|
||||
let context = createContext("this doesn't match anything", {
|
||||
providers: [UrlbarProviderQuickSuggest.name],
|
||||
|
@ -345,7 +336,6 @@ add_tasks_with_rust(async function neither_nonsponsored() {
|
|||
add_tasks_with_rust(async function caseInsensitiveAndLeadingSpaces() {
|
||||
UrlbarPrefs.set("suggest.quicksuggest.nonsponsored", true);
|
||||
UrlbarPrefs.set("suggest.quicksuggest.sponsored", true);
|
||||
await QuickSuggestTestUtils.forceSync();
|
||||
|
||||
let context = createContext(" " + SPONSORED_SEARCH_STRING.toUpperCase(), {
|
||||
providers: [UrlbarProviderQuickSuggest.name],
|
||||
|
@ -362,7 +352,6 @@ add_tasks_with_rust(async function caseInsensitiveAndLeadingSpaces() {
|
|||
add_tasks_with_rust(async function emptySearchStringsAndSpaces() {
|
||||
UrlbarPrefs.set("suggest.quicksuggest.nonsponsored", true);
|
||||
UrlbarPrefs.set("suggest.quicksuggest.sponsored", true);
|
||||
await QuickSuggestTestUtils.forceSync();
|
||||
|
||||
let searchStrings = ["", " ", " ", " "];
|
||||
for (let str of searchStrings) {
|
||||
|
@ -390,7 +379,6 @@ add_tasks_with_rust(async function browser_search_suggest_enabled() {
|
|||
UrlbarPrefs.set("suggest.quicksuggest.nonsponsored", true);
|
||||
UrlbarPrefs.set("suggest.quicksuggest.sponsored", true);
|
||||
UrlbarPrefs.set("browser.search.suggest.enabled", false);
|
||||
await QuickSuggestTestUtils.forceSync();
|
||||
|
||||
let context = createContext(SPONSORED_SEARCH_STRING, {
|
||||
providers: [UrlbarProviderQuickSuggest.name],
|
||||
|
@ -410,7 +398,6 @@ add_tasks_with_rust(async function browser_search_suggest_enabled() {
|
|||
UrlbarPrefs.set("suggest.quicksuggest.nonsponsored", true);
|
||||
UrlbarPrefs.set("suggest.quicksuggest.sponsored", true);
|
||||
UrlbarPrefs.set("suggest.searches", false);
|
||||
await QuickSuggestTestUtils.forceSync();
|
||||
|
||||
let context = createContext(SPONSORED_SEARCH_STRING, {
|
||||
providers: [UrlbarProviderQuickSuggest.name],
|
||||
|
@ -429,7 +416,6 @@ add_tasks_with_rust(async function browser_search_suggest_enabled() {
|
|||
add_tasks_with_rust(async function privateContext() {
|
||||
UrlbarPrefs.set("suggest.quicksuggest.nonsponsored", true);
|
||||
UrlbarPrefs.set("suggest.quicksuggest.sponsored", true);
|
||||
await QuickSuggestTestUtils.forceSync();
|
||||
|
||||
for (let privateSuggestionsEnabled of [true, false]) {
|
||||
UrlbarPrefs.set(
|
||||
|
@ -457,7 +443,6 @@ add_tasks_with_rust(async function suggestionsBeforeGeneral_only() {
|
|||
UrlbarPrefs.set("browser.search.suggest.enabled", true);
|
||||
UrlbarPrefs.set("suggest.searches", true);
|
||||
UrlbarPrefs.set("showSearchSuggestionsFirst", true);
|
||||
await QuickSuggestTestUtils.forceSync();
|
||||
|
||||
let context = createContext(SPONSORED_SEARCH_STRING, { isPrivate: false });
|
||||
await check_results({
|
||||
|
@ -496,7 +481,6 @@ add_tasks_with_rust(async function suggestionsBeforeGeneral_others() {
|
|||
UrlbarPrefs.set("browser.search.suggest.enabled", true);
|
||||
UrlbarPrefs.set("suggest.searches", true);
|
||||
UrlbarPrefs.set("showSearchSuggestionsFirst", true);
|
||||
await QuickSuggestTestUtils.forceSync();
|
||||
|
||||
let context = createContext(SPONSORED_SEARCH_STRING, { isPrivate: false });
|
||||
|
||||
|
@ -552,7 +536,6 @@ add_tasks_with_rust(async function generalBeforeSuggestions_only() {
|
|||
UrlbarPrefs.set("browser.search.suggest.enabled", true);
|
||||
UrlbarPrefs.set("suggest.searches", true);
|
||||
UrlbarPrefs.set("showSearchSuggestionsFirst", false);
|
||||
await QuickSuggestTestUtils.forceSync();
|
||||
|
||||
let context = createContext(SPONSORED_SEARCH_STRING, { isPrivate: false });
|
||||
await check_results({
|
||||
|
@ -591,7 +574,6 @@ add_tasks_with_rust(async function generalBeforeSuggestions_others() {
|
|||
UrlbarPrefs.set("browser.search.suggest.enabled", true);
|
||||
UrlbarPrefs.set("suggest.searches", true);
|
||||
UrlbarPrefs.set("showSearchSuggestionsFirst", false);
|
||||
await QuickSuggestTestUtils.forceSync();
|
||||
|
||||
let context = createContext(SPONSORED_SEARCH_STRING, { isPrivate: false });
|
||||
|
||||
|
@ -727,7 +709,6 @@ async function doDedupeAgainstURLTest({
|
|||
// Now do another search with quick suggest enabled.
|
||||
UrlbarPrefs.set("suggest.quicksuggest.nonsponsored", true);
|
||||
UrlbarPrefs.set("suggest.quicksuggest.sponsored", true);
|
||||
await QuickSuggestTestUtils.forceSync();
|
||||
|
||||
context = createContext(searchString, { isPrivate: false });
|
||||
|
||||
|
@ -753,8 +734,6 @@ async function doDedupeAgainstURLTest({
|
|||
|
||||
UrlbarPrefs.clear("suggest.quicksuggest.nonsponsored");
|
||||
UrlbarPrefs.clear("suggest.quicksuggest.sponsored");
|
||||
await QuickSuggestTestUtils.forceSync();
|
||||
|
||||
UrlbarPrefs.clear("suggest.searches");
|
||||
await PlacesUtils.history.clear();
|
||||
}
|
||||
|
@ -763,7 +742,6 @@ async function doDedupeAgainstURLTest({
|
|||
add_task(async function latencyTelemetry() {
|
||||
UrlbarPrefs.set("suggest.quicksuggest.nonsponsored", true);
|
||||
UrlbarPrefs.set("suggest.quicksuggest.sponsored", true);
|
||||
await QuickSuggestTestUtils.forceSync();
|
||||
|
||||
let histogram = Services.telemetry.getHistogramById(
|
||||
TELEMETRY_REMOTE_SETTINGS_LATENCY
|
||||
|
@ -904,13 +882,9 @@ add_task(async function setupAndTeardown() {
|
|||
});
|
||||
|
||||
// Timestamp templates in URLs should be replaced with real timestamps.
|
||||
//
|
||||
// TODO (bug 1859389): Make this work with Rust enabled once timestamp templates
|
||||
// are handled.
|
||||
add_task(async function timestamps() {
|
||||
add_tasks_with_rust(async function timestamps() {
|
||||
UrlbarPrefs.set("suggest.quicksuggest.nonsponsored", true);
|
||||
UrlbarPrefs.set("suggest.quicksuggest.sponsored", true);
|
||||
await QuickSuggestTestUtils.forceSync();
|
||||
|
||||
// Do a search.
|
||||
let context = createContext(TIMESTAMP_SEARCH_STRING, {
|
||||
|
@ -952,10 +926,7 @@ add_task(async function timestamps() {
|
|||
// timestamp may be different from the one in the user's history. In that case,
|
||||
// the two URLs should be treated as dupes and only the quick suggest should be
|
||||
// shown, not the URL from history.
|
||||
//
|
||||
// TODO (bug 1859389): Make this work with Rust enabled once timestamp templates
|
||||
// are handled.
|
||||
add_task(async function dedupeAgainstURL_timestamps() {
|
||||
add_tasks_with_rust(async function dedupeAgainstURL_timestamps() {
|
||||
// Disable search suggestions.
|
||||
UrlbarPrefs.set("suggest.searches", false);
|
||||
|
||||
|
@ -1022,7 +993,6 @@ add_task(async function dedupeAgainstURL_timestamps() {
|
|||
info("Doing second query");
|
||||
UrlbarPrefs.set("suggest.quicksuggest.nonsponsored", true);
|
||||
UrlbarPrefs.set("suggest.quicksuggest.sponsored", true);
|
||||
await QuickSuggestTestUtils.forceSync();
|
||||
context = createContext(TIMESTAMP_SEARCH_STRING, { isPrivate: false });
|
||||
|
||||
let expectedQuickSuggest = makeAmpResult({
|
||||
|
@ -1138,8 +1108,6 @@ add_task(async function dedupeAgainstURL_timestamps() {
|
|||
// Clean up.
|
||||
UrlbarPrefs.clear("suggest.quicksuggest.nonsponsored");
|
||||
UrlbarPrefs.clear("suggest.quicksuggest.sponsored");
|
||||
await QuickSuggestTestUtils.forceSync();
|
||||
|
||||
UrlbarPrefs.clear("suggest.searches");
|
||||
await PlacesUtils.history.clear();
|
||||
});
|
||||
|
@ -1317,7 +1285,6 @@ add_tasks_with_rust(async function block() {
|
|||
// the value of the `quickSuggestRemoteSettingsDataType` Nimbus variable.
|
||||
add_task(async function remoteSettingsDataType() {
|
||||
UrlbarPrefs.set("suggest.quicksuggest.sponsored", true);
|
||||
await QuickSuggestTestUtils.forceSync();
|
||||
|
||||
for (let dataType of [undefined, "test-data-type"]) {
|
||||
// Set up a mock Nimbus rollout with the data type.
|
||||
|
@ -1334,8 +1301,9 @@ add_task(async function remoteSettingsDataType() {
|
|||
expected.payload.title = dataType;
|
||||
}
|
||||
|
||||
// Re-sync.
|
||||
await QuickSuggestTestUtils.forceSync();
|
||||
// Re-enable to trigger sync from remote settings.
|
||||
UrlbarPrefs.set("suggest.quicksuggest.sponsored", false);
|
||||
UrlbarPrefs.set("suggest.quicksuggest.sponsored", true);
|
||||
|
||||
let context = createContext(SPONSORED_SEARCH_STRING, {
|
||||
providers: [UrlbarProviderQuickSuggest.name],
|
||||
|
@ -1396,14 +1364,13 @@ async function doSponsoredPriorityTest({
|
|||
}) {
|
||||
UrlbarPrefs.set("suggest.quicksuggest.nonsponsored", true);
|
||||
UrlbarPrefs.set("suggest.quicksuggest.sponsored", true);
|
||||
await QuickSuggestTestUtils.forceSync();
|
||||
|
||||
const cleanUpNimbusEnable = await UrlbarTestUtils.initNimbusFeature({
|
||||
...nimbusSettings,
|
||||
quickSuggestSponsoredPriority: true,
|
||||
});
|
||||
|
||||
await QuickSuggestTestUtils.setRemoteSettingsRecords([
|
||||
await QuickSuggestTestUtils.setRemoteSettingsResults([
|
||||
{
|
||||
type: "data",
|
||||
attachment: remoteSettingsData,
|
||||
|
@ -1430,8 +1397,6 @@ add_tasks_with_rust(async function tabToSearch() {
|
|||
// types of Suggest results can appear as best matches, and they all should
|
||||
// have the same behavior.
|
||||
UrlbarPrefs.set("suggest.quicksuggest.sponsored", true);
|
||||
await QuickSuggestTestUtils.forceSync();
|
||||
|
||||
Services.prefs.setBoolPref(
|
||||
"browser.urlbar.quicksuggest.sponsoredPriority",
|
||||
true
|
||||
|
@ -1505,8 +1470,6 @@ add_tasks_with_rust(async function position() {
|
|||
// types of Suggest results can appear as best matches, and they all should
|
||||
// have the same behavior.
|
||||
UrlbarPrefs.set("suggest.quicksuggest.sponsored", true);
|
||||
await QuickSuggestTestUtils.forceSync();
|
||||
|
||||
Services.prefs.setBoolPref(
|
||||
"browser.urlbar.quicksuggest.sponsoredPriority",
|
||||
true
|
||||
|
@ -1517,7 +1480,7 @@ add_tasks_with_rust(async function position() {
|
|||
|
||||
// Set the remote settings data with a suggestion containing a position.
|
||||
UrlbarPrefs.set("quicksuggest.allowPositionInSuggestions", true);
|
||||
await QuickSuggestTestUtils.setRemoteSettingsRecords([
|
||||
await QuickSuggestTestUtils.setRemoteSettingsResults([
|
||||
{
|
||||
type: "data",
|
||||
attachment: [
|
||||
|
@ -1565,13 +1528,7 @@ add_tasks_with_rust(async function position() {
|
|||
});
|
||||
|
||||
await cleanupPlaces();
|
||||
await QuickSuggestTestUtils.setRemoteSettingsRecords([
|
||||
{
|
||||
type: "data",
|
||||
attachment: REMOTE_SETTINGS_RESULTS,
|
||||
},
|
||||
]);
|
||||
|
||||
await QuickSuggestTestUtils.setRemoteSettingsResults(REMOTE_SETTINGS_RESULTS);
|
||||
UrlbarPrefs.clear("quicksuggest.allowPositionInSuggestions");
|
||||
Services.prefs.clearUserPref("browser.search.suggest.enabled");
|
||||
Services.prefs.clearUserPref("browser.urlbar.quicksuggest.sponsoredPriority");
|
||||
|
|
|
@ -45,7 +45,7 @@ const REMOTE_SETTINGS_RESULTS = [
|
|||
keywords: ["first", "1st", "two words", "a b c"],
|
||||
description: "Description for the First Addon",
|
||||
number_of_ratings: 1256,
|
||||
score: 0.25,
|
||||
is_top_pick: true,
|
||||
},
|
||||
{
|
||||
url: "https://example.com/second-addon",
|
||||
|
@ -56,7 +56,7 @@ const REMOTE_SETTINGS_RESULTS = [
|
|||
keywords: ["second", "2nd"],
|
||||
description: "Description for the Second Addon",
|
||||
number_of_ratings: 256,
|
||||
score: 0.25,
|
||||
is_top_pick: false,
|
||||
},
|
||||
{
|
||||
url: "https://example.com/third-addon",
|
||||
|
@ -67,7 +67,6 @@ const REMOTE_SETTINGS_RESULTS = [
|
|||
keywords: ["third", "3rd"],
|
||||
description: "Description for the Third Addon",
|
||||
number_of_ratings: 3,
|
||||
score: 0.25,
|
||||
},
|
||||
{
|
||||
url: "https://example.com/fourth-addon?utm_medium=aaa&utm_source=bbb",
|
||||
|
@ -78,21 +77,23 @@ const REMOTE_SETTINGS_RESULTS = [
|
|||
keywords: ["fourth", "4th"],
|
||||
description: "Description for the Fourth Addon",
|
||||
number_of_ratings: 4,
|
||||
score: 0.25,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
add_setup(async function init() {
|
||||
UrlbarPrefs.set("quicksuggest.enabled", true);
|
||||
UrlbarPrefs.set("suggest.quicksuggest.nonsponsored", true);
|
||||
|
||||
// Disable search suggestions so we don't hit the network.
|
||||
Services.prefs.setBoolPref("browser.search.suggest.enabled", false);
|
||||
|
||||
await QuickSuggestTestUtils.ensureQuickSuggestInit({
|
||||
remoteSettingsRecords: REMOTE_SETTINGS_RESULTS,
|
||||
remoteSettingsResults: REMOTE_SETTINGS_RESULTS,
|
||||
merinoSuggestions: MERINO_SUGGESTIONS,
|
||||
prefs: [["suggest.quicksuggest.nonsponsored", true]],
|
||||
});
|
||||
await waitForRemoteSettingsSuggestions();
|
||||
});
|
||||
|
||||
add_task(async function telemetryType() {
|
||||
|
@ -132,7 +133,7 @@ add_tasks_with_rust(async function quickSuggestPrefsDisabled() {
|
|||
});
|
||||
|
||||
UrlbarPrefs.set(pref, true);
|
||||
await QuickSuggestTestUtils.forceSync();
|
||||
await waitForRemoteSettingsSuggestions();
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -167,7 +168,7 @@ add_tasks_with_rust(async function addonSuggestionsSpecificPrefDisabled() {
|
|||
|
||||
// Revert.
|
||||
UrlbarPrefs.clear(pref);
|
||||
await QuickSuggestTestUtils.forceSync();
|
||||
await waitForRemoteSettingsSuggestions();
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -188,7 +189,7 @@ add_tasks_with_rust(async function nimbus() {
|
|||
const cleanUpNimbusEnable = await UrlbarTestUtils.initNimbusFeature({
|
||||
addonsFeatureGate: true,
|
||||
});
|
||||
await QuickSuggestTestUtils.forceSync();
|
||||
await waitForRemoteSettingsSuggestions();
|
||||
await check_results({
|
||||
context: createContext("test", {
|
||||
providers: [UrlbarProviderQuickSuggest.name],
|
||||
|
@ -205,7 +206,7 @@ add_tasks_with_rust(async function nimbus() {
|
|||
|
||||
// Enable locally.
|
||||
UrlbarPrefs.set("addons.featureGate", true);
|
||||
await QuickSuggestTestUtils.forceSync();
|
||||
await waitForRemoteSettingsSuggestions();
|
||||
|
||||
// Disable by Nimbus.
|
||||
const cleanUpNimbusDisable = await UrlbarTestUtils.initNimbusFeature({
|
||||
|
@ -222,7 +223,7 @@ add_tasks_with_rust(async function nimbus() {
|
|||
|
||||
// Revert.
|
||||
UrlbarPrefs.clear("addons.featureGate");
|
||||
await QuickSuggestTestUtils.forceSync();
|
||||
await waitForRemoteSettingsSuggestions();
|
||||
});
|
||||
|
||||
add_tasks_with_rust(async function hideIfAlreadyInstalled() {
|
||||
|
@ -263,7 +264,6 @@ add_tasks_with_rust(async function hideIfAlreadyInstalled() {
|
|||
xpi.remove(false);
|
||||
});
|
||||
|
||||
// TODO (bug 1861220): The Rust implementation doesn't support prefix matching.
|
||||
add_tasks_with_rust(async function remoteSettings() {
|
||||
const testCases = [
|
||||
{
|
||||
|
@ -495,10 +495,6 @@ add_task(async function merinoIsTopPick() {
|
|||
});
|
||||
|
||||
// Tests the "show less frequently" behavior.
|
||||
//
|
||||
// TODO (bug 1861220, bug 1861228): The Rust implementation doesn't support
|
||||
// prefix matching or the remote settings config, so this task is not run with
|
||||
// Rust enabled.
|
||||
add_task(async function showLessFrequently() {
|
||||
await doShowLessFrequentlyTests({
|
||||
feature: QuickSuggest.getFeature("AddonSuggestions"),
|
||||
|
@ -565,3 +561,11 @@ function makeExpectedResult({ suggestion, source, setUtmParams = true }) {
|
|||
},
|
||||
};
|
||||
}
|
||||
|
||||
async function waitForRemoteSettingsSuggestions(keyword = "first") {
|
||||
let feature = QuickSuggest.getFeature("AddonSuggestions");
|
||||
await TestUtils.waitForCondition(async () => {
|
||||
let suggestions = await feature.queryRemoteSettings(keyword);
|
||||
return !!suggestions.length;
|
||||
}, "Waiting for AddonSuggestions to serve remote settings suggestions");
|
||||
}
|
||||
|
|
|
@ -24,12 +24,14 @@ const MERINO_SUGGESTIONS = [
|
|||
];
|
||||
|
||||
add_setup(async function init() {
|
||||
UrlbarPrefs.set("quicksuggest.enabled", true);
|
||||
UrlbarPrefs.set("suggest.quicksuggest.nonsponsored", true);
|
||||
|
||||
// Disable search suggestions so we don't hit the network.
|
||||
Services.prefs.setBoolPref("browser.search.suggest.enabled", false);
|
||||
|
||||
await QuickSuggestTestUtils.ensureQuickSuggestInit({
|
||||
merinoSuggestions: MERINO_SUGGESTIONS,
|
||||
prefs: [["suggest.quicksuggest.nonsponsored", true]],
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -102,22 +102,22 @@ let gDateNowStub;
|
|||
let gStartupDateMsStub;
|
||||
|
||||
add_task(async function init() {
|
||||
UrlbarPrefs.set("quicksuggest.enabled", true);
|
||||
UrlbarPrefs.set("quicksuggest.impressionCaps.sponsoredEnabled", true);
|
||||
UrlbarPrefs.set("quicksuggest.impressionCaps.nonSponsoredEnabled", true);
|
||||
UrlbarPrefs.set("suggest.quicksuggest.nonsponsored", true);
|
||||
UrlbarPrefs.set("suggest.quicksuggest.sponsored", true);
|
||||
|
||||
// Disable search suggestions so we don't hit the network.
|
||||
Services.prefs.setBoolPref("browser.search.suggest.enabled", false);
|
||||
|
||||
await QuickSuggestTestUtils.ensureQuickSuggestInit({
|
||||
remoteSettingsRecords: [
|
||||
remoteSettingsResults: [
|
||||
{
|
||||
type: "data",
|
||||
attachment: REMOTE_SETTINGS_RESULTS,
|
||||
},
|
||||
],
|
||||
prefs: [
|
||||
["quicksuggest.impressionCaps.sponsoredEnabled", true],
|
||||
["quicksuggest.impressionCaps.nonSponsoredEnabled", true],
|
||||
["suggest.quicksuggest.nonsponsored", true],
|
||||
["suggest.quicksuggest.sponsored", true],
|
||||
],
|
||||
});
|
||||
|
||||
// Set up a sinon stub for the `Date.now()` implementation inside of
|
||||
|
|
|
@ -36,18 +36,19 @@ const REMOTE_SETTINGS_DATA = [
|
|||
];
|
||||
|
||||
add_setup(async function init() {
|
||||
UrlbarPrefs.set("quicksuggest.enabled", true);
|
||||
UrlbarPrefs.set("suggest.quicksuggest.nonsponsored", true);
|
||||
UrlbarPrefs.set("suggest.quicksuggest.sponsored", false);
|
||||
UrlbarPrefs.set("suggest.mdn", true);
|
||||
UrlbarPrefs.set("mdn.featureGate", true);
|
||||
|
||||
// Disable search suggestions so we don't hit the network.
|
||||
Services.prefs.setBoolPref("browser.search.suggest.enabled", false);
|
||||
|
||||
await QuickSuggestTestUtils.ensureQuickSuggestInit({
|
||||
remoteSettingsRecords: REMOTE_SETTINGS_DATA,
|
||||
prefs: [
|
||||
["suggest.quicksuggest.nonsponsored", true],
|
||||
["suggest.quicksuggest.sponsored", false],
|
||||
["suggest.mdn", true],
|
||||
["mdn.featureGate", true],
|
||||
],
|
||||
remoteSettingsResults: REMOTE_SETTINGS_DATA,
|
||||
});
|
||||
await waitForSuggestions();
|
||||
});
|
||||
|
||||
add_task(async function basic() {
|
||||
|
@ -117,7 +118,7 @@ add_task(async function disableByLocalPref() {
|
|||
|
||||
// Revert.
|
||||
UrlbarPrefs.set(pref, true);
|
||||
await QuickSuggestTestUtils.forceSync();
|
||||
await waitForSuggestions();
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -150,7 +151,7 @@ add_task(async function nimbus() {
|
|||
"urlbar",
|
||||
"config"
|
||||
);
|
||||
await QuickSuggestTestUtils.forceSync();
|
||||
await waitForSuggestions();
|
||||
await check_results({
|
||||
context: createContext(keyword, {
|
||||
providers: [UrlbarProviderQuickSuggest.name],
|
||||
|
@ -162,7 +163,7 @@ add_task(async function nimbus() {
|
|||
|
||||
// Enable locally.
|
||||
defaultPrefs.setBoolPref("mdn.featureGate", true);
|
||||
await QuickSuggestTestUtils.forceSync();
|
||||
await waitForSuggestions();
|
||||
|
||||
// Disable by Nimbus.
|
||||
const cleanUpNimbusDisable = await UrlbarTestUtils.initNimbusFeature(
|
||||
|
@ -181,7 +182,7 @@ add_task(async function nimbus() {
|
|||
|
||||
// Revert.
|
||||
defaultPrefs.setBoolPref("mdn.featureGate", true);
|
||||
await QuickSuggestTestUtils.forceSync();
|
||||
await waitForSuggestions();
|
||||
});
|
||||
|
||||
function makeExpectedResult({
|
||||
|
@ -219,3 +220,12 @@ function makeExpectedResult({
|
|||
},
|
||||
};
|
||||
}
|
||||
|
||||
async function waitForSuggestions() {
|
||||
let keyword = REMOTE_SETTINGS_DATA[0].attachment[0].keywords[0];
|
||||
let feature = QuickSuggest.getFeature("MDNSuggestions");
|
||||
await TestUtils.waitForCondition(async () => {
|
||||
let suggestions = await feature.queryRemoteSettings(keyword);
|
||||
return !!suggestions.length;
|
||||
}, "Waiting for MDNSuggestions to serve remote settings suggestions");
|
||||
}
|
||||
|
|
|
@ -97,20 +97,21 @@ ChromeUtils.defineLazyGetter(
|
|||
);
|
||||
|
||||
add_task(async function init() {
|
||||
UrlbarPrefs.set("quicksuggest.enabled", true);
|
||||
UrlbarPrefs.set("suggest.quicksuggest.nonsponsored", true);
|
||||
UrlbarPrefs.set("suggest.quicksuggest.sponsored", true);
|
||||
UrlbarPrefs.set("quicksuggest.shouldShowOnboardingDialog", false);
|
||||
|
||||
await MerinoTestUtils.server.start();
|
||||
|
||||
// Set up the remote settings client with the test data.
|
||||
await QuickSuggestTestUtils.ensureQuickSuggestInit({
|
||||
remoteSettingsRecords: [
|
||||
remoteSettingsResults: [
|
||||
{
|
||||
type: "data",
|
||||
attachment: REMOTE_SETTINGS_RESULTS,
|
||||
},
|
||||
],
|
||||
prefs: [
|
||||
["suggest.quicksuggest.nonsponsored", true],
|
||||
["suggest.quicksuggest.sponsored", true],
|
||||
],
|
||||
});
|
||||
|
||||
Assert.equal(
|
||||
|
@ -130,7 +131,7 @@ add_task(async function merinoDisabled() {
|
|||
// Clear the remote settings suggestions so that if Merino is actually queried
|
||||
// -- which would be a bug -- we don't accidentally mask the Merino suggestion
|
||||
// by also matching an RS suggestion with the same or higher score.
|
||||
await QuickSuggestTestUtils.setRemoteSettingsRecords([]);
|
||||
await QuickSuggestTestUtils.setRemoteSettingsResults([]);
|
||||
|
||||
let histograms = MerinoTestUtils.getAndClearHistograms();
|
||||
|
||||
|
@ -152,7 +153,7 @@ add_task(async function merinoDisabled() {
|
|||
|
||||
UrlbarPrefs.set("merino.endpointURL", mockEndpointUrl);
|
||||
|
||||
await QuickSuggestTestUtils.setRemoteSettingsRecords([
|
||||
await QuickSuggestTestUtils.setRemoteSettingsResults([
|
||||
{
|
||||
type: "data",
|
||||
attachment: REMOTE_SETTINGS_RESULTS,
|
||||
|
@ -168,7 +169,7 @@ add_task(async function dataCollectionDisabled() {
|
|||
// Clear the remote settings suggestions so that if Merino is actually queried
|
||||
// -- which would be a bug -- we don't accidentally mask the Merino suggestion
|
||||
// by also matching an RS suggestion with the same or higher score.
|
||||
await QuickSuggestTestUtils.setRemoteSettingsRecords([]);
|
||||
await QuickSuggestTestUtils.setRemoteSettingsResults([]);
|
||||
|
||||
let context = createContext(SEARCH_STRING, {
|
||||
providers: [UrlbarProviderQuickSuggest.name],
|
||||
|
@ -179,7 +180,7 @@ add_task(async function dataCollectionDisabled() {
|
|||
matches: [],
|
||||
});
|
||||
|
||||
await QuickSuggestTestUtils.setRemoteSettingsRecords([
|
||||
await QuickSuggestTestUtils.setRemoteSettingsResults([
|
||||
{
|
||||
type: "data",
|
||||
attachment: REMOTE_SETTINGS_RESULTS,
|
||||
|
|
|
@ -15,13 +15,12 @@ ChromeUtils.defineLazyGetter(
|
|||
);
|
||||
|
||||
add_task(async function init() {
|
||||
UrlbarPrefs.set("quicksuggest.enabled", true);
|
||||
UrlbarPrefs.set("suggest.quicksuggest.sponsored", true);
|
||||
UrlbarPrefs.set("quicksuggest.dataCollection.enabled", true);
|
||||
|
||||
await MerinoTestUtils.server.start();
|
||||
await QuickSuggestTestUtils.ensureQuickSuggestInit({
|
||||
prefs: [
|
||||
["suggest.quicksuggest.sponsored", true],
|
||||
["quicksuggest.dataCollection.enabled", true],
|
||||
],
|
||||
});
|
||||
await QuickSuggestTestUtils.ensureQuickSuggestInit();
|
||||
});
|
||||
|
||||
// In a single engagement, all requests should use the same session ID and the
|
||||
|
|
|
@ -134,6 +134,10 @@ let TESTS = {
|
|||
};
|
||||
|
||||
add_task(async function () {
|
||||
UrlbarPrefs.set("quicksuggest.enabled", true);
|
||||
UrlbarPrefs.set("suggest.quicksuggest.sponsored", true);
|
||||
UrlbarPrefs.set("suggest.quicksuggest.nonsponsored", true);
|
||||
|
||||
// Create results and suggestions based on `SUGGESTIONS_DATA`.
|
||||
let qsResults = [];
|
||||
let qsSuggestions = [];
|
||||
|
@ -206,16 +210,12 @@ add_task(async function () {
|
|||
}
|
||||
|
||||
await QuickSuggestTestUtils.ensureQuickSuggestInit({
|
||||
remoteSettingsRecords: [
|
||||
remoteSettingsResults: [
|
||||
{
|
||||
type: "data",
|
||||
attachment: qsResults,
|
||||
},
|
||||
],
|
||||
prefs: [
|
||||
["suggest.quicksuggest.sponsored", true],
|
||||
["suggest.quicksuggest.nonsponsored", true],
|
||||
],
|
||||
});
|
||||
|
||||
// Run a test for each keyword.
|
||||
|
|
|
@ -18,32 +18,31 @@ const REMOTE_SETTINGS_DATA = [
|
|||
title: "Pocket Suggestion 0",
|
||||
description: "Pocket description 0",
|
||||
lowConfidenceKeywords: [LOW_KEYWORD, "how to low"],
|
||||
highConfidenceKeywords: [HIGH_KEYWORD],
|
||||
score: 0.25,
|
||||
highConfidenceKeywords: [HIGH_KEYWORD, "how to high"],
|
||||
},
|
||||
{
|
||||
url: "https://example.com/pocket-1",
|
||||
title: "Pocket Suggestion 1",
|
||||
description: "Pocket description 1",
|
||||
lowConfidenceKeywords: ["other low"],
|
||||
highConfidenceKeywords: ["another high"],
|
||||
score: 0.25,
|
||||
lowConfidenceKeywords: ["other low", "both low and high"],
|
||||
highConfidenceKeywords: ["both low and high"],
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
add_setup(async function init() {
|
||||
UrlbarPrefs.set("quicksuggest.enabled", true);
|
||||
UrlbarPrefs.set("suggest.quicksuggest.nonsponsored", true);
|
||||
UrlbarPrefs.set("pocket.featureGate", true);
|
||||
|
||||
// Disable search suggestions so we don't hit the network.
|
||||
Services.prefs.setBoolPref("browser.search.suggest.enabled", false);
|
||||
|
||||
await QuickSuggestTestUtils.ensureQuickSuggestInit({
|
||||
remoteSettingsRecords: REMOTE_SETTINGS_DATA,
|
||||
prefs: [
|
||||
["suggest.quicksuggest.nonsponsored", true],
|
||||
["pocket.featureGate", true],
|
||||
],
|
||||
remoteSettingsResults: REMOTE_SETTINGS_DATA,
|
||||
});
|
||||
await waitForSuggestions();
|
||||
});
|
||||
|
||||
add_task(async function telemetryType() {
|
||||
|
@ -83,7 +82,7 @@ add_tasks_with_rust(async function nonsponsoredDisabled() {
|
|||
|
||||
UrlbarPrefs.set("suggest.quicksuggest.nonsponsored", true);
|
||||
UrlbarPrefs.clear("suggest.quicksuggest.sponsored");
|
||||
await QuickSuggestTestUtils.forceSync();
|
||||
await waitForSuggestions();
|
||||
});
|
||||
|
||||
// When Pocket-specific preferences are disabled, suggestions should not be
|
||||
|
@ -112,7 +111,7 @@ add_tasks_with_rust(async function pocketSpecificPrefsDisabled() {
|
|||
|
||||
// Revert.
|
||||
UrlbarPrefs.set(pref, true);
|
||||
await QuickSuggestTestUtils.forceSync();
|
||||
await waitForSuggestions();
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -133,7 +132,7 @@ add_tasks_with_rust(async function nimbus() {
|
|||
const cleanUpNimbusEnable = await UrlbarTestUtils.initNimbusFeature({
|
||||
pocketFeatureGate: true,
|
||||
});
|
||||
await QuickSuggestTestUtils.forceSync();
|
||||
await waitForSuggestions();
|
||||
await check_results({
|
||||
context: createContext(LOW_KEYWORD, {
|
||||
providers: [UrlbarProviderQuickSuggest.name],
|
||||
|
@ -145,7 +144,7 @@ add_tasks_with_rust(async function nimbus() {
|
|||
|
||||
// Enable locally.
|
||||
UrlbarPrefs.set("pocket.featureGate", true);
|
||||
await QuickSuggestTestUtils.forceSync();
|
||||
await waitForSuggestions();
|
||||
|
||||
// Disable by Nimbus.
|
||||
const cleanUpNimbusDisable = await UrlbarTestUtils.initNimbusFeature({
|
||||
|
@ -162,7 +161,7 @@ add_tasks_with_rust(async function nimbus() {
|
|||
|
||||
// Revert.
|
||||
UrlbarPrefs.set("pocket.featureGate", true);
|
||||
await QuickSuggestTestUtils.forceSync();
|
||||
await waitForSuggestions();
|
||||
});
|
||||
|
||||
// The suggestion should be shown as a top pick when a high-confidence keyword
|
||||
|
@ -180,7 +179,7 @@ add_tasks_with_rust(async function topPick() {
|
|||
});
|
||||
|
||||
// Low-confidence keywords should do prefix matching starting at the first word.
|
||||
add_tasks_with_rust(async function lowPrefixes() {
|
||||
add_task(async function lowPrefixes() {
|
||||
// search string -> should match
|
||||
let tests = {
|
||||
l: false,
|
||||
|
@ -212,14 +211,7 @@ add_tasks_with_rust(async function lowPrefixes() {
|
|||
|
||||
// Low-confidence keywords that start with "how to" should do prefix matching
|
||||
// starting at "how to" instead of the first word.
|
||||
//
|
||||
// Note: The Rust implementation doesn't support this.
|
||||
add_task(async function lowPrefixes_howTo() {
|
||||
Assert.ok(
|
||||
!UrlbarPrefs.get("quicksuggest.rustEnabled"),
|
||||
"The Rust implementation doesn't support the 'how to' special case"
|
||||
);
|
||||
|
||||
// search string -> should match
|
||||
let tests = {
|
||||
h: false,
|
||||
|
@ -248,7 +240,7 @@ add_task(async function lowPrefixes_howTo() {
|
|||
});
|
||||
|
||||
// High-confidence keywords should not do prefix matching at all.
|
||||
add_tasks_with_rust(async function highPrefixes() {
|
||||
add_task(async function highPrefixes() {
|
||||
// search string -> should match
|
||||
let tests = {
|
||||
h: false,
|
||||
|
@ -283,8 +275,108 @@ add_tasks_with_rust(async function highPrefixes() {
|
|||
}
|
||||
});
|
||||
|
||||
// High-confidence keywords starting with "how to" should also not do prefix
|
||||
// matching at all.
|
||||
add_task(async function highPrefixes_howTo() {
|
||||
// search string -> [should match low, should match high]
|
||||
let tests = {
|
||||
h: [false, false],
|
||||
ho: [false, false],
|
||||
how: [false, false],
|
||||
"how ": [false, false],
|
||||
"how t": [false, false],
|
||||
"how to": [true, false],
|
||||
"how to ": [true, false],
|
||||
"how to h": [false, false],
|
||||
"how to hi": [false, false],
|
||||
"how to hig": [false, false],
|
||||
"how to high": [false, true],
|
||||
};
|
||||
for (let [searchString, [shouldMatchLow, shouldMatchHigh]] of Object.entries(
|
||||
tests
|
||||
)) {
|
||||
info(
|
||||
"Doing search: " +
|
||||
JSON.stringify({ searchString, shouldMatchLow, shouldMatchHigh })
|
||||
);
|
||||
let matches = [];
|
||||
if (shouldMatchLow) {
|
||||
matches.push(
|
||||
makeExpectedResult({
|
||||
searchString,
|
||||
fullKeyword: "how to low",
|
||||
isTopPick: false,
|
||||
})
|
||||
);
|
||||
}
|
||||
if (shouldMatchHigh) {
|
||||
matches.push(
|
||||
makeExpectedResult({
|
||||
searchString,
|
||||
fullKeyword: "how to high",
|
||||
isTopPick: true,
|
||||
})
|
||||
);
|
||||
}
|
||||
await check_results({
|
||||
matches,
|
||||
context: createContext(searchString, {
|
||||
providers: [UrlbarProviderQuickSuggest.name],
|
||||
isPrivate: false,
|
||||
}),
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// When a search matches both a low and high-confidence keyword, the suggestion
|
||||
// should be shown as a top pick.
|
||||
add_task(async function topPickLowAndHigh() {
|
||||
let suggestion = REMOTE_SETTINGS_DATA[0].attachment[1];
|
||||
let finalSearchString = "both low and high";
|
||||
Assert.ok(
|
||||
suggestion.lowConfidenceKeywords.includes(finalSearchString),
|
||||
"Sanity check: lowConfidenceKeywords includes the final search string"
|
||||
);
|
||||
Assert.ok(
|
||||
suggestion.highConfidenceKeywords.includes(finalSearchString),
|
||||
"Sanity check: highConfidenceKeywords includes the final search string"
|
||||
);
|
||||
|
||||
// search string -> should be a top pick
|
||||
let tests = {
|
||||
"both low": false,
|
||||
"both low ": false,
|
||||
"both low a": false,
|
||||
"both low an": false,
|
||||
"both low and": false,
|
||||
"both low and ": false,
|
||||
"both low and h": false,
|
||||
"both low and hi": false,
|
||||
"both low and hig": false,
|
||||
"both low and high": true,
|
||||
[finalSearchString]: true,
|
||||
};
|
||||
for (let [searchString, isTopPick] of Object.entries(tests)) {
|
||||
info("Doing search: " + JSON.stringify({ searchString, isTopPick }));
|
||||
await check_results({
|
||||
context: createContext(searchString, {
|
||||
providers: [UrlbarProviderQuickSuggest.name],
|
||||
isPrivate: false,
|
||||
}),
|
||||
matches: [
|
||||
makeExpectedResult({
|
||||
searchString,
|
||||
fullKeyword: "both low and high",
|
||||
isTopPick,
|
||||
suggestion,
|
||||
}),
|
||||
],
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Keyword matching should be case insenstive.
|
||||
add_tasks_with_rust(async function uppercase() {
|
||||
add_task(async function uppercase() {
|
||||
await check_results({
|
||||
context: createContext(LOW_KEYWORD.toUpperCase(), {
|
||||
providers: [UrlbarProviderQuickSuggest.name],
|
||||
|
@ -313,7 +405,7 @@ add_tasks_with_rust(async function uppercase() {
|
|||
});
|
||||
|
||||
// Tests the "Not relevant" command: a dismissed suggestion shouldn't be added.
|
||||
add_tasks_with_rust(async function notRelevant() {
|
||||
add_task(async function notRelevant() {
|
||||
let result = makeExpectedResult({ searchString: LOW_KEYWORD });
|
||||
|
||||
info("Triggering the 'Not relevant' command");
|
||||
|
@ -378,7 +470,7 @@ add_tasks_with_rust(async function notRelevant() {
|
|||
|
||||
// Tests the "Not interested" command: all Pocket suggestions should be disabled
|
||||
// and not added anymore.
|
||||
add_tasks_with_rust(async function notInterested() {
|
||||
add_task(async function notInterested() {
|
||||
let result = makeExpectedResult({ searchString: LOW_KEYWORD });
|
||||
|
||||
info("Triggering the 'Not interested' command");
|
||||
|
@ -414,11 +506,11 @@ add_tasks_with_rust(async function notInterested() {
|
|||
});
|
||||
|
||||
UrlbarPrefs.clear("suggest.pocket");
|
||||
await QuickSuggestTestUtils.forceSync();
|
||||
await waitForSuggestions();
|
||||
});
|
||||
|
||||
// Tests the "show less frequently" behavior.
|
||||
add_tasks_with_rust(async function showLessFrequently() {
|
||||
add_task(async function showLessFrequently() {
|
||||
await doShowLessFrequentlyTests({
|
||||
feature: QuickSuggest.getFeature("PocketSuggestions"),
|
||||
showLessFrequentlyCountPref: "pocket.showLessFrequentlyCount",
|
||||
|
@ -497,3 +589,15 @@ function makeExpectedResult({
|
|||
},
|
||||
};
|
||||
}
|
||||
|
||||
async function waitForSuggestions(keyword = LOW_KEYWORD) {
|
||||
if (UrlbarPrefs.get("quicksuggest.rustEnabled")) {
|
||||
return;
|
||||
}
|
||||
|
||||
let feature = QuickSuggest.getFeature("PocketSuggestions");
|
||||
await TestUtils.waitForCondition(async () => {
|
||||
let suggestions = await feature.queryRemoteSettings(keyword);
|
||||
return !!suggestions.length;
|
||||
}, "Waiting for PocketSuggestions to serve remote settings suggestions");
|
||||
}
|
||||
|
|
|
@ -415,9 +415,13 @@ const TEST_CASES = [
|
|||
];
|
||||
|
||||
add_setup(async function () {
|
||||
UrlbarPrefs.set("quicksuggest.enabled", true);
|
||||
UrlbarPrefs.set("suggest.quicksuggest.sponsored", true);
|
||||
UrlbarPrefs.set("suggest.quicksuggest.nonsponsored", true);
|
||||
|
||||
// Setup for quick suggest result.
|
||||
await QuickSuggestTestUtils.ensureQuickSuggestInit({
|
||||
remoteSettingsRecords: [
|
||||
remoteSettingsResults: [
|
||||
{
|
||||
type: "data",
|
||||
attachment: [
|
||||
|
@ -431,10 +435,6 @@ add_setup(async function () {
|
|||
],
|
||||
},
|
||||
],
|
||||
prefs: [
|
||||
["suggest.quicksuggest.sponsored", true],
|
||||
["suggest.quicksuggest.nonsponsored", true],
|
||||
],
|
||||
});
|
||||
|
||||
// Setup for places result.
|
||||
|
|
|
@ -176,16 +176,17 @@ const MERINO_UNKNOWN_SUGGESTION = {
|
|||
};
|
||||
|
||||
add_setup(async function init() {
|
||||
UrlbarPrefs.set("quicksuggest.enabled", true);
|
||||
UrlbarPrefs.set("suggest.quicksuggest.sponsored", true);
|
||||
UrlbarPrefs.set("suggest.quicksuggest.nonsponsored", true);
|
||||
UrlbarPrefs.set("addons.featureGate", true);
|
||||
|
||||
// Disable search suggestions so we don't hit the network.
|
||||
Services.prefs.setBoolPref("browser.search.suggest.enabled", false);
|
||||
|
||||
await QuickSuggestTestUtils.ensureQuickSuggestInit({
|
||||
remoteSettingsRecords: REMOTE_SETTINGS_RECORDS,
|
||||
remoteSettingsResults: REMOTE_SETTINGS_RECORDS,
|
||||
merinoSuggestions: [],
|
||||
prefs: [
|
||||
["suggest.quicksuggest.sponsored", true],
|
||||
["suggest.quicksuggest.nonsponsored", true],
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -471,7 +472,7 @@ add_task(async function sponsoredWith_addonWith_addonWins_both() {
|
|||
});
|
||||
|
||||
add_task(async function merino_sponsored_addon_sponsoredWins() {
|
||||
await QuickSuggestTestUtils.setRemoteSettingsRecords([]);
|
||||
await QuickSuggestTestUtils.setRemoteSettingsResults([]);
|
||||
|
||||
MerinoTestUtils.server.response.body.suggestions = [
|
||||
MERINO_SPONSORED_SUGGESTION,
|
||||
|
@ -493,11 +494,11 @@ add_task(async function merino_sponsored_addon_sponsoredWins() {
|
|||
}),
|
||||
});
|
||||
|
||||
await QuickSuggestTestUtils.setRemoteSettingsRecords(REMOTE_SETTINGS_RECORDS);
|
||||
await QuickSuggestTestUtils.setRemoteSettingsResults(REMOTE_SETTINGS_RECORDS);
|
||||
});
|
||||
|
||||
add_task(async function merino_sponsored_addon_addonWins() {
|
||||
await QuickSuggestTestUtils.setRemoteSettingsRecords([]);
|
||||
await QuickSuggestTestUtils.setRemoteSettingsResults([]);
|
||||
|
||||
MerinoTestUtils.server.response.body.suggestions = [
|
||||
MERINO_SPONSORED_SUGGESTION,
|
||||
|
@ -518,11 +519,11 @@ add_task(async function merino_sponsored_addon_addonWins() {
|
|||
}),
|
||||
});
|
||||
|
||||
await QuickSuggestTestUtils.setRemoteSettingsRecords(REMOTE_SETTINGS_RECORDS);
|
||||
await QuickSuggestTestUtils.setRemoteSettingsResults(REMOTE_SETTINGS_RECORDS);
|
||||
});
|
||||
|
||||
add_task(async function merino_sponsored_unknown_sponsoredWins() {
|
||||
await QuickSuggestTestUtils.setRemoteSettingsRecords([]);
|
||||
await QuickSuggestTestUtils.setRemoteSettingsResults([]);
|
||||
|
||||
MerinoTestUtils.server.response.body.suggestions = [
|
||||
MERINO_SPONSORED_SUGGESTION,
|
||||
|
@ -544,11 +545,11 @@ add_task(async function merino_sponsored_unknown_sponsoredWins() {
|
|||
}),
|
||||
});
|
||||
|
||||
await QuickSuggestTestUtils.setRemoteSettingsRecords(REMOTE_SETTINGS_RECORDS);
|
||||
await QuickSuggestTestUtils.setRemoteSettingsResults(REMOTE_SETTINGS_RECORDS);
|
||||
});
|
||||
|
||||
add_task(async function merino_sponsored_unknown_unknownWins() {
|
||||
await QuickSuggestTestUtils.setRemoteSettingsRecords([]);
|
||||
await QuickSuggestTestUtils.setRemoteSettingsResults([]);
|
||||
|
||||
MerinoTestUtils.server.response.body.suggestions = [
|
||||
MERINO_SPONSORED_SUGGESTION,
|
||||
|
@ -568,7 +569,7 @@ add_task(async function merino_sponsored_unknown_unknownWins() {
|
|||
}),
|
||||
});
|
||||
|
||||
await QuickSuggestTestUtils.setRemoteSettingsRecords(REMOTE_SETTINGS_RECORDS);
|
||||
await QuickSuggestTestUtils.setRemoteSettingsResults(REMOTE_SETTINGS_RECORDS);
|
||||
});
|
||||
|
||||
add_task(async function stringValue() {
|
||||
|
|
|
@ -35,12 +35,14 @@ const MERINO_SUGGESTIONS = [
|
|||
];
|
||||
|
||||
add_setup(async function init() {
|
||||
UrlbarPrefs.set("quicksuggest.enabled", true);
|
||||
UrlbarPrefs.set("suggest.quicksuggest.nonsponsored", true);
|
||||
|
||||
// Disable search suggestions so we don't hit the network.
|
||||
Services.prefs.setBoolPref("browser.search.suggest.enabled", false);
|
||||
|
||||
await QuickSuggestTestUtils.ensureQuickSuggestInit({
|
||||
merinoSuggestions: MERINO_SUGGESTIONS,
|
||||
prefs: [["suggest.quicksuggest.nonsponsored", true]],
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
"use strict";
|
||||
|
||||
ChromeUtils.defineESModuleGetters(this, {
|
||||
MockRustSuggest: "resource://testing-common/QuickSuggestTestUtils.sys.mjs",
|
||||
setTimeout: "resource://gre/modules/Timer.sys.mjs",
|
||||
});
|
||||
|
||||
|
@ -26,24 +27,28 @@ const REMOTE_SETTINGS_SUGGESTION = {
|
|||
impression_url: "http://example.com/amp-impression",
|
||||
advertiser: "Amp",
|
||||
iab_category: "22 - Shopping",
|
||||
icon: "1234",
|
||||
};
|
||||
|
||||
let gMockRustSuggest;
|
||||
|
||||
add_setup(async function () {
|
||||
initUpdateTimerManager();
|
||||
|
||||
await QuickSuggestTestUtils.ensureQuickSuggestInit({
|
||||
remoteSettingsRecords: [
|
||||
// Since we are specifically testing init behavior of the Rust backend, we
|
||||
// avoid `QuickSuggestTestUtils.ensureQuickSuggestInit()` so we have more
|
||||
// control.
|
||||
gMockRustSuggest = new MockRustSuggest({
|
||||
data: [
|
||||
{
|
||||
type: "data",
|
||||
attachment: [REMOTE_SETTINGS_SUGGESTION],
|
||||
},
|
||||
],
|
||||
prefs: [
|
||||
["suggest.quicksuggest.sponsored", true],
|
||||
["quicksuggest.rustEnabled", false],
|
||||
],
|
||||
});
|
||||
UrlbarPrefs.set("quicksuggest.rustEnabled", false);
|
||||
UrlbarPrefs.set("quicksuggest.enabled", true);
|
||||
UrlbarPrefs.set("suggest.quicksuggest.sponsored", true);
|
||||
QuickSuggest.init();
|
||||
});
|
||||
|
||||
// IMPORTANT: This task must run first!
|
||||
|
@ -53,11 +58,6 @@ add_setup(async function () {
|
|||
add_task(async function firstRun() {
|
||||
Assert.ok(
|
||||
!UrlbarPrefs.get("quicksuggest.rustEnabled"),
|
||||
"rustEnabled pref is initially false (this task must run first!)"
|
||||
);
|
||||
Assert.strictEqual(
|
||||
QuickSuggest.rustBackend.isEnabled,
|
||||
false,
|
||||
"Rust backend is initially disabled (this task must run first!)"
|
||||
);
|
||||
Assert.ok(
|
||||
|
@ -67,7 +67,6 @@ add_task(async function firstRun() {
|
|||
|
||||
info("Enabling the Rust backend");
|
||||
UrlbarPrefs.set("quicksuggest.rustEnabled", true);
|
||||
Assert.ok(QuickSuggest.rustBackend.isEnabled, "Rust backend is now enabled");
|
||||
let { ingestPromise } = QuickSuggest.rustBackend;
|
||||
Assert.ok(ingestPromise, "Ingest started");
|
||||
|
||||
|
@ -117,54 +116,38 @@ add_task(async function interval() {
|
|||
|
||||
// Wait for a few ingests to happen.
|
||||
for (let i = 0; i < 3; i++) {
|
||||
info("Preparing for ingest at index " + i);
|
||||
|
||||
// Set a new suggestion so we can make sure ingest really happened.
|
||||
let suggestion = {
|
||||
...REMOTE_SETTINGS_SUGGESTION,
|
||||
url: REMOTE_SETTINGS_SUGGESTION.url + "/" + i,
|
||||
};
|
||||
await QuickSuggestTestUtils.setRemoteSettingsRecords(
|
||||
[
|
||||
gMockRustSuggest.update({
|
||||
data: [
|
||||
{
|
||||
type: "data",
|
||||
attachment: [suggestion],
|
||||
},
|
||||
],
|
||||
// Don't force sync since the whole point here is to make sure the backend
|
||||
// ingests on its own!
|
||||
{ forceSync: false }
|
||||
});
|
||||
|
||||
// Wait for ingest.
|
||||
await TestUtils.waitForCondition(
|
||||
() => QuickSuggest.rustBackend.ingestPromise != ingestPromise,
|
||||
"Waiting for ingest timer to fire and backend to start new ingest"
|
||||
);
|
||||
|
||||
// Wait for ingest to start and finish.
|
||||
info("Waiting for ingest to start at index " + i);
|
||||
({ ingestPromise } = await waitForIngestStart(ingestPromise));
|
||||
info("Waiting for ingest to finish at index " + i);
|
||||
({ ingestPromise } = QuickSuggest.rustBackend);
|
||||
await ingestPromise;
|
||||
await checkSuggestions([suggestion]);
|
||||
}
|
||||
|
||||
// In the loop above, there was one additional async call after awaiting the
|
||||
// ingest promise, to `checkSuggestions()`. It's possible, though unlikely,
|
||||
// that call took so long that another ingest has started. To be sure, wait
|
||||
// for one final ingest to start before continuing.
|
||||
({ ingestPromise } = await waitForIngestStart(ingestPromise));
|
||||
|
||||
// Now immediately disable the backend. New ingests should not start, but the
|
||||
// final one will still be ongoing.
|
||||
info("Disabling the backend");
|
||||
// Disable the backend. Ingestion should stop.
|
||||
let waitSecs = 3 * intervalSecs;
|
||||
info(`Disabling the backend and waiting ${waitSecs}s`);
|
||||
UrlbarPrefs.set("quicksuggest.rustEnabled", false);
|
||||
|
||||
info("Awaiting final ingest promise");
|
||||
await ingestPromise;
|
||||
|
||||
// Wait a few seconds.
|
||||
let waitSecs = 3 * intervalSecs;
|
||||
info(`Waiting ${waitSecs}s...`);
|
||||
// eslint-disable-next-line mozilla/no-arbitrary-setTimeout
|
||||
await new Promise(r => setTimeout(r, 1000 * waitSecs));
|
||||
|
||||
// No new ingests should have started.
|
||||
Assert.equal(
|
||||
QuickSuggest.rustBackend.ingestPromise,
|
||||
ingestPromise,
|
||||
|
@ -174,29 +157,6 @@ add_task(async function interval() {
|
|||
UrlbarPrefs.clear("quicksuggest.rustIngestIntervalSeconds");
|
||||
});
|
||||
|
||||
async function waitForIngestStart(oldIngestPromise) {
|
||||
let newIngestPromise;
|
||||
await TestUtils.waitForCondition(() => {
|
||||
let { ingestPromise } = QuickSuggest.rustBackend;
|
||||
if (ingestPromise != oldIngestPromise) {
|
||||
newIngestPromise = ingestPromise;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}, "Waiting for a new ingest to start");
|
||||
|
||||
Assert.equal(
|
||||
QuickSuggest.rustBackend.ingestPromise,
|
||||
newIngestPromise,
|
||||
"Sanity check: ingestPromise hasn't changed since waitForCondition returned"
|
||||
);
|
||||
|
||||
// A bare promise can't be returned because it will cause the awaiting caller
|
||||
// to await that promise! We're simply trying to return the promise, which the
|
||||
// caller can later await.
|
||||
return { ingestPromise: newIngestPromise };
|
||||
}
|
||||
|
||||
async function checkSuggestions(expected = [REMOTE_SETTINGS_SUGGESTION]) {
|
||||
let actual = await QuickSuggest.rustBackend.query("amp");
|
||||
Assert.deepEqual(
|
||||
|
|
|
@ -18,13 +18,14 @@ const { WEATHER_RS_DATA, WEATHER_SUGGESTION } = MerinoTestUtils;
|
|||
|
||||
add_task(async function init() {
|
||||
await QuickSuggestTestUtils.ensureQuickSuggestInit({
|
||||
remoteSettingsRecords: [
|
||||
remoteSettingsResults: [
|
||||
{
|
||||
type: "weather",
|
||||
weather: WEATHER_RS_DATA,
|
||||
},
|
||||
],
|
||||
});
|
||||
UrlbarPrefs.set("quicksuggest.enabled", true);
|
||||
|
||||
await MerinoTestUtils.initWeather();
|
||||
|
||||
|
@ -129,7 +130,7 @@ add_task(async function keywordsNotDefined() {
|
|||
});
|
||||
|
||||
// Set RS data without any keywords. Fetching should immediately stop.
|
||||
await QuickSuggestTestUtils.setRemoteSettingsRecords([
|
||||
await QuickSuggestTestUtils.setRemoteSettingsResults([
|
||||
{
|
||||
type: "weather",
|
||||
weather: {},
|
||||
|
@ -153,7 +154,7 @@ add_task(async function keywordsNotDefined() {
|
|||
// Set keywords. Fetching should immediately start.
|
||||
info("Setting keywords");
|
||||
let fetchPromise = QuickSuggest.weather.waitForFetches();
|
||||
await QuickSuggestTestUtils.setRemoteSettingsRecords([
|
||||
await QuickSuggestTestUtils.setRemoteSettingsResults([
|
||||
{
|
||||
type: "weather",
|
||||
weather: MerinoTestUtils.WEATHER_RS_DATA,
|
||||
|
@ -1230,7 +1231,7 @@ add_task(async function vpn() {
|
|||
});
|
||||
|
||||
// When a Nimbus experiment is installed, it should override the remote settings
|
||||
// weather record.
|
||||
// config.
|
||||
add_task(async function nimbusOverride() {
|
||||
// Sanity check initial state.
|
||||
assertEnabled({
|
||||
|
@ -1239,8 +1240,7 @@ add_task(async function nimbusOverride() {
|
|||
pendingFetchCount: 0,
|
||||
});
|
||||
|
||||
// Verify a search works as expected with the default remote settings weather
|
||||
// record (which was added in the init task).
|
||||
// Verify a search works as expected with the default remote settings config.
|
||||
await check_results({
|
||||
context: createContext(MerinoTestUtils.WEATHER_KEYWORD, {
|
||||
providers: [UrlbarProviderWeather.name],
|
||||
|
|
|
@ -14,13 +14,14 @@ const { WEATHER_RS_DATA, WEATHER_SUGGESTION } = MerinoTestUtils;
|
|||
|
||||
add_task(async function init() {
|
||||
await QuickSuggestTestUtils.ensureQuickSuggestInit({
|
||||
remoteSettingsRecords: [
|
||||
remoteSettingsResults: [
|
||||
{
|
||||
type: "weather",
|
||||
weather: WEATHER_RS_DATA,
|
||||
},
|
||||
],
|
||||
});
|
||||
UrlbarPrefs.set("quicksuggest.enabled", true);
|
||||
await MerinoTestUtils.initWeather();
|
||||
});
|
||||
|
||||
|
@ -747,7 +748,7 @@ async function doKeywordsTest({
|
|||
nimbusCleanup = await UrlbarTestUtils.initNimbusFeature(nimbusValues);
|
||||
}
|
||||
|
||||
await QuickSuggestTestUtils.setRemoteSettingsRecords([
|
||||
await QuickSuggestTestUtils.setRemoteSettingsResults([
|
||||
{
|
||||
type: "weather",
|
||||
weather: settingsData,
|
||||
|
@ -790,7 +791,7 @@ async function doKeywordsTest({
|
|||
if (!QuickSuggest.weather.suggestion) {
|
||||
fetchPromise = QuickSuggest.weather.waitForFetches();
|
||||
}
|
||||
await QuickSuggestTestUtils.setRemoteSettingsRecords([
|
||||
await QuickSuggestTestUtils.setRemoteSettingsResults([
|
||||
{
|
||||
type: "weather",
|
||||
weather: MerinoTestUtils.WEATHER_RS_DATA,
|
||||
|
@ -821,7 +822,7 @@ async function doMatchingQuickSuggestTest(pref, isSponsored) {
|
|||
|
||||
// Add a remote settings result to quick suggest.
|
||||
UrlbarPrefs.set(pref, true);
|
||||
await QuickSuggestTestUtils.setRemoteSettingsRecords([
|
||||
await QuickSuggestTestUtils.setRemoteSettingsResults([
|
||||
{
|
||||
type: "data",
|
||||
attachment: [
|
||||
|
@ -1258,7 +1259,7 @@ async function doIncrementTest({ desc, setup, tests }) {
|
|||
nimbusCleanup = await UrlbarTestUtils.initNimbusFeature(nimbusValues);
|
||||
}
|
||||
|
||||
await QuickSuggestTestUtils.setRemoteSettingsRecords([
|
||||
await QuickSuggestTestUtils.setRemoteSettingsResults([
|
||||
{
|
||||
type: "weather",
|
||||
weather: settingsData,
|
||||
|
@ -1321,7 +1322,7 @@ async function doIncrementTest({ desc, setup, tests }) {
|
|||
if (!QuickSuggest.weather.suggestion) {
|
||||
fetchPromise = QuickSuggest.weather.waitForFetches();
|
||||
}
|
||||
await QuickSuggestTestUtils.setRemoteSettingsRecords([
|
||||
await QuickSuggestTestUtils.setRemoteSettingsResults([
|
||||
{
|
||||
type: "weather",
|
||||
weather: MerinoTestUtils.WEATHER_RS_DATA,
|
||||
|
|
|
@ -101,18 +101,21 @@ add_setup(async function test_setup() {
|
|||
// FOG needs to be initialized in order for data to flow.
|
||||
Services.fog.initializeFOG();
|
||||
|
||||
UrlbarPrefs.set("quicksuggest.enabled", true);
|
||||
UrlbarPrefs.set("suggest.quicksuggest.nonsponsored", true);
|
||||
UrlbarPrefs.set("suggest.quicksuggest.sponsored", true);
|
||||
UrlbarPrefs.set("quicksuggest.shouldShowOnboardingDialog", false);
|
||||
|
||||
await MerinoTestUtils.server.start();
|
||||
|
||||
// Set up the remote settings client with the test data.
|
||||
await QuickSuggestTestUtils.ensureQuickSuggestInit({
|
||||
remoteSettingsRecords: [
|
||||
remoteSettingsResults: [
|
||||
{
|
||||
type: "data",
|
||||
attachment: REMOTE_SETTINGS_RESULTS,
|
||||
},
|
||||
],
|
||||
prefs: [
|
||||
["suggest.quicksuggest.nonsponsored", true],
|
||||
["suggest.quicksuggest.sponsored", true],
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче