diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
commit | 2aa4a82499d4becd2284cdb482213d541b8804dd (patch) | |
tree | b80bf8bf13c3766139fbacc530efd0dd9d54394c /dom/l10n/tests | |
parent | Initial commit. (diff) | |
download | firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip |
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'dom/l10n/tests')
52 files changed, 2913 insertions, 0 deletions
diff --git a/dom/l10n/tests/gtest/TestL10nOverlays.cpp b/dom/l10n/tests/gtest/TestL10nOverlays.cpp new file mode 100644 index 0000000000..c6c9da9939 --- /dev/null +++ b/dom/l10n/tests/gtest/TestL10nOverlays.cpp @@ -0,0 +1,77 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "gtest/gtest.h" +#include "mozilla/dom/L10nOverlays.h" +#include "mozilla/dom/Document.h" +#include "mozilla/dom/L10nOverlaysBinding.h" +#include "mozilla/dom/Element.h" +#include "mozilla/NullPrincipal.h" +#include "nsNetUtil.h" + +using mozilla::NullPrincipal; +using namespace mozilla::dom; + +static already_AddRefed<Document> SetUpDocument() { + nsCOMPtr<nsIURI> uri; + NS_NewURI(getter_AddRefs(uri), "about:blank"); + nsCOMPtr<nsIPrincipal> principal = + NullPrincipal::CreateWithoutOriginAttributes(); + nsCOMPtr<Document> document; + nsresult rv = NS_NewDOMDocument(getter_AddRefs(document), + u""_ns, // aNamespaceURI + u""_ns, // aQualifiedName + nullptr, // aDoctype + uri, uri, principal, + false, // aLoadedAsData + nullptr, // aEventObject + DocumentFlavorHTML); + + if (NS_WARN_IF(NS_FAILED(rv))) { + return nullptr; + } + return document.forget(); +} + +/** + * This test verifies that the basic C++ DOM L10nOverlays API + * works correctly. + */ +TEST(DOM_L10n_Overlays, Initial) +{ + mozilla::ErrorResult rv; + + // 1. Set up an HTML document. + nsCOMPtr<Document> doc = SetUpDocument(); + + // 2. Create a simple Element with a child. + // + // <div> + // <a data-l10n-name="link" href="https://www.mozilla.org"></a> + // </div> + // + RefPtr<Element> elem = doc->CreateHTMLElement(nsGkAtoms::div); + RefPtr<Element> span = doc->CreateHTMLElement(nsGkAtoms::a); + span->SetAttribute(u"data-l10n-name"_ns, u"link"_ns, rv); + span->SetAttribute(u"href"_ns, u"https://www.mozilla.org"_ns, rv); + elem->AppendChild(*span, rv); + + // 3. Create an L10nMessage with a translation for the element. + L10nMessage translation; + translation.mValue.AssignLiteral( + "Hello <a data-l10n-name=\"link\">World</a>."); + + // 4. Translate the element. + nsTArray<L10nOverlaysError> errors; + L10nOverlays::TranslateElement(*elem, translation, errors, rv); + + nsAutoString textContent; + elem->GetInnerHTML(textContent, rv); + + // 5. Verify that the innerHTML matches the expectations. + ASSERT_STREQ(NS_ConvertUTF16toUTF8(textContent).get(), + "Hello <a data-l10n-name=\"link\" " + "href=\"https://www.mozilla.org\">World</a>."); +} diff --git a/dom/l10n/tests/gtest/moz.build b/dom/l10n/tests/gtest/moz.build new file mode 100644 index 0000000000..0e1e2173a6 --- /dev/null +++ b/dom/l10n/tests/gtest/moz.build @@ -0,0 +1,11 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +UNIFIED_SOURCES += [ + "TestL10nOverlays.cpp", +] + +FINAL_LIBRARY = "xul-gtest" diff --git a/dom/l10n/tests/mochitest/.eslintrc.js b/dom/l10n/tests/mochitest/.eslintrc.js new file mode 100644 index 0000000000..1779fd7f1c --- /dev/null +++ b/dom/l10n/tests/mochitest/.eslintrc.js @@ -0,0 +1,5 @@ +"use strict"; + +module.exports = { + extends: ["plugin:mozilla/browser-test"], +}; diff --git a/dom/l10n/tests/mochitest/browser.ini b/dom/l10n/tests/mochitest/browser.ini new file mode 100644 index 0000000000..e1ef9ad541 --- /dev/null +++ b/dom/l10n/tests/mochitest/browser.ini @@ -0,0 +1,4 @@ +[document_l10n/non-system-principal/browser_resource_uri.js] +support-files = + document_l10n/non-system-principal/test.html + document_l10n/non-system-principal/localization/test.ftl diff --git a/dom/l10n/tests/mochitest/chrome.ini b/dom/l10n/tests/mochitest/chrome.ini new file mode 100644 index 0000000000..d9e984ffcc --- /dev/null +++ b/dom/l10n/tests/mochitest/chrome.ini @@ -0,0 +1,44 @@ +[l10n_overlays/test_attributes.html] +[l10n_overlays/test_functional_children.html] +[l10n_overlays/test_text_children.html] +[l10n_overlays/test_extra_text_markup.html] +[l10n_overlays/test_l10n_overlays.xhtml] +[l10n_overlays/test_same_id.html] +[l10n_overlays/test_same_id_args.html] +[l10n_overlays/test_title.html] + +[l10n_mutations/test_append_content_post_dcl.html] +[l10n_mutations/test_append_content_pre_dcl.html] +[l10n_mutations/test_append_fragment_post_dcl.html] +[l10n_mutations/test_disconnectedRoot_webcomponent.html] +[l10n_mutations/test_set_attributes.html] +[l10n_mutations/test_pause_observing.html] +[l10n_mutations/test_template.html] + +[dom_localization/test_attr_sanitized.html] +[dom_localization/test_getAttributes.html] +[dom_localization/test_setAttributes.html] +[dom_localization/test_translateElements.html] +[dom_localization/test_translateFragment.html] +[dom_localization/test_connectRoot.html] +[dom_localization/test_connectRoot_webcomponent.html] +[dom_localization/test_disconnectRoot.html] +[dom_localization/test_repeated_l10nid.html] +[dom_localization/test_translateRoots.html] +[dom_localization/test_l10n_mutations.html] +[dom_localization/test_overlay.html] +[dom_localization/test_overlay_repeated.html] +[dom_localization/test_overlay_missing_all.html] +[dom_localization/test_overlay_missing_children.html] +[dom_localization/test_overlay_sanitized.html] +[dom_localization/test_domloc.xhtml] + + +[document_l10n/test_docl10n.xhtml] +[document_l10n/test_docl10n.html] +[document_l10n/test_docl10n_sync.html] +[document_l10n/test_docl10n_ready_rejected.html] +[document_l10n/test_docl10n_removeResourceIds.html] +[document_l10n/test_docl10n_lazy.html] +[document_l10n/test_connectRoot_webcomponent.html] +[document_l10n/test_connectRoot_webcomponent_lazy.html] diff --git a/dom/l10n/tests/mochitest/document_l10n/README.txt b/dom/l10n/tests/mochitest/document_l10n/README.txt new file mode 100644 index 0000000000..b798a5039a --- /dev/null +++ b/dom/l10n/tests/mochitest/document_l10n/README.txt @@ -0,0 +1,3 @@ +Tests in this directory cover support for DocumentL10n +WebIDL API across different use cases such as +processes, principals and so on. diff --git a/dom/l10n/tests/mochitest/document_l10n/non-system-principal/README.txt b/dom/l10n/tests/mochitest/document_l10n/non-system-principal/README.txt new file mode 100644 index 0000000000..d0cc074166 --- /dev/null +++ b/dom/l10n/tests/mochitest/document_l10n/non-system-principal/README.txt @@ -0,0 +1,3 @@ +Tests in this directory cover the functionality +of DocumentL10n WebIDL API in non-system-principal +scenario. diff --git a/dom/l10n/tests/mochitest/document_l10n/non-system-principal/browser_resource_uri.js b/dom/l10n/tests/mochitest/document_l10n/non-system-principal/browser_resource_uri.js new file mode 100644 index 0000000000..00433c5314 --- /dev/null +++ b/dom/l10n/tests/mochitest/document_l10n/non-system-principal/browser_resource_uri.js @@ -0,0 +1,84 @@ +const { L10nRegistry, FileSource } = ChromeUtils.import( + "resource://gre/modules/L10nRegistry.jsm" +); + +let uri = + "chrome://mochitests/content/browser/dom/l10n/tests/mochitest//document_l10n/non-system-principal/"; +let protocol = Services.io + .getProtocolHandler("resource") + .QueryInterface(Ci.nsIResProtocolHandler); + +protocol.setSubstitution("l10n-test", Services.io.newURI(uri)); + +// Since we want the mock source to work with all locales, we're going +// to register it for currently used locales, and we'll put the path that +// doesn't use the `{locale}` component to make it work irrelevant of +// what locale the mochitest is running in. +// +// Notice: we're using a `chrome://` protocol here only for convenience reasons. +// Real sources should use `resource://` protocol. +let locales = Services.locale.appLocalesAsBCP47; +let mockSource = new FileSource("test", locales, `${uri}localization/`); +L10nRegistry.registerSources([mockSource]); + +registerCleanupFunction(() => { + protocol.setSubstitution("l10n-test", null); + L10nRegistry.removeSources(["test"]); + SpecialPowers.pushPrefEnv({ + set: [["dom.ipc.processPrelaunch.enabled", true]], + }); +}); + +add_task(async () => { + // Bug 1640333 - windows fails (sometimes) to ever get document.l10n.ready + // if e10s process caching is enabled + await SpecialPowers.pushPrefEnv({ + set: [["dom.ipc.processPrelaunch.enabled", false]], + }); + await BrowserTestUtils.withNewTab( + "resource://l10n-test/test.html", + async browser => { + await SpecialPowers.spawn(browser, [], async function() { + let document = content.document; + let window = document.defaultView; + + let { customMsg, l10nArgs } = await document.testsReadyPromise; + + let desc = document.getElementById("main-desc"); + + // We can test here for a particular value because we're + // using a mock file source which is locale independent. + // + // If you're writing a test that verifies that a UI + // widget got real localization, you should not rely on + // the particular value, but rather on the content not + // being empty (to keep the test pass in non-en-US locales). + is(desc.textContent, "This is a mock page title"); + + // Test for l10n.getAttributes + let label = document.getElementById("label1"); + is(l10nArgs.id, "subtitle"); + is(l10nArgs.args.name, "Firefox"); + + // Test for manual value formatting + is(customMsg, "This is a custom message formatted from JS."); + + // Since we applied the `data-l10n-id` attribute + // on `label` in this microtask, we have to wait for + // the next paint to verify that the MutationObserver + // applied the translation. + await new Promise(resolve => { + let verifyL10n = () => { + if (!label.textContent.includes("Firefox")) { + window.requestAnimationFrame(verifyL10n); + } else { + resolve(); + } + }; + + window.requestAnimationFrame(verifyL10n); + }); + }); + } + ); +}); diff --git a/dom/l10n/tests/mochitest/document_l10n/non-system-principal/localization/test.ftl b/dom/l10n/tests/mochitest/document_l10n/non-system-principal/localization/test.ftl new file mode 100644 index 0000000000..a5da5a8f00 --- /dev/null +++ b/dom/l10n/tests/mochitest/document_l10n/non-system-principal/localization/test.ftl @@ -0,0 +1,4 @@ +page-title = This is a mock page title +subtitle = This is a label for { $name } + +custom-message = This is a custom message formatted from JS. diff --git a/dom/l10n/tests/mochitest/document_l10n/non-system-principal/test.html b/dom/l10n/tests/mochitest/document_l10n/non-system-principal/test.html new file mode 100644 index 0000000000..3b6784ed6b --- /dev/null +++ b/dom/l10n/tests/mochitest/document_l10n/non-system-principal/test.html @@ -0,0 +1,36 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Test DocumentL10n in HTML environment</title> + <link rel="localization" href="test.ftl"/> + <script type="text/javascript"> + document.testsReadyPromise = new Promise((resolve) => { + // The test is in this file to ensure that we're testing + // the behavior in a non-system principal. + document.addEventListener("DOMContentLoaded", async () => { + await document.l10n.ready; + + // Assign the localization from JS + let label = document.getElementById("label1"); + document.l10n.setAttributes( + label, + "subtitle", + { + name: "Firefox", + } + ); + + const customMsg = await document.l10n.formatValue("custom-message"); + const l10nArgs = document.l10n.getAttributes(label); + resolve({customMsg, l10nArgs}); + }, {once: true}); + }); + </script> +</head> +<body> + <h1 id="main-desc" data-l10n-id="page-title"></h1> + + <p id="label1"></p> +</body> +</html> diff --git a/dom/l10n/tests/mochitest/document_l10n/test_connectRoot_webcomponent.html b/dom/l10n/tests/mochitest/document_l10n/test_connectRoot_webcomponent.html new file mode 100644 index 0000000000..3f2def3547 --- /dev/null +++ b/dom/l10n/tests/mochitest/document_l10n/test_connectRoot_webcomponent.html @@ -0,0 +1,90 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Test Web Component connecting into Document's l10n</title> + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> + <link rel="localization" href="browser/preferences/preferences.ftl"></link> + <script type="application/javascript"> + SimpleTest.waitForExplicitFinish(); + + // In this test we are introducing two widgets. The only difference between them + // is that the first one is using `connectRoot` with the `aTranslate` argument set to `true`, + // and the other one to `false`. + // + // In this test, we will inject both of them into the DOM for parsing. + // For a test that verifies the behavior when they're injected lazily, see + // `test_connectRoot_webcomponent_lazy.html` test. + // + // Since both widgets get injected into DOM during parsing, we expect both of them + // to get translated before `document.l10n.ready` is resolved. + + let passedTests = 0; + + class FluentWidget extends HTMLElement { + constructor() { + super(); + const shadowRoot = this.attachShadow({mode: "open"}); + const t = document.querySelector("#fluent-widget-template"); + const instance = t.content.cloneNode(true); + shadowRoot.appendChild(instance); + } + async connectedCallback() { + MozXULElement.insertFTLIfNeeded("browser/preferences/preferences.ftl"); + + document.l10n.connectRoot(this.shadowRoot, true); + + let label = this.shadowRoot.getElementById("label"); + + await document.l10n.ready; + is(label.textContent, "Learn more", "localization content applied to element"); + passedTests++; + if (passedTests == 2) { + SimpleTest.finish(); + } + } + } + + class FluentWidget2 extends HTMLElement { + constructor() { + super(); + const shadowRoot = this.attachShadow({mode: "open"}); + const t = document.querySelector("#fluent-widget-template"); + const instance = t.content.cloneNode(true); + shadowRoot.appendChild(instance); + } + async connectedCallback() { + MozXULElement.insertFTLIfNeeded("browser/preferences/preferences.ftl"); + + document.l10n.connectRoot(this.shadowRoot, false); + + let label = this.shadowRoot.getElementById("label"); + + await document.l10n.ready; + is(label.textContent, "Learn more", "localization content applied to element"); + passedTests++; + if (passedTests == 2) { + SimpleTest.finish(); + } + } + } + + customElements.define("fluent-widget", FluentWidget); + customElements.define("fluent-widget2", FluentWidget2); + </script> +</head> +<body> + <template id="fluent-widget-template"> + <div> + <button id="label" data-l10n-id="do-not-track-learn-more"></button> + </div> + </template> + <fluent-widget></fluent-widget> + <fluent-widget2></fluent-widget2> + <script> + // This trick makes sure that we connect the widgets before parsing is completed. + document.write(""); + </script> +</body> +</html> diff --git a/dom/l10n/tests/mochitest/document_l10n/test_connectRoot_webcomponent_lazy.html b/dom/l10n/tests/mochitest/document_l10n/test_connectRoot_webcomponent_lazy.html new file mode 100644 index 0000000000..fee14d6206 --- /dev/null +++ b/dom/l10n/tests/mochitest/document_l10n/test_connectRoot_webcomponent_lazy.html @@ -0,0 +1,98 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Test Web Component connecting into Document's l10n</title> + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> + <script type="application/javascript"> + SimpleTest.waitForExplicitFinish(); + + // In this test we are introducing two widgets. The only difference between them + // is that the first one is using `connectRoot` with the `aTranslate` argument set to `true`, + // and the other one to `false`. + // + // In this test, we will inject both of them lazily, after initial parsing is completed. + // For a test that verifies the behavior when they're injected during parsing, see + // `test_connectRoot_webcomponent.html` test. + // + // The expected difference is that when both get lazily injected into the DOM, the first one + // will get translated, while the other will not. + // The latter behavior will be used by widgets that will want to translate the initial DOM on their + // own before connecting the root. + + let firstWidgetTranslated = false; + + class FluentWidget extends HTMLElement { + constructor() { + super(); + const shadowRoot = this.attachShadow({mode: "open"}); + const t = document.querySelector("#fluent-widget-template"); + const instance = t.content.cloneNode(true); + shadowRoot.appendChild(instance); + } + connectedCallback() { + MozXULElement.insertFTLIfNeeded("browser/preferences/preferences.ftl"); + + document.l10n.connectRoot(this.shadowRoot, true); + + let label = this.shadowRoot.getElementById("label"); + + let verifyL10n = () => { + if (label.textContent.length > 0) { + window.removeEventListener("MozAfterPaint", verifyL10n); + is(label.textContent, "Learn more", "localization content applied to element"); + firstWidgetTranslated = true; + } + }; + window.addEventListener("MozAfterPaint", verifyL10n); + } + } + + class FluentWidget2 extends HTMLElement { + constructor() { + super(); + const shadowRoot = this.attachShadow({mode: "open"}); + const t = document.querySelector("#fluent-widget-template"); + const instance = t.content.cloneNode(true); + shadowRoot.appendChild(instance); + } + connectedCallback() { + MozXULElement.insertFTLIfNeeded("browser/preferences/preferences.ftl"); + + document.l10n.connectRoot(this.shadowRoot, false); + + let label = this.shadowRoot.getElementById("label"); + + let verifyL10n = () => { + if (firstWidgetTranslated) { + is(label.textContent.length, 0, "This widget should remain untranslated."); + window.removeEventListener("MozAfterPaint", verifyL10n); + SimpleTest.finish(); + } + }; + window.addEventListener("MozAfterPaint", verifyL10n); + } + } + + customElements.define("fluent-widget", FluentWidget); + customElements.define("fluent-widget2", FluentWidget2); + + window.addEventListener("load", () => { + window.requestIdleCallback(async () => { + let widget = document.createElement("fluent-widget"); + document.body.appendChild(widget); + let widget2 = document.createElement("fluent-widget2"); + document.body.appendChild(widget2); + }); + }, { once: true }); + </script> +</head> +<body> + <template id="fluent-widget-template"> + <div> + <button id="label" data-l10n-id="do-not-track-learn-more"></button> + </div> + </template> +</body> +</html> diff --git a/dom/l10n/tests/mochitest/document_l10n/test_docl10n.html b/dom/l10n/tests/mochitest/document_l10n/test_docl10n.html new file mode 100644 index 0000000000..9f2c916f47 --- /dev/null +++ b/dom/l10n/tests/mochitest/document_l10n/test_docl10n.html @@ -0,0 +1,57 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Test DocumentL10n in HTML environment</title> + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> + <link rel="localization" href="crashreporter/aboutcrashes.ftl"/> + <script type="application/javascript"> + "use strict"; + SimpleTest.waitForExplicitFinish(); + + is(document.l10n.ready && document.l10n.ready.then !== undefined, true, + "document.l10n.ready exists and is a Promise"); + + (async function() { + await document.l10n.ready; + + // Test for initial localization applied. + let desc = document.getElementById("main-desc"); + is(desc.textContent.length > 0, true); + + // Test for manual value formatting. + let msg = await document.l10n.formatValue("id-heading"); + is(msg.length > 0, true); + + // Test for mutations applied. + let verifyL10n = () => { + if (label.textContent.length > 0) { + window.removeEventListener("MozAfterPaint", verifyL10n); + SimpleTest.finish(); + } + }; + window.addEventListener("MozAfterPaint", verifyL10n); + + let label = document.getElementById("label1"); + document.l10n.setAttributes( + label, + "date-crashed-heading", + { + name: "John", + } + ); + + // Test for l10n.getAttributes + let l10nArgs = document.l10n.getAttributes(label); + is(l10nArgs.id, "date-crashed-heading"); + is(l10nArgs.args.name, "John"); + })(); + </script> +</head> +<body> + <h1 id="main-desc" data-l10n-id="crash-reports-title"></h1> + + <p id="label1"></p> +</body> +</html> diff --git a/dom/l10n/tests/mochitest/document_l10n/test_docl10n.xhtml b/dom/l10n/tests/mochitest/document_l10n/test_docl10n.xhtml new file mode 100644 index 0000000000..3b20c0620e --- /dev/null +++ b/dom/l10n/tests/mochitest/document_l10n/test_docl10n.xhtml @@ -0,0 +1,60 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- This Source Code Form is subject to the terms of the Mozilla Public + - License, v. 2.0. If a copy of the MPL was not distributed with this + - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> + +<!DOCTYPE html> + +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> + <meta charset="utf-8"></meta> + <title>Test DocumentL10n in HTML environment</title> + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"></link> + <link rel="localization" href="crashreporter/aboutcrashes.ftl"/> + <script type="application/javascript"> + "use strict"; + SimpleTest.waitForExplicitFinish(); + + document.addEventListener("DOMContentLoaded", async function() { + await document.l10n.ready; + + // Test for initial localization applied. + let desc = document.getElementById("main-desc"); + is(desc.textContent.length > 0, true); + + // Test for manual value formatting + let msg = await document.l10n.formatValue("id-heading"); + is(msg.length > 0, true); + + // Test for mutations applied. + let verifyL10n = () => { + if (label.textContent.length > 0) { + window.removeEventListener("MozAfterPaint", verifyL10n); + SimpleTest.finish(); + } + }; + window.addEventListener("MozAfterPaint", verifyL10n); + + let label = document.getElementById("label1"); + document.l10n.setAttributes( + label, + "date-crashed-heading", + { + name: "John", + } + ); + + // Test for l10n.getAttributes + let l10nArgs = document.l10n.getAttributes(label); + is(l10nArgs.id, "date-crashed-heading"); + is(l10nArgs.args.name, "John"); + }, { once: true}); + </script> +</head> +<body> + <h1 id="main-desc" data-l10n-id="crash-reports-title"></h1> + + <p id="label1" /> +</body> +</html> diff --git a/dom/l10n/tests/mochitest/document_l10n/test_docl10n_lazy.html b/dom/l10n/tests/mochitest/document_l10n/test_docl10n_lazy.html new file mode 100644 index 0000000000..6be2a6f45e --- /dev/null +++ b/dom/l10n/tests/mochitest/document_l10n/test_docl10n_lazy.html @@ -0,0 +1,44 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Test Lazy DocumentL10n in HTML environment</title> + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> + <script type="application/javascript"> + "use strict"; + SimpleTest.waitForExplicitFinish(); + + is(document.l10n, null, "document.l10n is null."); + + window.addEventListener("load", async () => { + is(document.l10n, null, "document.l10n is null after load."); + + let desc = document.getElementById("main-desc"); + is(desc.textContent.length, 0, "main-desc is not translated"); + + let link = document.createElement("link"); + link.setAttribute("rel", "localization"); + link.setAttribute("href", "crashreporter/aboutcrashes.ftl"); + document.head.appendChild(link); + + // Verify now that `l10n.ready` exists and is fulfilled. + await document.l10n.ready; + + // Lazy initialized localization should translate the document. + is(desc.textContent.length > 0, true, "main-desc is translated"); + + document.head.removeChild(link); + + is(document.l10n, null, "document.l10n is null"); + + SimpleTest.finish(); + }, { once: true}); + </script> +</head> +<body> + <h1 id="main-desc" data-l10n-id="crash-reports-title"></h1> + + <p id="label1"></p> +</body> +</html> diff --git a/dom/l10n/tests/mochitest/document_l10n/test_docl10n_ready_rejected.html b/dom/l10n/tests/mochitest/document_l10n/test_docl10n_ready_rejected.html new file mode 100644 index 0000000000..933305138b --- /dev/null +++ b/dom/l10n/tests/mochitest/document_l10n/test_docl10n_ready_rejected.html @@ -0,0 +1,27 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Test mozIDOMLocalization.ready rejected state</title> + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> + <link rel="localization" href="path/to_non_existing.ftl"/> + <script type="application/javascript"> + "use strict"; + SimpleTest.waitForExplicitFinish(); + + document.addEventListener("DOMContentLoaded", async function() { + document.l10n.ready.then(() => { + is(1, 2, "the ready should not resolve"); + SimpleTest.finish(); + }, (err) => { + is(1, 1, "the ready should reject"); + SimpleTest.finish(); + }); + }); + </script> +</head> +<body> + <h1 data-l10n-id="non-existing-id"></h1> +</body> +</html> diff --git a/dom/l10n/tests/mochitest/document_l10n/test_docl10n_removeResourceIds.html b/dom/l10n/tests/mochitest/document_l10n/test_docl10n_removeResourceIds.html new file mode 100644 index 0000000000..19e4153e00 --- /dev/null +++ b/dom/l10n/tests/mochitest/document_l10n/test_docl10n_removeResourceIds.html @@ -0,0 +1,59 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Test DocumentL10n::RemoveResourceIds</title> + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> + <link rel="localization" href="toolkit/about/aboutAddons.ftl"/> + <link rel="localization" href="toolkit/about/aboutSupport.ftl"/> + <script type="application/javascript"> + "use strict"; + /* eslint-disable mozilla/prefer-formatValues */ + + SimpleTest.waitForExplicitFinish(); + + window.onload = async function() { + { + // 1. An example message from aboutAddons should be available. + let value = await document.l10n.formatValue("shortcuts-browserAction2"); + is(value.length > 0, true, "localized value retrieved"); + } + + { + // 2. Remove aboutAddons.ftl + let link = document.head.querySelector("link[href*=aboutAddons]"); + document.head.removeChild(link); + } + + { + // 3. An example message from aboutSupport should still be available. + let value = await document.l10n.formatValue("features-version"); + is(value.length > 0, true, "localized value retrieved"); + + // 4. An example message from aboutAddons should not be available. + await document.l10n.formatValue("shortcuts-browserAction").then( + () => { + ok(false, "localization should not be available"); + }, + () => { + ok(true, "localization should not be available"); + }); + } + + { + // 5. Remove aboutSupport.ftl + let link = document.head.querySelector("link[href*=aboutSupport]"); + document.head.removeChild(link); + + // 6. document.l10n should be null. + is(document.l10n, null, "document.l10n should be null"); + + SimpleTest.finish(); + } + }; + </script> +</head> +<body> +</body> +</html> diff --git a/dom/l10n/tests/mochitest/document_l10n/test_docl10n_sync.html b/dom/l10n/tests/mochitest/document_l10n/test_docl10n_sync.html new file mode 100644 index 0000000000..f931ed3351 --- /dev/null +++ b/dom/l10n/tests/mochitest/document_l10n/test_docl10n_sync.html @@ -0,0 +1,54 @@ +<!DOCTYPE HTML> +<html data-l10n-sync> +<head> + <meta charset="utf-8"> + <title>Test DocumentL10n in HTML environment</title> + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> + <link rel="localization" href="crashreporter/aboutcrashes.ftl"/> + <script type="application/javascript"> + "use strict"; + SimpleTest.waitForExplicitFinish(); + + document.addEventListener("DOMContentLoaded", async function() { + await document.l10n.ready; + + // Test for initial localization applied. + let desc = document.getElementById("main-desc"); + is(desc.textContent.length > 0, true); + + // Test for manual value formatting. + let msg = await document.l10n.formatValue("id-heading"); + is(msg.length > 0, true); + + // Test for mutations applied. + let verifyL10n = () => { + if (label.textContent.length > 0) { + window.removeEventListener("MozAfterPaint", verifyL10n); + SimpleTest.finish(); + } + }; + window.addEventListener("MozAfterPaint", verifyL10n); + + let label = document.getElementById("label1"); + document.l10n.setAttributes( + label, + "date-crashed-heading", + { + name: "John", + } + ); + + // Test for l10n.getAttributes + let l10nArgs = document.l10n.getAttributes(label); + is(l10nArgs.id, "date-crashed-heading"); + is(l10nArgs.args.name, "John"); + }, { once: true}); + </script> +</head> +<body> + <h1 id="main-desc" data-l10n-id="crash-reports-title"></h1> + + <p id="label1"></p> +</body> +</html> diff --git a/dom/l10n/tests/mochitest/document_l10n/test_unpriv_iframe.html b/dom/l10n/tests/mochitest/document_l10n/test_unpriv_iframe.html new file mode 100644 index 0000000000..4f4b29c500 --- /dev/null +++ b/dom/l10n/tests/mochitest/document_l10n/test_unpriv_iframe.html @@ -0,0 +1,26 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Ensure unprivilaged document cannot access document.l10n in an iframe</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> + <script type="application/javascript"> + "use strict"; + SimpleTest.waitForExplicitFinish(); + + addLoadEvent(function() { + let frame = document.getElementById("frame"); + let frame2 = document.getElementById("frame2"); + + is("l10n" in frame.contentDocument, false); + is("l10n" in frame2.contentDocument, false); + }); + addLoadEvent(SimpleTest.finish); + </script> +</head> +<body> + <iframe id="frame" src="about:blank"></iframe> + <iframe id="frame2" src="about:crashes"></iframe> +</body> +</html> diff --git a/dom/l10n/tests/mochitest/dom_localization/test_attr_sanitized.html b/dom/l10n/tests/mochitest/dom_localization/test_attr_sanitized.html new file mode 100644 index 0000000000..19ff6aee2f --- /dev/null +++ b/dom/l10n/tests/mochitest/dom_localization/test_attr_sanitized.html @@ -0,0 +1,47 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Test DOMLocalization's attr sanitization functionality</title> + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> + <script type="application/javascript"> + "use strict"; + async function* generateBundles(resourceIds) { + const bundle = new FluentBundle("en-US"); + bundle.addResource(new FluentResource(` +key1 = Value for Key 1 + +key2 = Value for <a>Key 2<a/>. + `)); + yield bundle; + } + + SimpleTest.waitForExplicitFinish(); + addLoadEvent(async () => { + const domLoc = new DOMLocalization( + [], + false, + { generateBundles }, + ); + + await domLoc.translateFragment(document.body); + + const elem1 = document.querySelector("#elem1"); + const elem2 = document.querySelector("#elem2"); + + ok(elem1.textContent.includes("Value for")); + ok(!elem1.hasAttribute("title")); + + ok(elem2.textContent.includes("Value for")); + ok(!elem2.hasAttribute("title")); + + SimpleTest.finish(); + }); + </script> +</head> +<body> + <p id="elem1" title="Old Translation" data-l10n-id="key1"></p> + <p id="elem2" title="Old Translation" data-l10n-id="key2"></p> +</body> +</html> diff --git a/dom/l10n/tests/mochitest/dom_localization/test_connectRoot.html b/dom/l10n/tests/mochitest/dom_localization/test_connectRoot.html new file mode 100644 index 0000000000..ae6c6a8e01 --- /dev/null +++ b/dom/l10n/tests/mochitest/dom_localization/test_connectRoot.html @@ -0,0 +1,43 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Test DOMLocalization.prototype.connectRoot</title> + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> + <script type="application/javascript"> + "use strict"; + async function* generateBundles(resourceIds) { + const bundle = new FluentBundle("en-US"); + bundle.addResource(new FluentResource(` +key1 = Value for Key 1 + `)); + yield bundle; + } + + window.onload = async function() { + SimpleTest.waitForExplicitFinish(); + + const p1 = document.getElementById("p1"); + + const domLoc = new DOMLocalization( + [], + false, + { generateBundles }, + ); + + await domLoc.translateRoots(); + is(p1.textContent.length == 0, true); + const body = document.body; + domLoc.connectRoot(body); + await domLoc.translateRoots(); + is(p1.textContent.length > 0, true); + + SimpleTest.finish(); + }; + </script> +</head> +<body> + <p id="p1" data-l10n-id="key1"></p> +</body> +</html> diff --git a/dom/l10n/tests/mochitest/dom_localization/test_connectRoot_webcomponent.html b/dom/l10n/tests/mochitest/dom_localization/test_connectRoot_webcomponent.html new file mode 100644 index 0000000000..49ead91d4b --- /dev/null +++ b/dom/l10n/tests/mochitest/dom_localization/test_connectRoot_webcomponent.html @@ -0,0 +1,70 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Test DOMLocalization.prototype.connectRoot with Web Components</title> + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> + <script type="application/javascript"> + SimpleTest.waitForExplicitFinish(); + + class FluentWidget extends HTMLElement { + constructor() { + super(); + const shadowRoot = this.attachShadow({mode: "open"}); + const t = document.querySelector("#fluent-widget-template"); + const instance = t.content.cloneNode(true); + shadowRoot.appendChild(instance); + } + connectedCallback() { + document.domLoc.connectRoot(this.shadowRoot); + ok(true); + + let label = this.shadowRoot.getElementById("label"); + + // Test for mutations applied. + let verifyL10n = () => { + if (label.textContent.length > 0) { + window.removeEventListener("MozAfterPaint", verifyL10n); + // Notice: In normal tests we do not want to test against any particular + // value as per https://firefox-source-docs.mozilla.org/intl/l10n/l10n/fluent_tutorial.html#testing + // But in this particular test, since we do not rely on actual real + // localization, but instead we mock it in the test, we can test + // against the actual value safely. + is(label.textContent, "Value for Key 1", "localization content applied to element"); + SimpleTest.finish(); + } + }; + window.addEventListener("MozAfterPaint", verifyL10n); + + document.domLoc.setAttributes(label, "key1"); + } + } + customElements.define("fluent-widget", FluentWidget); + </script> + <script type="application/javascript"> + "use strict"; + async function* generateBundles(resourceIds) { + const bundle = new FluentBundle("en-US"); + bundle.addResource(new FluentResource(` +key1 = Value for Key 1 + `)); + yield bundle; + } + + document.domLoc = new DOMLocalization( + [], + false, + { generateBundles } + ); + </script> +</head> +<body> + <template id="fluent-widget-template"> + <div> + <p id="label"></p> + </div> + </template> + <fluent-widget id="widget1"></fluent-widget> +</body> +</html> diff --git a/dom/l10n/tests/mochitest/dom_localization/test_disconnectRoot.html b/dom/l10n/tests/mochitest/dom_localization/test_disconnectRoot.html new file mode 100644 index 0000000000..58279c4dec --- /dev/null +++ b/dom/l10n/tests/mochitest/dom_localization/test_disconnectRoot.html @@ -0,0 +1,58 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Test DOMLocalization.prototype.disconnectRoot</title> + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> + <script type="application/javascript"> + "use strict"; + async function* generateBundles(resourceIds) { + const bundle = new FluentBundle("en-US"); + bundle.addResource(new FluentResource(` +key1 = Value for Key 1 +key2 = Value for Key 2 + `)); + yield bundle; + } + + window.onload = async function() { + SimpleTest.waitForExplicitFinish(); + + const p1 = document.getElementById("p1"); + + const domLoc = new DOMLocalization( + [], + false, + { generateBundles }, + ); + + await domLoc.translateRoots(); + is(p1.textContent.length == 0, true); + + const body = document.body; + + domLoc.connectRoot(body); + await domLoc.translateRoots(); + is(p1.textContent.includes("Key 1"), true); + is(p1.textContent.includes("Key 2"), false); + + domLoc.disconnectRoot(body); + domLoc.setAttributes(p1, "key2"); + await domLoc.translateRoots(); + is(p1.textContent.includes("Key 1"), true); + is(p1.textContent.includes("Key 2"), false); + + domLoc.connectRoot(body); + await domLoc.translateRoots(); + is(p1.textContent.includes("Key 1"), false); + is(p1.textContent.includes("Key 2"), true); + + SimpleTest.finish(); + }; + </script> +</head> +<body> + <p id="p1" data-l10n-id="key1"></p> +</body> +</html> diff --git a/dom/l10n/tests/mochitest/dom_localization/test_domloc.xhtml b/dom/l10n/tests/mochitest/dom_localization/test_domloc.xhtml new file mode 100644 index 0000000000..3cf167cce5 --- /dev/null +++ b/dom/l10n/tests/mochitest/dom_localization/test_domloc.xhtml @@ -0,0 +1,66 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" + type="text/css"?> + + +<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + title="Testing DOMLocalization in XUL environment"> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" /> + <script type="application/javascript"> + <![CDATA[ + async function* generateBundles(resourceIds) { + const bundle = new FluentBundle("en-US"); + bundle.addResource(new FluentResource(` +file-menu = + .label = File + .accesskey = F +new-tab = + .label = New Tab + .accesskey = N +container = Some text with an <image data-l10n-name="foo"> inside it. +`)); + yield bundle; + } + + SimpleTest.waitForExplicitFinish(); + + + const domLoc = new DOMLocalization( + [], + false, + { generateBundles }, + ); + + async function foo() { + domLoc.addResourceIds(["dummy.ftl"]); + domLoc.connectRoot(document.documentElement); + await domLoc.translateRoots(); + + is(document.getElementById('file-menu').getAttribute('label'), 'File'); + is(document.getElementById('file-menu').getAttribute('accesskey'), 'F'); + + is(document.getElementById('new-tab').getAttribute('label'), 'New Tab'); + is(document.getElementById('new-tab').getAttribute('accesskey'), 'N'); + + ok(document.querySelector("image"), + "Image should still be present after localization."); + SimpleTest.finish(); + } + + window.onload = foo; + + ]]> + </script> + <description data-l10n-id="container"><image data-l10n-name="foo"/></description> + + <menubar id="main-menubar"> + <menu id="file-menu" data-l10n-id="file-menu"> + <menupopup id="menu_FilePopup"> + <menuitem id="new-tab" data-l10n-id="new-tab"> + </menuitem> + </menupopup> + </menu> + </menubar> +</window> diff --git a/dom/l10n/tests/mochitest/dom_localization/test_getAttributes.html b/dom/l10n/tests/mochitest/dom_localization/test_getAttributes.html new file mode 100644 index 0000000000..c0fdf96026 --- /dev/null +++ b/dom/l10n/tests/mochitest/dom_localization/test_getAttributes.html @@ -0,0 +1,52 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Test DOMLocalization.prototype.getAttributes</title> + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> + <script type="application/javascript"> + "use strict"; + + async function* generateBundles(resourceIds) {} + + window.onload = function() { + SimpleTest.waitForExplicitFinish(); + + const domLoc = new DOMLocalization( + [], + false, + { generateBundles }, + ); + + const p1 = document.querySelectorAll("p")[0]; + const p2 = document.querySelectorAll("p")[1]; + const p3 = document.querySelectorAll("p")[2]; + const attrs1 = domLoc.getAttributes(p1); + const attrs2 = domLoc.getAttributes(p2); + const attrs3 = domLoc.getAttributes(p3); + isDeeply(attrs1, { + id: null, + args: null, + }); + isDeeply(attrs2, { + id: "id1", + args: null, + }); + isDeeply(attrs3, { + id: "id2", + args: { + userName: "John", + }, + }); + + SimpleTest.finish(); + }; + </script> +</head> +<body> + <p /> + <p data-l10n-id="id1" /> + <p data-l10n-id="id2" data-l10n-args='{"userName": "John"}' /> +</body> +</html> diff --git a/dom/l10n/tests/mochitest/dom_localization/test_l10n_mutations.html b/dom/l10n/tests/mochitest/dom_localization/test_l10n_mutations.html new file mode 100644 index 0000000000..e29e5f347a --- /dev/null +++ b/dom/l10n/tests/mochitest/dom_localization/test_l10n_mutations.html @@ -0,0 +1,53 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Test DOMLocalization's MutationObserver</title> + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> + <script type="application/javascript"> + "use strict"; + async function* generateBundles(resourceIds) { + const bundle = new FluentBundle("en-US"); + bundle.addResource(new FluentResource("title = Hello World")); + bundle.addResource(new FluentResource("title2 = Hello Another World")); + yield bundle; + } + + window.onload = async function() { + SimpleTest.waitForExplicitFinish(); + + const domLoc = new DOMLocalization( + [], + false, + { generateBundles }, + ); + + const h1 = document.querySelectorAll("h1")[0]; + + domLoc.addResourceIds(["dummy.ftl"]); + domLoc.connectRoot(document.body); + + await domLoc.translateRoots(); + + is(h1.textContent, "Hello World"); + + + const mo = new MutationObserver(function onMutations(mutations) { + is(h1.textContent, "Hello Another World"); + mo.disconnect(); + SimpleTest.finish(); + }); + + mo.observe(h1, { childList: true, characterData: true }); + + domLoc.setAttributes(h1, "title2"); + }; + </script> +</head> +<body> + <div> + <h1 data-l10n-id="title"></h1> + </div> +</body> +</html> diff --git a/dom/l10n/tests/mochitest/dom_localization/test_overlay.html b/dom/l10n/tests/mochitest/dom_localization/test_overlay.html new file mode 100644 index 0000000000..8127e1ba2e --- /dev/null +++ b/dom/l10n/tests/mochitest/dom_localization/test_overlay.html @@ -0,0 +1,56 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Test DOMLocalization's DOMOverlay functionality</title> + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> + <script type="application/javascript"> + "use strict"; + async function* generateBundles(resourceIds) { + const bundle = new FluentBundle("en-US"); + bundle.addResource(new FluentResource("title = <strong>Hello</strong> World")); + bundle.addResource(new FluentResource(`title2 = This is <a data-l10n-name="link">a link</a>!`)); + yield bundle; + } + + window.onload = async function() { + SimpleTest.waitForExplicitFinish(); + + const domLoc = new DOMLocalization( + [], + false, + { generateBundles }, + ); + + const p1 = document.querySelectorAll("p")[0]; + const p2 = document.querySelectorAll("p")[1]; + const a = p2.querySelector("a"); + // We want to test that the event listener persists after + // translateFragment(). + a.addEventListener("click", function(e) { + SimpleTest.finish(); + // We cannot connect to non-local connections on automation, so prevent + // the navigation. + e.preventDefault(); + }); + + await domLoc.translateFragment(document.body); + + + is(p1.querySelector("strong").textContent, "Hello"); + + is(p2.querySelector("a").getAttribute("href"), "http://www.mozilla.org"); + is(p2.querySelector("a").textContent, "a link"); + + a.click(); + }; + </script> +</head> +<body> + <p data-l10n-id="title" /> + <p data-l10n-id="title2"> + <a href="http://www.mozilla.org" data-l10n-name="link"></a> + </p> +</body> +</html> diff --git a/dom/l10n/tests/mochitest/dom_localization/test_overlay_missing_all.html b/dom/l10n/tests/mochitest/dom_localization/test_overlay_missing_all.html new file mode 100644 index 0000000000..2200fe7ea6 --- /dev/null +++ b/dom/l10n/tests/mochitest/dom_localization/test_overlay_missing_all.html @@ -0,0 +1,40 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Test DOMLocalization's DOMOverlay functionality</title> + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> + <script type="application/javascript"> + "use strict"; + async function* generateBundles(resourceIds) { + const bundle = new FluentBundle("en-US"); + // No translations! + yield bundle; + } + + SimpleTest.waitForExplicitFinish(); + addLoadEvent(async () => { + const domLoc = new DOMLocalization( + [], + false, + { generateBundles }, + ); + + await domLoc.translateFragment(document.body).then(() => { + ok(false, "Expected translateFragment to throw on missing l10n-id"); + }, () => { + ok(true, "Expected translateFragment to throw on missing l10n-id"); + }); + SimpleTest.finish(); + }); + </script> +</head> +<body> + <p data-l10n-id="title"> + <a href="http://www.mozilla.org"></a> + <a href="http://www.firefox.com"></a> + <a href="http://www.w3.org"></a> + </p> +</body> +</html> diff --git a/dom/l10n/tests/mochitest/dom_localization/test_overlay_missing_children.html b/dom/l10n/tests/mochitest/dom_localization/test_overlay_missing_children.html new file mode 100644 index 0000000000..20d58ba630 --- /dev/null +++ b/dom/l10n/tests/mochitest/dom_localization/test_overlay_missing_children.html @@ -0,0 +1,49 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Test DOMLocalization's DOMOverlay functionality</title> + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> + <script type="application/javascript"> + "use strict"; + async function* generateBundles(resourceIds) { + const bundle = new FluentBundle("en-US"); + bundle.addResource(new FluentResource(`title = Visit <a data-l10n-name="mozilla-link">Mozilla</a> or <a data-l10n-name="firefox-link">Firefox</a> website!`)); + yield bundle; + } + + window.onload = async function() { + SimpleTest.waitForExplicitFinish(); + + const domLoc = new DOMLocalization( + [], + false, + { generateBundles }, + ); + + await domLoc.translateFragment(document.body); + + const p1 = document.querySelectorAll("p")[0]; + const linkList = p1.querySelectorAll("a"); + + + is(linkList[0].getAttribute("href"), "http://www.mozilla.org"); + is(linkList[0].textContent, "Mozilla"); + is(linkList[1].getAttribute("href"), "http://www.firefox.com"); + is(linkList[1].textContent, "Firefox"); + + is(linkList.length, 2, "There should be exactly two links in the result."); + + SimpleTest.finish(); + }; + </script> +</head> +<body> + <p data-l10n-id="title"> + <a href="http://www.mozilla.org" data-l10n-name="mozilla-link"></a> + <a href="http://www.firefox.com" data-l10n-name="firefox-link"></a> + <a href="http://www.w3.org" data-l10n-name="w3-link"></a> + </p> +</body> +</html> diff --git a/dom/l10n/tests/mochitest/dom_localization/test_overlay_repeated.html b/dom/l10n/tests/mochitest/dom_localization/test_overlay_repeated.html new file mode 100644 index 0000000000..2d202b2e7f --- /dev/null +++ b/dom/l10n/tests/mochitest/dom_localization/test_overlay_repeated.html @@ -0,0 +1,46 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Test DOMLocalization's DOMOverlay functionality</title> + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> + <script type="application/javascript"> + "use strict"; + async function* generateBundles(resourceIds) { + const bundle = new FluentBundle("en-US"); + bundle.addResource(new FluentResource(`title = Visit <a data-l10n-name="mozilla-link">Mozilla</a> or <a data-l10n-name="firefox-link">Firefox</a> website!`)); + yield bundle; + } + + window.onload = async function() { + SimpleTest.waitForExplicitFinish(); + + const domLoc = new DOMLocalization( + [], + false, + { generateBundles }, + ); + + await domLoc.translateFragment(document.body); + + const p1 = document.querySelectorAll("p")[0]; + const linkList = p1.querySelectorAll("a"); + + + is(linkList[0].getAttribute("href"), "http://www.mozilla.org"); + is(linkList[0].textContent, "Mozilla"); + is(linkList[1].getAttribute("href"), "http://www.firefox.com"); + is(linkList[1].textContent, "Firefox"); + + SimpleTest.finish(); + }; + </script> +</head> +<body> + <p data-l10n-id="title"> + <a href="http://www.mozilla.org" data-l10n-name="mozilla-link"></a> + <a href="http://www.firefox.com" data-l10n-name="firefox-link"></a> + </p> +</body> +</html> diff --git a/dom/l10n/tests/mochitest/dom_localization/test_overlay_sanitized.html b/dom/l10n/tests/mochitest/dom_localization/test_overlay_sanitized.html new file mode 100644 index 0000000000..74472e88b0 --- /dev/null +++ b/dom/l10n/tests/mochitest/dom_localization/test_overlay_sanitized.html @@ -0,0 +1,50 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Test DOMLocalization's DOMOverlay functionality</title> + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> + <script type="application/javascript"> + "use strict"; + async function* generateBundles(resourceIds) { + const bundle = new FluentBundle("en-US"); + bundle.addResource(new FluentResource(` +key1 = + .href = https://www.hacked.com + +key2 = + .href = https://pl.wikipedia.org +`)); + yield bundle; + } + + async function test() { + const domLoc = new DOMLocalization( + [], + false, + { generateBundles }, + ); + + await domLoc.translateFragment(document.body); + + const key1Elem = document.querySelector("[data-l10n-id=key1]"); + const key2Elem = document.querySelector("[data-l10n-id=key2]"); + + + is(key1Elem.hasAttribute("href"), false, "href translation should not be allowed"); + is(key2Elem.getAttribute("href"), "https://pl.wikipedia.org", + "href translation should be allowed"); + + SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addLoadEvent(test); + </script> +</head> +<body> + <a data-l10n-id="key1"></a> + <a data-l10n-id="key2" data-l10n-attrs="href"></a> +</body> +</html> diff --git a/dom/l10n/tests/mochitest/dom_localization/test_repeated_l10nid.html b/dom/l10n/tests/mochitest/dom_localization/test_repeated_l10nid.html new file mode 100644 index 0000000000..5ac848c033 --- /dev/null +++ b/dom/l10n/tests/mochitest/dom_localization/test_repeated_l10nid.html @@ -0,0 +1,58 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Test DOMLocalization's matching l10nIds functionality</title> + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> + <script type="application/javascript"> + "use strict"; + async function* generateBundles(resourceIds) { + const bundle = new FluentBundle("en-US"); + bundle.addResource(new FluentResource(` +key1 = Translation For Key 1 + +key2 = Visit <a data-l10n-name="link">this link<a/>. + `)); + yield bundle; + } + + SimpleTest.waitForExplicitFinish(); + addLoadEvent(async () => { + const domLoc = new DOMLocalization( + [], + false, + { generateBundles }, + ); + + await domLoc.translateFragment(document.body); + + ok(document.querySelector("#elem1").textContent.includes("Key 1")); + ok(document.querySelector("#elem2").textContent.includes("Key 1")); + + const elem3 = document.querySelector("#elem3"); + const elem4 = document.querySelector("#elem4"); + + ok(elem3.textContent.includes("Visit")); + is(elem3.querySelector("a").getAttribute("href"), "http://www.mozilla.org"); + + ok(elem4.textContent.includes("Visit")); + is(elem4.querySelector("a").getAttribute("href"), "http://www.firefox.com"); + + SimpleTest.finish(); + }); + </script> +</head> +<body> + <h1 id="elem1" data-l10n-id="key1"></h1> + <h2 id="elem2" data-l10n-id="key1"></h2> + + <p id="elem3" data-l10n-id="key2"> + <a href="http://www.mozilla.org" data-l10n-name="link"></a> + </p> + + <p id="elem4" data-l10n-id="key2"> + <a href="http://www.firefox.com" data-l10n-name="link"></a> + </p> +</body> +</html> diff --git a/dom/l10n/tests/mochitest/dom_localization/test_setAttributes.html b/dom/l10n/tests/mochitest/dom_localization/test_setAttributes.html new file mode 100644 index 0000000000..1c4d93c2e7 --- /dev/null +++ b/dom/l10n/tests/mochitest/dom_localization/test_setAttributes.html @@ -0,0 +1,38 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Test DOMLocalization.prototype.setAttributes</title> + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> + <script type="application/javascript"> + "use strict"; + + async function* generateBundles(resourceIds) {} + + window.onload = function() { + SimpleTest.waitForExplicitFinish(); + + const domLoc = new DOMLocalization( + [], + false, + { generateBundles }, + ); + + const p1 = document.querySelectorAll("p")[0]; + + domLoc.setAttributes(p1, "title"); + is(p1.getAttribute("data-l10n-id"), "title"); + + domLoc.setAttributes(p1, "title2", {userName: "John"}); + is(p1.getAttribute("data-l10n-id"), "title2"); + is(p1.getAttribute("data-l10n-args"), JSON.stringify({userName: "John"})); + + SimpleTest.finish(); + }; + </script> +</head> +<body> + <p /> +</body> +</html> diff --git a/dom/l10n/tests/mochitest/dom_localization/test_translateElements.html b/dom/l10n/tests/mochitest/dom_localization/test_translateElements.html new file mode 100644 index 0000000000..20bc015a66 --- /dev/null +++ b/dom/l10n/tests/mochitest/dom_localization/test_translateElements.html @@ -0,0 +1,42 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Test DOMLocalization.prototype.translateElements</title> + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> + <script type="application/javascript"> + "use strict"; + async function* generateBundles(resourceIds) { + const bundle = new FluentBundle("en-US"); + bundle.addResource(new FluentResource("title = Hello World")); + bundle.addResource(new FluentResource("link =\n .title = Click me")); + yield bundle; + } + + window.onload = async function() { + SimpleTest.waitForExplicitFinish(); + + const domLoc = new DOMLocalization( + [], + false, + { generateBundles }, + ); + + const p1 = document.querySelectorAll("p")[0]; + const link1 = document.querySelectorAll("a")[0]; + + await domLoc.translateElements([p1, link1]); + + is(p1.textContent, "Hello World"); + is(link1.getAttribute("title"), "Click me"); + + SimpleTest.finish(); + }; + </script> +</head> +<body> + <p data-l10n-id="title" /> + <a data-l10n-id="link" /> +</body> +</html> diff --git a/dom/l10n/tests/mochitest/dom_localization/test_translateFragment.html b/dom/l10n/tests/mochitest/dom_localization/test_translateFragment.html new file mode 100644 index 0000000000..8f592bbfb7 --- /dev/null +++ b/dom/l10n/tests/mochitest/dom_localization/test_translateFragment.html @@ -0,0 +1,44 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Test DOMLocalization.prototype.translateFragment</title> + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> + <script type="application/javascript"> + "use strict"; + async function* generateBundles(resourceIds) { + const bundle = new FluentBundle("en-US"); + bundle.addResource(new FluentResource("title = Hello World")); + bundle.addResource(new FluentResource("subtitle = Welcome to FluentBundle")); + yield bundle; + } + + window.onload = async function() { + SimpleTest.waitForExplicitFinish(); + + const domLoc = new DOMLocalization( + [], + false, + { generateBundles }, + ); + + const frag = document.querySelectorAll("div")[0]; + const h1 = document.querySelectorAll("h1")[0]; + const p1 = document.querySelectorAll("p")[0]; + + await domLoc.translateFragment(frag); + is(h1.textContent, "Hello World"); + is(p1.textContent, "Welcome to FluentBundle"); + + SimpleTest.finish(); + }; + </script> +</head> +<body> + <div> + <h1 data-l10n-id="title" /> + <p data-l10n-id="subtitle" /> + </div> +</body> +</html> diff --git a/dom/l10n/tests/mochitest/dom_localization/test_translateRoots.html b/dom/l10n/tests/mochitest/dom_localization/test_translateRoots.html new file mode 100644 index 0000000000..1daba8c5ae --- /dev/null +++ b/dom/l10n/tests/mochitest/dom_localization/test_translateRoots.html @@ -0,0 +1,52 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Test DOMLocalization.prototype.translateRoots</title> + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> + <script type="application/javascript"> + "use strict"; + async function* generateBundles(resourceIds) { + const bundle = new FluentBundle("en-US"); + bundle.addResource(new FluentResource("title = Hello World")); + bundle.addResource(new FluentResource("title2 = Hello Another World")); + yield bundle; + } + + window.onload = async function() { + SimpleTest.waitForExplicitFinish(); + + const domLoc = new DOMLocalization( + [], + false, + { generateBundles }, + ); + + const frag1 = document.querySelectorAll("div")[0]; + const frag2 = document.querySelectorAll("div")[1]; + const h1 = document.querySelectorAll("h1")[0]; + const h2 = document.querySelectorAll("h2")[0]; + + domLoc.addResourceIds(["dummy.ftl"]); + domLoc.connectRoot(frag1); + domLoc.connectRoot(frag2); + + await domLoc.translateRoots(); + + is(h1.textContent, "Hello World"); + is(h2.textContent, "Hello Another World"); + + SimpleTest.finish(); + }; + </script> +</head> +<body> + <div> + <h1 data-l10n-id="title"></h1> + </div> + <div> + <h2 data-l10n-id="title2"></h2> + </div> +</body> +</html> diff --git a/dom/l10n/tests/mochitest/l10n_mutations/test_append_content_post_dcl.html b/dom/l10n/tests/mochitest/l10n_mutations/test_append_content_post_dcl.html new file mode 100644 index 0000000000..2b2cf6d881 --- /dev/null +++ b/dom/l10n/tests/mochitest/l10n_mutations/test_append_content_post_dcl.html @@ -0,0 +1,30 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Test L10n Mutations for ContentAppended after DOMContentLoaded</title> + <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> + <link rel="localization" href="crashreporter/aboutcrashes.ftl"/> + <script type="application/javascript"> + "use strict"; + SimpleTest.waitForExplicitFinish(); + + document.addEventListener("DOMContentLoaded", function() { + let elem = document.createElement("div"); + document.l10n.setAttributes(elem, "crash-reports-title"); + is(elem.textContent.length, 0); + let verifyL10n = () => { + if (elem.textContent.length > 0) { + window.removeEventListener("MozAfterPaint", verifyL10n); + SimpleTest.finish(); + } + }; + window.addEventListener("MozAfterPaint", verifyL10n); + document.body.appendChild(elem); + }); + </script> +</head> +<body> +</body> +</html> diff --git a/dom/l10n/tests/mochitest/l10n_mutations/test_append_content_pre_dcl.html b/dom/l10n/tests/mochitest/l10n_mutations/test_append_content_pre_dcl.html new file mode 100644 index 0000000000..80255cf649 --- /dev/null +++ b/dom/l10n/tests/mochitest/l10n_mutations/test_append_content_pre_dcl.html @@ -0,0 +1,28 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Test L10n Mutations for ContentAppended before DOMContentLoaded</title> + <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> + <link rel="localization" href="crashreporter/aboutcrashes.ftl"/> +</head> +<body> + <script type="application/javascript"> + "use strict"; + SimpleTest.waitForExplicitFinish(); + + let elem = document.createElement("div"); + document.l10n.setAttributes(elem, "crash-reports-title"); + is(elem.textContent.length, 0); + let verifyL10n = () => { + if (elem.textContent.length > 0) { + window.removeEventListener("MozAfterPaint", verifyL10n); + SimpleTest.finish(); + } + }; + window.addEventListener("MozAfterPaint", verifyL10n); + document.body.appendChild(elem); + </script> +</body> +</html> diff --git a/dom/l10n/tests/mochitest/l10n_mutations/test_append_fragment_post_dcl.html b/dom/l10n/tests/mochitest/l10n_mutations/test_append_fragment_post_dcl.html new file mode 100644 index 0000000000..b87479868e --- /dev/null +++ b/dom/l10n/tests/mochitest/l10n_mutations/test_append_fragment_post_dcl.html @@ -0,0 +1,39 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Test L10n Mutations for appending a fragment after DOMContentLoaded</title> + <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> + <link rel="localization" href="crashreporter/aboutcrashes.ftl"/> + <script type="application/javascript"> + "use strict"; + SimpleTest.waitForExplicitFinish(); + + document.addEventListener("DOMContentLoaded", function() { + let frag = document.createDocumentFragment(); + let elem = document.createElement("div"); + document.l10n.setAttributes(elem, "crash-reports-title"); + frag.appendChild(elem); + + let elem2 = document.createElement("div"); + document.l10n.setAttributes(elem2, "crash-reports-title"); + frag.appendChild(elem2); + + is(elem.textContent.length, 0); + is(elem2.textContent.length, 0); + + let verifyL10n = () => { + if (elem.textContent.length > 0 && elem2.textContent.length > 0) { + window.removeEventListener("MozAfterPaint", verifyL10n); + SimpleTest.finish(); + } + }; + window.addEventListener("MozAfterPaint", verifyL10n); + document.body.appendChild(frag); + }); + </script> +</head> +<body> +</body> +</html> diff --git a/dom/l10n/tests/mochitest/l10n_mutations/test_disconnectedRoot_webcomponent.html b/dom/l10n/tests/mochitest/l10n_mutations/test_disconnectedRoot_webcomponent.html new file mode 100644 index 0000000000..9d9f0cfb2a --- /dev/null +++ b/dom/l10n/tests/mochitest/l10n_mutations/test_disconnectedRoot_webcomponent.html @@ -0,0 +1,146 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Test DOMLocalization.prototype.connectRoot with Web Components</title> + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> + <script type="application/javascript"> + "use strict"; + async function* generateBundles(resourceIds) { + const bundle = new FluentBundle("en-US"); + bundle.addResource(new FluentResource(` +key1 = Key 1 +key2 = Key 2 +key3 = Key 3 +key4 = Key 4 + `)); + yield bundle; + } + + document.domLoc = new DOMLocalization( + [], + false, + { generateBundles } + ); + document.domLoc.connectRoot(document.documentElement); + </script> + <script type="application/javascript"> + // In this test we're going to use two elements - `shadowLabel` and `lightLabel`. + // We create a new `DOMLocalization` and connect it to the document's root first. + // + // Then, we connect and disconnect it on root and element within the shadow DOM and + // apply new `data-l10n-id` onto both labels. + // Once the `lightLabel` get a new translation, we check what happened to the `shadowLabel` + // to ensure that depending on the status of connection between the shadow DOM and the `DOMLocalization` + // the `shadowLabel` either gets translated or not. + + SimpleTest.waitForExplicitFinish(); + + class FluentWidget extends HTMLElement { + constructor() { + super(); + const shadowRoot = this.attachShadow({mode: "open"}); + const t = document.querySelector("#fluent-widget-template"); + const instance = t.content.cloneNode(true); + shadowRoot.appendChild(instance); + } + connectedCallback() { + this.runTests(); + } + runTests() { + // First, let's verify that the mutation will not be applied since + // the shadow DOM is not connected to the `DOMLocalization`. + let shadowLabel = this.shadowRoot.getElementById("shadowLabel"); + let lightLabel = document.getElementById("lightLabel"); + + let verifyL10n = () => { + if (lightLabel.textContent == "Key 1") { + is(shadowLabel.textContent, "", "document.l10n content not applied to an element in the shadow DOM"); + window.removeEventListener("MozAfterPaint", verifyL10n); + this.testPart2(); + } + }; + window.addEventListener("MozAfterPaint", verifyL10n); + + document.domLoc.setAttributes(shadowLabel, "key1"); + document.domLoc.setAttributes(lightLabel, "key1"); + } + testPart2() { + // Next, we connect the shadow root to DOMLocalization and the next attribute + // change should result in a translation being applied. + document.domLoc.connectRoot(this.shadowRoot); + + let shadowLabel = this.shadowRoot.getElementById("shadowLabel"); + let lightLabel = document.getElementById("lightLabel"); + + // Test that mutation was applied. + let verifyL10n = () => { + if (lightLabel.textContent == "Key 2") { + is(shadowLabel.textContent, "Key 2", "document.l10n content applied to an element in the shadow DOM"); + window.removeEventListener("MozAfterPaint", verifyL10n); + this.testPart3(); + } + }; + window.addEventListener("MozAfterPaint", verifyL10n); + + document.domLoc.setAttributes(shadowLabel, "key2"); + document.domLoc.setAttributes(lightLabel, "key2"); + } + testPart3() { + // After we disconnect the shadow root, the mutations should + // not be applied onto the `shadowLabel`. + document.domLoc.disconnectRoot(this.shadowRoot); + + let shadowLabel = this.shadowRoot.getElementById("shadowLabel"); + let lightLabel = document.getElementById("lightLabel"); + + let verifyL10n = () => { + if (lightLabel.textContent == "Key 3") { + is(shadowLabel.textContent, "Key 2", "document.l10n content not applied to an element in the shadow DOM"); + window.removeEventListener("MozAfterPaint", verifyL10n); + this.testPart4(); + } + }; + window.addEventListener("MozAfterPaint", verifyL10n); + + document.domLoc.setAttributes(shadowLabel, "key3"); + document.domLoc.setAttributes(lightLabel, "key3"); + } + testPart4() { + // Finally, we'll connect it back, but this time, we'll connect + // not the shadow root, but an element within it. + // This should still result in the `shadowLabel` receiving a new translation. + document.domLoc.connectRoot(this.shadowRoot.getElementById("shadowDiv")); + + let shadowLabel = this.shadowRoot.getElementById("shadowLabel"); + let lightLabel = document.getElementById("lightLabel"); + + // Test that mutation was applied. + let verifyL10n = () => { + if (lightLabel.textContent == "Key 4") { + is(shadowLabel.textContent, "Key 4", "document.l10n content applied to an element in the shadow DOM"); + window.removeEventListener("MozAfterPaint", verifyL10n); + SimpleTest.finish(); + } + }; + window.addEventListener("MozAfterPaint", verifyL10n); + + document.domLoc.setAttributes(shadowLabel, "key4"); + document.domLoc.setAttributes(lightLabel, "key4"); + } + } + customElements.define("fluent-widget", FluentWidget); + </script> +</head> +<body> + <p id="lightLabel"></p> + + <template id="fluent-widget-template"> + <div id="shadowDiv"> + <p id="shadowLabel"></p> + </div> + </template> + <fluent-widget id="widget1"></fluent-widget> +</body> +</html> diff --git a/dom/l10n/tests/mochitest/l10n_mutations/test_pause_observing.html b/dom/l10n/tests/mochitest/l10n_mutations/test_pause_observing.html new file mode 100644 index 0000000000..1ba6e897f3 --- /dev/null +++ b/dom/l10n/tests/mochitest/l10n_mutations/test_pause_observing.html @@ -0,0 +1,44 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Test L10n Mutations for Pause/Resume Observing</title> + <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> + <link rel="localization" href="crashreporter/aboutcrashes.ftl"/> + <script type="application/javascript"> + "use strict"; + SimpleTest.waitForExplicitFinish(); + + document.addEventListener("DOMContentLoaded", function() { + let elem1 = document.createElement("div"); + let elem2 = document.createElement("div"); + let elem3 = document.createElement("div"); + is(elem1.textContent.length, 0); + is(elem2.textContent.length, 0); + is(elem3.textContent.length, 0); + + document.l10n.setAttributes(elem1, "crash-reports-title"); + document.l10n.setAttributes(elem2, "crash-reports-title"); + document.l10n.setAttributes(elem3, "crash-reports-title"); + + let verifyL10n = () => { + if (elem1.textContent.length > 0 && + elem2.textContent.length == 0 && + elem3.textContent.length > 0) { + window.removeEventListener("MozAfterPaint", verifyL10n); + SimpleTest.finish(); + } + }; + window.addEventListener("MozAfterPaint", verifyL10n); + document.body.appendChild(elem1); + document.l10n.pauseObserving(); + document.body.appendChild(elem2); + document.l10n.resumeObserving(); + document.body.appendChild(elem3); + }); + </script> +</head> +<body> +</body> +</html> diff --git a/dom/l10n/tests/mochitest/l10n_mutations/test_set_attributes.html b/dom/l10n/tests/mochitest/l10n_mutations/test_set_attributes.html new file mode 100644 index 0000000000..587c62d7b6 --- /dev/null +++ b/dom/l10n/tests/mochitest/l10n_mutations/test_set_attributes.html @@ -0,0 +1,37 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Test L10n Mutations for AttributeChange after DOMContentLoaded</title> + <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> + <link rel="localization" href="crashreporter/aboutcrashes.ftl"/> + <link rel="localization" href="toolkit/about/aboutCompat.ftl"/> + <script type="application/javascript"> + "use strict"; + SimpleTest.waitForExplicitFinish(); + + document.addEventListener("DOMContentLoaded", async function() { + await document.l10n.ready; + let elem = document.getElementById("elem1"); + let elem2 = document.getElementById("elem2"); + is(elem.textContent.length, 0); + is(elem2.textContent.includes("Initial string"), true); + document.l10n.setAttributes(elem, "crash-reports-title"); + elem2.setAttribute("data-l10n-args", JSON.stringify({bug: "New string"})); + + let verifyL10n = () => { + if (elem.textContent.length > 0 && elem2.textContent.includes("New string")) { + window.removeEventListener("MozAfterPaint", verifyL10n); + SimpleTest.finish(); + } + }; + window.addEventListener("MozAfterPaint", verifyL10n); + }); + </script> +</head> +<body> + <div id="elem1"></div> + <div id="elem2" data-l10n-id="label-more-information" data-l10n-args='{"bug":"Initial string"}'></div> +</body> +</html> diff --git a/dom/l10n/tests/mochitest/l10n_mutations/test_template.html b/dom/l10n/tests/mochitest/l10n_mutations/test_template.html new file mode 100644 index 0000000000..3d8bc60c7b --- /dev/null +++ b/dom/l10n/tests/mochitest/l10n_mutations/test_template.html @@ -0,0 +1,37 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Test L10n Mutations in Template elements</title> + <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> + <link rel="localization" href="crashreporter/aboutcrashes.ftl"/> + <script type="application/javascript"> + "use strict"; + SimpleTest.waitForExplicitFinish(); + + document.addEventListener("DOMContentLoaded", async () => { + await document.l10n.ready; + let template = document.getElementById("template"); + let clone = document.importNode(template.content, true); + let span = clone.querySelector("span"); + is(span.textContent.length, 0, + "Element has not been translated while in template"); + document.body.appendChild(clone); + + let verifyL10n = () => { + if (span.textContent.length > 0) { + window.removeEventListener("MozAfterPaint", verifyL10n); + SimpleTest.finish(); + } + }; + window.addEventListener("MozAfterPaint", verifyL10n); + }); + </script> +</head> +<body> + <template id="template"> + <span data-l10n-id="crash-reports-title"></span> + </template> +</body> +</html> diff --git a/dom/l10n/tests/mochitest/l10n_overlays/test_attributes.html b/dom/l10n/tests/mochitest/l10n_overlays/test_attributes.html new file mode 100644 index 0000000000..3d1f6048b2 --- /dev/null +++ b/dom/l10n/tests/mochitest/l10n_overlays/test_attributes.html @@ -0,0 +1,86 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Test L10nOverlays Top-level attributes</title> + <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> + <script type="application/javascript"> + /* global L10nOverlays */ + "use strict"; + + function elem(name) { + return function(str) { + const element = document.createElement(name); + // eslint-disable-next-line no-unsanitized/property + element.innerHTML = str; + return element; + }; + } + + const { translateElement } = L10nOverlays; + + { + // Allowed attribute + const element = elem("div")``; + const translation = { + value: null, + attributes: [ + {name: "title", value: "FOO"}, + ], + }; + translateElement(element, translation); + is(element.outerHTML, '<div title="FOO"></div>'); + } + + { + // Forbidden attribute + const element = elem("input")``; + const translation = { + value: null, + attributes: [ + {name: "disabled", value: "DISABLED"}, + ], + }; + translateElement(element, translation); + is(element.outerHTML, "<input>"); + } + + { + // Attributes do not leak on first translation + const element = elem("div")`Foo`; + element.setAttribute("title", "Title"); + + const translation = { + value: "FOO", + attributes: null, + }; + translateElement(element, translation); + is(element.outerHTML, "<div>FOO</div>"); + } + + { + // Attributes do not leak on retranslation + const element = elem("div")`Foo`; + + const translationA = { + value: "FOO A", + attributes: [ + {name: "title", value: "TITLE A"}, + ], + }; + + const translationB = { + value: "FOO B", + attributes: null, + }; + translateElement(element, translationA); + is(element.outerHTML, '<div title="TITLE A">FOO A</div>'); + translateElement(element, translationB); + is(element.outerHTML, "<div>FOO B</div>"); + } + </script> +</head> +<body> +</body> +</html> diff --git a/dom/l10n/tests/mochitest/l10n_overlays/test_extra_text_markup.html b/dom/l10n/tests/mochitest/l10n_overlays/test_extra_text_markup.html new file mode 100644 index 0000000000..5f60599dfc --- /dev/null +++ b/dom/l10n/tests/mochitest/l10n_overlays/test_extra_text_markup.html @@ -0,0 +1,136 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Test L10nOverlays Localized text markup</title> + <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> + <script type="application/javascript"> + /* global L10nOverlays */ + "use strict"; + + function elem(name) { + return function(str) { + const element = document.createElement(name); + // eslint-disable-next-line no-unsanitized/property + element.innerHTML = str; + return element; + }; + } + + const { translateElement } = L10nOverlays; + + // Localized text markup + { + // allowed element + const element = elem("div")`Foo`; + const translation = { + value: "FOO <em>BAR</em> BAZ", + attributes: null, + }; + + translateElement(element, translation); + is(element.innerHTML, "FOO <em>BAR</em> BAZ"); + } + + { + // forbidden element + const element = elem("div")`Foo`; + const translation = { + value: 'FOO <img src="img.png" />', + attributes: null, + }; + + translateElement(element, translation); + is(element.innerHTML, "FOO "); + } + + { + // forbiden element with text + const element = elem("div")`Foo`; + const translation = { + value: "FOO <a>a</a>", + attributes: null, + }; + + translateElement(element, translation); + is(element.innerHTML, "FOO a"); + } + + { + // nested HTML is forbidden + const element = elem("div")`Foo`; + const translation = { + value: "FOO <em><strong>BAR</strong></em> BAZ", + attributes: null, + }; + + translateElement(element, translation); + is(element.innerHTML, "FOO <em>BAR</em> BAZ"); + } + + // Attributes of localized text markup + { + // allowed attribute + const element = elem("div")`Foo Bar`; + const translation = { + value: 'FOO <em title="BAR">BAR</em>', + attributes: null, + }; + + translateElement(element, translation); + is(element.innerHTML, + 'FOO <em title="BAR">BAR</em>'); + } + + { + // forbidden attribute + const element = elem("div")`Foo Bar`; + const translation = { + value: 'FOO <em class="BAR" title="BAR">BAR</em>', + attributes: null, + }; + + translateElement(element, translation); + is(element.innerHTML, + 'FOO <em title="BAR">BAR</em>'); + } + + { + // attributes do not leak on first translation + const element = elem("div")` + <em title="Foo">Foo</a>`; + const translation = { + value: "<em>FOO</em>", + attributes: null, + }; + + translateElement(element, translation); + is(element.innerHTML, + "<em>FOO</em>"); + } + + { + // attributes do not leak on retranslation + const element = elem("div")``; + const translationA = { + value: '<em title="FOO A">FOO A</em>', + attributes: null, + }; + const translationB = { + value: "<em>FOO B</em>", + attributes: null, + }; + + translateElement(element, translationA); + is(element.innerHTML, + '<em title="FOO A">FOO A</em>'); + translateElement(element, translationB); + is(element.innerHTML, + "<em>FOO B</em>"); + } + </script> +</head> +<body> +</body> +</html> diff --git a/dom/l10n/tests/mochitest/l10n_overlays/test_functional_children.html b/dom/l10n/tests/mochitest/l10n_overlays/test_functional_children.html new file mode 100644 index 0000000000..dba5b6e633 --- /dev/null +++ b/dom/l10n/tests/mochitest/l10n_overlays/test_functional_children.html @@ -0,0 +1,344 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Test L10nOverlays functional children test</title> + <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> + <script type="application/javascript"> + /* global L10nOverlays */ + "use strict"; + + function elem(name) { + return function(str) { + const element = document.createElement(name); + // eslint-disable-next-line no-unsanitized/property + element.innerHTML = str; + return element; + }; + } + + const { translateElement } = L10nOverlays; + + // Child without name + { + // in source + const element = elem("div")` + <a>Foo</a>`; + const translation = { + value: "FOO", + attributes: null, + }; + translateElement(element, translation); + is(element.innerHTML, "FOO"); + } + + { + // in translation + const element = elem("div")`Foo`; + const translation = { + value: "<a>FOO</a>", + attributes: null, + }; + translateElement(element, translation); + is(element.innerHTML, "FOO"); + } + + { + // in both + const element = elem("div")` + <a>Foo</a>`; + const translation = { + value: "<a>FOO</a>", + attributes: null, + }; + translateElement(element, translation); + is(element.innerHTML, "FOO"); + } + + // Child with name + { + // in source + const element = elem("div")` + <a data-l10n-name="foo">Foo</a>`; + const translation = { + value: "<a>FOO</a>", + attributes: null, + }; + translateElement(element, translation); + is(element.innerHTML, "FOO"); + } + + { + // in translation + const element = elem("div")` + <a>Foo</a>`; + const translation = { + value: '<a data-l10n-name="foo">FOO</a>', + attributes: null, + }; + translateElement(element, translation); + is(element.innerHTML, "FOO"); + } + + { + // in both + const element = elem("div")` + <a data-l10n-name="foo">Foo</a>`; + const translation = { + value: '<a data-l10n-name="foo">FOO</a>', + attributes: null, + }; + translateElement(element, translation); + is(element.innerHTML, '<a data-l10n-name="foo">FOO</a>'); + } + + { + // translation without text content + const element = elem("div")` + <a data-l10n-name="foo">Foo</a>`; + const translation = { + value: '<a data-l10n-name="foo"></a>', + attributes: null, + }; + translateElement(element, translation); + is(element.innerHTML, '<a data-l10n-name="foo"></a>'); + } + + { + // different names + const element = elem("div")` + <a data-l10n-name="foo">Foo</a>`; + const translation = { + value: '<a data-l10n-name="bar">BAR</a>', + attributes: null, + }; + translateElement(element, translation); + is(element.innerHTML, "BAR"); + } + + { + // of different types + const element = elem("div")` + <a data-l10n-name="foo">Foo</a>`; + const translation = { + value: '<div data-l10n-name="foo">FOO</div>', + attributes: null, + }; + translateElement(element, translation); + is(element.innerHTML, "FOO"); + } + + { + // used twice + const element = elem("div")` + <a data-l10n-name="foo">Foo</a>`; + const translation = { + value: '<a data-l10n-name="foo">FOO 1</a> <a data-l10n-name="foo">FOO 2</a>', + attributes: null, + }; + translateElement(element, translation); + is(element.innerHTML, '<a data-l10n-name="foo">FOO 1</a> FOO 2'); + } + + // Two named children + { + // in order + const element = elem("div")` + <a data-l10n-name="foo">Foo</a> + <a data-l10n-name="bar">Bar</a>`; + const translation = { + value: '<a data-l10n-name="foo">FOO</a><a data-l10n-name="bar">BAR</a>', + attributes: null, + }; + translateElement(element, translation); + is(element.innerHTML, '<a data-l10n-name="foo">FOO</a><a data-l10n-name="bar">BAR</a>'); + } + + { + // out of order + const element = elem("div")` + <a data-l10n-name="foo">Foo</a> + <a data-l10n-name="bar">Bar</a>`; + const translation = { + value: '<a data-l10n-name="bar">BAR</a><a data-l10n-name="foo">FOO</a>', + attributes: null, + }; + translateElement(element, translation); + is(element.innerHTML, '<a data-l10n-name="bar">BAR</a><a data-l10n-name="foo">FOO</a>'); + } + + { + // nested in source + const element = elem("div")` + <a data-l10n-name="foo"> + Foo 1 + <a data-l10n-name="bar">Bar</a> + Foo 2 + </a>`; + const translation = { + value: '<a data-l10n-name="foo">FOO</a><a data-l10n-name="bar">BAR</a>', + attributes: null, + }; + translateElement(element, translation); + is( + element.innerHTML, + '<a data-l10n-name="foo">FOO</a><a data-l10n-name="bar">BAR</a>' + ); + } + + { + // nested in translation + const element = elem("div")` + <div data-l10n-name="foo">Foo</div> + <div data-l10n-name="bar">Bar</div>`; + const translation = { + value: '<div data-l10n-name="foo">FOO 1 <div data-l10n-name="bar">BAR</div> FOO 2</div>', + attributes: null, + }; + translateElement(element, translation); + is( + element.innerHTML, + '<div data-l10n-name="foo">FOO 1 BAR FOO 2</div>' + ); + } + + // Child attributes + { + // functional attribute in source + const element = elem("div")` + <a data-l10n-name="foo" class="foo">Foo</a>`; + const translation = { + value: '<a data-l10n-name="foo">FOO</a>', + attributes: null, + }; + + translateElement(element, translation); + is(element.innerHTML, + '<a data-l10n-name="foo" class="foo">FOO</a>'); + } + + { + // functional attribute in translation + const element = elem("div")` + <a data-l10n-name="foo">Foo</a>`; + const translation = { + value: '<a data-l10n-name="foo" class="bar">FOO</a>', + attributes: null, + }; + + translateElement(element, translation); + is(element.innerHTML, + '<a data-l10n-name="foo">FOO</a>'); + } + + { + // functional attribute in both + const element = elem("div")` + <a data-l10n-name="foo" class="foo">Foo</a>`; + const translation = { + value: '<a data-l10n-name="foo" class="bar">FOO</a>', + attributes: null, + }; + + translateElement(element, translation); + is(element.innerHTML, + '<a data-l10n-name="foo" class="foo">FOO</a>'); + } + + { + // localizable attribute in source + const element = elem("div")` + <a data-l10n-name="foo" title="Foo">Foo</a>`; + const translation = { + value: '<a data-l10n-name="foo">FOO</a>', + attributes: null, + }; + + translateElement(element, translation); + is(element.innerHTML, + '<a data-l10n-name="foo">FOO</a>'); + } + + { + // localizable attribute in translation + const element = elem("div")` + <a data-l10n-name="foo">Foo</a>`; + const translation = { + value: '<a data-l10n-name="foo" title="FOO">FOO</a>', + attributes: null, + }; + + translateElement(element, translation); + is(element.innerHTML, + '<a data-l10n-name="foo" title="FOO">FOO</a>'); + } + + { + // localizable attribute in both + const element = elem("div")` + <a data-l10n-name="foo" title="Foo">Foo</a>`; + const translation = { + value: '<a data-l10n-name="foo" title="BAR">FOO</a>', + attributes: null, + }; + + translateElement(element, translation); + is(element.innerHTML, + '<a data-l10n-name="foo" title="BAR">FOO</a>'); + } + + { + // localizable attribute does not leak on retranslation + const element = elem("div")` + <a data-l10n-name="foo">Foo</a>`; + const translationA = { + value: '<a data-l10n-name="foo" title="FOO A">FOO A</a>', + attributes: null, + }; + const translationB = { + value: '<a data-l10n-name="foo">FOO B</a>', + attributes: null, + }; + + translateElement(element, translationA); + is(element.innerHTML, + '<a data-l10n-name="foo" title="FOO A">FOO A</a>'); + translateElement(element, translationB); + is(element.innerHTML, + '<a data-l10n-name="foo">FOO B</a>'); + } + + // Child attributes overrides + { + // the source can override child's attributes + const element = elem("div")` + <a data-l10n-name="foo" data-l10n-attrs="class" class="foo">Foo</a>`; + const translation = { + value: '<a data-l10n-name="foo" class="FOO">FOO</a>', + attributes: null, + }; + + translateElement(element, translation); + is(element.innerHTML, + '<a data-l10n-name="foo" data-l10n-attrs="class" class="FOO">FOO</a>'); + } + + { + // the translation cannot override child's attributes + const element = elem("div")` + <a data-l10n-name="foo" class="foo">Foo</a>`; + const translation = { + value: '<a data-l10n-name="foo" data-l10n-attrs="class" class="FOO">FOO</a>', + attributes: null, + }; + + translateElement(element, translation); + is(element.innerHTML, + '<a data-l10n-name="foo" class="foo">FOO</a>'); + } + </script> +</head> +<body> +</body> +</html> diff --git a/dom/l10n/tests/mochitest/l10n_overlays/test_l10n_overlays.xhtml b/dom/l10n/tests/mochitest/l10n_overlays/test_l10n_overlays.xhtml new file mode 100644 index 0000000000..494958c573 --- /dev/null +++ b/dom/l10n/tests/mochitest/l10n_overlays/test_l10n_overlays.xhtml @@ -0,0 +1,87 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" + type="text/css"?> + + +<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + xmlns:html="http://www.w3.org/1999/xhtml" + title="Testing DocumentL10n in XUL environment"> + + <linkset> + <html:link rel="localization" href="toolkit/about/aboutAddons.ftl"/> + </linkset> + + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" /> + <script type="application/javascript"> + <![CDATA[ + /* global L10nOverlays */ + + function elem(name) { + return function(str) { + const element = document.createXULElement(name); + // eslint-disable-next-line no-unsanitized/property + element.innerHTML = str; + return element; + }; + } + + const { translateElement } = L10nOverlays; + + SimpleTest.waitForExplicitFinish(); + + { + // Allowed attribute + const element = elem("description")``; + const translation = { + value: null, + attributes: [ + {name: "title", value: "FOO"}, + ], + }; + translateElement(element, translation); + is(element.outerHTML, '<description xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" title="FOO"/>'); + } + + document.addEventListener("DOMContentLoaded", () => { + { + // Handle HTML translation + const element = document.getElementById("test2"); + const translation = { + value: "This is <a data-l10n-name=\"link\">a link</a>.", + attributes: null, + }; + translateElement(element, translation); + is(element.innerHTML, 'This is <html:a xmlns:html="http://www.w3.org/1999/xhtml" data-l10n-name=\"link\" href="https://www.mozilla.org\">a link</html:a>.'); + } + + { + // Don't handle XUL translation + // + // Current iteration of L10nOverlays will replace + // XUL elements from translation with text. + // + // See bug 1545704 for details. + const element = document.getElementById("test3"); + const translation = { + value: "This is <description data-l10n-name=\"desc\">a desc</description>.", + attributes: null, + }; + translateElement(element, translation); + is(element.innerHTML, 'This is a desc.'); + } + SimpleTest.finish(); + }, {once: true}); + + ]]> + </script> + + <description id="test2"> + <html:a data-l10n-name="link" href="https://www.mozilla.org"/> + </description> + + <box id="test3"> + <description data-l10n-name="desc"/> + </box> +</window> diff --git a/dom/l10n/tests/mochitest/l10n_overlays/test_same_id.html b/dom/l10n/tests/mochitest/l10n_overlays/test_same_id.html new file mode 100644 index 0000000000..a6567eda4e --- /dev/null +++ b/dom/l10n/tests/mochitest/l10n_overlays/test_same_id.html @@ -0,0 +1,57 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Test Amount of mutations generated from DOM Overlays</title> + <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> + <link rel="localization" href="toolkit/about/aboutTelemetry.ftl"/> + <script type="application/javascript"> + "use strict"; + SimpleTest.waitForExplicitFinish(); + + let config = { + attributes: true, + attributeOldValue: true, + characterData: true, + characterDataOldValue: true, + childList: true, + subtree: true, + }; + let allMutations = []; + + document.addEventListener("DOMContentLoaded", async function() { + await document.l10n.ready; + + let inputElem = document.getElementById("search-input"); + + // Test for initial localization applied. + is(inputElem.getAttribute("placeholder").length > 0, true); + + let observer = new MutationObserver((mutations) => { + for (let mutation of mutations) { + allMutations.push(mutation); + } + }); + observer.observe(inputElem, config); + + document.l10n.setAttributes(inputElem, "about-telemetry-filter-all-placeholder"); + + // Due to the async iteractions between nsINode.localize + // and DOMLocalization.jsm, we'll need to wait two frames + // to verify that no mutations happened. + requestAnimationFrame(() => { + requestAnimationFrame(() => { + // Since the l10n-id is the same as the previous one + // no mutation should happen in result. + is(allMutations.length, 0); + SimpleTest.finish(); + }); + }); + }, { once: true}); + </script> +</head> +<body> + <input id="search-input" data-l10n-id="about-telemetry-filter-all-placeholder"></input> +</body> +</html> diff --git a/dom/l10n/tests/mochitest/l10n_overlays/test_same_id_args.html b/dom/l10n/tests/mochitest/l10n_overlays/test_same_id_args.html new file mode 100644 index 0000000000..af2a108e46 --- /dev/null +++ b/dom/l10n/tests/mochitest/l10n_overlays/test_same_id_args.html @@ -0,0 +1,57 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Test Amount of mutations generated from DOM Overlays</title> + <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> + <link rel="localization" href="toolkit/about/aboutTelemetry.ftl"/> + <script type="application/javascript"> + "use strict"; + SimpleTest.waitForExplicitFinish(); + + let config = { + attributes: true, + attributeOldValue: true, + characterData: true, + characterDataOldValue: true, + childList: true, + subtree: true, + }; + let allMutations = []; + + document.addEventListener("DOMContentLoaded", async function() { + await document.l10n.ready; + + let inputElem = document.getElementById("search-input"); + + // Test for initial localization applied. + is(inputElem.getAttribute("placeholder").length > 0, true); + + let observer = new MutationObserver((mutations) => { + for (let mutation of mutations) { + allMutations.push(mutation); + } + }); + observer.observe(inputElem, config); + + document.l10n.setAttributes(inputElem, "about-telemetry-filter-placeholder", {selectedTitle: "Test"}); + + // Due to the async iteractions between nsINode.localize + // and DOMLocalization.jsm, we'll need to wait two frames + // to verify that no mutations happened. + requestAnimationFrame(() => { + requestAnimationFrame(() => { + // Since the l10n-id is the same as the previous one + // no mutation should happen in result. + is(allMutations.length, 0); + SimpleTest.finish(); + }); + }); + }, { once: true}); + </script> +</head> +<body> + <input id="search-input" data-l10n-id="about-telemetry-filter-placeholder" data-l10n-args='{"selectedTitle":"Test"}'></input> +</body> +</html> diff --git a/dom/l10n/tests/mochitest/l10n_overlays/test_text_children.html b/dom/l10n/tests/mochitest/l10n_overlays/test_text_children.html new file mode 100644 index 0000000000..1c2fab7ade --- /dev/null +++ b/dom/l10n/tests/mochitest/l10n_overlays/test_text_children.html @@ -0,0 +1,74 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Test L10nOverlays Text-semantic argument elements</title> + <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> + <script type="application/javascript"> + /* global L10nOverlays */ + "use strict"; + + function elem(name) { + return function(str) { + const element = document.createElement(name); + // eslint-disable-next-line no-unsanitized/property + element.innerHTML = str; + return element; + }; + } + + const { translateElement } = L10nOverlays; + + { + // without data-l10n-name + const element = elem("div")` + <em class="bar"></em>`; + const translation = { + value: '<em title="FOO">FOO</em>', + attributes: null, + }; + + translateElement(element, translation); + is( + element.innerHTML, + '<em title="FOO">FOO</em>' + ); + } + + { + // mismatched types + const element = elem("div")` + <button data-l10n-name="foo"></button>`; + const translation = { + value: '<em data-l10n-name="foo" title="FOO">FOO</em>', + attributes: null, + }; + + translateElement(element, translation); + is( + element.innerHTML, + "FOO" + ); + } + + { + // types and names mismatch + const element = elem("div")` + <em data-l10n-name="foo" class="foo"></em>`; + const translation = { + value: '<em data-l10n-name="foo" title="FOO">FOO</em>', + attributes: null, + }; + + translateElement(element, translation); + is( + element.innerHTML, + '<em data-l10n-name="foo" class="foo" title="FOO">FOO</em>' + ); + } + </script> +</head> +<body> +</body> +</html> diff --git a/dom/l10n/tests/mochitest/l10n_overlays/test_title.html b/dom/l10n/tests/mochitest/l10n_overlays/test_title.html new file mode 100644 index 0000000000..4571589b8e --- /dev/null +++ b/dom/l10n/tests/mochitest/l10n_overlays/test_title.html @@ -0,0 +1,60 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Test L10nOverlays Special treatment of the title element</title> + <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> + <script type="application/javascript"> + /* global L10nOverlays */ + "use strict"; + + function elem(name) { + return function(str) { + const element = document.createElement(name); + // eslint-disable-next-line no-unsanitized/property + element.innerHTML = str; + return element; + }; + } + + const { translateElement } = L10nOverlays; + + { + // Text is fine. + const element = elem("title")``; + const translation = { + value: 'Text', + attributes: null, + }; + + translateElement(element, translation); + is( + element.innerHTML, + 'Text' + ); + } + + { + // Markup is ignored. + const element = elem("title")``; + const translation = { + value: '<em>Markup</em>', + attributes: null, + }; + + translateElement(element, translation); + is( + element.textContent, + '<em>Markup</em>' + ); + is( + element.innerHTML, + '<em>Markup</em>' + ); + } + </script> +</head> +<body> +</body> +</html> diff --git a/dom/l10n/tests/mochitest/mochitest.ini b/dom/l10n/tests/mochitest/mochitest.ini new file mode 100644 index 0000000000..82af57c47c --- /dev/null +++ b/dom/l10n/tests/mochitest/mochitest.ini @@ -0,0 +1 @@ +[document_l10n/test_unpriv_iframe.html] |