tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit 847e480a9049747038be5901180ee85629f69112
parent 9533b94cc4eef92d8289e006728345f3e1a31c12
Author: Drew Willcoxon <adw@mozilla.com>
Date:   Mon, 13 Oct 2025 21:19:02 +0000

Bug 1993371 - Update region- and locale-handling in Suggest tests. r=daisuke

This rewrites `QuickSuggestTestUtils.withLocales` with the following goals in
mind:

* Wait on `SharedRemoteSettingsService` to update itself due to the
  region/locale change (bug 1966166 D268036).

* Re-initialize Suggest (by default) so that the Suggest prefs are properly set
  depending on the new region/locale.

* Force sync so that the Suggest store is populated with RS data for the new
  region/locale, so that callers don't need to do that.

I also factored out and reworked some code so that hopefully it's easier to
understand overall.

I split `withLocales` into `withRegionAndLocale` and `setRegionAndLocale`.
Callers can use `setRegionAndLocale` when they don't need to reset the original
region/locale. The motivation for this is the important-dates test: In
bug 1992811 I'm going to enable important dates for en locales in EU countries,
and I want to test a variety of regions/locales. By default, the test assumes
the US region and the en-US locale, and I use `setRegionAndLocale` in the setup
task for that.

Depends on D268036

Differential Revision: https://phabricator.services.mozilla.com/D268042

Diffstat:
Mbrowser/components/urlbar/tests/quicksuggest/QuickSuggestTestUtils.sys.mjs | 245++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------------
Mbrowser/components/urlbar/tests/quicksuggest/unit/test_quicksuggest_exposures_locales.js | 16+++-------------
Mbrowser/components/urlbar/tests/quicksuggest/unit/test_quicksuggest_importantDatesSuggestions.js | 122++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
Mbrowser/components/urlbar/tests/quicksuggest/unit/test_quicksuggest_offlineDefault.js | 8+++-----
Mbrowser/components/urlbar/tests/quicksuggest/unit/test_quicksuggest_remoteSettingsFilter.js | 19+++++++------------
Mbrowser/components/urlbar/tests/quicksuggest/unit/test_weather.js | 18+++++++++++++-----
6 files changed, 313 insertions(+), 115 deletions(-)

