test_l10nregistry.js (18862B)
1 /* Any copyrighequal dedicated to the Public Domain. 2 http://creativecommons.org/publicdomain/zero/1.0/ */ 3 4 const {setTimeout} = ChromeUtils.importESModule("resource://gre/modules/Timer.sys.mjs"); 5 6 const l10nReg = new L10nRegistry(); 7 8 add_task(function test_methods_presence() { 9 equal(typeof l10nReg.generateBundles, "function"); 10 equal(typeof l10nReg.generateBundlesSync, "function"); 11 equal(typeof l10nReg.getAvailableLocales, "function"); 12 equal(typeof l10nReg.registerSources, "function"); 13 equal(typeof l10nReg.removeSources, "function"); 14 equal(typeof l10nReg.updateSources, "function"); 15 }); 16 17 /** 18 * Test that passing empty resourceIds list works. 19 */ 20 add_task(async function test_empty_resourceids() { 21 const fs = []; 22 23 const source = L10nFileSource.createMock("test", "", ["en-US"], "/localization/{locale}", fs); 24 l10nReg.registerSources([source]); 25 26 const bundles = l10nReg.generateBundles(["en-US"], []); 27 28 const done = (await bundles.next()).done; 29 30 equal(done, true); 31 32 // cleanup 33 l10nReg.clearSources(); 34 }); 35 36 /** 37 * Test that passing empty sources list works. 38 */ 39 add_task(async function test_empty_sources() { 40 const fs = []; 41 const bundles = l10nReg.generateBundlesSync(["en-US"], fs); 42 43 const done = (await bundles.next()).done; 44 45 equal(done, true); 46 47 // cleanup 48 l10nReg.clearSources(); 49 }); 50 51 /** 52 * This test tests generation of a proper context for a single 53 * source scenario 54 */ 55 add_task(async function test_methods_calling() { 56 const fs = [ 57 { path: "/localization/en-US/browser/menu.ftl", source: "key = Value" } 58 ]; 59 const source = L10nFileSource.createMock("test", "", ["en-US"], "/localization/{locale}", fs); 60 l10nReg.registerSources([source]); 61 62 const bundles = l10nReg.generateBundles(["en-US"], ["/browser/menu.ftl"]); 63 64 const bundle = (await bundles.next()).value; 65 66 equal(bundle.hasMessage("key"), true); 67 68 // cleanup 69 l10nReg.clearSources(); 70 }); 71 72 /** 73 * This test verifies that the public methods return expected values 74 * for the single source scenario 75 */ 76 add_task(async function test_has_one_source() { 77 const fs = [ 78 {path: "./app/data/locales/en-US/test.ftl", source: "key = value en-US"} 79 ]; 80 let oneSource = L10nFileSource.createMock("app", "", ["en-US"], "./app/data/locales/{locale}/", fs); 81 l10nReg.registerSources([oneSource]); 82 83 84 // has one source 85 86 equal(l10nReg.getSourceNames().length, 1); 87 equal(l10nReg.hasSource("app"), true); 88 89 90 // returns a single context 91 92 let bundles = l10nReg.generateBundles(["en-US"], ["test.ftl"]); 93 let bundle0 = (await bundles.next()).value; 94 equal(bundle0.hasMessage("key"), true); 95 96 equal((await bundles.next()).done, true); 97 98 99 // returns no contexts for missing locale 100 101 bundles = l10nReg.generateBundles(["pl"], ["test.ftl"]); 102 103 equal((await bundles.next()).done, true); 104 105 // cleanup 106 l10nReg.clearSources(); 107 }); 108 109 /** 110 * This test verifies that public methods return expected values 111 * for the dual source scenario. 112 */ 113 add_task(async function test_has_two_sources() { 114 const fs = [ 115 { path: "./platform/data/locales/en-US/test.ftl", source: "key = platform value" }, 116 { path: "./app/data/locales/pl/test.ftl", source: "key = app value" } 117 ]; 118 let oneSource = L10nFileSource.createMock("platform", "", ["en-US"], "./platform/data/locales/{locale}/", fs); 119 let secondSource = L10nFileSource.createMock("app", "", ["pl"], "./app/data/locales/{locale}/", fs); 120 l10nReg.registerSources([oneSource, secondSource]); 121 122 // has two sources 123 124 equal(l10nReg.getSourceNames().length, 2); 125 equal(l10nReg.hasSource("app"), true); 126 equal(l10nReg.hasSource("platform"), true); 127 128 129 // returns correct contexts for en-US 130 131 let bundles = l10nReg.generateBundles(["en-US"], ["test.ftl"]); 132 let bundle0 = (await bundles.next()).value; 133 134 equal(bundle0.hasMessage("key"), true); 135 let msg = bundle0.getMessage("key"); 136 equal(bundle0.formatPattern(msg.value), "platform value"); 137 138 equal((await bundles.next()).done, true); 139 140 141 // returns correct contexts for [pl, en-US] 142 143 bundles = l10nReg.generateBundles(["pl", "en-US"], ["test.ftl"]); 144 bundle0 = (await bundles.next()).value; 145 equal(bundle0.locales[0], "pl"); 146 equal(bundle0.hasMessage("key"), true); 147 let msg0 = bundle0.getMessage("key"); 148 equal(bundle0.formatPattern(msg0.value), "app value"); 149 150 let bundle1 = (await bundles.next()).value; 151 equal(bundle1.locales[0], "en-US"); 152 equal(bundle1.hasMessage("key"), true); 153 let msg1 = bundle1.getMessage("key"); 154 equal(bundle1.formatPattern(msg1.value), "platform value"); 155 156 equal((await bundles.next()).done, true); 157 158 // cleanup 159 l10nReg.clearSources(); 160 }); 161 162 /** 163 * This test verifies that behavior specific to the L10nFileSource 164 * works correctly. 165 * 166 * In particular it tests that L10nFileSource correctly returns 167 * missing files as `false` instead of `undefined`. 168 */ 169 add_task(function test_indexed() { 170 let oneSource = new L10nFileSource("langpack-pl", "app", ["pl"], "/data/locales/{locale}/", {}, [ 171 "/data/locales/pl/test.ftl", 172 ]); 173 equal(oneSource.hasFile("pl", "test.ftl"), "present"); 174 equal(oneSource.hasFile("pl", "missing.ftl"), "missing"); 175 }); 176 177 /** 178 * This test checks if the correct order of contexts is used for 179 * scenarios where a new file source is added on top of the default one. 180 */ 181 add_task(async function test_override() { 182 const fs = [ 183 { path: "/app/data/locales/pl/test.ftl", source: "key = value" }, 184 { path: "/data/locales/pl/test.ftl", source: "key = addon value"}, 185 ]; 186 let fileSource = L10nFileSource.createMock("app", "", ["pl"], "/app/data/locales/{locale}/", fs); 187 let oneSource = L10nFileSource.createMock("langpack-pl", "", ["pl"], "/data/locales/{locale}/", fs); 188 l10nReg.registerSources([fileSource, oneSource]); 189 190 equal(l10nReg.getSourceNames().length, 2); 191 equal(l10nReg.hasSource("langpack-pl"), true); 192 193 let bundles = l10nReg.generateBundles(["pl"], ["test.ftl"]); 194 let bundle0 = (await bundles.next()).value; 195 equal(bundle0.locales[0], "pl"); 196 equal(bundle0.hasMessage("key"), true); 197 let msg0 = bundle0.getMessage("key"); 198 equal(bundle0.formatPattern(msg0.value), "addon value"); 199 200 let bundle1 = (await bundles.next()).value; 201 equal(bundle1.locales[0], "pl"); 202 equal(bundle1.hasMessage("key"), true); 203 let msg1 = bundle1.getMessage("key"); 204 equal(bundle1.formatPattern(msg1.value), "value"); 205 206 equal((await bundles.next()).done, true); 207 208 // cleanup 209 l10nReg.clearSources(); 210 }); 211 212 /** 213 * This test verifies that new contexts are returned 214 * after source update. 215 */ 216 add_task(async function test_updating() { 217 const fs = [ 218 { path: "/data/locales/pl/test.ftl", source: "key = value" } 219 ]; 220 let oneSource = L10nFileSource.createMock("langpack-pl", "", ["pl"], "/data/locales/{locale}/", fs); 221 l10nReg.registerSources([oneSource]); 222 223 let bundles = l10nReg.generateBundles(["pl"], ["test.ftl"]); 224 let bundle0 = (await bundles.next()).value; 225 equal(bundle0.locales[0], "pl"); 226 equal(bundle0.hasMessage("key"), true); 227 let msg0 = bundle0.getMessage("key"); 228 equal(bundle0.formatPattern(msg0.value), "value"); 229 230 231 const newSource = L10nFileSource.createMock("langpack-pl", "", ["pl"], "/data/locales/{locale}/", [ 232 { path: "/data/locales/pl/test.ftl", source: "key = new value" } 233 ]); 234 l10nReg.updateSources([newSource]); 235 236 equal(l10nReg.getSourceNames().length, 1); 237 bundles = l10nReg.generateBundles(["pl"], ["test.ftl"]); 238 bundle0 = (await bundles.next()).value; 239 msg0 = bundle0.getMessage("key"); 240 equal(bundle0.formatPattern(msg0.value), "new value"); 241 242 // cleanup 243 l10nReg.clearSources(); 244 }); 245 246 /** 247 * This test verifies that generated contexts return correct values 248 * after sources are being removed. 249 */ 250 add_task(async function test_removing() { 251 const fs = [ 252 { path: "/app/data/locales/pl/test.ftl", source: "key = value" }, 253 { path: "/data/locales/pl/test.ftl", source: "key = addon value" }, 254 ]; 255 256 let fileSource = L10nFileSource.createMock("app", "", ["pl"], "/app/data/locales/{locale}/", fs); 257 let oneSource = L10nFileSource.createMock("langpack-pl", "", ["pl"], "/data/locales/{locale}/", fs); 258 l10nReg.registerSources([fileSource, oneSource]); 259 260 equal(l10nReg.getSourceNames().length, 2); 261 equal(l10nReg.hasSource("langpack-pl"), true); 262 263 let bundles = l10nReg.generateBundles(["pl"], ["test.ftl"]); 264 let bundle0 = (await bundles.next()).value; 265 equal(bundle0.locales[0], "pl"); 266 equal(bundle0.hasMessage("key"), true); 267 let msg0 = bundle0.getMessage("key"); 268 equal(bundle0.formatPattern(msg0.value), "addon value"); 269 270 let bundle1 = (await bundles.next()).value; 271 equal(bundle1.locales[0], "pl"); 272 equal(bundle1.hasMessage("key"), true); 273 let msg1 = bundle1.getMessage("key"); 274 equal(bundle1.formatPattern(msg1.value), "value"); 275 276 equal((await bundles.next()).done, true); 277 278 // Remove langpack 279 280 l10nReg.removeSources(["langpack-pl"]); 281 282 equal(l10nReg.getSourceNames().length, 1); 283 equal(l10nReg.hasSource("langpack-pl"), false); 284 285 bundles = l10nReg.generateBundles(["pl"], ["test.ftl"]); 286 bundle0 = (await bundles.next()).value; 287 equal(bundle0.locales[0], "pl"); 288 equal(bundle0.hasMessage("key"), true); 289 msg0 = bundle0.getMessage("key"); 290 equal(bundle0.formatPattern(msg0.value), "value"); 291 292 equal((await bundles.next()).done, true); 293 294 // Remove app source 295 296 l10nReg.removeSources(["app"]); 297 298 equal(l10nReg.getSourceNames().length, 0); 299 300 bundles = l10nReg.generateBundles(["pl"], ["test.ftl"]); 301 equal((await bundles.next()).done, true); 302 303 // cleanup 304 l10nReg.clearSources(); 305 }); 306 307 /** 308 * This test verifies that the logic works correctly when there's a missing 309 * file in the FileSource scenario. 310 */ 311 add_task(async function test_missing_file() { 312 const fs = [ 313 { path: "./app/data/locales/en-US/test.ftl", source: "key = value en-US" }, 314 { path: "./platform/data/locales/en-US/test.ftl", source: "key = value en-US" }, 315 { path: "./platform/data/locales/en-US/test2.ftl", source: "key2 = value2 en-US" }, 316 ]; 317 let oneSource = L10nFileSource.createMock("app", "", ["en-US"], "./app/data/locales/{locale}/", fs); 318 let twoSource = L10nFileSource.createMock("platform", "", ["en-US"], "./platform/data/locales/{locale}/", fs); 319 l10nReg.registerSources([oneSource, twoSource]); 320 321 // has two sources 322 323 equal(l10nReg.getSourceNames().length, 2); 324 equal(l10nReg.hasSource("app"), true); 325 equal(l10nReg.hasSource("platform"), true); 326 327 328 // returns a single context 329 330 let bundles = l10nReg.generateBundles(["en-US"], ["test.ftl", "test2.ftl"]); 331 332 // First permutation: 333 // [platform, platform] - both present 334 let bundle1 = (await bundles.next()); 335 equal(bundle1.value.hasMessage("key"), true); 336 337 // Second permutation skipped: 338 // [platform, app] - second missing 339 // Third permutation: 340 // [app, platform] - both present 341 let bundle2 = (await bundles.next()); 342 equal(bundle2.value.hasMessage("key"), true); 343 344 // Fourth permutation skipped: 345 // [app, app] - second missing 346 equal((await bundles.next()).done, true); 347 348 // cleanup 349 l10nReg.clearSources(); 350 }); 351 352 add_task(async function test_hasSource() { 353 equal(l10nReg.hasSource("nonsense"), false, "Non-existing source doesn't exist"); 354 equal(l10nReg.hasSource("app"), false, "hasSource returns true before registering a source"); 355 let oneSource = new L10nFileSource("app", "app", ["en-US"], "/{locale}/"); 356 l10nReg.registerSources([oneSource]); 357 equal(l10nReg.hasSource("app"), true, "hasSource returns true after registering a source"); 358 l10nReg.clearSources(); 359 }); 360 361 /** 362 * This test verifies that we handle correctly a scenario where a source 363 * is being removed while the iterator operates. 364 */ 365 add_task(async function test_remove_source_mid_iter_cycle() { 366 const fs = [ 367 { path: "./platform/data/locales/en-US/test.ftl", source: "key = platform value" }, 368 { path: "./app/data/locales/pl/test.ftl", source: "key = app value" }, 369 ]; 370 let oneSource = L10nFileSource.createMock("platform", "", ["en-US"], "./platform/data/locales/{locale}/", fs); 371 let secondSource = L10nFileSource.createMock("app", "", ["pl"], "./app/data/locales/{locale}/", fs); 372 l10nReg.registerSources([oneSource, secondSource]); 373 374 let bundles = l10nReg.generateBundles(["en-US", "pl"], ["test.ftl"]); 375 376 let bundle0 = await bundles.next(); 377 378 // The registry has a copy of the file sources, so it will be unaffected. 379 l10nReg.removeSources(["app"]); 380 381 let bundle1 = await bundles.next(); 382 383 equal((await bundles.next()).done, true); 384 385 // cleanup 386 l10nReg.clearSources(); 387 }); 388 389 add_task(async function test_metasources() { 390 let fs = [ 391 { path: "/localization/en-US/browser/menu1.ftl", source: "key1 = Value" }, 392 { path: "/localization/en-US/browser/menu2.ftl", source: "key2 = Value" }, 393 { path: "/localization/en-US/browser/menu3.ftl", source: "key3 = Value" }, 394 { path: "/localization/en-US/browser/menu4.ftl", source: "key4 = Value" }, 395 { path: "/localization/en-US/browser/menu5.ftl", source: "key5 = Value" }, 396 { path: "/localization/en-US/browser/menu6.ftl", source: "key6 = Value" }, 397 { path: "/localization/en-US/browser/menu7.ftl", source: "key7 = Value" }, 398 { path: "/localization/en-US/browser/menu8.ftl", source: "key8 = Value" }, 399 ]; 400 401 const browser = L10nFileSource.createMock("browser", "app", ["en-US"], "/localization/{locale}", fs); 402 const toolkit = L10nFileSource.createMock("toolkit", "app", ["en-US"], "/localization/{locale}", fs); 403 const browser2 = L10nFileSource.createMock("browser2", "langpack", ["en-US"], "/localization/{locale}", fs); 404 const toolkit2 = L10nFileSource.createMock("toolkit2", "langpack", ["en-US"], "/localization/{locale}", fs); 405 l10nReg.registerSources([toolkit, browser, toolkit2, browser2]); 406 407 let res = [ 408 "/browser/menu1.ftl", 409 "/browser/menu2.ftl", 410 "/browser/menu3.ftl", 411 "/browser/menu4.ftl", 412 "/browser/menu5.ftl", 413 "/browser/menu6.ftl", 414 "/browser/menu7.ftl", 415 {path: "/browser/menu8.ftl", optional: false}, 416 ]; 417 418 const bundles = l10nReg.generateBundles(["en-US"], res); 419 420 let nbundles = 0; 421 while (!(await bundles.next()).done) { 422 nbundles += 1; 423 } 424 425 // If metasources are working properly, we'll generate 2^8 = 256 bundles for 426 // each metasource giving 512 bundles in total. Otherwise, we generate 427 // 4^8 = 65536 bundles. 428 equal(nbundles, 512); 429 430 // cleanup 431 l10nReg.clearSources(); 432 }); 433 434 /** 435 * This test verifies that when a required resource is missing for a locale, 436 * we do not produce a bundle for that locale. 437 */ 438 add_task(async function test_missing_required_resource() { 439 const fs = [ 440 { path: "./platform/data/locales/en-US/test.ftl", source: "test-key = en-US value" }, 441 { path: "./platform/data/locales/pl/missing-in-en-US.ftl", source: "missing-key = pl value" }, 442 { path: "./platform/data/locales/pl/test.ftl", source: "test-key = pl value" }, 443 ]; 444 let source = L10nFileSource.createMock("platform", "", ["en-US", "pl"], "./platform/data/locales/{locale}/", fs); 445 l10nReg.registerSources([source]); 446 447 equal(l10nReg.getSourceNames().length, 1); 448 equal(l10nReg.hasSource("platform"), true); 449 450 451 // returns correct contexts for [en-US, pl] 452 453 let bundles = l10nReg.generateBundlesSync(["en-US", "pl"], ["test.ftl", "missing-in-en-US.ftl"]); 454 let bundle0 = (await bundles.next()).value; 455 456 equal(bundle0.locales[0], "pl"); 457 equal(bundle0.hasMessage("test-key"), true); 458 equal(bundle0.hasMessage("missing-key"), true); 459 460 let msg0 = bundle0.getMessage("test-key"); 461 equal(bundle0.formatPattern(msg0.value), "pl value"); 462 463 let msg1 = bundle0.getMessage("missing-key"); 464 equal(bundle0.formatPattern(msg1.value), "pl value"); 465 466 equal((await bundles.next()).done, true); 467 468 469 // returns correct contexts for [pl, en-US] 470 471 bundles = l10nReg.generateBundlesSync(["pl", "en-US"], ["test.ftl", {path: "missing-in-en-US.ftl", optional: false}]); 472 bundle0 = (await bundles.next()).value; 473 474 equal(bundle0.locales[0], "pl"); 475 equal(bundle0.hasMessage("test-key"), true); 476 477 msg0 = bundle0.getMessage("test-key"); 478 equal(bundle0.formatPattern(msg0.value), "pl value"); 479 480 msg1 = bundle0.getMessage("missing-key"); 481 equal(bundle0.formatPattern(msg1.value), "pl value"); 482 483 equal((await bundles.next()).done, true); 484 485 // cleanup 486 l10nReg.clearSources(); 487 }); 488 489 /** 490 * This test verifies that when an optional resource is missing, we continue 491 * to produce a bundle for that locale. The bundle will have missing entries 492 * with regard to the missing optional resource. 493 */ 494 add_task(async function test_missing_optional_resource() { 495 const fs = [ 496 { path: "./platform/data/locales/en-US/test.ftl", source: "test-key = en-US value" }, 497 { path: "./platform/data/locales/pl/missing-in-en-US.ftl", source: "missing-key = pl value" }, 498 { path: "./platform/data/locales/pl/test.ftl", source: "test-key = pl value" }, 499 ]; 500 let source = L10nFileSource.createMock("platform", "", ["en-US", "pl"], "./platform/data/locales/{locale}/", fs); 501 l10nReg.registerSources([source]); 502 503 equal(l10nReg.getSourceNames().length, 1); 504 equal(l10nReg.hasSource("platform"), true); 505 506 507 // returns correct contexts for [en-US, pl] 508 509 let bundles = l10nReg.generateBundlesSync(["en-US", "pl"], ["test.ftl", { path: "missing-in-en-US.ftl", optional: true }]); 510 let bundle0 = (await bundles.next()).value; 511 512 equal(bundle0.locales[0], "en-US"); 513 equal(bundle0.hasMessage("test-key"), true); 514 equal(bundle0.hasMessage("missing-key"), false); 515 516 let msg0 = bundle0.getMessage("test-key"); 517 equal(bundle0.formatPattern(msg0.value), "en-US value"); 518 519 equal(bundle0.getMessage("missing-key"), null); 520 521 let bundle1 = (await bundles.next()).value; 522 523 equal(bundle1.locales[0], "pl"); 524 equal(bundle1.hasMessage("test-key"), true); 525 equal(bundle1.hasMessage("missing-key"), true); 526 527 msg0 = bundle1.getMessage("test-key"); 528 equal(bundle1.formatPattern(msg0.value), "pl value"); 529 530 msg1 = bundle1.getMessage("missing-key"); 531 equal(bundle1.formatPattern(msg1.value), "pl value"); 532 533 equal((await bundles.next()).done, true); 534 535 // returns correct contexts for [pl, en-US] 536 537 bundles = l10nReg.generateBundlesSync(["pl", "en-US"], ["test.ftl", { path: "missing-in-en-US.ftl", optional: true }]); 538 bundle0 = (await bundles.next()).value; 539 540 equal(bundle0.locales[0], "pl"); 541 equal(bundle0.hasMessage("test-key"), true); 542 equal(bundle0.hasMessage("missing-key"), true); 543 544 msg0 = bundle0.getMessage("test-key"); 545 equal(bundle0.formatPattern(msg0.value), "pl value"); 546 547 msg1 = bundle0.getMessage("missing-key"); 548 equal(bundle0.formatPattern(msg1.value), "pl value"); 549 550 bundle1 = (await bundles.next()).value; 551 552 equal(bundle1.locales[0], "en-US"); 553 equal(bundle1.hasMessage("test-key"), true); 554 equal(bundle1.hasMessage("missing-key"), false); 555 556 msg0 = bundle1.getMessage("test-key"); 557 equal(bundle1.formatPattern(msg0.value), "en-US value"); 558 559 equal(bundle1.getMessage("missing-key"), null); 560 561 // cleanup 562 l10nReg.clearSources(); 563 });