Source code
Revision control
Copy as Markdown
Other Tools
Test Info: Errors
- This test failed 5 times in the preceding 30 days. quicksearch this test
- Manifest: browser/components/preferences/tests/languages/browser-srd.toml
/* Any copyright is dedicated to the Public Domain.
AddonTestUtils.initMochitest(this);
// Installed locales should populate immediately even when the remote locale
// fetch is still pending.
add_task(async function testInstalledLocalesWhileRemotePending() {
let sandbox = sinon.createSandbox();
let resolveRemote;
let remotePromise = new Promise(resolve => {
resolveRemote = resolve;
});
sandbox
.stub(LangPackMatcher.mockable, "getAvailableLangpacks")
.callsFake(() => remotePromise);
await SpecialPowers.pushPrefEnv({
set: [
["intl.multilingual.enabled", true],
["intl.multilingual.downloadEnabled", true],
],
});
let doc = await openLanguagesPrefs();
let win = doc.defaultView;
await waitForLanguageUI(doc);
let sc = getSettingControl("browserLanguagePreferred", win);
let children = Array.from(sc.controlEl.children);
ok(
children.some(el => el.value === "en-US"),
"Installed en-US is shown while remote is pending"
);
ok(
!children.some(el => el.localName === "hr"),
"No separator present while remote is pending"
);
// Now resolve the remote fetch and verify remote locales appear.
resolveRemote(["de"].map(createRemoteLangpack));
await waitForRemoteSeparator(win);
children = Array.from(sc.controlEl.children);
let hrIndex = children.findIndex(el => el.localName === "hr");
let remoteValues = children.slice(hrIndex + 1).map(el => el.value);
Assert.deepEqual(remoteValues, ["de"], "Remote locales appear after resolve");
sandbox.restore();
BrowserTestUtils.removeTab(gBrowser.selectedTab);
});
// Remote locales should only be fetched once the user navigates to the
// languages pane, not when prefs are opened to a different pane.
add_task(async function testRemoteLocalesNotFetchedUntilLanguagesPaneShown() {
let sandbox = sinon.createSandbox();
sandbox
.stub(LangPackMatcher.mockable, "getAvailableLangpacks")
.resolves(["de"].map(createRemoteLangpack));
await SpecialPowers.pushPrefEnv({
set: [
["intl.multilingual.enabled", true],
["intl.multilingual.downloadEnabled", true],
],
});
let paneInitialized = TestUtils.topicObserved("languages-pane-loaded");
await openPreferencesViaOpenPreferencesAPI("paneSync", { leaveOpen: true });
let doc = gBrowser.contentDocument;
let win = doc.defaultView;
// findInPage.js initializes all panes in a requestIdleCallback after the
// initial pane loads. Wait for the browserLanguagePreferred select to be
// populated so that the languages.mjs settings have run their refresh and
// remote language fetching has had a chance to run. If it always loads we
// should have hit the API at that point.
await paneInitialized;
let preferredLanguage = await settingControlRenders(
"browserLanguagePreferred",
win
);
await BrowserTestUtils.waitForMutationCondition(
preferredLanguage,
{ childList: true, subtree: true },
() => preferredLanguage.controlEl?.children?.length
);
is(
LangPackMatcher.mockable.getAvailableLangpacks.callCount,
0,
"Remote langpacks aren't fetched while a different pane is shown"
);
let paneLoaded = waitForPaneChange("languages");
synthesizeClick(doc.getElementById("category-languages"));
await paneLoaded;
await waitForRemoteSeparator(win);
is(
LangPackMatcher.mockable.getAvailableLangpacks.callCount,
1,
"Remote langpacks are fetched after switching to the languages pane"
);
let sc = getSettingControl("browserLanguagePreferred", win);
let children = Array.from(sc.controlEl.children);
let hrIndex = children.findIndex(el => el.localName === "hr");
let remoteValues = children.slice(hrIndex + 1).map(el => el.value);
Assert.deepEqual(
remoteValues,
["de"],
"Remote locales appear after switching to the languages pane"
);
sandbox.restore();
BrowserTestUtils.removeTab(gBrowser.selectedTab);
});
// When downloadEnabled is true, remote locales from AMO should appear after
// a separator below the installed locales.
add_task(async function testRemoteLocalesAppearAfterSeparator() {
let sandbox = sinon.createSandbox();
sandbox
.stub(LangPackMatcher.mockable, "getAvailableLangpacks")
.resolves(["de", "fr"].map(createRemoteLangpack));
await SpecialPowers.pushPrefEnv({
set: [
["intl.multilingual.enabled", true],
["intl.multilingual.downloadEnabled", true],
],
});
let doc = await openLanguagesPrefs();
let win = doc.defaultView;
await waitForRemoteSeparator(win);
let sc = getSettingControl("browserLanguagePreferred", win);
let children = Array.from(sc.controlEl.children);
let hrIndex = children.findIndex(el => el.localName === "hr");
Assert.greater(hrIndex, 0, "Separator appears after installed locales");
let installedValues = children.slice(0, hrIndex).map(el => el.value);
let remoteValues = children.slice(hrIndex + 1).map(el => el.value);
ok(installedValues.includes("en-US"), "en-US is in the installed section");
Assert.deepEqual(
remoteValues,
["de", "fr"],
"Remote locales appear after separator"
);
is(
LangPackMatcher.mockable.getAvailableLangpacks.callCount,
1,
"getAvailableLangpacks was called once to fetch remote locales"
);
// Trigger a setting refresh by toggling a pref that the remote locales
// setting listens to, then verify the list was cached and not re-fetched.
Services.prefs.setBoolPref("intl.multilingual.downloadEnabled", false);
Services.prefs.setBoolPref("intl.multilingual.downloadEnabled", true);
await waitForSettingControlChange(sc);
is(
LangPackMatcher.mockable.getAvailableLangpacks.callCount,
1,
"getAvailableLangpacks was not called again after refresh (cached)"
);
sandbox.restore();
BrowserTestUtils.removeTab(gBrowser.selectedTab);
});
// Locales that are already installed should not also appear in the remote section.
add_task(async function testInstalledLocalesNotDuplicatedInRemoteSection() {
let sandbox = sinon.createSandbox();
sandbox
.stub(LangPackMatcher.mockable, "getAvailableLangpacks")
.resolves(["de", "fr"].map(createRemoteLangpack));
await SpecialPowers.pushPrefEnv({
set: [
["intl.multilingual.enabled", true],
["intl.multilingual.downloadEnabled", true],
["extensions.langpacks.signatures.required", false],
],
});
let addon = await installLangpack("fr");
let doc = await openLanguagesPrefs();
let win = doc.defaultView;
await waitForRemoteSeparator(win);
let sc = getSettingControl("browserLanguagePreferred", win);
let children = Array.from(sc.controlEl.children);
let hrIndex = children.findIndex(el => el.localName === "hr");
let installedValues = children.slice(0, hrIndex).map(el => el.value);
let remoteValues = children.slice(hrIndex + 1).map(el => el.value);
ok(
installedValues.includes("fr"),
"Installed fr appears in the installed section"
);
ok(
!remoteValues.includes("fr"),
"Installed fr is not duplicated in the remote section"
);
Assert.deepEqual(
remoteValues,
["de"],
"Only non-installed de appears in the remote section"
);
await addon.uninstall();
sandbox.restore();
BrowserTestUtils.removeTab(gBrowser.selectedTab);
});
// Selecting a remote locale should trigger langpack installation and, when
// liveReload is off, show the restart confirmation.
add_task(
async function testSelectingRemoteLocaleInstallsLangpackAndShowsRestart() {
let sandbox = sinon.createSandbox();
sandbox
.stub(LangPackMatcher.mockable, "getAvailableLangpacks")
.resolves(["fr"].map(createRemoteLangpack));
sandbox.stub(LangPackMatcher.mockable, "installLangPack").resolves(true);
await SpecialPowers.pushPrefEnv({
set: [
["intl.multilingual.enabled", true],
["intl.multilingual.downloadEnabled", true],
["intl.multilingual.liveReload", false],
["intl.multilingual.liveReloadBidirectional", false],
["intl.locale.requested", "en-US"],
],
});
let doc = await openLanguagesPrefs();
let win = doc.defaultView;
await waitForRemoteSeparator(win);
assertRestartMessageHidden(doc);
let sc = getSettingControl("browserLanguagePreferred", win);
await changeMozSelectValue(sc.controlEl, "fr");
ok(
LangPackMatcher.mockable.installLangPack.calledOnce,
"installLangPack was called for the remote locale"
);
is(
LangPackMatcher.mockable.installLangPack.firstCall.args[0].target_locale,
"fr",
"installLangPack was called with the fr langpack"
);
await waitForRestartMessage(doc);
sandbox.restore();
BrowserTestUtils.removeTab(gBrowser.selectedTab);
}
);
// If the langpack installation throws, set() should reset the dropdown to the
// current locale rather than leaving it on the failed remote locale.
add_task(async function testFailedRemoteLocaleInstallResetsDropdown() {
let sandbox = sinon.createSandbox();
sandbox
.stub(LangPackMatcher.mockable, "getAvailableLangpacks")
.resolves(["fr"].map(createRemoteLangpack));
sandbox
.stub(LangPackMatcher.mockable, "installLangPack")
.rejects(new Error("Simulated install failure"));
await SpecialPowers.pushPrefEnv({
set: [
["intl.multilingual.enabled", true],
["intl.multilingual.downloadEnabled", true],
["intl.locale.requested", "en-US"],
],
});
let doc = await openLanguagesPrefs();
let win = doc.defaultView;
await waitForRemoteSeparator(win);
let sc = getSettingControl("browserLanguagePreferred", win);
await changeMozSelectValue(sc.controlEl, "fr");
is(
sc.controlEl.value,
"en-US",
"Dropdown resets to current locale after failed install"
);
// An error message should be shown after a failed install.
let messageControl = getSettingControl("browserLanguageMessage", win);
await BrowserTestUtils.waitForMutationCondition(
messageControl,
{ attributes: true, attributeFilter: ["hidden"] },
() => !messageControl.hidden
);
ok(
messageControl.controlEl.shadowRoot.querySelector(
"moz-message-bar[type=error]"
),
"Error message bar is shown after failed install"
);
sandbox.restore();
BrowserTestUtils.removeTab(gBrowser.selectedTab);
});
// Both language selects should be disabled while a locale is being downloaded
// and re-enabled when the download completes.
add_task(async function testSelectsDisabledDuringDownload() {
let sandbox = sinon.createSandbox();
sandbox
.stub(LangPackMatcher.mockable, "getAvailableLangpacks")
.resolves(["fr"].map(createRemoteLangpack));
let resolveInstall;
sandbox.stub(LangPackMatcher.mockable, "installLangPack").callsFake(
() =>
new Promise(resolve => {
resolveInstall = resolve;
})
);
await SpecialPowers.pushPrefEnv({
set: [
["intl.multilingual.enabled", true],
["intl.multilingual.downloadEnabled", true],
["intl.multilingual.liveReload", false],
["intl.multilingual.liveReloadBidirectional", false],
["intl.locale.requested", "en-US"],
["extensions.langpacks.signatures.required", false],
],
});
let addon = await installLangpack("de");
let doc = await openLanguagesPrefs();
let win = doc.defaultView;
await waitForRemoteSeparator(win);
let preferred = getSettingControl("browserLanguagePreferred", win);
let fallback = getSettingControl("browserLanguageFallback", win);
await changeMozSelectValue(preferred.controlEl, "de");
await waitForSettingVisible("browserLanguageFallback", win);
ok(!preferred.controlEl.disabled, "Preferred is enabled before download");
ok(!fallback.controlEl.disabled, "Fallback is enabled before download");
// Trigger a remote install (don't await, it will pend).
let setPromise = changeMozSelectValue(preferred.controlEl, "fr");
// Wait for the installing state to propagate to the UI.
await waitForSettingControlChange(preferred);
ok(preferred.controlEl.disabled, "Preferred is disabled during download");
ok(fallback.controlEl.disabled, "Fallback is disabled during download");
resolveInstall(true);
await setPromise;
// Wait for re-enable after download completes.
await waitForSettingControlChange(preferred);
ok(!preferred.controlEl.disabled, "Preferred is re-enabled after download");
ok(!fallback.controlEl.disabled, "Fallback is re-enabled after download");
await addon.uninstall();
sandbox.restore();
BrowserTestUtils.removeTab(gBrowser.selectedTab);
});