diff --git a/browser/components/urlbar/tests/quicksuggest/QuickSuggestTestUtils.sys.mjs b/browser/components/urlbar/tests/quicksuggest/QuickSuggestTestUtils.sys.mjs @@ -1426,102 +1426,195 @@ class _QuickSuggestTestUtils { } /** - * Sets the app's home region and locales, calls your callback, and resets - * the region and locales. + * Sets the app's home region and locale, calls your callback, and resets the + * region and locale. * * @param {object} options * Options object. - * @param {Array} options.locales - * An array of locale strings. The entire array will be set as the available - * locales, and the first locale in the array will be set as the requested - * locale. * @param {Function} options.callback - * The callback to be called with the {@link locales} set. This function can - * be async. - * @param {string} options.homeRegion - * The home region to set, an all-caps country code, e.g., "US", "CA", "DE". - * Leave undefined to skip setting a region. + * The callback to call. + * @param {string} [options.region] + * The region to set. See `setRegionAndLocale`. + * @param {string} [options.locale] + * The locale to set. See `setRegionAndLocale`. + * @param {boolean} [options.skipSuggestReset] + * Whether Suggest reset should be skipped after setting the new region and + * locale. See `setRegionAndLocale`. */ - async withLocales({ locales, callback, homeRegion = undefined }) { - let promiseChanges = async desiredLocales => { - this.#log( - "withLocales", - "Changing locales from " + - JSON.stringify(Services.locale.requestedLocales) + - " to " + - JSON.stringify(desiredLocales) - ); + async withRegionAndLocale({ + callback, + region = undefined, + locale = undefined, + skipSuggestReset = false, + }) { + this.#log("withRegionAndLocale", "Calling setRegionAndLocale"); + let cleanup = await this.setRegionAndLocale({ + region, + locale, + skipSuggestReset, + }); - if (desiredLocales[0] == Services.locale.requestedLocales[0]) { - // Nothing happens when the locale doesn't actually change. - this.#log("withLocales", "Locale is already " + desiredLocales[0]); - return; - } + this.#log("withRegionAndLocale", "Calling callback"); + await callback(); - this.#log("withLocales", "Waiting for intl:requested-locales-changed"); - await lazy.TestUtils.topicObserved("intl:requested-locales-changed"); - this.#log("withLocales", "Observed intl:requested-locales-changed"); + this.#log("withRegionAndLocale", "Calling cleanup"); + await cleanup(); - // 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"); - await Promise.race([ - lazy.TestUtils.topicObserved( - lazy.SearchUtils.TOPIC_SEARCH_SERVICE, - (subject, data) => { - this.#log( - "withLocales", - "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" - ); - resolve(); - }, 2000); - }), - ]); - - this.#log("withLocales", "Done waiting for locale changes"); - }; + this.#log("withRegionAndLocale", "Done"); + } - let originalHome = lazy.Region.home; - if (homeRegion) { - lazy.Region._setHomeRegion(homeRegion, true); + /** + * Sets the app's home region and locale and waits for all relevant + * notifications. Returns an async cleanup function that should be called to + * restore the previous region and locale. + * + * See also `withRegionAndLocale`. + * + * @param {object} options + * Options object. + * @param {string} [options.region] + * The home region to set. If falsey, the current region will remain + * unchanged. + * @param {string} [options.locale] + * The locale to set. If falsey, the current locale will remain unchanged. + * @param {Array} [options.availableLocales] + * Normally this should be left undefined. If defined, + * `Services.locale.availableLocales` will be set to this array. Otherwise + * it will be set to `[locale]`. + * @param {boolean} [options.skipSuggestReset] + * Normally this function resets `QuickSuggest` after the new region and + * locale are set, which will cause all Suggest prefs to be set according to + * the new region and locale. That's undesirable in some cases, for example + * when you're testing region/locale combinations where Suggest or one of + * its features isn't enabled by default. Pass in true to skip reset. + * @returns {Promise<Function>} + * An async cleanup function. + */ + async setRegionAndLocale({ + region = undefined, + locale = undefined, + availableLocales = undefined, + skipSuggestReset = false, + }) { + let oldRegion = lazy.Region.home; + let newRegion = region ?? oldRegion; + let regionPromise = this.#waitForAllRegionChanges(newRegion); + if (region) { + this.#log("setRegionAndLocale", "Setting region: " + region); + lazy.Region._setHomeRegion(region, true); } - let available = Services.locale.availableLocales; - let requested = Services.locale.requestedLocales; + let { + availableLocales: oldAvailableLocales, + requestedLocales: oldRequestedLocales, + } = Services.locale; + let newLocale = locale ?? oldRequestedLocales[0]; + let localePromise = this.#waitForAllLocaleChanges(newLocale); + if (locale) { + this.#log("setRegionAndLocale", "Setting locale: " + locale); + Services.locale.availableLocales = availableLocales ?? [locale]; + Services.locale.requestedLocales = [locale]; + } - let newRequested = locales.slice(0, 1); - let promise = promiseChanges(newRequested); - Services.locale.availableLocales = locales; - Services.locale.requestedLocales = newRequested; - await promise; + this.#log("setRegionAndLocale", "Waiting for region and locale changes"); + await Promise.all([regionPromise, localePromise]); this.Assert.equal( + lazy.Region.home, + newRegion, + "Region is now " + newRegion + ); + this.Assert.equal( Services.locale.appLocaleAsBCP47, - locales[0], - "App locale is now " + locales[0] + newLocale, + "App locale is now " + newLocale ); - await callback(); + if (!skipSuggestReset) { + this.#log("setRegionAndLocale", "Waiting for _test_reset"); + await lazy.QuickSuggest._test_reset(); + } + + if (this.#remoteSettingsServer) { + this.#log("setRegionAndLocale", "Waiting for forceSync"); + await this.forceSync(); + } + + this.#log("setRegionAndLocale", "Done"); + + return async () => { + this.#log( + "setRegionAndLocale", + "Cleanup started, calling setRegionAndLocale with old region and locale" + ); + await this.setRegionAndLocale({ + skipSuggestReset, + region: oldRegion, + locale: oldRequestedLocales[0], + availableLocales: oldAvailableLocales, + }); + this.#log("setRegionAndLocale", "Cleanup done"); + }; + } + + async #waitForAllRegionChanges(region) { + await lazy.TestUtils.waitForCondition( + () => lazy.SharedRemoteSettingsService.country == region, + "Waiting for SharedRemoteSettingsService.country to be " + region + ); + } + + async #waitForAllLocaleChanges(locale) { + let promises = [ + lazy.TestUtils.waitForCondition( + () => lazy.SharedRemoteSettingsService.locale == locale, + "#waitForAllLocaleChanges: Waiting for SharedRemoteSettingsService.locale to be " + + locale + ), + ]; - if (homeRegion) { - lazy.Region._setHomeRegion(originalHome, true); + if (locale == Services.locale.requestedLocales[0]) { + // "intl:app-locales-changed" isn't sent when the locale doesn't change. + this.#log("#waitForAllLocaleChanges", "Locale is already " + locale); + } else { + this.#log( + "#waitForAllLocaleChanges", + "Waiting for intl:app-locales-changed" + ); + promises.push(lazy.TestUtils.topicObserved("intl:app-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("#waitForAllLocaleChanges", "Waiting for TOPIC_SEARCH_SERVICE"); + promises.push( + Promise.race([ + lazy.TestUtils.topicObserved( + lazy.SearchUtils.TOPIC_SEARCH_SERVICE, + (subject, data) => { + this.#log( + "setLocales", + "Observed TOPIC_SEARCH_SERVICE with data: " + data + ); + return data == "engines-reloaded"; + } + ), + new Promise(resolve => { + lazy.setTimeout(() => { + this.#log( + "setLocales", + "Timed out waiting for TOPIC_SEARCH_SERVICE (not an error)" + ); + resolve(); + }, 2000); + }), + ]) + ); } - promise = promiseChanges(requested); - Services.locale.availableLocales = available; - Services.locale.requestedLocales = requested; - await promise; + await Promise.all(promises); + this.#log("#waitForAllLocaleChanges", "Done"); } #log(fnName, msg) { diff --git a/browser/components/urlbar/tests/quicksuggest/unit/test_quicksuggest_exposures_locales.js b/browser/components/urlbar/tests/quicksuggest/unit/test_quicksuggest_exposures_locales.js @@ -175,16 +175,10 @@ async function doLocaleTest({ info("Doing locale test: " + JSON.stringify({ homeRegion, locale })); // Set the region and locale. - await QuickSuggestTestUtils.withLocales({ - homeRegion, - locales: [locale], + await QuickSuggestTestUtils.withRegionAndLocale({ + locale, + region: homeRegion, callback: async () => { - // Reinitialize Suggest, which will set default-branch values for - // Suggest prefs appropriate to the locale. - info("Reinitializing Suggest"); - await QuickSuggest._test_reset(); - info("Done reinitializing Suggest"); - // Sanity-check prefs. At this point, the value of `quickSuggestEnabled` // will be the value of its fallback pref, `quicksuggest.enabled`. assertSuggestPrefs(expectedQuickSuggestEnabled); @@ -228,10 +222,6 @@ async function doLocaleTest({ }, }); } - - // Reinitialize Suggest so prefs go back to their defaults now that the app is - // back to its default locale. - await QuickSuggest._test_reset(); } function assertSuggestPrefs(expectedEnabled) { diff --git a/browser/components/urlbar/tests/quicksuggest/unit/test_quicksuggest_importantDatesSuggestions.js b/browser/components/urlbar/tests/quicksuggest/unit/test_quicksuggest_importantDatesSuggestions.js @@ -4,9 +4,11 @@ https://creativecommons.org/publicdomain/zero/1.0/ */ "use strict"; const REMOTE_SETTINGS_RECORDS = [ + // US region, en-US locale { type: "dynamic-suggestions", suggestion_type: "important_dates", + filter_expression: "env.country == 'US' && env.locale == 'en-US'", attachment: [ { keywords: [["event", [" 1"]]], @@ -37,6 +39,29 @@ const REMOTE_SETTINGS_RECORDS = [ }, ], }, + + // DE region, de locale + { + type: "dynamic-suggestions", + suggestion_type: "important_dates", + filter_expression: "env.country == 'DE' && env.locale == 'de'", + attachment: [ + { + keywords: ["de de event"], + data: { + result: { + isImportantDate: true, + payload: { + dates: ["2026-01-01"], + name: "DE de Event", + }, + }, + }, + }, + ], + }, + + // other non-important-dates records { type: "dynamic-suggestions", suggestion_type: "other_suggestions", @@ -84,11 +109,15 @@ let SystemDate; add_setup(async function () { await QuickSuggestTestUtils.ensureQuickSuggestInit({ remoteSettingsRecords: REMOTE_SETTINGS_RECORDS, - prefs: [ - ["importantDates.featureGate", true], - ["quicksuggest.dynamicSuggestionTypes", "other_suggestions"], - ], + prefs: [["quicksuggest.dynamicSuggestionTypes", "other_suggestions"]], + }); + + // All tasks will assume US region, en-US locale by default. + await QuickSuggestTestUtils.setRegionAndLocale({ + region: "US", + locale: "en-US", }); + await Services.search.init(); SystemDate = Cu.getGlobalForObject(QuickSuggestTestUtils).Date; @@ -321,6 +350,91 @@ add_task(async function testTwoSuggestions() { await QuickSuggestTestUtils.forceSync(); }); +add_task(async function otherRegionsAndLocales() { + let tests = [ + // DE region, de locale (should match) + { + region: "DE", + locale: "de", + matchingQuery: "de de event", + expectedResultData: { + date: "Donnerstag, 1. Januar 2026", + description: "DE de Event", + }, + nonMatchingQueries: [ + "de en-US event", // DE region, en-US locale + "event 1", // US region, en-US locale + ], + }, + + // XX region, en-US locale (should not match) + { + region: "XX", + locale: "en-US", + matchingQuery: null, + nonMatchingQueries: [ + "de de event", // DE region, de locale + "de en-us event", // DE region, en-US locale + "event 1", // US region, en-US locale + ], + }, + + // US region, de locale (should not match) + { + region: "US", + locale: "af", + matchingQuery: null, + nonMatchingQueries: [ + "de de event", // DE region, de locale + "de en-us event", // DE region, en-US locale + "event 1", // US region, en-US locale + ], + }, + ]; + + for (let { + region, + locale, + matchingQuery, + expectedResultData, + nonMatchingQueries, + } of tests) { + info("Doing subtest: " + JSON.stringify({ region, locale })); + + await QuickSuggestTestUtils.withRegionAndLocale({ + region, + locale, + callback: async () => { + Assert.equal( + UrlbarPrefs.get("quickSuggestEnabled"), + !!matchingQuery, + "quickSuggestEnabled should be enabled as expected" + ); + Assert.equal( + UrlbarPrefs.get("importantDates.featureGate"), + !!matchingQuery, + "importantDates.featureGate should be enabled as expected" + ); + + setTime("2025-03-01T00:00"); + + if (matchingQuery) { + info("Checking matching query: " + matchingQuery); + await checkDatesResults( + matchingQuery, + makeExpectedResult(expectedResultData) + ); + } + + for (let query of nonMatchingQueries) { + info("Checking non-matching query: " + query); + await checkDatesResults(query, null); + } + }, + }); + } +}); + /** * Stubs the Date object of the system global to use timeStr * when the constructor is called without arguments. diff --git a/browser/components/urlbar/tests/quicksuggest/unit/test_quicksuggest_offlineDefault.js b/browser/components/urlbar/tests/quicksuggest/unit/test_quicksuggest_offlineDefault.js @@ -147,12 +147,10 @@ async function doTest({ locale, region, expectSuggestToBeEnabled }) { } // Set the region and locale, call the function, check the pref values. - await QuickSuggestTestUtils.withLocales({ - homeRegion: region, - locales: [locale], + await QuickSuggestTestUtils.withRegionAndLocale({ + region, + locale, callback: async () => { - await QuickSuggest._test_reset(); - for (let [name, value] of Object.entries(expectedPrefs)) { // Check the default-branch value. Assert.strictEqual( diff --git a/browser/components/urlbar/tests/quicksuggest/unit/test_quicksuggest_remoteSettingsFilter.js b/browser/components/urlbar/tests/quicksuggest/unit/test_quicksuggest_remoteSettingsFilter.js @@ -241,19 +241,14 @@ add_task(async function () { async function doTests({ locales, homeRegion, tests }) { for (let locale of locales) { - // Disable and reenable Suggest so its store is recreated with the - // appropriate remote settings app context for the locale and region. - info("Disabling Suggest: " + JSON.stringify({ locales, homeRegion })); - UrlbarPrefs.set("quicksuggest.enabled", false); - - await QuickSuggestTestUtils.withLocales({ - homeRegion, - locales: [locale], + await QuickSuggestTestUtils.withRegionAndLocale({ + locale, + region: homeRegion, + // AMP and Wikipedia suggestions are not enabled by default for all + // regions/locales in this test, so don't reset Suggest so that they + // remain enabled rather than being set according to region/locale. + skipSuggestReset: true, callback: async () => { - info("Reenabling Suggest: " + JSON.stringify({ locale, homeRegion })); - UrlbarPrefs.set("quicksuggest.enabled", true); - await QuickSuggestTestUtils.forceSync(); - for (let { expected, queries } of tests) { for (let query of queries) { info( diff --git a/browser/components/urlbar/tests/quicksuggest/unit/test_weather.js b/browser/components/urlbar/tests/quicksuggest/unit/test_weather.js @@ -298,8 +298,12 @@ async function doLocaleTest({ shouldRunTask, osUnit, unitsByLocale }) { // Check locales. for (let [locale, temperatureUnit] of Object.entries(unitsByLocale)) { - await QuickSuggestTestUtils.withLocales({ - locales: [locale], + await QuickSuggestTestUtils.withRegionAndLocale({ + locale, + // Weather suggestions are not enabled by default for all regions/locale + // combinations in this test, so don't reset Suggest so that they remain + // enabled rather than being set according to region/locale. + skipSuggestReset: true, callback: async () => { info("Checking locale: " + locale); await check_results({ @@ -445,9 +449,13 @@ add_task(async function queryOutsideNorthAmerica_clientOutsideNorthAmerica() { }); async function doRegionTest({ homeRegion, locale, query, expectedTitleL10n }) { - await QuickSuggestTestUtils.withLocales({ - homeRegion, - locales: [locale], + await QuickSuggestTestUtils.withRegionAndLocale({ + locale, + region: homeRegion, + // Weather suggestions are not enabled by default for all regions/locale + // combinations in this test, so don't reset Suggest so that they remain + // enabled rather than being set according to region/locale. + skipSuggestReset: true, callback: async () => { info( "Doing region test: " + JSON.stringify({ homeRegion, locale, query })