summaryrefslogtreecommitdiffstats
path: root/browser/components/urlbar/tests/quicksuggest/unit/test_weather_keywords.js
diff options
context:
space:
mode:
Diffstat (limited to 'browser/components/urlbar/tests/quicksuggest/unit/test_weather_keywords.js')
-rw-r--r--browser/components/urlbar/tests/quicksuggest/unit/test_weather_keywords.js1503
1 files changed, 1503 insertions, 0 deletions
diff --git a/browser/components/urlbar/tests/quicksuggest/unit/test_weather_keywords.js b/browser/components/urlbar/tests/quicksuggest/unit/test_weather_keywords.js
new file mode 100644
index 0000000000..efa5922c3e
--- /dev/null
+++ b/browser/components/urlbar/tests/quicksuggest/unit/test_weather_keywords.js
@@ -0,0 +1,1503 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Tests the keywords behavior of quick suggest weather.
+
+"use strict";
+
+ChromeUtils.defineESModuleGetters(this, {
+ UrlbarProviderWeather: "resource:///modules/UrlbarProviderWeather.sys.mjs",
+});
+
+const { WEATHER_RS_DATA } = MerinoTestUtils;
+
+add_setup(async () => {
+ await QuickSuggestTestUtils.ensureQuickSuggestInit({
+ remoteSettingsRecords: [
+ {
+ type: "weather",
+ weather: WEATHER_RS_DATA,
+ },
+ ],
+ prefs: [["suggest.quicksuggest.nonsponsored", true]],
+ });
+ await MerinoTestUtils.initWeather();
+});
+
+// * Settings data: none
+// * Nimbus values: none
+// * Min keyword length pref: none
+// * Expected: no suggestion
+add_tasks_with_rust(async function () {
+ await doKeywordsTest({
+ desc: "No data",
+ tests: {
+ "": false,
+ w: false,
+ we: false,
+ wea: false,
+ weat: false,
+ weath: false,
+ weathe: false,
+ weather: false,
+ f: false,
+ fo: false,
+ for: false,
+ fore: false,
+ forec: false,
+ foreca: false,
+ forecas: false,
+ forecast: false,
+ },
+ });
+});
+
+// * Settings data: empty
+// * Nimbus values: none
+// * Min keyword length pref: none
+// * Expected: no suggestion
+add_tasks_with_rust(async function () {
+ await doKeywordsTest({
+ desc: "Empty settings",
+ settingsData: {},
+ tests: {
+ "": false,
+ w: false,
+ we: false,
+ wea: false,
+ weat: false,
+ weath: false,
+ weathe: false,
+ weather: false,
+ f: false,
+ fo: false,
+ for: false,
+ fore: false,
+ forec: false,
+ foreca: false,
+ forecas: false,
+ forecast: false,
+ },
+ });
+});
+
+// * Settings data: keywords only
+// * Nimbus values: none
+// * Min keyword length pref: none
+// * Expected: full keywords only
+//
+// JS backend only. The Rust component expects settings data to contain
+// min_keyword_length.
+add_task(
+ {
+ skip_if: () => UrlbarPrefs.get("quickSuggestRustEnabled"),
+ },
+ async function () {
+ await doKeywordsTest({
+ desc: "Settings only, keywords only",
+ settingsData: {
+ keywords: ["weather", "forecast"],
+ },
+ tests: {
+ "": false,
+ w: false,
+ we: false,
+ wea: false,
+ weat: false,
+ weath: false,
+ weathe: false,
+ weather: true,
+ f: false,
+ fo: false,
+ for: false,
+ fore: false,
+ forec: false,
+ foreca: false,
+ forecas: false,
+ forecast: true,
+ },
+ });
+ }
+);
+
+// * Settings data: keywords and min keyword length = 0
+// * Nimbus values: none
+// * Min keyword length pref: none
+// * Expected: full keywords only
+//
+// JS backend only. The Rust component doesn't treat minKeywordLength == 0 as a
+// special case.
+add_task(
+ {
+ skip_if: () => UrlbarPrefs.get("quickSuggestRustEnabled"),
+ },
+ async function () {
+ await doKeywordsTest({
+ desc: "Settings only, min keyword length = 0",
+ settingsData: {
+ keywords: ["weather", "forecast"],
+ min_keyword_length: 0,
+ },
+ tests: {
+ "": false,
+ w: false,
+ we: false,
+ wea: false,
+ weat: false,
+ weath: false,
+ weathe: false,
+ weather: true,
+ f: false,
+ fo: false,
+ for: false,
+ fore: false,
+ forec: false,
+ foreca: false,
+ forecas: false,
+ forecast: true,
+ },
+ });
+ }
+);
+
+// * Settings data: keywords and min keyword length > 0
+// * Nimbus values: none
+// * Min keyword length pref: none
+// * Expected: use settings data
+add_tasks_with_rust(async function () {
+ await doKeywordsTest({
+ desc: "Settings only, min keyword length > 0",
+ settingsData: {
+ keywords: ["weather", "forecast"],
+ min_keyword_length: 3,
+ },
+ tests: {
+ "": false,
+ w: false,
+ we: false,
+ wea: true,
+ weat: true,
+ weath: true,
+ weathe: true,
+ weather: true,
+ f: false,
+ fo: false,
+ for: true,
+ fore: true,
+ forec: true,
+ foreca: true,
+ forecas: true,
+ forecast: true,
+ },
+ });
+});
+
+// * Settings data: keywords and min keyword length = 0
+// * Nimbus values: none
+// * Min keyword length pref: 6
+// * Expected: use settings keywords and min keyword length pref
+add_tasks_with_rust(async function () {
+ await doKeywordsTest({
+ desc: "Settings only, min keyword length = 0, pref exists",
+ settingsData: {
+ keywords: ["weather", "forecast"],
+ min_keyword_length: 0,
+ },
+ minKeywordLength: 6,
+ tests: {
+ "": false,
+ w: false,
+ we: false,
+ wea: false,
+ weat: false,
+ weath: false,
+ weathe: true,
+ weather: true,
+ f: false,
+ fo: false,
+ for: false,
+ fore: false,
+ forec: false,
+ foreca: true,
+ forecas: true,
+ forecast: true,
+ },
+ });
+});
+
+// * Settings data: keywords and min keyword length > 0
+// * Nimbus values: none
+// * Min keyword length pref: 6
+// * Expected: use settings keywords and min keyword length pref
+add_tasks_with_rust(async function () {
+ await doKeywordsTest({
+ desc: "Settings only, min keyword length > 0, pref exists",
+ settingsData: {
+ keywords: ["weather", "forecast"],
+ min_keyword_length: 3,
+ },
+ minKeywordLength: 6,
+ tests: {
+ "": false,
+ w: false,
+ we: false,
+ wea: false,
+ weat: false,
+ weath: false,
+ weathe: true,
+ weather: true,
+ f: false,
+ fo: false,
+ for: false,
+ fore: false,
+ forec: false,
+ foreca: true,
+ forecas: true,
+ forecast: true,
+ },
+ });
+});
+
+// * Settings data: empty
+// * Nimbus values: empty
+// * Min keyword length pref: none
+// * Expected: no suggestion
+add_tasks_with_rust(async function () {
+ await doKeywordsTest({
+ desc: "Settings: empty; Nimbus: empty",
+ settingsData: {},
+ nimbusValues: {},
+ tests: {
+ "": false,
+ w: false,
+ we: false,
+ wea: false,
+ weat: false,
+ weath: false,
+ weathe: false,
+ weather: false,
+ f: false,
+ fo: false,
+ for: false,
+ fore: false,
+ forec: false,
+ foreca: false,
+ forecas: false,
+ forecast: false,
+ },
+ });
+});
+
+// * Settings data: keywords only
+// * Nimbus values: keywords only
+// * Min keyword length pref: none
+// * Expected: full keywords in Nimbus
+//
+// JS backend only. The Rust component expects settings data to contain
+// min_keyword_length.
+add_task(
+ {
+ skip_if: () => UrlbarPrefs.get("quickSuggestRustEnabled"),
+ },
+ async function () {
+ await doKeywordsTest({
+ desc: "Settings: keywords; Nimbus: keywords",
+ settingsData: {
+ keywords: ["weather"],
+ },
+ nimbusValues: {
+ weatherKeywords: ["forecast"],
+ },
+ tests: {
+ "": false,
+ w: false,
+ we: false,
+ wea: false,
+ weat: false,
+ weath: false,
+ weathe: false,
+ weather: false,
+ f: false,
+ fo: false,
+ for: false,
+ fore: false,
+ forec: false,
+ foreca: false,
+ forecas: false,
+ forecast: true,
+ },
+ });
+ }
+);
+
+// * Settings data: keywords and min keyword length = 0
+// * Nimbus values: keywords only
+// * Min keyword length pref: none
+// * Expected: full keywords in Nimbus
+//
+// JS backend only. The Rust component doesn't treat minKeywordLength == 0 as a
+// special case.
+add_task(
+ {
+ skip_if: () => UrlbarPrefs.get("quickSuggestRustEnabled"),
+ },
+ async function () {
+ await doKeywordsTest({
+ desc: "Settings: keywords, min keyword length = 0; Nimbus: keywords",
+ settingsData: {
+ keywords: ["weather"],
+ min_keyword_length: 0,
+ },
+ nimbusValues: {
+ weatherKeywords: ["forecast"],
+ },
+ tests: {
+ "": false,
+ w: false,
+ we: false,
+ wea: false,
+ weat: false,
+ weath: false,
+ weathe: false,
+ weather: false,
+ f: false,
+ fo: false,
+ for: false,
+ fore: false,
+ forec: false,
+ foreca: false,
+ forecas: false,
+ forecast: true,
+ },
+ });
+ }
+);
+
+// * Settings data: keywords and min keyword length > 0
+// * Nimbus values: keywords only
+// * Min keyword length pref: none
+// * Expected: Nimbus keywords with settings min keyword length.
+// Even when Rust is enabled, UrlbarProviderWeather should serve the
+// suggestion since the keywords come from Nimbus.
+add_tasks_with_rust(async function () {
+ await doKeywordsTest({
+ desc: "Settings: keywords, min keyword length > 0; Nimbus: keywords",
+ settingsData: {
+ keywords: ["weather"],
+ min_keyword_length: 3,
+ },
+ nimbusValues: {
+ weatherKeywords: ["forecast"],
+ },
+ alwaysExpectMerinoResult: true,
+ tests: {
+ "": false,
+ w: false,
+ we: false,
+ wea: false,
+ weat: false,
+ weath: false,
+ weathe: false,
+ weather: false,
+ f: false,
+ fo: false,
+ for: true,
+ fore: true,
+ forec: true,
+ foreca: true,
+ forecas: true,
+ forecast: true,
+ },
+ });
+});
+
+// * Settings data: keywords and min keyword length > 0
+// * Nimbus values: keywords and min keyword length = 0
+// * Min keyword length pref: none
+// * Expected: Nimbus keywords with settings min keyword length.
+// Even when Rust is enabled, UrlbarProviderWeather should serve the
+// suggestion since the keywords come from Nimbus.
+add_tasks_with_rust(async function () {
+ await doKeywordsTest({
+ desc: "Settings: keywords, min keyword length > 0; Nimbus: keywords, min keyword length = 0",
+ settingsData: {
+ keywords: ["weather"],
+ min_keyword_length: 3,
+ },
+ nimbusValues: {
+ weatherKeywords: ["forecast"],
+ weatherKeywordsMinimumLength: 0,
+ },
+ alwaysExpectMerinoResult: true,
+ tests: {
+ "": false,
+ w: false,
+ we: false,
+ wea: false,
+ weat: false,
+ weath: false,
+ weathe: false,
+ weather: false,
+ f: false,
+ fo: false,
+ for: true,
+ fore: true,
+ forec: true,
+ foreca: true,
+ forecas: true,
+ forecast: true,
+ },
+ });
+});
+
+// * Settings data: keywords and min keyword length > 0
+// * Nimbus values: keywords and min keyword length > 0
+// * Min keyword length pref: none
+// * Expected: use Nimbus values.
+// Even when Rust is enabled, UrlbarProviderWeather should serve the
+// suggestion since the keywords come from Nimbus.
+add_tasks_with_rust(async function () {
+ await doKeywordsTest({
+ desc: "Settings: keywords, min keyword length > 0; Nimbus: keywords, min keyword length > 0",
+ settingsData: {
+ keywords: ["weather"],
+ min_keyword_length: 3,
+ },
+ nimbusValues: {
+ weatherKeywords: ["forecast"],
+ weatherKeywordsMinimumLength: 4,
+ },
+ alwaysExpectMerinoResult: true,
+ tests: {
+ "": false,
+ w: false,
+ we: false,
+ wea: false,
+ weat: false,
+ weath: false,
+ weathe: false,
+ weather: false,
+ f: false,
+ fo: false,
+ for: false,
+ fore: true,
+ forec: true,
+ foreca: true,
+ forecas: true,
+ forecast: true,
+ },
+ });
+});
+
+// * Settings data: keywords and min keyword length > 0
+// * Nimbus values: keywords and min keyword length = 0
+// * Min keyword length pref: exists
+// * Expected: Nimbus keywords with min keyword length pref.
+// Even when Rust is enabled, UrlbarProviderWeather should serve the
+// suggestion since the keywords come from Nimbus.
+add_tasks_with_rust(async function () {
+ await doKeywordsTest({
+ desc: "Settings: keywords, min keyword length > 0; Nimbus: keywords, min keyword length = 0; pref exists",
+ settingsData: {
+ keywords: ["weather"],
+ min_keyword_length: 3,
+ },
+ nimbusValues: {
+ weatherKeywords: ["forecast"],
+ weatherKeywordsMinimumLength: 0,
+ },
+ minKeywordLength: 6,
+ alwaysExpectMerinoResult: true,
+ tests: {
+ "": false,
+ w: false,
+ we: false,
+ wea: false,
+ weat: false,
+ weath: false,
+ weathe: false,
+ weather: false,
+ f: false,
+ fo: false,
+ for: false,
+ fore: false,
+ forec: false,
+ foreca: true,
+ forecas: true,
+ forecast: true,
+ },
+ });
+});
+
+// * Settings data: keywords and min keyword length > 0
+// * Nimbus values: keywords and min keyword length > 0
+// * Min keyword length pref: exists
+// * Expected: Nimbus keywords with min keyword length pref
+// Even when Rust is enabled, UrlbarProviderWeather should serve the
+// suggestion since the keywords come from Nimbus.
+add_tasks_with_rust(async function () {
+ await doKeywordsTest({
+ desc: "Settings: keywords, min keyword length > 0; Nimbus: keywords, min keyword length > 0; pref exists",
+ settingsData: {
+ keywords: ["weather", "forecast"],
+ min_keyword_length: 3,
+ },
+ nimbusValues: {
+ weatherKeywords: ["forecast"],
+ weatherKeywordsMinimumLength: 4,
+ },
+ minKeywordLength: 6,
+ alwaysExpectMerinoResult: true,
+ tests: {
+ "": false,
+ w: false,
+ we: false,
+ wea: false,
+ weat: false,
+ weath: false,
+ weathe: false,
+ weather: false,
+ f: false,
+ fo: false,
+ for: false,
+ fore: false,
+ forec: false,
+ foreca: true,
+ forecas: true,
+ forecast: true,
+ },
+ });
+});
+
+// * Settings data: none
+// * Nimbus values: keywords only
+// * Min keyword length pref: none
+// * Expected: full keywords
+//
+// TODO bug 1879209: This doesn't work with the Rust backend because if
+// min_keyword_length isn't specified on ingest, the Rust database will retain
+// the last known good min_keyword_length, which interferes with this task.
+add_task(
+ {
+ skip_if: () => UrlbarPrefs.get("quickSuggestRustEnabled"),
+ },
+ async function () {
+ await doKeywordsTest({
+ desc: "Settings: none; Nimbus: keywords",
+ nimbusValues: {
+ weatherKeywords: ["weather", "forecast"],
+ },
+ tests: {
+ "": false,
+ w: false,
+ we: false,
+ wea: false,
+ weat: false,
+ weath: false,
+ weathe: false,
+ weather: true,
+ f: false,
+ fo: false,
+ for: false,
+ fore: false,
+ forec: false,
+ foreca: false,
+ forecas: false,
+ forecast: true,
+ },
+ });
+ }
+);
+
+// * Settings data: none
+// * Nimbus values: keywords and min keyword length = 0
+// * Min keyword length pref: none
+// * Expected: full keywords
+//
+// TODO bug 1879209: This doesn't work with the Rust backend because if
+// min_keyword_length isn't specified on ingest, the Rust database will retain
+// the last known good min_keyword_length, which interferes with this task.
+add_task(
+ {
+ skip_if: () => UrlbarPrefs.get("quickSuggestRustEnabled"),
+ },
+ async function () {
+ await doKeywordsTest({
+ desc: "Settings: none; Nimbus: keywords, min keyword length = 0",
+ nimbusValues: {
+ weatherKeywords: ["weather", "forecast"],
+ weatherKeywordsMinimumLength: 0,
+ },
+ tests: {
+ "": false,
+ w: false,
+ we: false,
+ wea: false,
+ weat: false,
+ weath: false,
+ weathe: false,
+ weather: true,
+ f: false,
+ fo: false,
+ for: false,
+ fore: false,
+ forec: false,
+ foreca: false,
+ forecas: false,
+ forecast: true,
+ },
+ });
+ }
+);
+
+// * Settings data: none
+// * Nimbus values: keywords and min keyword length > 0
+// * Min keyword length pref: none
+// * Expected: use Nimbus values
+// Even when Rust is enabled, UrlbarProviderWeather should serve the
+// suggestion since the keywords come from Nimbus.
+add_tasks_with_rust(async function () {
+ await doKeywordsTest({
+ desc: "Settings: none; Nimbus: keywords, min keyword length > 0",
+ nimbusValues: {
+ weatherKeywords: ["weather", "forecast"],
+ weatherKeywordsMinimumLength: 3,
+ },
+ alwaysExpectMerinoResult: true,
+ tests: {
+ "": false,
+ w: false,
+ we: false,
+ wea: true,
+ weat: true,
+ weath: true,
+ weathe: true,
+ weather: true,
+ f: false,
+ fo: false,
+ for: true,
+ fore: true,
+ forec: true,
+ foreca: true,
+ forecas: true,
+ forecast: true,
+ },
+ });
+});
+
+// * Settings data: none
+// * Nimbus values: keywords and min keyword length > 0
+// * Min keyword length pref: exists
+// * Expected: use Nimbus keywords and min keyword length pref
+// Even when Rust is enabled, UrlbarProviderWeather should serve the
+// suggestion since the keywords come from Nimbus.
+add_tasks_with_rust(async function () {
+ await doKeywordsTest({
+ desc: "Settings: none; Nimbus: keywords, min keyword length > 0; pref exists",
+ nimbusValues: {
+ weatherKeywords: ["weather", "forecast"],
+ weatherKeywordsMinimumLength: 3,
+ },
+ minKeywordLength: 6,
+ alwaysExpectMerinoResult: true,
+ tests: {
+ "": false,
+ w: false,
+ we: false,
+ wea: false,
+ weat: false,
+ weath: false,
+ weathe: true,
+ weather: true,
+ f: false,
+ fo: false,
+ for: false,
+ fore: false,
+ forec: false,
+ foreca: true,
+ forecas: true,
+ forecast: true,
+ },
+ });
+});
+
+// When `weatherKeywords` is non-null and `weatherKeywordsMinimumLength` is
+// larger than the length of all keywords, the suggestion should not be
+// triggered.
+// Even when Rust is enabled, UrlbarProviderWeather should serve the
+// suggestion since the keywords come from Nimbus.
+add_tasks_with_rust(async function minLength_large() {
+ await doKeywordsTest({
+ desc: "Large min length",
+ nimbusValues: {
+ weatherKeywords: ["weather", "forecast"],
+ weatherKeywordsMinimumLength: 999,
+ },
+ alwaysExpectMerinoResult: true,
+ tests: {
+ "": false,
+ w: false,
+ we: false,
+ wea: false,
+ weat: false,
+ weath: false,
+ weathe: false,
+ weather: false,
+ f: false,
+ fo: false,
+ for: false,
+ fore: false,
+ forec: false,
+ foreca: false,
+ forecas: false,
+ forecast: false,
+ },
+ });
+});
+
+// Leading and trailing spaces should be ignored.
+add_tasks_with_rust(async function leadingAndTrailingSpaces() {
+ await doKeywordsTest({
+ settingsData: {
+ keywords: ["weather"],
+ min_keyword_length: 3,
+ },
+ tests: {
+ " wea": true,
+ " wea": true,
+ "wea ": true,
+ "wea ": true,
+ " wea ": true,
+ " weat": true,
+ " weat": true,
+ "weat ": true,
+ "weat ": true,
+ " weat ": true,
+ },
+ });
+});
+
+add_tasks_with_rust(async function caseInsensitive() {
+ await doKeywordsTest({
+ desc: "Case insensitive",
+ settingsData: {
+ keywords: ["weather"],
+ min_keyword_length: 3,
+ },
+ tests: {
+ wea: true,
+ WEA: true,
+ Wea: true,
+ WeA: true,
+ WEATHER: true,
+ Weather: true,
+ WeAtHeR: true,
+ },
+ });
+});
+
+async function doKeywordsTest({
+ desc,
+ tests,
+ nimbusValues = null,
+ settingsData = null,
+ minKeywordLength = undefined,
+ alwaysExpectMerinoResult = false,
+}) {
+ info("Doing keywords test: " + desc);
+ info(JSON.stringify({ nimbusValues, settingsData, minKeywordLength }));
+
+ // If the JS backend is enabled, a suggestion hasn't already been fetched, and
+ // the data contains keywords, a fetch will start. Wait for it to finish later
+ // below.
+ let fetchPromise;
+ if (
+ !QuickSuggest.weather.suggestion &&
+ !UrlbarPrefs.get("quickSuggestRustEnabled") &&
+ (nimbusValues?.weatherKeywords || settingsData?.keywords)
+ ) {
+ fetchPromise = QuickSuggest.weather.waitForFetches();
+ }
+
+ let nimbusCleanup;
+ if (nimbusValues) {
+ nimbusCleanup = await UrlbarTestUtils.initNimbusFeature(nimbusValues);
+ }
+
+ await QuickSuggestTestUtils.setRemoteSettingsRecords([
+ {
+ type: "weather",
+ weather: settingsData,
+ },
+ ]);
+
+ if (minKeywordLength) {
+ UrlbarPrefs.set("weather.minKeywordLength", minKeywordLength);
+ }
+
+ if (fetchPromise) {
+ info("Waiting for fetch");
+ assertFetchingStarted({ pendingFetchCount: 1 });
+ await fetchPromise;
+ info("Got fetch");
+ }
+
+ let expectedResult = makeWeatherResult(
+ !alwaysExpectMerinoResult
+ ? undefined
+ : { source: "merino", provider: "accuweather", telemetryType: null }
+ );
+
+ for (let [searchString, expected] of Object.entries(tests)) {
+ info(
+ "Doing keywords test search: " +
+ JSON.stringify({
+ searchString,
+ expected,
+ })
+ );
+
+ await check_results({
+ context: createContext(searchString, {
+ providers: [
+ UrlbarProviderQuickSuggest.name,
+ UrlbarProviderWeather.name,
+ ],
+ isPrivate: false,
+ }),
+ matches: expected ? [expectedResult] : [],
+ });
+ }
+
+ await nimbusCleanup?.();
+
+ fetchPromise = null;
+ if (!QuickSuggest.weather.suggestion) {
+ fetchPromise = QuickSuggest.weather.waitForFetches();
+ }
+ await QuickSuggestTestUtils.setRemoteSettingsRecords([
+ {
+ type: "weather",
+ weather: MerinoTestUtils.WEATHER_RS_DATA,
+ },
+ ]);
+
+ UrlbarPrefs.clear("weather.minKeywordLength");
+ await fetchPromise;
+}
+
+// When a sponsored quick suggest result matches the same keyword as the weather
+// result, the weather result should be shown and the quick suggest result
+// should not be shown.
+add_tasks_with_rust(async function matchingQuickSuggest_sponsored() {
+ await doMatchingQuickSuggestTest("suggest.quicksuggest.sponsored", true);
+});
+
+// When a non-sponsored quick suggest result matches the same keyword as the
+// weather result, the weather result should be shown and the quick suggest
+// result should not be shown.
+add_tasks_with_rust(async function matchingQuickSuggest_nonsponsored() {
+ await doMatchingQuickSuggestTest("suggest.quicksuggest.nonsponsored", false);
+});
+
+async function doMatchingQuickSuggestTest(pref, isSponsored) {
+ let keyword = "test";
+
+ let attachment = isSponsored
+ ? {
+ id: 1,
+ url: "http://example.com/amp",
+ title: "AMP Suggestion",
+ keywords: [keyword],
+ click_url: "http://example.com/amp-click",
+ impression_url: "http://example.com/amp-impression",
+ advertiser: "Amp",
+ iab_category: "22 - Shopping",
+ icon: "1234",
+ }
+ : {
+ id: 2,
+ url: "http://example.com/wikipedia",
+ title: "Wikipedia Suggestion",
+ keywords: [keyword],
+ click_url: "http://example.com/wikipedia-click",
+ impression_url: "http://example.com/wikipedia-impression",
+ advertiser: "Wikipedia",
+ iab_category: "5 - Education",
+ icon: "1234",
+ };
+
+ // Add a remote settings result to quick suggest.
+ let oldPrefValue = UrlbarPrefs.get(pref);
+ UrlbarPrefs.set(pref, true);
+ await QuickSuggestTestUtils.setRemoteSettingsRecords([
+ {
+ type: "data",
+ attachment: [attachment],
+ },
+ {
+ type: "weather",
+ weather: MerinoTestUtils.WEATHER_RS_DATA,
+ },
+ ]);
+
+ // First do a search to verify the quick suggest result matches the keyword.
+ let payload;
+ if (!UrlbarPrefs.get("quickSuggestRustEnabled")) {
+ payload = {
+ source: "remote-settings",
+ provider: "AdmWikipedia",
+ sponsoredImpressionUrl: attachment.impression_url,
+ sponsoredClickUrl: attachment.click_url,
+ sponsoredBlockId: attachment.id,
+ };
+ } else {
+ payload = {
+ source: "rust",
+ provider: isSponsored ? "Amp" : "Wikipedia",
+ };
+ if (isSponsored) {
+ payload.sponsoredImpressionUrl = attachment.impression_url;
+ payload.sponsoredClickUrl = attachment.click_url;
+ payload.sponsoredBlockId = attachment.id;
+ }
+ }
+
+ info("Doing first search for quick suggest result");
+ await check_results({
+ context: createContext(keyword, {
+ providers: [UrlbarProviderQuickSuggest.name, UrlbarProviderWeather.name],
+ isPrivate: false,
+ }),
+ matches: [
+ {
+ type: UrlbarUtils.RESULT_TYPE.URL,
+ source: UrlbarUtils.RESULT_SOURCE.SEARCH,
+ heuristic: false,
+ payload: {
+ ...payload,
+ telemetryType: isSponsored ? "adm_sponsored" : "adm_nonsponsored",
+ qsSuggestion: keyword,
+ title: attachment.title,
+ url: attachment.url,
+ displayUrl: attachment.url.replace(/[/]$/, ""),
+ originalUrl: attachment.url,
+ icon: null,
+ sponsoredAdvertiser: attachment.advertiser,
+ sponsoredIabCategory: attachment.iab_category,
+ isSponsored,
+ descriptionL10n: isSponsored
+ ? { id: "urlbar-result-action-sponsored" }
+ : undefined,
+ helpUrl: QuickSuggest.HELP_URL,
+ helpL10n: {
+ id: "urlbar-result-menu-learn-more-about-firefox-suggest",
+ },
+ isBlockable: true,
+ blockL10n: {
+ id: "urlbar-result-menu-dismiss-firefox-suggest",
+ },
+ },
+ },
+ ],
+ });
+
+ // Set up the keyword for the weather suggestion and do a second search to
+ // verify only the weather result matches.
+ info("Doing second search for weather suggestion");
+ let cleanup = await UrlbarTestUtils.initNimbusFeature({
+ weatherKeywords: [keyword],
+ weatherKeywordsMinimumLength: 1,
+ });
+ await check_results({
+ context: createContext(keyword, {
+ providers: [UrlbarProviderQuickSuggest.name, UrlbarProviderWeather.name],
+ isPrivate: false,
+ }),
+ // The result should always come from Merino.
+ matches: [
+ makeWeatherResult({
+ source: "merino",
+ provider: "accuweather",
+ telemetryType: null,
+ }),
+ ],
+ });
+ await cleanup();
+
+ UrlbarPrefs.set(pref, oldPrefValue);
+}
+
+add_tasks_with_rust(async function () {
+ await doIncrementTest({
+ desc: "Settings only without cap",
+ setup: {
+ settingsData: {
+ weather: {
+ keywords: ["forecast", "wind"],
+ min_keyword_length: 3,
+ },
+ },
+ },
+ tests: [
+ {
+ minKeywordLength: 3,
+ canIncrement: true,
+ searches: {
+ fo: false,
+ for: true,
+ fore: true,
+ forec: true,
+ wi: false,
+ win: true,
+ wind: true,
+ },
+ },
+ {
+ minKeywordLength: 4,
+ canIncrement: true,
+ searches: {
+ fo: false,
+ for: false,
+ fore: true,
+ forec: true,
+ wi: false,
+ win: false,
+ wind: true,
+ },
+ },
+ {
+ minKeywordLength: 5,
+ canIncrement: true,
+ searches: {
+ fo: false,
+ for: false,
+ fore: false,
+ forec: true,
+ wi: false,
+ win: false,
+ wind: false,
+ },
+ },
+ ],
+ });
+});
+
+add_tasks_with_rust(async function () {
+ await doIncrementTest({
+ desc: "Settings only with cap",
+ setup: {
+ settingsData: {
+ weather: {
+ keywords: ["forecast", "wind"],
+ min_keyword_length: 3,
+ },
+ configuration: {
+ show_less_frequently_cap: 3,
+ },
+ },
+ },
+ tests: [
+ {
+ minKeywordLength: 3,
+ canIncrement: true,
+ searches: {
+ fo: false,
+ for: true,
+ fore: true,
+ forec: true,
+ foreca: true,
+ forecas: true,
+ wi: false,
+ win: true,
+ wind: true,
+ },
+ },
+ {
+ minKeywordLength: 4,
+ canIncrement: true,
+ searches: {
+ fo: false,
+ for: false,
+ fore: true,
+ forec: true,
+ foreca: true,
+ forecas: true,
+ wi: false,
+ win: false,
+ wind: true,
+ },
+ },
+ {
+ minKeywordLength: 5,
+ canIncrement: true,
+ searches: {
+ fo: false,
+ for: false,
+ fore: false,
+ forec: true,
+ foreca: true,
+ forecas: true,
+ wi: false,
+ win: false,
+ wind: false,
+ windy: false,
+ },
+ },
+ {
+ minKeywordLength: 6,
+ canIncrement: false,
+ searches: {
+ fo: false,
+ for: false,
+ fore: false,
+ forec: false,
+ foreca: true,
+ forecas: true,
+ wi: false,
+ win: false,
+ wind: false,
+ windy: false,
+ },
+ },
+ {
+ minKeywordLength: 6,
+ canIncrement: false,
+ searches: {
+ fo: false,
+ for: false,
+ fore: false,
+ forec: false,
+ foreca: true,
+ forecas: true,
+ wi: false,
+ win: false,
+ wind: false,
+ windy: false,
+ },
+ },
+ ],
+ });
+});
+
+add_tasks_with_rust(async function () {
+ await doIncrementTest({
+ desc: "Settings and Nimbus without cap",
+ setup: {
+ settingsData: {
+ weather: {
+ keywords: ["weather"],
+ min_keyword_length: 5,
+ },
+ },
+ nimbusValues: {
+ weatherKeywords: ["forecast", "wind"],
+ weatherKeywordsMinimumLength: 3,
+ },
+ },
+ // The suggestion should be served by UrlbarProviderWeather and therefore
+ // be from Merino.
+ alwaysExpectMerinoResult: true,
+ tests: [
+ {
+ minKeywordLength: 3,
+ canIncrement: true,
+ searches: {
+ we: false,
+ wea: false,
+ weat: false,
+ weath: false,
+ fo: false,
+ for: true,
+ fore: true,
+ forec: true,
+ wi: false,
+ win: true,
+ wind: true,
+ },
+ },
+ {
+ minKeywordLength: 4,
+ canIncrement: true,
+ searches: {
+ we: false,
+ wea: false,
+ weat: false,
+ weath: false,
+ fo: false,
+ for: false,
+ fore: true,
+ forec: true,
+ wi: false,
+ win: false,
+ wind: true,
+ },
+ },
+ {
+ minKeywordLength: 5,
+ canIncrement: true,
+ searches: {
+ we: false,
+ wea: false,
+ weat: false,
+ weath: false,
+ fo: false,
+ for: false,
+ fore: false,
+ forec: true,
+ wi: false,
+ win: false,
+ wind: false,
+ windy: false,
+ },
+ },
+ ],
+ });
+});
+
+add_task(async function () {
+ await doIncrementTest({
+ desc: "Settings and Nimbus with cap in Nimbus",
+ setup: {
+ settingsData: {
+ weather: {
+ keywords: ["weather"],
+ min_keyword_length: 5,
+ },
+ },
+ nimbusValues: {
+ weatherKeywords: ["forecast", "wind"],
+ weatherKeywordsMinimumLength: 3,
+ weatherKeywordsMinimumLengthCap: 6,
+ },
+ },
+ // The suggestion should be served by UrlbarProviderWeather and therefore
+ // be from Merino.
+ alwaysExpectMerinoResult: true,
+ tests: [
+ {
+ minKeywordLength: 3,
+ canIncrement: true,
+ searches: {
+ we: false,
+ wea: false,
+ weat: false,
+ weath: false,
+ fo: false,
+ for: true,
+ fore: true,
+ forec: true,
+ foreca: true,
+ forecas: true,
+ wi: false,
+ win: true,
+ wind: true,
+ },
+ },
+ {
+ minKeywordLength: 4,
+ canIncrement: true,
+ searches: {
+ we: false,
+ wea: false,
+ weat: false,
+ weath: false,
+ fo: false,
+ for: false,
+ fore: true,
+ forec: true,
+ foreca: true,
+ forecas: true,
+ wi: false,
+ win: false,
+ wind: true,
+ },
+ },
+ {
+ minKeywordLength: 5,
+ canIncrement: true,
+ searches: {
+ we: false,
+ wea: false,
+ weat: false,
+ weath: false,
+ fo: false,
+ for: false,
+ fore: false,
+ forec: true,
+ foreca: true,
+ forecas: true,
+ wi: false,
+ win: false,
+ wind: false,
+ windy: false,
+ },
+ },
+ {
+ minKeywordLength: 6,
+ canIncrement: false,
+ searches: {
+ we: false,
+ wea: false,
+ weat: false,
+ weath: false,
+ fo: false,
+ for: false,
+ fore: false,
+ forec: false,
+ foreca: true,
+ forecas: true,
+ wi: false,
+ win: false,
+ wind: false,
+ windy: false,
+ },
+ },
+ {
+ minKeywordLength: 6,
+ canIncrement: false,
+ searches: {
+ we: false,
+ wea: false,
+ weat: false,
+ weath: false,
+ fo: false,
+ for: false,
+ fore: false,
+ forec: false,
+ foreca: true,
+ forecas: true,
+ wi: false,
+ win: false,
+ wind: false,
+ windy: false,
+ },
+ },
+ ],
+ });
+});
+
+async function doIncrementTest({
+ desc,
+ setup,
+ tests,
+ alwaysExpectMerinoResult = false,
+}) {
+ info("Doing increment test: " + desc);
+ info(JSON.stringify({ setup }));
+
+ let { nimbusValues, settingsData } = setup;
+
+ // If the JS backend is enabled, a suggestion hasn't already been fetched, and
+ // the data contains keywords, a fetch will start. Wait for it to finish later
+ // below.
+ let fetchPromise;
+ if (
+ !QuickSuggest.weather.suggestion &&
+ !UrlbarPrefs.get("quickSuggestRustEnabled") &&
+ (nimbusValues?.weatherKeywords || settingsData?.weather?.keywords)
+ ) {
+ fetchPromise = QuickSuggest.weather.waitForFetches();
+ }
+
+ let nimbusCleanup;
+ if (nimbusValues) {
+ nimbusCleanup = await UrlbarTestUtils.initNimbusFeature(nimbusValues);
+ }
+
+ await QuickSuggestTestUtils.setRemoteSettingsRecords([
+ {
+ type: "weather",
+ weather: settingsData?.weather,
+ },
+ {
+ type: "configuration",
+ configuration: settingsData?.configuration,
+ },
+ ]);
+
+ if (fetchPromise) {
+ info("Waiting for fetch");
+ assertFetchingStarted({ pendingFetchCount: 1 });
+ await fetchPromise;
+ info("Got fetch");
+ }
+
+ let expectedResult = makeWeatherResult(
+ !alwaysExpectMerinoResult
+ ? undefined
+ : { source: "merino", provider: "accuweather", telemetryType: null }
+ );
+
+ for (let { minKeywordLength, canIncrement, searches } of tests) {
+ info(
+ "Doing increment test case: " +
+ JSON.stringify({
+ minKeywordLength,
+ canIncrement,
+ })
+ );
+
+ Assert.equal(
+ QuickSuggest.weather.minKeywordLength,
+ minKeywordLength,
+ "minKeywordLength should be correct"
+ );
+ Assert.equal(
+ QuickSuggest.weather.canIncrementMinKeywordLength,
+ canIncrement,
+ "canIncrement should be correct"
+ );
+
+ for (let [searchString, expected] of Object.entries(searches)) {
+ await check_results({
+ context: createContext(searchString, {
+ providers: [
+ UrlbarProviderQuickSuggest.name,
+ UrlbarProviderWeather.name,
+ ],
+ isPrivate: false,
+ }),
+ matches: expected ? [expectedResult] : [],
+ });
+ }
+
+ QuickSuggest.weather.incrementMinKeywordLength();
+ info(
+ "Incremented min keyword length, new value is: " +
+ QuickSuggest.weather.minKeywordLength
+ );
+ }
+
+ await nimbusCleanup?.();
+
+ fetchPromise = null;
+ if (!QuickSuggest.weather.suggestion) {
+ fetchPromise = QuickSuggest.weather.waitForFetches();
+ }
+ await QuickSuggestTestUtils.setRemoteSettingsRecords([
+ {
+ type: "weather",
+ weather: MerinoTestUtils.WEATHER_RS_DATA,
+ },
+ ]);
+ UrlbarPrefs.clear("weather.minKeywordLength");
+ await fetchPromise;
+}
+
+function assertFetchingStarted() {
+ info("Asserting fetching has started");
+
+ Assert.notEqual(
+ QuickSuggest.weather._test_fetchTimer,
+ 0,
+ "Fetch timer is non-zero"
+ );
+ Assert.ok(QuickSuggest.weather._test_merino, "Merino client is non-null");
+ Assert.equal(
+ QuickSuggest.weather._test_pendingFetchCount,
+ 1,
+ "Expected pending fetch count"
+ );
+}