browser_browser_languages_subdialog.js (33693B)
1 /* Any copyright is dedicated to the Public Domain. 2 * http://creativecommons.org/publicdomain/zero/1.0/ */ 3 4 requestLongerTimeout(2); 5 6 const { AddonTestUtils } = ChromeUtils.importESModule( 7 "resource://testing-common/AddonTestUtils.sys.mjs" 8 ); 9 10 AddonTestUtils.initMochitest(this); 11 12 const BROWSER_LANGUAGES_URL = 13 "chrome://browser/content/preferences/dialogs/browserLanguages.xhtml"; 14 const DICTIONARY_ID_PL = "pl@dictionaries.addons.mozilla.org"; 15 const TELEMETRY_CATEGORY = "intl.ui.browserLanguage"; 16 17 function langpackId(locale) { 18 return `langpack-${locale}@firefox.mozilla.org`; 19 } 20 21 function getManifestData(locale, version = "2.0") { 22 return { 23 langpack_id: locale, 24 name: `${locale} Language Pack`, 25 description: `${locale} Language pack`, 26 languages: { 27 [locale]: { 28 chrome_resources: { 29 branding: `browser/chrome/${locale}/locale/branding/`, 30 }, 31 version: "1", 32 }, 33 }, 34 browser_specific_settings: { 35 gecko: { 36 id: langpackId(locale), 37 strict_min_version: AppConstants.MOZ_APP_VERSION, 38 strict_max_version: AppConstants.MOZ_APP_VERSION, 39 }, 40 }, 41 version, 42 manifest_version: 2, 43 sources: { 44 browser: { 45 base_path: "browser/", 46 }, 47 }, 48 author: "Mozilla", 49 }; 50 } 51 52 let testLocales = ["fr", "pl", "he"]; 53 let testLangpacks; 54 55 function createLangpack(locale, version) { 56 return AddonTestUtils.createTempXPIFile({ 57 "manifest.json": getManifestData(locale, version), 58 [`browser/${locale}/branding/brand.ftl`]: "-brand-short-name = Firefox", 59 }); 60 } 61 62 function createTestLangpacks() { 63 if (!testLangpacks) { 64 testLangpacks = Promise.all( 65 testLocales.map(async locale => [locale, await createLangpack(locale)]) 66 ); 67 } 68 return testLangpacks; 69 } 70 71 function createLocaleResult(target_locale, url) { 72 return { 73 guid: langpackId(target_locale), 74 type: "language", 75 target_locale, 76 current_compatible_version: { 77 files: [ 78 { 79 platform: "all", 80 url, 81 }, 82 ], 83 }, 84 }; 85 } 86 87 async function createLanguageToolsFile() { 88 let langpacks = await createTestLangpacks(); 89 let results = langpacks.map(([locale, file]) => 90 createLocaleResult(locale, Services.io.newFileURI(file).spec) 91 ); 92 93 let filename = "language-tools.json"; 94 let files = { [filename]: { results } }; 95 let tempdir = AddonTestUtils.tempDir.clone(); 96 let dir = await AddonTestUtils.promiseWriteFilesToDir(tempdir.path, files); 97 dir.append(filename); 98 99 return dir; 100 } 101 102 async function createDictionaryBrowseResults() { 103 let testDir = gTestPath.substr(0, gTestPath.lastIndexOf("/")); 104 let dictionaryPath = testDir + "/addons/pl-dictionary.xpi"; 105 let filename = "dictionaries.json"; 106 let response = { 107 page_size: 25, 108 page_count: 1, 109 count: 1, 110 results: [ 111 { 112 current_version: { 113 id: 1823648, 114 compatibility: { 115 firefox: { max: "9999", min: "4.0" }, 116 }, 117 files: [ 118 { 119 platform: "all", 120 url: dictionaryPath, 121 }, 122 ], 123 version: "1.0.20160228", 124 }, 125 default_locale: "pl", 126 description: "Polish spell-check", 127 guid: DICTIONARY_ID_PL, 128 name: "Polish Dictionary", 129 slug: "polish-spellchecker-dictionary", 130 status: "public", 131 summary: "Polish dictionary", 132 type: "dictionary", 133 }, 134 ], 135 }; 136 137 let files = { [filename]: response }; 138 let dir = await AddonTestUtils.promiseWriteFilesToDir( 139 AddonTestUtils.tempDir.path, 140 files 141 ); 142 dir.append(filename); 143 144 return dir; 145 } 146 147 function assertLocaleOrder(list, locales, selectedLocale) { 148 is( 149 list.itemCount, 150 locales.split(",").length, 151 "The right number of locales are in the list" 152 ); 153 is( 154 Array.from(list.children) 155 .map(child => child.value) 156 .join(","), 157 locales, 158 "The listed locales are in order" 159 ); 160 is( 161 list.selectedItem.value, 162 selectedLocale, 163 "The selected item locale matches" 164 ); 165 } 166 167 function assertAvailableLocales(list, locales) { 168 let items = Array.from(list.menupopup.children); 169 let listLocales = items.filter(item => item.value && item.value != "search"); 170 is( 171 listLocales.length, 172 locales.length, 173 "The right number of locales are available" 174 ); 175 is( 176 listLocales 177 .map(item => item.value) 178 .sort() 179 .join(","), 180 locales.sort().join(","), 181 "The available locales match" 182 ); 183 is(items[0].getAttribute("class"), "label-item", "The first row is a label"); 184 } 185 186 function getDialogId(dialogDoc) { 187 return dialogDoc.ownerGlobal.arguments[0].telemetryId; 188 } 189 190 function assertTelemetryRecorded(events) { 191 let snapshot = Services.telemetry.snapshotEvents( 192 Ci.nsITelemetry.DATASET_PRERELEASE_CHANNELS, 193 true 194 ); 195 196 // Make sure we got some data. 197 ok( 198 snapshot.parent && !!snapshot.parent.length, 199 "Got parent telemetry events in the snapshot" 200 ); 201 202 // Only look at the related events after stripping the timestamp and category. 203 let relatedEvents = snapshot.parent 204 .filter(([, category]) => category == TELEMETRY_CATEGORY) 205 .map(relatedEvent => relatedEvent.slice(2, 6)); 206 207 // Events are now an array of: method, object[, value[, extra]] as expected. 208 Assert.deepEqual(relatedEvents, events, "The events are recorded correctly"); 209 } 210 211 async function selectLocale(localeCode, available, selected, dialogDoc) { 212 let [locale] = Array.from(available.menupopup.children).filter( 213 item => item.value == localeCode 214 ); 215 available.selectedItem = locale; 216 217 // Get ready for the selected list to change. 218 let added = waitForMutation(selected, { childList: true }, target => 219 Array.from(target.children).some(el => el.value == localeCode) 220 ); 221 222 // Add the locale. 223 dialogDoc.getElementById("add").doCommand(); 224 225 // Wait for the list to update. 226 await added; 227 } 228 229 // Select a locale from the list of already added locales. 230 function selectAddedLocale(localeCode, selected) { 231 selected.selectedItem = selected.querySelector(`[value="${localeCode}"]`); 232 } 233 234 async function openDialog(doc, search = false) { 235 let dialogLoaded = promiseLoadSubDialog(BROWSER_LANGUAGES_URL); 236 if (search) { 237 doc.getElementById("primaryBrowserLocaleSearch").doCommand(); 238 doc.getElementById("primaryBrowserLocale").menupopup.hidePopup(); 239 } else { 240 doc.getElementById("manageBrowserLanguagesButton").doCommand(); 241 } 242 let dialogWin = await dialogLoaded; 243 let dialogDoc = dialogWin.document; 244 return { 245 dialog: dialogDoc.getElementById("BrowserLanguagesDialog"), 246 dialogDoc, 247 available: dialogDoc.getElementById("availableLocales"), 248 selected: dialogDoc.getElementById("selectedLocales"), 249 }; 250 } 251 252 add_task(async function testDisabledBrowserLanguages() { 253 let langpacksFile = await createLanguageToolsFile(); 254 let langpacksUrl = Services.io.newFileURI(langpacksFile).spec; 255 256 await SpecialPowers.pushPrefEnv({ 257 set: [ 258 ["intl.multilingual.enabled", true], 259 ["intl.multilingual.downloadEnabled", true], 260 ["intl.multilingual.liveReload", false], 261 ["intl.multilingual.liveReloadBidirectional", false], 262 ["intl.locale.requested", "en-US,pl,he,de"], 263 ["extensions.langpacks.signatures.required", false], 264 ["extensions.getAddons.langpacks.url", langpacksUrl], 265 ], 266 }); 267 268 // Install an old pl langpack. 269 let oldLangpack = await createLangpack("pl", "1.0"); 270 await AddonTestUtils.promiseInstallFile(oldLangpack); 271 272 // Install all the other available langpacks. 273 let pl; 274 let langpacks = await createTestLangpacks(); 275 let addons = await Promise.all( 276 langpacks.map(async ([locale, file]) => { 277 if (locale == "pl") { 278 pl = await AddonManager.getAddonByID(langpackId("pl")); 279 // Disable pl so it's removed from selected. 280 await pl.disable(); 281 return pl; 282 } 283 let install = await AddonTestUtils.promiseInstallFile(file); 284 return install.addon; 285 }) 286 ); 287 288 await openPreferencesViaOpenPreferencesAPI("paneGeneral", { 289 leaveOpen: true, 290 }); 291 292 let doc = gBrowser.contentDocument; 293 let { dialogDoc, available, selected } = await openDialog(doc); 294 295 // pl is not selected since it's disabled. 296 is(pl.userDisabled, true, "pl is disabled"); 297 is(pl.version, "1.0", "pl is the old 1.0 version"); 298 assertLocaleOrder(selected, "en-US,he", "en-US"); 299 300 // Wait for the children menu to be populated. 301 await BrowserTestUtils.waitForCondition( 302 () => !!available.children.length, 303 "Children list populated" 304 ); 305 306 // Only fr is enabled and not selected, so it's the only locale available. 307 assertAvailableLocales(available, ["fr"]); 308 309 // Search for more languages. 310 available.menupopup.lastElementChild.doCommand(); 311 available.menupopup.hidePopup(); 312 await waitForMutation(available.menupopup, { childList: true }, () => 313 Array.from(available.menupopup.children).some( 314 locale => locale.value == "pl" 315 ) 316 ); 317 318 // pl is now available since it is available remotely. 319 assertAvailableLocales(available, ["fr", "pl"]); 320 321 let installId = null; 322 AddonTestUtils.promiseInstallEvent("onInstallEnded").then(([install]) => { 323 installId = install.installId; 324 }); 325 326 // Add pl. 327 await selectLocale("pl", available, selected, dialogDoc); 328 assertLocaleOrder(selected, "pl,en-US,he", "pl"); 329 330 // Find pl again since it's been upgraded. 331 pl = await AddonManager.getAddonByID(langpackId("pl")); 332 is(pl.userDisabled, false, "pl is now enabled"); 333 is(pl.version, "2.0", "pl is upgraded to version 2.0"); 334 335 let dialogId = getDialogId(dialogDoc); 336 ok(dialogId, "There's a dialogId"); 337 ok(installId, "There's an installId"); 338 339 await Promise.all(addons.map(addon => addon.uninstall())); 340 BrowserTestUtils.removeTab(gBrowser.selectedTab); 341 342 assertTelemetryRecorded([ 343 ["manage", "main", dialogId], 344 ["search", "dialog", dialogId], 345 ["add", "dialog", dialogId, { installId }], 346 347 // Cancel is recorded when the tab is closed. 348 ["cancel", "dialog", dialogId], 349 ]); 350 }); 351 352 add_task(async function testReorderingBrowserLanguages() { 353 await SpecialPowers.pushPrefEnv({ 354 set: [ 355 ["intl.multilingual.enabled", true], 356 ["intl.multilingual.downloadEnabled", true], 357 ["intl.multilingual.liveReload", false], 358 ["intl.multilingual.liveReloadBidirectional", false], 359 ["intl.locale.requested", "en-US,pl,he,de"], 360 ["extensions.langpacks.signatures.required", false], 361 ], 362 }); 363 364 // Install all the available langpacks. 365 let langpacks = await createTestLangpacks(); 366 let addons = await Promise.all( 367 langpacks.map(async ([, file]) => { 368 let install = await AddonTestUtils.promiseInstallFile(file); 369 return install.addon; 370 }) 371 ); 372 373 await openPreferencesViaOpenPreferencesAPI("paneGeneral", { 374 leaveOpen: true, 375 }); 376 377 let doc = gBrowser.contentDocument; 378 let messageBar = doc.getElementById("confirmBrowserLanguage"); 379 is(messageBar.hidden, true, "The message bar is hidden at first"); 380 381 // Open the dialog. 382 let { dialog, dialogDoc, selected } = await openDialog(doc); 383 let firstDialogId = getDialogId(dialogDoc); 384 385 // The initial order is set by the pref, filtered by available. 386 assertLocaleOrder(selected, "en-US,pl,he", "en-US"); 387 388 // Moving pl down changes the order. 389 selectAddedLocale("pl", selected); 390 dialogDoc.getElementById("down").doCommand(); 391 assertLocaleOrder(selected, "en-US,he,pl", "pl"); 392 393 // Accepting the change shows the confirm message bar. 394 let dialogClosed = BrowserTestUtils.waitForEvent(dialog, "dialogclosing"); 395 dialog.acceptDialog(); 396 await dialogClosed; 397 398 // The message bar uses async `formatValues` and that may resolve 399 // after the dialog is closed. 400 await BrowserTestUtils.waitForMutationCondition( 401 messageBar, 402 { attributes: true }, 403 () => !messageBar.hidden 404 ); 405 is( 406 messageBar.querySelector("button").getAttribute("locales"), 407 "en-US,he,pl", 408 "The locales are set on the message bar button" 409 ); 410 411 // Open the dialog again. 412 let newDialog = await openDialog(doc); 413 dialog = newDialog.dialog; 414 dialogDoc = newDialog.dialogDoc; 415 let secondDialogId = getDialogId(dialogDoc); 416 selected = newDialog.selected; 417 418 // The initial order comes from the previous settings. 419 assertLocaleOrder(selected, "en-US,he,pl", "en-US"); 420 421 // Select pl in the list. 422 selectAddedLocale("pl", selected); 423 // Move pl back up. 424 dialogDoc.getElementById("up").doCommand(); 425 assertLocaleOrder(selected, "en-US,pl,he", "pl"); 426 427 // Accepting the change hides the confirm message bar. 428 dialogClosed = BrowserTestUtils.waitForEvent(dialog, "dialogclosing"); 429 dialog.acceptDialog(); 430 await dialogClosed; 431 is(messageBar.hidden, true, "The message bar is hidden again"); 432 433 ok(firstDialogId, "There was an id on the first dialog"); 434 ok(secondDialogId, "There was an id on the second dialog"); 435 Assert.notEqual( 436 firstDialogId, 437 secondDialogId, 438 "The dialog ids are different" 439 ); 440 Assert.less( 441 parseInt(firstDialogId), 442 parseInt(secondDialogId), 443 "The second dialog id is larger than the first" 444 ); 445 446 // Open the dialog yet again. 447 newDialog = await openDialog(doc); 448 dialog = newDialog.dialog; 449 dialogDoc = newDialog.dialogDoc; 450 let thirdDialogId = getDialogId(dialogDoc); 451 selected = newDialog.selected; 452 453 // Move pl to the top. 454 selectAddedLocale("pl", selected); 455 assertLocaleOrder(selected, "en-US,he,pl", "pl"); 456 dialogDoc.getElementById("up").doCommand(); 457 dialogDoc.getElementById("up").doCommand(); 458 assertLocaleOrder(selected, "pl,en-US,he", "pl"); 459 460 dialogClosed = BrowserTestUtils.waitForEvent(dialog, "dialogclosing"); 461 dialog.acceptDialog(); 462 await dialogClosed; 463 464 await Promise.all(addons.map(addon => addon.uninstall())); 465 BrowserTestUtils.removeTab(gBrowser.selectedTab); 466 467 assertTelemetryRecorded([ 468 ["manage", "main", firstDialogId], 469 ["reorder", "dialog", firstDialogId], 470 ["accept", "dialog", firstDialogId], 471 ["set_fallback", "dialog", firstDialogId], 472 ["manage", "main", secondDialogId], 473 ["reorder", "dialog", secondDialogId], 474 ["accept", "dialog", secondDialogId], 475 ["manage", "main", thirdDialogId], 476 ["reorder", "dialog", thirdDialogId], 477 ["reorder", "dialog", thirdDialogId], 478 ["accept", "dialog", thirdDialogId], 479 ]); 480 }); 481 482 add_task(async function testAddAndRemoveSelectedLanguages() { 483 await SpecialPowers.pushPrefEnv({ 484 set: [ 485 ["intl.multilingual.enabled", true], 486 ["intl.multilingual.downloadEnabled", true], 487 ["intl.multilingual.liveReload", false], 488 ["intl.multilingual.liveReloadBidirectional", false], 489 ["intl.locale.requested", "en-US"], 490 ["extensions.langpacks.signatures.required", false], 491 ], 492 }); 493 494 let langpacks = await createTestLangpacks(); 495 let addons = await Promise.all( 496 langpacks.map(async ([, file]) => { 497 let install = await AddonTestUtils.promiseInstallFile(file); 498 return install.addon; 499 }) 500 ); 501 502 await openPreferencesViaOpenPreferencesAPI("paneGeneral", { 503 leaveOpen: true, 504 }); 505 506 let doc = gBrowser.contentDocument; 507 let messageBar = doc.getElementById("confirmBrowserLanguage"); 508 is(messageBar.hidden, true, "The message bar is hidden at first"); 509 510 // Open the dialog. 511 let { dialog, dialogDoc, available, selected } = await openDialog(doc); 512 let dialogId = getDialogId(dialogDoc); 513 514 // loadLocalesFromAMO is async but `initAvailableLocales` doesn't wait 515 // for it to be resolved, so we have to wait for the list to be populated 516 // before we test for its values. 517 await BrowserTestUtils.waitForMutationCondition( 518 available.menupopup, 519 { attributes: true, childList: true }, 520 () => { 521 let listLocales = Array.from(available.menupopup.children).filter( 522 item => item.value && item.value != "search" 523 ); 524 return listLocales.length == 3; 525 } 526 ); 527 // The initial order is set by the pref. 528 assertLocaleOrder(selected, "en-US", "en-US"); 529 assertAvailableLocales(available, ["fr", "pl", "he"]); 530 531 let removeButton = dialogDoc.getElementById("remove"); 532 // Cannot remove the default locale. 533 is(removeButton.disabled, true, "Remove en-US should be disabled"); 534 535 // Add pl and fr to selected. 536 await selectLocale("pl", available, selected, dialogDoc); 537 await selectLocale("fr", available, selected, dialogDoc); 538 539 assertLocaleOrder(selected, "fr,pl,en-US", "fr"); 540 assertAvailableLocales(available, ["he"]); 541 542 // Can remove the added locale again. 543 is(removeButton.disabled, false, "Remove fr should be not be disabled"); 544 545 selectAddedLocale("en-US", selected); 546 // Cannot remove the default locale, even after adding more. 547 is(removeButton.disabled, true, "Remove en-us should still be disabled"); 548 549 // Remove pl and fr from selected. 550 selectAddedLocale("fr", selected); 551 is(removeButton.disabled, false, "Remove fr should be not be disabled"); 552 removeButton.doCommand(); 553 // Selection moves to pl. 554 assertLocaleOrder(selected, "pl,en-US", "pl"); 555 is(removeButton.disabled, false, "Remove pl should be not be disabled"); 556 removeButton.doCommand(); 557 assertLocaleOrder(selected, "en-US", "en-US"); 558 assertAvailableLocales(available, ["fr", "pl", "he"]); 559 is(removeButton.disabled, true, "Remove en-us should be disabled at end"); 560 561 // Add he to selected. 562 await selectLocale("he", available, selected, dialogDoc); 563 assertLocaleOrder(selected, "he,en-US", "he"); 564 assertAvailableLocales(available, ["pl", "fr"]); 565 566 // Accepting the change shows the confirm message bar. 567 let dialogClosed = BrowserTestUtils.waitForEvent(dialog, "dialogclosing"); 568 dialog.acceptDialog(); 569 await dialogClosed; 570 571 await waitForMutation( 572 messageBar, 573 { attributes: true, attributeFilter: ["hidden"] }, 574 target => !target.hidden 575 ); 576 577 is(messageBar.hidden, false, "The message bar is now visible"); 578 is( 579 messageBar.querySelector("button").getAttribute("locales"), 580 "he,en-US", 581 "The locales are set on the message bar button" 582 ); 583 584 await Promise.all(addons.map(addon => addon.uninstall())); 585 BrowserTestUtils.removeTab(gBrowser.selectedTab); 586 587 assertTelemetryRecorded([ 588 ["manage", "main", dialogId], 589 590 // Install id is not recorded since it was already installed. 591 ["add", "dialog", dialogId], 592 ["add", "dialog", dialogId], 593 594 ["remove", "dialog", dialogId], 595 ["remove", "dialog", dialogId], 596 597 ["add", "dialog", dialogId], 598 ["accept", "dialog", dialogId], 599 ]); 600 }); 601 602 add_task(async function testInstallFromAMO() { 603 let langpacks = await AddonManager.getAddonsByTypes(["locale"]); 604 is(langpacks.length, 0, "There are no langpacks installed"); 605 606 let langpacksFile = await createLanguageToolsFile(); 607 let langpacksUrl = Services.io.newFileURI(langpacksFile).spec; 608 let dictionaryBrowseFile = await createDictionaryBrowseResults(); 609 let browseApiEndpoint = Services.io.newFileURI(dictionaryBrowseFile).spec; 610 611 await SpecialPowers.pushPrefEnv({ 612 set: [ 613 ["intl.multilingual.enabled", true], 614 ["intl.multilingual.downloadEnabled", true], 615 ["intl.multilingual.liveReload", false], 616 ["intl.multilingual.liveReloadBidirectional", false], 617 ["intl.locale.requested", "en-US"], 618 ["extensions.getAddons.langpacks.url", langpacksUrl], 619 ["extensions.langpacks.signatures.required", false], 620 ["extensions.getAddons.get.url", browseApiEndpoint], 621 ], 622 }); 623 624 await openPreferencesViaOpenPreferencesAPI("paneGeneral", { 625 leaveOpen: true, 626 }); 627 628 let doc = gBrowser.contentDocument; 629 let messageBar = doc.getElementById("confirmBrowserLanguage"); 630 is(messageBar.hidden, true, "The message bar is hidden at first"); 631 632 // Verify only en-US is listed on the main pane. 633 let getMainPaneLocales = () => { 634 let available = doc.getElementById("primaryBrowserLocale"); 635 let availableLocales = Array.from(available.menupopup.children); 636 return availableLocales 637 .map(item => item.value) 638 .sort() 639 .join(","); 640 }; 641 is(getMainPaneLocales(), "en-US,search", "Only en-US installed to start"); 642 643 // Open the dialog. 644 let { dialog, dialogDoc, available, selected } = await openDialog(doc, true); 645 let firstDialogId = getDialogId(dialogDoc); 646 647 // Make sure the message bar is still hidden. 648 is( 649 messageBar.hidden, 650 true, 651 "The message bar is still hidden after searching" 652 ); 653 654 if (available.itemCount == 1) { 655 await waitForMutation( 656 available.menupopup, 657 { childList: true }, 658 () => available.itemCount > 1 659 ); 660 } 661 662 // The initial order is set by the pref. 663 assertLocaleOrder(selected, "en-US", "en-US"); 664 assertAvailableLocales(available, ["fr", "he", "pl"]); 665 is( 666 Services.locale.availableLocales.join(","), 667 "en-US", 668 "There is only one installed locale" 669 ); 670 671 // Verify that there are no extra dictionaries. 672 let dicts = await AddonManager.getAddonsByTypes(["dictionary"]); 673 is(dicts.length, 0, "There are no installed dictionaries"); 674 675 let installId = null; 676 AddonTestUtils.promiseInstallEvent("onInstallEnded").then(([install]) => { 677 installId = install.installId; 678 }); 679 680 // Add Polish, this will install the langpack. 681 await selectLocale("pl", available, selected, dialogDoc); 682 683 ok(installId, "We got an installId for the langpack installation"); 684 685 let langpack = await AddonManager.getAddonByID(langpackId("pl")); 686 Assert.deepEqual( 687 langpack.installTelemetryInfo, 688 { source: "about:preferences" }, 689 "The source is set to preferences" 690 ); 691 692 // Verify the list is correct. 693 assertLocaleOrder(selected, "pl,en-US", "pl"); 694 assertAvailableLocales(available, ["fr", "he"]); 695 is( 696 Services.locale.availableLocales.sort().join(","), 697 "en-US,pl", 698 "Polish is now installed" 699 ); 700 701 await BrowserTestUtils.waitForCondition(async () => { 702 let newDicts = await AddonManager.getAddonsByTypes(["dictionary"]); 703 let done = !!newDicts.length; 704 705 if (done) { 706 is( 707 newDicts[0].id, 708 DICTIONARY_ID_PL, 709 "The polish dictionary was installed" 710 ); 711 } 712 713 return done; 714 }); 715 716 // Move pl down the list, which prevents an error since it isn't valid. 717 dialogDoc.getElementById("down").doCommand(); 718 assertLocaleOrder(selected, "en-US,pl", "pl"); 719 720 // Test that disabling the langpack removes it from the list. 721 let dialogClosed = BrowserTestUtils.waitForEvent(dialog, "dialogclosing"); 722 dialog.acceptDialog(); 723 await dialogClosed; 724 725 // Verify pl is now available to select. 726 is(getMainPaneLocales(), "en-US,pl,search", "en-US and pl now available"); 727 728 // Disable the Polish langpack. 729 langpack = await AddonManager.getAddonByID("langpack-pl@firefox.mozilla.org"); 730 await langpack.disable(); 731 732 ({ dialogDoc, available, selected } = await openDialog(doc, true)); 733 let secondDialogId = getDialogId(dialogDoc); 734 735 // Wait for the available langpacks to load. 736 if (available.itemCount == 1) { 737 await waitForMutation( 738 available.menupopup, 739 { childList: true }, 740 () => available.itemCount > 1 741 ); 742 } 743 assertLocaleOrder(selected, "en-US", "en-US"); 744 assertAvailableLocales(available, ["fr", "he", "pl"]); 745 746 // Uninstall the langpack and dictionary. 747 let installs = await AddonManager.getAddonsByTypes(["locale", "dictionary"]); 748 is(installs.length, 2, "There is one langpack and one dictionary installed"); 749 await Promise.all(installs.map(item => item.uninstall())); 750 751 BrowserTestUtils.removeTab(gBrowser.selectedTab); 752 753 assertTelemetryRecorded([ 754 // First dialog installs a locale and accepts. 755 ["search", "main", firstDialogId], 756 // It has an installId since it was downloaded. 757 ["add", "dialog", firstDialogId, { installId }], 758 // It got moved down to avoid errors with finding translations. 759 ["reorder", "dialog", firstDialogId], 760 ["accept", "dialog", firstDialogId], 761 762 // The second dialog just checks the state and is closed with the tab. 763 ["search", "main", secondDialogId], 764 ["cancel", "dialog", secondDialogId], 765 ]); 766 }); 767 768 let hasSearchOption = popup => 769 Array.from(popup.children).some(el => el.value == "search"); 770 771 add_task(async function testDownloadEnabled() { 772 await SpecialPowers.pushPrefEnv({ 773 set: [ 774 ["intl.multilingual.enabled", true], 775 ["intl.multilingual.downloadEnabled", true], 776 ["intl.multilingual.liveReload", false], 777 ["intl.multilingual.liveReloadBidirectional", false], 778 ], 779 }); 780 781 await openPreferencesViaOpenPreferencesAPI("paneGeneral", { 782 leaveOpen: true, 783 }); 784 let doc = gBrowser.contentDocument; 785 786 let defaultMenulist = doc.getElementById("primaryBrowserLocale"); 787 ok( 788 hasSearchOption(defaultMenulist.menupopup), 789 "There's a search option in the General pane" 790 ); 791 792 let { available } = await openDialog(doc, false); 793 ok( 794 hasSearchOption(available.menupopup), 795 "There's a search option in the dialog" 796 ); 797 798 BrowserTestUtils.removeTab(gBrowser.selectedTab); 799 }); 800 801 add_task(async function testDownloadDisabled() { 802 await SpecialPowers.pushPrefEnv({ 803 set: [ 804 ["intl.multilingual.enabled", true], 805 ["intl.multilingual.downloadEnabled", false], 806 ["intl.multilingual.liveReload", false], 807 ["intl.multilingual.liveReloadBidirectional", false], 808 ], 809 }); 810 811 await openPreferencesViaOpenPreferencesAPI("paneGeneral", { 812 leaveOpen: true, 813 }); 814 let doc = gBrowser.contentDocument; 815 816 let defaultMenulist = doc.getElementById("primaryBrowserLocale"); 817 ok( 818 !hasSearchOption(defaultMenulist.menupopup), 819 "There's no search option in the General pane" 820 ); 821 822 let { available } = await openDialog(doc, false); 823 ok( 824 !hasSearchOption(available.menupopup), 825 "There's no search option in the dialog" 826 ); 827 828 BrowserTestUtils.removeTab(gBrowser.selectedTab); 829 }); 830 831 add_task(async function testReorderMainPane() { 832 await SpecialPowers.pushPrefEnv({ 833 set: [ 834 ["intl.multilingual.enabled", true], 835 ["intl.multilingual.downloadEnabled", false], 836 ["intl.multilingual.liveReload", false], 837 ["intl.multilingual.liveReloadBidirectional", false], 838 ["intl.locale.requested", "en-US"], 839 ["extensions.langpacks.signatures.required", false], 840 ], 841 }); 842 843 // Clear the telemetry from other tests. 844 Services.telemetry.clearEvents(); 845 846 let langpacks = await createTestLangpacks(); 847 let addons = await Promise.all( 848 langpacks.map(async ([, file]) => { 849 let install = await AddonTestUtils.promiseInstallFile(file); 850 return install.addon; 851 }) 852 ); 853 854 await openPreferencesViaOpenPreferencesAPI("paneGeneral", { 855 leaveOpen: true, 856 }); 857 let doc = gBrowser.contentDocument; 858 859 let messageBar = doc.getElementById("confirmBrowserLanguage"); 860 is(messageBar.hidden, true, "The message bar is hidden at first"); 861 862 let available = doc.getElementById("primaryBrowserLocale"); 863 let availableLocales = Array.from(available.menupopup.children); 864 let availableCodes = availableLocales 865 .map(item => item.value) 866 .sort() 867 .join(","); 868 is( 869 availableCodes, 870 "en-US,fr,he,pl", 871 "All of the available locales are listed" 872 ); 873 874 is(available.selectedItem.value, "en-US", "English is selected"); 875 876 let hebrew = availableLocales.find(item => item.value == "he"); 877 hebrew.click(); 878 available.menupopup.hidePopup(); 879 880 await BrowserTestUtils.waitForCondition( 881 () => !messageBar.hidden, 882 "Wait for message bar to show" 883 ); 884 885 is(messageBar.hidden, false, "The message bar is now shown"); 886 is( 887 messageBar.querySelector("button").getAttribute("locales"), 888 "he,en-US", 889 "The locales are set on the message bar button" 890 ); 891 892 await Promise.all(addons.map(addon => addon.uninstall())); 893 BrowserTestUtils.removeTab(gBrowser.selectedTab); 894 895 assertTelemetryRecorded([["reorder", "main"]]); 896 }); 897 898 add_task(async function testLiveLanguageReloading() { 899 await SpecialPowers.pushPrefEnv({ 900 set: [ 901 ["intl.multilingual.enabled", true], 902 ["intl.multilingual.downloadEnabled", true], 903 ["intl.multilingual.liveReload", true], 904 ["intl.multilingual.liveReloadBidirectional", false], 905 ["intl.locale.requested", "en-US,fr,he,de"], 906 ["extensions.langpacks.signatures.required", false], 907 ], 908 }); 909 910 // Clear the telemetry from other tests. 911 Services.telemetry.clearEvents(); 912 913 let langpacks = await createTestLangpacks(); 914 let addons = await Promise.all( 915 langpacks.map(async ([, file]) => { 916 let install = await AddonTestUtils.promiseInstallFile(file); 917 return install.addon; 918 }) 919 ); 920 921 await openPreferencesViaOpenPreferencesAPI("paneGeneral", { 922 leaveOpen: true, 923 }); 924 925 let doc = gBrowser.contentDocument; 926 927 let available = doc.getElementById("primaryBrowserLocale"); 928 let availableLocales = Array.from(available.menupopup.children); 929 930 is( 931 Services.locale.appLocaleAsBCP47, 932 "en-US", 933 "The app locale starts as English." 934 ); 935 936 Assert.deepEqual( 937 Services.locale.requestedLocales, 938 ["en-US", "fr", "he", "de"], 939 "The locale order starts as what was initially requested." 940 ); 941 942 // French and English are both LTR languages. 943 let french = availableLocales.find(item => item.value == "fr"); 944 945 french.click(); 946 available.menupopup.hidePopup(); 947 948 is( 949 Services.locale.appLocaleAsBCP47, 950 "fr", 951 "The app locale was changed to French" 952 ); 953 954 Assert.deepEqual( 955 Services.locale.requestedLocales, 956 ["fr", "en-US", "he", "de"], 957 "The locale order is switched to french first." 958 ); 959 960 await Promise.all(addons.map(addon => addon.uninstall())); 961 BrowserTestUtils.removeTab(gBrowser.selectedTab); 962 963 assertTelemetryRecorded([["reorder", "main"]]); 964 }); 965 966 add_task(async function testLiveLanguageReloadingBidiOff() { 967 await SpecialPowers.pushPrefEnv({ 968 set: [ 969 ["intl.multilingual.enabled", true], 970 ["intl.multilingual.downloadEnabled", true], 971 ["intl.multilingual.liveReload", true], 972 ["intl.multilingual.liveReloadBidirectional", false], 973 ["intl.locale.requested", "en-US,fr,he,de"], 974 ["extensions.langpacks.signatures.required", false], 975 ], 976 }); 977 978 // Clear the telemetry from other tests. 979 Services.telemetry.clearEvents(); 980 981 let langpacks = await createTestLangpacks(); 982 let addons = await Promise.all( 983 langpacks.map(async ([, file]) => { 984 let install = await AddonTestUtils.promiseInstallFile(file); 985 return install.addon; 986 }) 987 ); 988 989 await openPreferencesViaOpenPreferencesAPI("paneGeneral", { 990 leaveOpen: true, 991 }); 992 993 let doc = gBrowser.contentDocument; 994 995 let available = doc.getElementById("primaryBrowserLocale"); 996 let availableLocales = Array.from(available.menupopup.children); 997 998 is( 999 Services.locale.appLocaleAsBCP47, 1000 "en-US", 1001 "The app locale starts as English." 1002 ); 1003 1004 Assert.deepEqual( 1005 Services.locale.requestedLocales, 1006 ["en-US", "fr", "he", "de"], 1007 "The locale order starts as what was initially requested." 1008 ); 1009 1010 let messageBar = doc.getElementById("confirmBrowserLanguage"); 1011 is(messageBar.hidden, true, "The message bar is hidden at first"); 1012 1013 // English is LTR and Hebrew is RTL. 1014 let hebrew = availableLocales.find(item => item.value == "he"); 1015 1016 hebrew.click(); 1017 available.menupopup.hidePopup(); 1018 1019 await BrowserTestUtils.waitForCondition( 1020 () => !messageBar.hidden, 1021 "Wait for message bar to show" 1022 ); 1023 1024 is(messageBar.hidden, false, "The message bar is now shown"); 1025 1026 is( 1027 Services.locale.appLocaleAsBCP47, 1028 "en-US", 1029 "The app locale remains in English" 1030 ); 1031 1032 Assert.deepEqual( 1033 Services.locale.requestedLocales, 1034 ["en-US", "fr", "he", "de"], 1035 "The locale order did not change." 1036 ); 1037 1038 await Promise.all(addons.map(addon => addon.uninstall())); 1039 1040 assertTelemetryRecorded([["reorder", "main"]]); 1041 1042 BrowserTestUtils.removeTab(gBrowser.selectedTab); 1043 }); 1044 1045 add_task(async function testLiveLanguageReloadingBidiOn() { 1046 await SpecialPowers.pushPrefEnv({ 1047 set: [ 1048 ["intl.multilingual.enabled", true], 1049 ["intl.multilingual.downloadEnabled", true], 1050 ["intl.multilingual.liveReload", true], 1051 ["intl.multilingual.liveReloadBidirectional", true], 1052 ["intl.locale.requested", "en-US,fr,he,de"], 1053 ["extensions.langpacks.signatures.required", false], 1054 ], 1055 }); 1056 1057 // Clear the telemetry from other tests. 1058 Services.telemetry.clearEvents(); 1059 1060 let langpacks = await createTestLangpacks(); 1061 let addons = await Promise.all( 1062 langpacks.map(async ([, file]) => { 1063 let install = await AddonTestUtils.promiseInstallFile(file); 1064 return install.addon; 1065 }) 1066 ); 1067 1068 await openPreferencesViaOpenPreferencesAPI("paneGeneral", { 1069 leaveOpen: true, 1070 }); 1071 1072 let doc = gBrowser.contentDocument; 1073 1074 let available = doc.getElementById("primaryBrowserLocale"); 1075 let availableLocales = Array.from(available.menupopup.children); 1076 1077 is( 1078 Services.locale.appLocaleAsBCP47, 1079 "en-US", 1080 "The app locale starts as English." 1081 ); 1082 1083 Assert.deepEqual( 1084 Services.locale.requestedLocales, 1085 ["en-US", "fr", "he", "de"], 1086 "The locale order starts as what was initially requested." 1087 ); 1088 1089 let messageBar = doc.getElementById("confirmBrowserLanguage"); 1090 is(messageBar.hidden, true, "The message bar is hidden at first"); 1091 1092 // English is LTR and Hebrew is RTL. 1093 let hebrew = availableLocales.find(item => item.value == "he"); 1094 1095 hebrew.click(); 1096 available.menupopup.hidePopup(); 1097 1098 is(messageBar.hidden, true, "The message bar is still hidden"); 1099 1100 is( 1101 Services.locale.appLocaleAsBCP47, 1102 "he", 1103 "The app locale was changed to Hebrew." 1104 ); 1105 1106 Assert.deepEqual( 1107 Services.locale.requestedLocales, 1108 ["he", "en-US", "fr", "de"], 1109 "The locale changed with Hebrew first." 1110 ); 1111 1112 await Promise.all(addons.map(addon => addon.uninstall())); 1113 1114 assertTelemetryRecorded([["reorder", "main"]]); 1115 1116 BrowserTestUtils.removeTab(gBrowser.selectedTab); 1117 });