summaryrefslogtreecommitdiffstats
path: root/toolkit/components/extensions/test/xpcshell/test_ext_i18n.js
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/components/extensions/test/xpcshell/test_ext_i18n.js')
-rw-r--r--toolkit/components/extensions/test/xpcshell/test_ext_i18n.js571
1 files changed, 571 insertions, 0 deletions
diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_i18n.js b/toolkit/components/extensions/test/xpcshell/test_ext_i18n.js
new file mode 100644
index 0000000000..048e675a3e
--- /dev/null
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_i18n.js
@@ -0,0 +1,571 @@
+"use strict";
+
+const { Preferences } = ChromeUtils.importESModule(
+ "resource://gre/modules/Preferences.sys.mjs"
+);
+
+// ExtensionContent.jsm needs to know when it's running from xpcshell,
+// to use the right timeout for content scripts executed at document_idle.
+ExtensionTestUtils.mockAppInfo();
+
+const server = createHttpServer();
+server.registerDirectory("/data/", do_get_file("data"));
+
+const BASE_URL = `http://localhost:${server.identity.primaryPort}/data`;
+
+var originalReqLocales = Services.locale.requestedLocales;
+
+registerCleanupFunction(() => {
+ Preferences.reset("intl.accept_languages");
+ Services.locale.requestedLocales = originalReqLocales;
+});
+
+add_task(async function test_i18n() {
+ function runTests(assertEq) {
+ let _ = browser.i18n.getMessage.bind(browser.i18n);
+
+ let url = browser.runtime.getURL("/");
+ assertEq(
+ url,
+ `moz-extension://${_("@@extension_id")}/`,
+ "@@extension_id builtin message"
+ );
+
+ assertEq("Foo.", _("Foo"), "Simple message in selected locale.");
+
+ assertEq("(bar)", _("bar"), "Simple message fallback in default locale.");
+
+ assertEq("", _("some-unknown-locale-string"), "Unknown locale string.");
+
+ assertEq("", _("@@unknown_builtin_string"), "Unknown built-in string.");
+ assertEq(
+ "",
+ _("@@bidi_unknown_builtin_string"),
+ "Unknown built-in bidi string."
+ );
+
+ assertEq("Føo.", _("Föo"), "Multi-byte message in selected locale.");
+
+ let substitutions = [];
+ substitutions[4] = "5";
+ substitutions[13] = "14";
+
+ assertEq(
+ "'$0' '14' '' '5' '$$$$' '$'.",
+ _("basic_substitutions", substitutions),
+ "Basic numeric substitutions"
+ );
+
+ assertEq(
+ "'$0' '' 'just a string' '' '$$$$' '$'.",
+ _("basic_substitutions", "just a string"),
+ "Basic numeric substitutions, with non-array value"
+ );
+
+ let values = _("named_placeholder_substitutions", [
+ "(subst $1 $2)",
+ "(2 $1 $2)",
+ ]).split("\n");
+
+ assertEq(
+ "_foo_ (subst $1 $2) _bar_",
+ values[0],
+ "Named and numeric substitution"
+ );
+
+ assertEq(
+ "(2 $1 $2)",
+ values[1],
+ "Numeric substitution amid named placeholders"
+ );
+
+ assertEq("$bad name$", values[2], "Named placeholder with invalid key");
+
+ assertEq("", values[3], "Named placeholder with an invalid value");
+
+ assertEq(
+ "Accepted, but shouldn't break.",
+ values[4],
+ "Named placeholder with a strange content value"
+ );
+
+ assertEq("$foo", values[5], "Non-placeholder token that should be ignored");
+ }
+
+ let extension = ExtensionTestUtils.loadExtension({
+ manifest: {
+ default_locale: "jp",
+
+ content_scripts: [
+ { matches: ["http://*/*/file_sample.html"], js: ["content.js"] },
+ ],
+ },
+
+ files: {
+ "_locales/en_US/messages.json": {
+ foo: {
+ message: "Foo.",
+ description: "foo",
+ },
+
+ föo: {
+ message: "Føo.",
+ description: "foo",
+ },
+
+ basic_substitutions: {
+ message: "'$0' '$14' '$1' '$5' '$$$$$' '$$'.",
+ description: "foo",
+ },
+
+ Named_placeholder_substitutions: {
+ message:
+ "$Foo$\n$2\n$bad name$\n$bad_value$\n$bad_content_value$\n$foo",
+ description: "foo",
+ placeholders: {
+ foO: {
+ content: "_foo_ $1 _bar_",
+ description: "foo",
+ },
+
+ "bad name": {
+ content: "Nope.",
+ description: "bad name",
+ },
+
+ bad_value: "Nope.",
+
+ bad_content_value: {
+ content: ["Accepted, but shouldn't break."],
+ description: "bad value",
+ },
+ },
+ },
+
+ broken_placeholders: {
+ message: "$broken$",
+ description: "broken placeholders",
+ placeholders: "foo.",
+ },
+ },
+
+ "_locales/jp/messages.json": {
+ foo: {
+ message: "(foo)",
+ description: "foo",
+ },
+
+ bar: {
+ message: "(bar)",
+ description: "bar",
+ },
+ },
+
+ "content.js":
+ "new " +
+ function (runTestsFn) {
+ runTestsFn((...args) => {
+ browser.runtime.sendMessage(["assertEq", ...args]);
+ });
+
+ browser.runtime.sendMessage(["content-script-finished"]);
+ } +
+ `(${runTests})`,
+ },
+
+ background:
+ "new " +
+ function (runTestsFn) {
+ browser.runtime.onMessage.addListener(([msg, ...args]) => {
+ if (msg == "assertEq") {
+ browser.test.assertEq(...args);
+ } else {
+ browser.test.sendMessage(msg, ...args);
+ }
+ });
+
+ runTestsFn(browser.test.assertEq.bind(browser.test));
+ } +
+ `(${runTests})`,
+ });
+
+ await extension.startup();
+
+ let contentPage = await ExtensionTestUtils.loadContentPage(
+ `${BASE_URL}/file_sample.html`
+ );
+ await extension.awaitMessage("content-script-finished");
+ await contentPage.close();
+
+ await extension.unload();
+});
+
+add_task(async function test_i18n_negotiation() {
+ function runTests(expected) {
+ let _ = browser.i18n.getMessage.bind(browser.i18n);
+
+ browser.test.assertEq(expected, _("foo"), "Got expected message");
+ }
+
+ let extensionData = {
+ manifest: {
+ default_locale: "en_US",
+
+ content_scripts: [
+ { matches: ["http://*/*/file_sample.html"], js: ["content.js"] },
+ ],
+ },
+
+ files: {
+ "_locales/en_US/messages.json": {
+ foo: {
+ message: "English.",
+ description: "foo",
+ },
+ },
+
+ "_locales/jp/messages.json": {
+ foo: {
+ message: "\u65e5\u672c\u8a9e",
+ description: "foo",
+ },
+ },
+
+ "content.js":
+ "new " +
+ function (runTestsFn) {
+ browser.test.onMessage.addListener(expected => {
+ runTestsFn(expected);
+
+ browser.test.sendMessage("content-script-finished");
+ });
+ browser.test.sendMessage("content-ready");
+ } +
+ `(${runTests})`,
+ },
+
+ background:
+ "new " +
+ function (runTestsFn) {
+ browser.test.onMessage.addListener(expected => {
+ runTestsFn(expected);
+
+ browser.test.sendMessage("background-script-finished");
+ });
+ } +
+ `(${runTests})`,
+ };
+
+ // At the moment extension language negotiation is tied to Firefox language
+ // negotiation result. That means that to test an extension in `fr`, we need
+ // to mock `fr` being available in Firefox and then request it.
+ //
+ // In the future, we should provide some way for tests to decouple their
+ // language selection from that of Firefox.
+ Services.locale.availableLocales = ["en-US", "fr", "jp"];
+
+ let contentPage = await ExtensionTestUtils.loadContentPage(
+ `${BASE_URL}/file_sample.html`
+ );
+
+ for (let [lang, msg] of [
+ ["en-US", "English."],
+ ["jp", "\u65e5\u672c\u8a9e"],
+ ]) {
+ Services.locale.requestedLocales = [lang];
+
+ let extension = ExtensionTestUtils.loadExtension(extensionData);
+ await extension.startup();
+ await extension.awaitMessage("content-ready");
+
+ extension.sendMessage(msg);
+ await extension.awaitMessage("background-script-finished");
+ await extension.awaitMessage("content-script-finished");
+
+ await extension.unload();
+ }
+ Services.locale.requestedLocales = originalReqLocales;
+
+ await contentPage.close();
+});
+
+add_task(async function test_get_accept_languages() {
+ function checkResults(source, results, expected) {
+ browser.test.assertEq(
+ expected.length,
+ results.length,
+ `got expected number of languages in ${source}`
+ );
+ results.forEach((lang, index) => {
+ browser.test.assertEq(
+ expected[index],
+ lang,
+ `got expected language in ${source}`
+ );
+ });
+ }
+
+ function background(checkResultsFn) {
+ browser.test.onMessage.addListener(([msg, expected]) => {
+ browser.i18n.getAcceptLanguages().then(results => {
+ checkResultsFn("background", results, expected);
+
+ browser.test.sendMessage("background-done");
+ });
+ });
+ }
+
+ function content(checkResultsFn) {
+ browser.test.onMessage.addListener(([msg, expected]) => {
+ browser.i18n.getAcceptLanguages().then(results => {
+ checkResultsFn("contentScript", results, expected);
+
+ browser.test.sendMessage("content-done");
+ });
+ });
+ browser.test.sendMessage("content-loaded");
+ }
+
+ let extension = ExtensionTestUtils.loadExtension({
+ manifest: {
+ content_scripts: [
+ {
+ matches: ["http://*/*/file_sample.html"],
+ run_at: "document_start",
+ js: ["content_script.js"],
+ },
+ ],
+ },
+
+ background: `(${background})(${checkResults})`,
+
+ files: {
+ "content_script.js": `(${content})(${checkResults})`,
+ },
+ });
+
+ let contentPage = await ExtensionTestUtils.loadContentPage(
+ `${BASE_URL}/file_sample.html`
+ );
+
+ await extension.startup();
+ await extension.awaitMessage("content-loaded");
+
+ // TODO bug 1765375: ", en" is missing on Android.
+ let expectedLangs =
+ AppConstants.platform == "android" ? ["en-US"] : ["en-US", "en"];
+ extension.sendMessage(["expect-results", expectedLangs]);
+ await extension.awaitMessage("background-done");
+ await extension.awaitMessage("content-done");
+
+ expectedLangs = ["en-US", "en", "fr-CA", "fr"];
+ Preferences.set("intl.accept_languages", expectedLangs.toString());
+ extension.sendMessage(["expect-results", expectedLangs]);
+ await extension.awaitMessage("background-done");
+ await extension.awaitMessage("content-done");
+ Preferences.reset("intl.accept_languages");
+
+ await contentPage.close();
+
+ await extension.unload();
+});
+
+add_task(async function test_get_ui_language() {
+ function getResults() {
+ return {
+ getUILanguage: browser.i18n.getUILanguage(),
+ getMessage: browser.i18n.getMessage("@@ui_locale"),
+ };
+ }
+
+ function checkResults(source, results, expected) {
+ browser.test.assertEq(
+ expected,
+ results.getUILanguage,
+ `Got expected getUILanguage result in ${source}`
+ );
+ browser.test.assertEq(
+ expected,
+ results.getMessage,
+ `Got expected getMessage result in ${source}`
+ );
+ }
+
+ function background(getResultsFn, checkResultsFn) {
+ browser.test.onMessage.addListener(([msg, expected]) => {
+ checkResultsFn("background", getResultsFn(), expected);
+
+ browser.test.sendMessage("background-done");
+ });
+ }
+
+ function content(getResultsFn, checkResultsFn) {
+ browser.test.onMessage.addListener(([msg, expected]) => {
+ checkResultsFn("contentScript", getResultsFn(), expected);
+
+ browser.test.sendMessage("content-done");
+ });
+ browser.test.sendMessage("content-loaded");
+ }
+
+ let extension = ExtensionTestUtils.loadExtension({
+ manifest: {
+ content_scripts: [
+ {
+ matches: ["http://*/*/file_sample.html"],
+ run_at: "document_start",
+ js: ["content_script.js"],
+ },
+ ],
+ },
+
+ background: `(${background})(${getResults}, ${checkResults})`,
+
+ files: {
+ "content_script.js": `(${content})(${getResults}, ${checkResults})`,
+ },
+ });
+
+ let contentPage = await ExtensionTestUtils.loadContentPage(
+ `${BASE_URL}/file_sample.html`
+ );
+
+ await extension.startup();
+ await extension.awaitMessage("content-loaded");
+
+ extension.sendMessage(["expect-results", "en-US"]);
+
+ await extension.awaitMessage("background-done");
+ await extension.awaitMessage("content-done");
+
+ // We don't currently have a good way to mock this.
+ if (false) {
+ Services.locale.requestedLocales = ["he"];
+
+ extension.sendMessage(["expect-results", "he"]);
+
+ await extension.awaitMessage("background-done");
+ await extension.awaitMessage("content-done");
+ }
+
+ await contentPage.close();
+
+ await extension.unload();
+});
+
+add_task(async function test_detect_language() {
+ const af_string =
+ " aam skukuza die naam beteken hy wat skoonvee of hy wat alles onderstebo keer wysig " +
+ "bosveldkampe boskampe is kleiner afgeleë ruskampe wat oor min fasiliteite beskik daar is geen restaurante " +
+ "of winkels nie en slegs oornagbesoekers word toegelaat bateleur";
+ // String with intermixed French/English text
+ const fr_en_string =
+ "France is the largest country in Western Europe and the third-largest in Europe as a whole. " +
+ "A accès aux chiens et aux frontaux qui lui ont été il peut consulter et modifier ses collections et exporter " +
+ "Cet article concerne le pays européen aujourd’hui appelé République française. Pour d’autres usages du nom France, " +
+ "Pour une aide rapide et effective, veuiller trouver votre aide dans le menu ci-dessus." +
+ "Motoring events began soon after the construction of the first successful gasoline-fueled automobiles. The quick brown fox jumped over the lazy dog";
+
+ function checkResult(source, result, expected) {
+ browser.test.assertEq(
+ expected.isReliable,
+ result.isReliable,
+ "result.confident is true"
+ );
+ browser.test.assertEq(
+ expected.languages.length,
+ result.languages.length,
+ `result.languages contains the expected number of languages in ${source}`
+ );
+ expected.languages.forEach((lang, index) => {
+ browser.test.assertEq(
+ lang.percentage,
+ result.languages[index].percentage,
+ `element ${index} of result.languages array has the expected percentage in ${source}`
+ );
+ browser.test.assertEq(
+ lang.language,
+ result.languages[index].language,
+ `element ${index} of result.languages array has the expected language in ${source}`
+ );
+ });
+ }
+
+ function backgroundScript(checkResultFn) {
+ browser.test.onMessage.addListener(([msg, expected]) => {
+ browser.i18n.detectLanguage(msg).then(result => {
+ checkResultFn("background", result, expected);
+ browser.test.sendMessage("background-done");
+ });
+ });
+ }
+
+ function content(checkResultFn) {
+ browser.test.onMessage.addListener(([msg, expected]) => {
+ browser.i18n.detectLanguage(msg).then(result => {
+ checkResultFn("contentScript", result, expected);
+ browser.test.sendMessage("content-done");
+ });
+ });
+ browser.test.sendMessage("content-loaded");
+ }
+
+ let extension = ExtensionTestUtils.loadExtension({
+ manifest: {
+ content_scripts: [
+ {
+ matches: ["http://*/*/file_sample.html"],
+ run_at: "document_start",
+ js: ["content_script.js"],
+ },
+ ],
+ },
+
+ background: `(${backgroundScript})(${checkResult})`,
+
+ files: {
+ "content_script.js": `(${content})(${checkResult})`,
+ },
+ });
+
+ let contentPage = await ExtensionTestUtils.loadContentPage(
+ `${BASE_URL}/file_sample.html`
+ );
+
+ await extension.startup();
+ await extension.awaitMessage("content-loaded");
+
+ let expected = {
+ isReliable: true,
+ languages: [
+ {
+ language: "fr",
+ percentage: 67,
+ },
+ {
+ language: "en",
+ percentage: 32,
+ },
+ ],
+ };
+ extension.sendMessage([fr_en_string, expected]);
+ await extension.awaitMessage("background-done");
+ await extension.awaitMessage("content-done");
+
+ expected = {
+ isReliable: true,
+ languages: [
+ {
+ language: "af",
+ percentage: 99,
+ },
+ ],
+ };
+ extension.sendMessage([af_string, expected]);
+ await extension.awaitMessage("background-done");
+ await extension.awaitMessage("content-done");
+
+ await contentPage.close();
+
+ await extension.unload();
+});