test_langPackMatcher.js (8550B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 const { getAddonAndLocalAPIsMocker } = ChromeUtils.importESModule( 6 "resource://testing-common/LangPackMatcherTestUtils.sys.mjs" 7 ); 8 const { LangPackMatcher } = ChromeUtils.importESModule( 9 "resource://gre/modules/LangPackMatcher.sys.mjs" 10 ); 11 const { sinon } = ChromeUtils.importESModule( 12 "resource://testing-common/Sinon.sys.mjs" 13 ); 14 15 const sandbox = sinon.createSandbox(); 16 const mockAddonAndLocaleAPIs = getAddonAndLocalAPIsMocker(this, sandbox); 17 18 add_task(function initSandbox() { 19 registerCleanupFunction(() => { 20 sandbox.restore(); 21 }); 22 }); 23 24 add_task(function test_appLocaleLanguageMismatch() { 25 sandbox.restore(); 26 mockAddonAndLocaleAPIs({ 27 systemLocale: "es-ES", 28 appLocale: "en-US", 29 }); 30 31 deepEqual(LangPackMatcher.getAppAndSystemLocaleInfo(), { 32 systemLocaleRaw: "es-ES", 33 systemLocale: { baseName: "es-ES", language: "es", region: "ES" }, 34 appLocaleRaw: "en-US", 35 appLocale: { baseName: "en-US", language: "en", region: "US" }, 36 matchType: "language-mismatch", 37 canLiveReload: true, 38 displayNames: { 39 systemLanguage: "Español (ES)", 40 appLanguage: "English (US)", 41 }, 42 }); 43 }); 44 45 add_task(function test_appLocaleRegionMismatch() { 46 sandbox.restore(); 47 mockAddonAndLocaleAPIs({ 48 sandbox, 49 systemLocale: "en-CA", 50 appLocale: "en-US", 51 }); 52 53 deepEqual(LangPackMatcher.getAppAndSystemLocaleInfo(), { 54 systemLocaleRaw: "en-CA", 55 systemLocale: { baseName: "en-CA", language: "en", region: "CA" }, 56 appLocaleRaw: "en-US", 57 appLocale: { baseName: "en-US", language: "en", region: "US" }, 58 matchType: "region-mismatch", 59 canLiveReload: true, 60 displayNames: { 61 systemLanguage: "English (CA)", 62 appLanguage: "English (US)", 63 }, 64 }); 65 }); 66 67 add_task(function test_appLocaleScriptMismatch() { 68 sandbox.restore(); 69 // Script mismatch: 70 mockAddonAndLocaleAPIs({ 71 sandbox, 72 systemLocale: "zh-Hans-CN", 73 appLocale: "zh-CN", 74 }); 75 76 deepEqual(LangPackMatcher.getAppAndSystemLocaleInfo(), { 77 systemLocaleRaw: "zh-Hans-CN", 78 systemLocale: { baseName: "zh-Hans-CN", language: "zh", region: "CN" }, 79 appLocaleRaw: "zh-CN", 80 appLocale: { baseName: "zh-CN", language: "zh", region: "CN" }, 81 matchType: "match", 82 canLiveReload: true, 83 displayNames: { 84 systemLanguage: "Chinese (Hans, China)", 85 appLanguage: "简体中文", 86 }, 87 }); 88 }); 89 90 add_task(function test_appLocaleInvalidSystem() { 91 sandbox.restore(); 92 // Script mismatch: 93 mockAddonAndLocaleAPIs({ 94 sandbox, 95 systemLocale: "Not valid", 96 appLocale: "en-US", 97 }); 98 99 deepEqual(LangPackMatcher.getAppAndSystemLocaleInfo(), { 100 systemLocaleRaw: "Not valid", 101 systemLocale: null, 102 appLocaleRaw: "en-US", 103 appLocale: { baseName: "en-US", language: "en", region: "US" }, 104 matchType: "unknown", 105 canLiveReload: null, 106 displayNames: { systemLanguage: null, appLanguage: "English (US)" }, 107 }); 108 }); 109 110 add_task(function test_bidiSwitchDisabled() { 111 Services.prefs.setBoolPref( 112 "intl.multilingual.liveReloadBidirectional", 113 false 114 ); 115 sandbox.restore(); 116 // Script mismatch: 117 mockAddonAndLocaleAPIs({ 118 sandbox, 119 systemLocale: "ar-EG", 120 appLocale: "en-US", 121 }); 122 123 deepEqual(LangPackMatcher.getAppAndSystemLocaleInfo(), { 124 systemLocaleRaw: "ar-EG", 125 systemLocale: { baseName: "ar-EG", language: "ar", region: "EG" }, 126 appLocaleRaw: "en-US", 127 appLocale: { baseName: "en-US", language: "en", region: "US" }, 128 matchType: "language-mismatch", 129 canLiveReload: false, 130 displayNames: { 131 systemLanguage: "Arabic (Egypt)", 132 appLanguage: "English (US)", 133 }, 134 }); 135 Services.prefs.clearUserPref("intl.multilingual.liveReloadBidirectional"); 136 }); 137 138 add_task(async function test_bidiSwitchEnabled() { 139 Services.prefs.setBoolPref("intl.multilingual.liveReloadBidirectional", true); 140 sandbox.restore(); 141 // Script mismatch: 142 mockAddonAndLocaleAPIs({ 143 sandbox, 144 systemLocale: "ar-EG", 145 appLocale: "en-US", 146 }); 147 148 deepEqual(LangPackMatcher.getAppAndSystemLocaleInfo(), { 149 systemLocaleRaw: "ar-EG", 150 systemLocale: { baseName: "ar-EG", language: "ar", region: "EG" }, 151 appLocaleRaw: "en-US", 152 appLocale: { baseName: "en-US", language: "en", region: "US" }, 153 matchType: "language-mismatch", 154 canLiveReload: true, 155 displayNames: { 156 systemLanguage: "Arabic (Egypt)", 157 appLanguage: "English (US)", 158 }, 159 }); 160 161 Services.prefs.clearUserPref("intl.multilingual.liveReloadBidirectional"); 162 }); 163 164 function shuffle(array) { 165 return array 166 .map(value => ({ value, sort: Math.random() })) 167 .sort((a, b) => a.sort - b.sort) 168 .map(({ value }) => value); 169 } 170 171 add_task(async function test_negotiateLangPacks() { 172 const negotiations = [ 173 { 174 // Exact match found. 175 systemLocale: "en-US", 176 availableLangPacks: ["en", "en-US", "zh", "zh-CN", "zh-Hans-CN"], 177 expectedLangPack: "en-US", 178 expectedDisplayName: "English (US)", 179 }, 180 { 181 // Region-less match. 182 systemLocale: "en-CA", 183 availableLangPacks: ["en", "en-US", "zh", "zh-CN", "zh-Hans-CN"], 184 expectedLangPack: "en", 185 expectedDisplayName: "English", 186 }, 187 { 188 // Fallback to a different region. 189 systemLocale: "en-CA", 190 availableLangPacks: ["en-US", "zh", "zh-CN", "zh-Hans-CN"], 191 expectedLangPack: "en-US", 192 expectedDisplayName: "English (US)", 193 }, 194 { 195 // Match with a script. zh-Hans-CN is the locale used with simplified 196 // Chinese scripts, while zh-CN uses the Latin script. 197 systemLocale: "zh-Hans-CN", 198 availableLangPacks: ["en", "en-US", "zh", "zh-CN", "zh-TW", "zh-Hans-CN"], 199 expectedLangPack: "zh-Hans-CN", 200 expectedDisplayName: "Chinese (Hans, China)", 201 }, 202 { 203 // Match excluding script but matching region. zh-Hant-TW should 204 // match zh-TW before zh-CN. 205 systemLocale: "zh-Hant-TW", 206 availableLangPacks: ["en", "zh", "zh-CN", "zh-TW"], 207 expectedLangPack: "zh-TW", 208 expectedDisplayName: "正體中文", 209 }, 210 { 211 // No reasonable match could be found. 212 systemLocale: "tlh", // Klingon 213 availableLangPacks: ["en", "en-US", "zh", "zh-CN", "zh-Hans-CN"], 214 expectedLangPack: null, 215 expectedDisplayName: null, 216 }, 217 { 218 // Weird, but valid locale identifiers. 219 systemLocale: "en-US-u-hc-h23-ca-islamic-civil-ss-true", 220 availableLangPacks: ["en", "en-US", "zh", "zh-CN", "zh-Hans-CN"], 221 expectedLangPack: "en-US", 222 expectedDisplayName: "English (US)", 223 }, 224 { 225 // Invalid system locale 226 systemLocale: "Not valid", 227 availableLangPacks: ["en", "en-US", "zh", "zh-CN", "zh-Hans-CN"], 228 expectedLangPack: null, 229 expectedDisplayName: null, 230 }, 231 ]; 232 233 for (const { 234 systemLocale, 235 availableLangPacks, 236 expectedLangPack, 237 expectedDisplayName, 238 } of negotiations) { 239 sandbox.restore(); 240 const { resolveLangPacks } = mockAddonAndLocaleAPIs({ 241 sandbox, 242 systemLocale, 243 }); 244 245 const promise = LangPackMatcher.negotiateLangPackForLanguageMismatch(); 246 // Shuffle the order to ensure that this test doesn't require on ordering of the 247 // langpack responses. 248 resolveLangPacks(shuffle(availableLangPacks)); 249 const { langPack, langPackDisplayName } = await promise; 250 equal( 251 langPack?.target_locale, 252 expectedLangPack, 253 `Resolve the systemLocale "${systemLocale}" with available langpacks: ${JSON.stringify( 254 availableLangPacks 255 )}` 256 ); 257 equal( 258 langPackDisplayName, 259 expectedDisplayName, 260 "The display name matches." 261 ); 262 } 263 }); 264 265 add_task(async function test_ensureLangPackInstalled() { 266 sandbox.restore(); 267 const { resolveLangPacks, resolveInstaller } = mockAddonAndLocaleAPIs({ 268 sandbox, 269 systemLocale: "es-ES", 270 appLocale: "en-US", 271 }); 272 273 const negotiatePromise = 274 LangPackMatcher.negotiateLangPackForLanguageMismatch(); 275 resolveLangPacks(["es-ES"]); 276 const { langPack } = await negotiatePromise; 277 278 const installPromise1 = LangPackMatcher.ensureLangPackInstalled(langPack); 279 const installPromise2 = LangPackMatcher.ensureLangPackInstalled(langPack); 280 281 resolveInstaller(["fake langpack"]); 282 283 info("Ensure both installers resolve when called twice in a row."); 284 await installPromise1; 285 await installPromise2; 286 ok(true, "Both were called."); 287 });