"use strict"; const {Preferences} = ChromeUtils.import("resource://gre/modules/Preferences.jsm"); // 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"); let expectedLangs = ["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() { if (AppConstants.MOZ_BUILD_APP !== "browser") { // This is not supported on Android. return; } 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(); });