summaryrefslogtreecommitdiffstats
path: root/dom/l10n/tests/mochitest/l10n_overlays
diff options
context:
space:
mode:
Diffstat (limited to 'dom/l10n/tests/mochitest/l10n_overlays')
-rw-r--r--dom/l10n/tests/mochitest/l10n_overlays/test_attributes.html86
-rw-r--r--dom/l10n/tests/mochitest/l10n_overlays/test_extra_text_markup.html136
-rw-r--r--dom/l10n/tests/mochitest/l10n_overlays/test_functional_children.html344
-rw-r--r--dom/l10n/tests/mochitest/l10n_overlays/test_l10n_overlays.xhtml87
-rw-r--r--dom/l10n/tests/mochitest/l10n_overlays/test_same_id.html57
-rw-r--r--dom/l10n/tests/mochitest/l10n_overlays/test_same_id_args.html57
-rw-r--r--dom/l10n/tests/mochitest/l10n_overlays/test_text_children.html74
-rw-r--r--dom/l10n/tests/mochitest/l10n_overlays/test_title.html60
8 files changed, 901 insertions, 0 deletions
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..ecaefbd68d
--- /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, 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, 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..e43c394970
--- /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, 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, 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,
+ '&lt;em&gt;Markup&lt;/em&gt;'
+ );
+ }
+ </script>
+</head>
+<body>
+</body>
+</html>