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:
Cristian Tuns 2023-10-27 19:35:54 -04:00
Родитель 1e7df617ec
Коммит 90898c25ea
39 изменённых файлов: 768 добавлений и 1249 удалений

Просмотреть файл

@ -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],
],
});
});