LibWeb: Implement HTMLElement.innerText closer to spec

And here's the wild part: instead of cloning WPT tests, import the
relevant WPT tests that this fixes into our own test suite.

This works by adding a small Ladybird-specific callback in
resources/testharnessreport.js (which is what that file is meant for!)

Note that these run as text tests, and so they must signal the runner
when they are done. Tests using the "usual" WPT harness should just
work, but tests that do something more freestyle will need manual
signaling if they are to be imported.

I've also increased the test timeout here from 30 to 60 seconds,
to accommodate the larger WPT-style tests.
This commit is contained in:
Andreas Kling 2024-10-24 19:21:55 +02:00 committed by Andreas Kling
commit ec0838b84e
Notes: github-actions[bot] 2024-10-27 11:11:23 +00:00
25 changed files with 6638 additions and 21 deletions

View file

@ -0,0 +1,88 @@
<!DOCTYPE html>
<title>innerText/outerText getter test with dynamic style changes</title>
<script src="../../../../resources/testharness.js"></script>
<script src="../../../../resources/testharnessreport.js"></script>
<div id="container"></div>
<script>
let container = document.querySelector('#container');
function testText(html, expectedPlain, msg, mutate) {
test(function() {
container.innerHTML = html;
// Cause a flush of style and layout
document.body.offsetTop;
mutate();
var e = document.getElementById('target');
if (!e) {
e = container.firstChild;
}
assert_equals(e.innerText, expectedPlain, "innerText");
assert_equals(e.outerText, expectedPlain, "outerText");
container.textContext = '';
}, msg + ' (' + format_value(html) + ')');
}
function setStyle(id, attr, value) {
let el = document.getElementById(id);
if (el) {
el.style[attr] = value;
}
}
testText("<div id='target'><div id='child'>abc", "ABC",
"text-transform applied to child element", function() {
setStyle("child", "text-transform", "uppercase");
});
testText("<div id='parent'><div id='target'>abc", "ABC",
"text-transform applied to parent element", function() {
setStyle("parent", "text-transform", "uppercase");
});
testText("<div id='target'>abc<div id='child'>def", "abc",
"display: none applied to child element", function() {
setStyle("child", "display", "none");
});
testText("<div id='parent'>invisible<div id='target'>abc", "abc",
"display: none applied to parent element", function() {
setStyle("parent", "display", "none");
});
testText("<div id='target'>abc", "abc\ndef",
"insert node into sub-tree", function() {
let el = document.getElementById("target");
if (el) {
let c = document.createTextNode("def");
let d = document.createElement("div");
d.appendChild(c);
el.appendChild(d);
}
});
testText("<div id='target'>abc<div id='remove'>def", "abc",
"remove node from sub-tree", function() {
let el = document.getElementById("target");
let victim = document.getElementById("remove");
if (el && victim) {
el.removeChild(victim);
}
});
testText("<div id='target'>", "abcdef",
"insert whole sub-tree", function() {
var el = document.getElementById("target");
if (el) {
var def = document.createTextNode("def");
var s = document.createElement("span");
s.appendChild(def);
var abc = document.createTextNode("abc");
var d = document.createElement("div");
d.appendChild(abc);
d.appendChild(s);
el.appendChild(d);
}
});
</script>

View file

@ -0,0 +1,18 @@
<!DOCTYPE html>
<title>Test innerText/outerText for a combination of a list item with ::first-letter in multicol</title>
<link rel="help" href="https://html.spec.whatwg.org/multipage/dom.html#dom-innertext">
<link rel="help" href="https://crbug.com/1174985">
<script src="../../../../resources/testharness.js"></script>
<script src="../../../../resources/testharnessreport.js"></script>
<style>
#item { display: list-item; }
#item::first-letter { background: lime; }
.col { column-count: 1; }
</style>
<div id="item" class="col"><div class="col">PASS</div></div>
<script>
test(() => {
assert_equals(item.innerText, "PASS", "innerText");
assert_equals(item.outerText, "PASS", "outerText");
}, "");
</script>

View file

@ -0,0 +1,414 @@
testText("<div>abc", "abc", "Simplest possible test");
/**** white-space:normal ****/
testText("<div> abc", "abc", "Leading whitespace removed");
testText("<div>abc ", "abc", "Trailing whitespace removed");
testText("<div>abc def", "abc def", "Internal whitespace compressed");
testText("<div>abc\ndef", "abc def", "\\n converted to space");
testText("<div>abc\rdef", "abc def", "\\r converted to space");
testText("<div>abc\tdef", "abc def", "\\t converted to space");
testText("<div>abc <br>def", "abc\ndef", "Trailing whitespace before hard line break removed");
testText("<div>abc<br> def", "abc\ndef", "Leading whitespace after hard line break removed");
/**** <pre> ****/
testText("<pre> abc", " abc", "Leading whitespace preserved");
testText("<pre>abc ", "abc ", "Trailing whitespace preserved");
testText("<pre>abc def", "abc def", "Internal whitespace preserved");
testText("<pre>abc\ndef", "abc\ndef", "\\n preserved");
testText("<pre>abc\rdef", "abc\ndef", "\\r converted to newline");
testText("<pre>abc\tdef", "abc\tdef", "\\t preserved");
testText("<div><pre>abc</pre><pre>def</pre>", "abc\ndef", "Two <pre> siblings");
/**** <div style="white-space:pre"> ****/
testText("<div style='white-space:pre'> abc", " abc", "Leading whitespace preserved");
testText("<div style='white-space:pre'>abc ", "abc ", "Trailing whitespace preserved");
testText("<div style='white-space:pre'>abc def", "abc def", "Internal whitespace preserved");
testText("<div style='white-space:pre'>abc\ndef", "abc\ndef", "\\n preserved");
testText("<div style='white-space:pre'>abc\rdef", "abc\ndef", "\\r converted to newline");
testText("<div style='white-space:pre'>abc\tdef", "abc\tdef", "\\t preserved");
/**** <span style="white-space:pre"> ****/
testText("<span style='white-space:pre'> abc", " abc", "Leading whitespace preserved");
testText("<span style='white-space:pre'>abc ", "abc ", "Trailing whitespace preserved");
testText("<span style='white-space:pre'>abc def", "abc def", "Internal whitespace preserved");
testText("<span style='white-space:pre'>abc\ndef", "abc\ndef", "\\n preserved");
testText("<span style='white-space:pre'>abc\rdef", "abc\ndef", "\\r converted to newline");
testText("<span style='white-space:pre'>abc\tdef", "abc\tdef", "\\t preserved");
/**** <div style="white-space:pre-line"> ****/
testText("<div style='white-space:pre-line'> abc", "abc", "Leading whitespace removed");
testText("<div style='white-space:pre-line'>abc ", "abc", "Trailing whitespace removed");
testText("<div style='white-space:pre-line'>abc def", "abc def", "Internal whitespace collapsed");
testText("<div style='white-space:pre-line'>abc\ndef", "abc\ndef", "\\n preserved");
testText("<div style='white-space:pre-line'>abc\rdef", "abc\ndef", "\\r converted to newline");
testText("<div style='white-space:pre-line'>abc\tdef", "abc def", "\\t converted to space");
/**** Collapsing whitespace across element boundaries ****/
testText("<div><span>abc </span> def", "abc def", "Whitespace collapses across element boundaries");
testText("<div><span>abc </span><span></span> def", "abc def", "Whitespace collapses across element boundaries");
testText("<div><span>abc </span><span style='white-space:pre'></span> def", "abc def", "Whitespace collapses across element boundaries");
testText("<div>abc <input> def", "abc def", "Whitespace around <input> should not be collapsed");
testText("<div>abc <span style='display:inline-block'></span> def", "abc def", "Whitespace around inline-block should not be collapsed");
testText("<div>abc <span style='display:inline-block'> def </span> ghi", "abc def ghi", "Trailing space at end of inline-block should be collapsed");
testText("<div>abc <span style='display:inline-flex'></span> def", "abc def", "Whitespace around inline-flex should not be collapsed");
testText("<div>abc <span style='display:inline-flex'> def </span> ghi", "abc def ghi", "Trailing space at end of inline-flex should be collapsed");
testText("<div>abc <span style='display:inline-grid'></span> def", "abc def", "Whitespace around inline-grid should not be collapsed");
testText("<div>abc <span style='display:inline-grid'> def </span> ghi", "abc def ghi", "Trailing space at end of grid-flex should be collapsed");
testText("<div><input> <div>abc</div>", "abc", "Whitespace between <input> and block should be collapsed");
testText("<div><span style='inline-block'></span> <div>abc</div>", "abc", "Whitespace between inline-block and block should be collapsed");
testText("<div><span style='inline-flex'></span> <div>abc</div>", "abc", "Whitespace between inline-flex and block should be collapsed");
testText("<div><span style='inline-grid'></span> <div>abc</div>", "abc", "Whitespace between inline-grid and block should be collapsed");
testText("<div>abc <img> def", "abc def", "Whitespace around <img> should not be collapsed");
testText("<div>abc <img width=1 height=1> def", "abc def", "Whitespace around <img> should not be collapsed");
testText("<div><img> abc", " abc", "Leading whitesapce should not be collapsed");
testText("<div>abc <img>", "abc ", "Trailing whitesapce should not be collapsed");
testText("<div>abc <b></b> def", "abc def", "Whitespace around empty span should be collapsed");
testText("<div>abc <b><i></i></b> def", "abc def", "Whitespace around empty spans should be collapsed");
testText("<div><canvas></canvas> abc", " abc", "<canvas> should not collapse following space");
testText("<div>abc <img style='display:block'> def", "abc\ndef", "Replaced element <img> with display:block should be treated as block-level");
testText("<div>abc <canvas style='display:block'></canvas> def", "abc\ndef", "Replaced element <canvas> with display:block should be treated as block-level");
/**** Soft line breaks ****/
testText("<div style='width:0'>abc def", "abc def", "Soft line breaks ignored");
testText("<div style='width:0'>abc-def", "abc-def", "Soft line break at hyphen ignored");
testText("<div style='width:0'><span>abc</span> <span>def</span>", "abc def", "Whitespace text node preserved");
/**** Soft line breaks when word-break:break-word is in effect ****/
/* (based on Testcase #2 at https://bugzilla.mozilla.org/show_bug.cgi?id=1241631) */
testText("<div style='width:1px; word-break:break-word'>Hello Kitty</div>", "Hello Kitty", "Soft breaks ignored in presence of word-break:break-word");
testText("<div style='width:1px; word-break:break-word'><x>Hello</x> <x>Kitty</x></div>", "Hello Kitty", "Element boundaries ignored for soft break handling (1)");
testText("<div style='width:1px; word-break:break-word'><x>Hello</x> <x> Kitty</x></div>", "Hello Kitty", "Whitespace collapses across element boundaries at soft break (1)");
testText("<div style='width:1px; word-break:break-word'><x>Hello</x><x> Kitty</x></div>", "Hello Kitty", "Element boundaries ignored for soft break handling (2)");
testText("<div style='width:1px; word-break:break-word'><x>Hello </x> <x>Kitty</x></div>", "Hello Kitty", "Whitespace collapses across element boundaries at soft break (2)");
testText("<div style='width:1px; word-break:break-word'><x>Hello </x><x>Kitty</x></div>", "Hello Kitty", "Element boundaries ignored for soft break handling (3)");
testText("<div style='width:1px; word-break:break-word'><x>Hello </x><x> Kitty</x></div>", "Hello Kitty", "Whitespace collapses across element boundaries at soft break (3)");
testText("<div style='width:1px; word-break:break-word'><x>Hello </x> <x> Kitty</x></div>", "Hello Kitty", "Whitespace collapses across element boundaries at soft break (4)");
testText("<div style='width:1px; word-break:break-word'><x>Hello</x> Kitty</div>", "Hello Kitty", "Element boundaries ignored for soft break handling (4)");
testText("<div style='width:1px; word-break:break-word'><x>Hello </x>Kitty</div>", "Hello Kitty", "Element boundaries ignored for soft break handling (5)");
testText("<div style='width:1px; word-break:break-word; text-transform:uppercase'>Hello Kitty</div>", "HELLO KITTY", "Soft breaks ignored, text-transform applied");
testText("<div style='width:1px; word-break:break-word'>Hello<br> Kitty</div>", "Hello\nKitty", "<br> returned as newline, following space collapsed");
testText("<div style='width:1px; word-break:break-word'>Hello <br>Kitty</div>", "Hello\nKitty", "<br> returned as newline, preceding space collapsed");
testText("<div style='width:1px; word-break:break-word'><x>Hello </x> <br> <x> Kitty</x></div>", "Hello\nKitty", "<br> returned as newline, adjacent spaces collapsed across element boundaries");
/**** first-line/first-letter ****/
testText("<div class='first-line-uppercase' style='width:0'>abc def", "ABC def", "::first-line styles applied");
testText("<div class='first-letter-uppercase' style='width:0'>abc def", "Abc def", "::first-letter styles applied");
testText("<div class='first-letter-float' style='width:0'>abc def", "abc def", "::first-letter float ignored");
/**** &nbsp; ****/
testText("<div>&nbsp;", "\xA0", "&nbsp; preserved");
/**** display:none ****/
testText("<div style='display:none'>abc", "abc", "display:none container");
testText("<div style='display:none'>abc def", "abc def", "No whitespace compression in display:none container");
testText("<div style='display:none'> abc def ", " abc def ", "No removal of leading/trailing whitespace in display:none container");
testText("<div>123<span style='display:none'>abc", "123", "display:none child not rendered");
testText("<div style='display:none'><span id='target'>abc", "abc", "display:none container with non-display-none target child");
testTextInSVG("<div id='target'>abc", "abc", "non-display-none child of svg");
testTextInSVG("<div style='display:none' id='target'>abc", "abc", "display:none child of svg");
testTextInSVG("<div style='display:none'><div id='target'>abc", "abc", "child of display:none child of svg");
/**** display:contents ****/
if (CSS.supports("display", "contents")) {
testText("<div style='display:contents'>abc", "abc", "display:contents container");
testText("<div><div style='display:contents'>abc", "abc", "display:contents container");
testText("<div>123<span style='display:contents'>abc", "123abc", "display:contents rendered");
testText("<div style='display:contents'> ", "", "display:contents not processed via textContent");
testText("<div><div style='display:contents'> ", "", "display:contents not processed via textContent");
}
/**** visibility:hidden ****/
testText("<div style='visibility:hidden'>abc", "", "visibility:hidden container");
testText("<div>123<span style='visibility:hidden'>abc", "123", "visibility:hidden child not rendered");
testText("<div style='visibility:hidden'>123<span style='visibility:visible'>abc", "abc", "visibility:visible child rendered");
/**** visibility:collapse ****/
testText("<table><tbody style='visibility:collapse'><tr><td>abc", "", "visibility:collapse row-group");
testText("<table><tr style='visibility:collapse'><td>abc", "", "visibility:collapse row");
testText("<table><tr><td style='visibility:collapse'>abc", "", "visibility:collapse cell");
testText("<table><tbody style='visibility:collapse'><tr><td style='visibility:visible'>abc", "abc",
"visibility:collapse row-group with visible cell");
testText("<table><tr style='visibility:collapse'><td style='visibility:visible'>abc", "abc",
"visibility:collapse row with visible cell");
testText("<div style='display:flex'><span style='visibility:collapse'>1</span><span>2</span></div>",
"2", "visibility:collapse honored on flex item");
testText("<div style='display:grid'><span style='visibility:collapse'>1</span><span>2</span></div>",
"2", "visibility:collapse honored on grid item");
/**** opacity:0 ****/
testText("<div style='opacity:0'>abc", "abc", "opacity:0 container");
testText("<div style='opacity:0'>abc def", "abc def", "Whitespace compression in opacity:0 container");
testText("<div style='opacity:0'> abc def ", "abc def", "Remove leading/trailing whitespace in opacity:0 container");
testText("<div>123<span style='opacity:0'>abc", "123abc", "opacity:0 child rendered");
/**** generated content ****/
testText("<div class='before'>", "", "Generated content not included");
testText("<div><div class='before'>", "", "Generated content on child not included");
/**** innerText on replaced elements ****/
testText("<button>abc", "abc", "<button> contents preserved");
testText("<fieldset>abc", "abc", "<fieldset> contents preserved");
testText("<fieldset><legend>abc", "abc", "<fieldset> <legend> contents preserved");
testText("<input type='text' value='abc'>", "", "<input> contents ignored");
testText("<textarea>abc", "", "<textarea> contents ignored");
testText("<iframe>abc", "", "<iframe> contents ignored");
testText("<iframe><div id='target'>abc", "", "<iframe> contents ignored");
testText("<iframe src='data:text/html,abc'>", "","<iframe> subdocument ignored");
testText("<audio style='display:block'>abc", "", "<audio> contents ignored");
testText("<audio style='display:block'><source id='target' class='poke' style='display:block'>", "abc", "<audio> contents ok for element not being rendered");
testText("<audio style='display:block'><source id='target' class='poke' style='display:none'>", "abc", "<audio> contents ok for element not being rendered");
testText("<video>abc", "", "<video> contents ignored");
testText("<video style='display:block'><source id='target' class='poke' style='display:block'>", "abc", "<video> contents ok for element not being rendered");
testText("<video style='display:block'><source id='target' class='poke' style='display:none'>", "abc", "<video> contents ok for element not being rendered");
testText("<canvas>abc", "", "<canvas> contents ignored");
testText("<canvas><div id='target'>abc", "abc", "<canvas><div id='target'> contents ok for element not being rendered");
testText("<img alt='abc'>", "", "<img> alt text ignored");
testText("<img src='about:blank' class='poke'>", "", "<img> contents ignored");
testText("<div><svg><text>abc</text></svg></div>", "abc", "<svg> text contents preserved");
testText("<div><svg><defs><text>abc</text></defs></svg></div>", "", "<svg><defs> text contents ignored");
testText("<div><svg><stop>abc</stop></svg></div>", "", "<svg> non-rendered text ignored");
testText("<svg><foreignObject><span id='target'>abc</span></foreignObject></svg>", "abc", "<foreignObject> contents preserved");
/**** <select>, <optgroup> & <option> ****/
testText("<select size='1'><option>abc</option><option>def", "abc\ndef", "<select size='1'> contents of options preserved");
testText("<select size='2'><option>abc</option><option>def", "abc\ndef", "<select size='2'> contents of options preserved");
testText("<select size='1'><option id='target'>abc</option><option>def", "abc", "<select size='1'> contents of target option preserved");
testText("<select size='2'><option id='target'>abc</option><option>def", "abc", "<select size='2'> contents of target option preserved");
testText("<div>a<select></select>bc", "abc", "empty <select>");
testText("<div>a<select><optgroup></select>bc", "a\nbc", "empty <optgroup> in <select>");
testText("<div>a<select><option></select>bc", "a\nbc", "empty <option> in <select>");
testText("<select class='poke'></select>", "", "<select> containing text node child");
testText("<select><optgroup class='poke-optgroup'></select>", "", "<optgroup> containing <optgroup>");
testText("<select><optgroup><option>abc</select>", "abc", "<optgroup> containing <option>");
testText("<select><option class='poke-div'>123</select>", "123\nabc", "<div> in <option>");
testText("<div>a<optgroup></optgroup>bc", "a\nbc", "empty <optgroup> in <div>");
testText("<div>a<optgroup>123</optgroup>bc", "a\nbc", "<optgroup> in <div>");
testText("<div>a<option></option>bc", "a\nbc", "empty <option> in <div>");
testText("<div>a<option>123</option>bc", "a\n123\nbc", "<option> in <div>");
/**** innerText on replaced element children ****/
testText("<div><button>abc", "abc", "<button> contents preserved");
testText("<div><fieldset>abc", "abc", "<fieldset> contents preserved");
testText("<div><fieldset><legend>abc", "abc", "<fieldset> <legend> contents preserved");
testText("<div><input type='text' value='abc'>", "", "<input> contents ignored");
testText("<div><textarea>abc", "", "<textarea> contents ignored");
testText("<div><select size='1'><option>abc</option><option>def", "abc\ndef", "<select size='1'> contents of options preserved");
testText("<div><select size='2'><option>abc</option><option>def", "abc\ndef", "<select size='2'> contents of options preserved");
testText("<div><iframe>abc", "", "<iframe> contents ignored");
testText("<div><iframe src='data:text/html,abc'>", ""," <iframe> subdocument ignored");
testText("<div><audio>abc", "", "<audio> contents ignored");
testText("<div><video>abc", "", "<video> contents ignored");
testText("<div><canvas>abc", "", "<canvas> contents ignored");
testText("<div><object>abc", "", "<object> contents ignored");
testText("<div><img alt='abc'>", "", "<img> alt text ignored");
/**** Lines around blocks ****/
testText("<div>123<div>abc</div>def", "123\nabc\ndef", "Newline at block boundary");
testText("<div>123<span style='display:block'>abc</span>def", "123\nabc\ndef", "Newline at display:block boundary");
testText("<div>abc<div></div>def", "abc\ndef", "Empty block induces single line break");
testText("<div>abc<div></div><div></div>def", "abc\ndef", "Consecutive empty blocks ignored");
testText("<div><p>abc", "abc", "No blank lines around <p> alone");
testText("<div><p>abc</p> ", "abc", "No blank lines around <p> followed by only collapsible whitespace");
testText("<div> <p>abc</p>", "abc", "No blank lines around <p> preceded by only collapsible whitespace");
testText("<div><p>abc<p>def", "abc\n\ndef", "Blank line between consecutive <p>s");
testText("<div><p>abc</p> <p>def", "abc\n\ndef", "Blank line between consecutive <p>s separated only by collapsible whitespace");
testText("<div><p>abc</p><div></div><p>def", "abc\n\ndef", "Blank line between consecutive <p>s separated only by empty block");
testText("<div><p>abc</p><div>123</div><p>def", "abc\n\n123\n\ndef", "Blank lines between <p>s separated by non-empty block");
testText("<div>abc<div><p>123</p></div>def", "abc\n\n123\n\ndef", "Blank lines around a <p> in its own block");
testText("<div>abc<p>def", "abc\n\ndef", "Blank line before <p>");
testText("<div><p>abc</p>def", "abc\n\ndef", "Blank line after <p>");
testText("<div><p>abc<p></p><p></p><p>def", "abc\n\ndef", "One blank line between <p>s, ignoring empty <p>s");
testText("<div style='visibility:hidden'><p><span style='visibility:visible'>abc</span></p>\n<div style='visibility:visible'>def</div>",
"abc\ndef", "Invisible <p> doesn't induce extra line breaks");
testText("<div>abc<div style='margin:2em'>def", "abc\ndef", "No blank lines around <div> with margin");
testText("<div>123<span style='display:inline-block'>abc</span>def", "123abcdef", "No newlines at display:inline-block boundary");
testText("<div>123<span style='display:inline-block'> abc </span>def", "123abcdef", "Leading/trailing space removal at display:inline-block boundary");
testText("<div>123<span style='display:inline-flex'> abc </span>def", "123abcdef", "Leading/trailing space removal at display:inline-flex boundary");
testText("<div>123<span style='display:inline-grid'> abc </span>def", "123abcdef", "Leading/trailing space removal at display:inline-grid boundary");
testText("<div>123<p style='margin:0px'>abc</p>def", "123\n\nabc\n\ndef", "Blank lines around <p> even without margin");
testText("<div>123<h1>abc</h1>def", "123\nabc\ndef", "No blank lines around <h1>");
testText("<div>123<h2>abc</h2>def", "123\nabc\ndef", "No blank lines around <h2>");
testText("<div>123<h3>abc</h3>def", "123\nabc\ndef", "No blank lines around <h3>");
testText("<div>123<h4>abc</h4>def", "123\nabc\ndef", "No blank lines around <h4>");
testText("<div>123<h5>abc</h5>def", "123\nabc\ndef", "No blank lines around <h5>");
testText("<div>123<h6>abc</h6>def", "123\nabc\ndef", "No blank lines around <h6>");
testText("<div>123<p style='display:block'>abc", "123\n\nabc", "2 blank lines around <p> even when display:block");
testText("<div>123<p style='display:inline-block'>abc", "123\n\nabc", "2 blank lines around <p> even when display:inline-block");
/**** Spans ****/
testText("<div>123<span>abc</span>def", "123abcdef", "<span> boundaries are irrelevant");
testText("<div>123 <span>abc</span> def", "123 abc def", "<span> boundaries are irrelevant");
testText("<div style='width:0'>123 <span>abc</span> def", "123 abc def", "<span> boundaries are irrelevant");
testText("<div>123<em>abc</em>def", "123abcdef", "<em> gets no special treatment");
testText("<div>123<b>abc</b>def", "123abcdef", "<b> gets no special treatment");
testText("<div>123<i>abc</i>def", "123abcdef", "<i> gets no special treatment");
testText("<div>123<strong>abc</strong>def", "123abcdef", "<strong> gets no special treatment");
testText("<div>123<tt>abc</tt>def", "123abcdef", "<tt> gets no special treatment");
testText("<div>123<code>abc</code>def", "123abcdef", "<code> gets no special treatment");
/**** Soft hyphen ****/
testText("<div>abc&shy;def", "abc\xADdef", "soft hyphen preserved");
testText("<div style='width:0'>abc&shy;def", "abc\xADdef", "soft hyphen preserved");
/**** Tables ****/
testText("<div><table style='white-space:pre'> <td>abc</td> </table>", "abc", "Ignoring non-rendered table whitespace");
testText("<div><table><tr><td>abc<td>def</table>", "abc\tdef", "Tab-separated table cells");
testText("<div><table><tr><td>abc<td><td>def</table>", "abc\t\tdef", "Tab-separated table cells including empty cells");
testText("<div><table><tr><td>abc<td><td></table>", "abc\t\t", "Tab-separated table cells including trailing empty cells");
testText("<div><table><tr><td>abc<tr><td>def</table>", "abc\ndef", "Newline-separated table rows");
testText("<div>abc<table><td>def</table>ghi", "abc\ndef\nghi", "Newlines around table");
testText("<div><table style='border-collapse:collapse'><tr><td>abc<td>def</table>", "abc\tdef",
"Tab-separated table cells in a border-collapse table");
testText("<div><table><tfoot>x</tfoot><tbody>y</tbody></table>", "xy", "tfoot not reordered");
testText("<table><tfoot><tr><td>footer</tfoot><thead><tr><td style='visibility:collapse'>thead</thead><tbody><tr><td>tbody</tbody></table>",
"footer\n\ntbody", "");
testText("<table><tr><td id=target>abc</td><td>def</td>", "abc", "No tab on table-cell itself");
testText("<table><tr id=target><td>abc</td><td>def</td></tr><tr id=target><td>ghi</td><td>jkl</td></tr>", "abc\tdef", "No newline on table-row itself");
/**** Table captions ****/
testText("<div><table><tr><td>abc<caption>def</caption></table>", "abc\ndef", "Newline between cells and caption");
/**** display:table ****/
testText("<div><div class='table'><span class='cell'>abc</span>\n<span class='cell'>def</span></div>",
"abc\tdef", "Tab-separated table cells");
testText("<div><div class='table'><span class='row'><span class='cell'>abc</span></span>\n<span class='row'><span class='cell'>def</span></span></div>",
"abc\ndef", "Newline-separated table rows");
testText("<div>abc<div class='table'><span class='cell'>def</span></div>ghi", "abc\ndef\nghi", "Newlines around table");
/**** display:inline-table ****/
testText("<div><div class='itable'><span class='cell'>abc</span>\n<span class='cell'>def</span></div>", "abc\tdef", "Tab-separated table cells");
testText("<div><div class='itable'><span class='row'><span class='cell'>abc</span></span>\n<span class='row'><span class='cell'>def</span></span></div>",
"abc\ndef", "Newline-separated table rows");
testText("<div>abc<div class='itable'><span class='cell'>def</span></div>ghi", "abcdefghi", "No newlines around inline-table");
testText("<div>abc<div class='itable'><span class='row'><span class='cell'>def</span></span>\n<span class='row'><span class='cell'>123</span></span></div>ghi",
"abcdef\n123ghi", "Single newline in two-row inline-table");
/**** display:table-row/table-cell/table-caption ****/
testText("<div style='display:table-row'>", "", "display:table-row on the element itself");
testText("<div style='display:table-cell'>", "", "display:table-cell on the element itself");
testText("<div style='display:table-caption'>", "", "display:table-caption on the element itself");
/**** Lists ****/
testText("<div><ol><li>abc", "abc", "<ol> list items get no special treatment");
testText("<div><ul><li>abc", "abc", "<ul> list items get no special treatment");
/**** Misc elements ****/
testText("<div><script style='display:block'>abc", "abc", "display:block <script> is rendered");
testText("<div><style style='display:block'>abc", "abc", "display:block <style> is rendered");
testText("<div><noscript style='display:block'>abc", "", "display:block <noscript> is not rendered (it's not parsed!)");
testText("<div><template style='display:block'>abc", "",
"display:block <template> contents are not rendered (the contents are in a different document)");
testText("<div>abc<br>def", "abc\ndef", "<br> induces line break");
testText("<div>abc<br>", "abc\n", "<br> induces line break even at end of block");
testText("<div><br class='poke'>", "\n", "<br> content ignored");
testText("<div>abc<hr>def", "abc\ndef", "<hr> induces line break");
testText("<div>abc<hr><hr>def", "abc\ndef", "<hr><hr> induces just one line break");
testText("<div>abc<hr><hr><hr>def", "abc\ndef", "<hr><hr><hr> induces just one line break");
testText("<div><hr class='poke'>", "abc", "<hr> content rendered");
testText("<div>abc<!--comment-->def", "abcdef", "comment ignored");
testText("<br>", "", "<br>");
testText("<p>", "", "empty <p>");
testText("<div>", "", "empty <div>");
testText("<div><details><summary>abc</summary>123", "abc", "unopened <details> ignored");
testText("<div><details open><summary>abc</summary>123", "abc\n123", "opened <details> content shown");
/**** text-transform ****/
testText("<div><div style='text-transform:uppercase'>abc", "ABC", "text-transform is applied");
testText("<div><div style='text-transform:uppercase'>Ma\xDF", "MASS", "text-transform handles es-zet");
testText("<div><div lang='tr' style='text-transform:uppercase'>i \u0131", "\u0130 I", "text-transform handles Turkish casing");
/**** block-in-inline ****/
testText("<div>abc<span>123<div>456</div>789</span>def", "abc123\n456\n789def", "block-in-inline doesn't add unnecessary newlines");
/**** floats ****/
testText("<div>abc<div style='float:left'>123</div>def", "abc\n123\ndef", "floats induce a block boundary");
testText("<div>abc<span style='float:left'>123</span>def", "abc\n123\ndef", "floats induce a block boundary");
testText("<div style='float:left'>123", "123", "float on the element itself");
/**** position ****/
testText("<div>abc<div style='position:absolute'>123</div>def", "abc\n123\ndef", "position:absolute induces a block boundary");
testText("<div>abc<span style='position:absolute'>123</span>def", "abc\n123\ndef", "position:absolute induces a block boundary");
testText("<div style='position:absolute'>123", "123", "position:absolute on the element itself");
testText("<div>abc<div style='position:relative'>123</div>def", "abc\n123\ndef", "position:relative has no effect");
testText("<div>abc<span style='position:relative'>123</span>def", "abc123def", "position:relative has no effect");
/**** text-overflow:ellipsis ****/
testText("<div style='overflow:hidden'>abc", "abc", "overflow:hidden ignored");
// XXX Chrome skips content with width:0 or height:0 and overflow:hidden;
// should we spec that?
testText("<div style='width:0; overflow:hidden'>abc", "abc", "overflow:hidden ignored even with zero width");
testText("<div style='height:0; overflow:hidden'>abc", "abc", "overflow:hidden ignored even with zero height");
testText("<div style='width:0; overflow:hidden; text-overflow:ellipsis'>abc", "abc", "text-overflow:ellipsis ignored");
/**** Support on non-HTML elements ****/
testText("<svg>abc", undefined, "innerText not supported on SVG elements");
testText("<math>abc", undefined, "innerText not supported on MathML elements");
/**** Ruby ****/
testText("<div><ruby>abc<rt>def</rt></ruby>", "abcdef", "<rt> and no <rp>");
testText("<div><ruby>abc<rp>(</rp><rt>def</rt><rp>)</rp></ruby>", "abcdef", "<rp>");
testText("<div><rp>abc</rp>", "", "Lone <rp>");
testText("<div><rp style='visibility:hidden'>abc</rp>", "", "visibility:hidden <rp>");
testText("<div><rp style='display:block'>abc</rp>def", "abc\ndef", "display:block <rp>");
testText("<div><rp style='display:block'> abc </rp>def", "abc\ndef", "display:block <rp> with whitespace");
testText("<div><select class='poke-rp'></select>", "", "<rp> in a <select>");
/**** Shadow DOM ****/
if ("attachShadow" in document.body) {
testText("<div class='shadow'>", "", "Shadow DOM contents ignored");
testText("<div><div class='shadow'>", "", "Shadow DOM contents ignored");
}
/**** Flexbox ****/
if (CSS.supports('display', 'flex')) {
testText("<div style='display:flex'><div style='order:1'>1</div><div>2</div></div>",
"1\n2", "CSS 'order' property ignored");
testText("<div style='display:flex'><span>1</span><span>2</span></div>",
"1\n2", "Flex items blockified");
}
/**** Grid ****/
if (CSS.supports('display', 'grid')) {
testText("<div style='display:grid'><div style='order:1'>1</div><div>2</div></div>",
"1\n2", "CSS 'order' property ignored");
testText("<div style='display:grid'><span>1</span><span>2</span></div>",
"1\n2", "Grid items blockified");
}

View file

@ -0,0 +1,64 @@
<!DOCTYPE html>
<title>innerText/outerText getter test</title>
<script src="../../../../resources/testharness.js"></script>
<script src="../../../../resources/testharnessreport.js"></script>
<style>
.before::before { content:'abc'; }
.table { display:table; }
.itable { display:inline-table; }
.row { display:table-row; }
.cell { display:table-cell; }
.first-line-uppercase::first-line { text-transform:uppercase; }
.first-letter-uppercase::first-letter { text-transform:uppercase; }
.first-letter-float::first-letter { float:left; }
</style>
<div id="container"></div>
<svg id="svgContainer"></svg>
<script>
let container = document.querySelector('#container');
let svgContainer = document.querySelector('#svgContainer');
function testText(html, expectedPlain, msg) {
textTextInContainer(container, html, expectedPlain, msg);
}
function testTextInSVG(html, expectedPlain, msg) {
textTextInContainer(svgContainer, html, expectedPlain, msg);
}
function textTextInContainer(cont, html, expectedPlain, msg) {
test(function() {
container.innerHTML = html;
if (cont != container) {
while (container.firstChild) {
cont.appendChild(container.firstChild);
}
}
var e = document.getElementById('target');
if (!e) {
e = cont.firstChild;
}
var pokes = document.getElementsByClassName('poke');
for (var i = 0; i < pokes.length; ++i) {
pokes[i].textContent = 'abc';
}
['rp', 'optgroup', 'div'].forEach(function(tag) {
pokes = document.getElementsByClassName('poke-' + tag);
for (var i = 0; i < pokes.length; ++i) {
var el = document.createElement(tag);
el.textContent = "abc";
pokes[i].appendChild(el);
}
});
var shadows = document.getElementsByClassName('shadow');
for (var i = 0; i < shadows.length; ++i) {
var s = shadows[i].attachShadow({ mode: "open" });
s.textContent = 'abc';
}
while (e && e.nodeType != Node.ELEMENT_NODE) {
e = e.nextSibling;
}
assert_equals(e.innerText, expectedPlain, "innerText");
assert_equals(e.outerText, expectedPlain, "outerText");
cont.textContent = '';
}, msg + ' (' + format_value(html) + ')');
}
</script>
<script src="getter-tests.js"></script>

View file

@ -0,0 +1,19 @@
<!DOCTYPE html>
<link rel=author href="mailto:jarhar@chromium.org">
<link rel=help href="https://bugs.chromium.org/p/chromium/issues/detail?id=1411135">
<div id=parentelement><div id=childelement>hello world</div></div>
<script>
let removed = false;
childelement.addEventListener('DOMNodeRemoved', () => {
if (!removed) {
removed = true;
childelement.remove();
}
});
parentelement.innerText = 'hello world';
if (window.internals)
internals.signalTextTestIsDone("PASS");
</script>

View file

@ -0,0 +1,42 @@
testText("<div>", "abc", "abc", "Simplest possible test");
testHTML("<div>", "abc\ndef", "abc<br>def", "Newlines convert to <br> in non-white-space:pre elements");
testHTML("<pre>", "abc\ndef", "abc<br>def", "Newlines convert to <br> in <pre> element");
testHTML("<textarea>", "abc\ndef", "abc<br>def", "Newlines convert to <br> in <textarea> element");
testHTML("<div style='white-space:pre'>", "abc\ndef", "abc<br>def", "Newlines convert to <br> in white-space:pre element");
testHTML("<div>", "abc\rdef", "abc<br>def", "CRs convert to <br> in non-white-space:pre elements");
testHTML("<pre>", "abc\rdef", "abc<br>def", "CRs convert to <br> in <pre> element");
testHTML("<div>", "abc\r\ndef", "abc<br>def", "Newline/CR pair converts to <br> in non-white-space:pre element");
testHTML("<div>", "abc\n\ndef", "abc<br><br>def", "Newline/newline pair converts to two <br>s in non-white-space:pre element");
testHTML("<div>", "abc\r\rdef", "abc<br><br>def", "CR/CR pair converts to two <br>s in non-white-space:pre element");
testHTML("<div style='white-space:pre'>", "abc\rdef", "abc<br>def", "CRs convert to <br> in white-space:pre element");
testText("<div>", "abc<def", "abc<def", "< preserved");
testText("<div>", "abc>def", "abc>def", "> preserved");
testText("<div>", "abc&", "abc&", "& preserved");
testText("<div>", "abc\"def", "abc\"def", "\" preserved");
testText("<div>", "abc\'def", "abc\'def", "\' preserved");
testHTML("<svg>", "abc", "", "innerText not supported on SVG elements");
testHTML("<math>", "abc", "", "innerText not supported on MathML elements");
testText("<div>", "abc\0def", "abc\0def", "Null characters preserved");
testText("<div>", "abc\tdef", "abc\tdef", "Tabs preserved");
testText("<div>", " abc", " abc", "Leading whitespace preserved");
testText("<div>", "abc ", "abc ", "Trailing whitespace preserved");
testText("<div>", "abc def", "abc def", "Whitespace not compressed");
testText("<div>abc\n\n", "abc", "abc", "Existing text deleted");
testText("<div><br>", "abc", "abc", "Existing <br> deleted");
testHTML("<div>", "", "", "Assigning the empty string");
testHTML("<div>", null, "", "Assigning null");
testHTML("<div>", undefined, "undefined", "Assigning undefined");
testHTML("<div>", "\rabc", "<br>abc", "Start with CR");
testHTML("<div>", "\nabc", "<br>abc", "Start with LF");
testHTML("<div>", "\r\nabc", "<br>abc", "Start with CRLF");
testHTML("<div>", "abc\r", "abc<br>", "End with CR");
testHTML("<div>", "abc\n", "abc<br>", "End with LF");
testHTML("<div>", "abc\r\n", "abc<br>", "End with CRLF");
// Setting innerText on these should not throw
["area", "base", "basefont", "bgsound", "br", "col", "embed", "frame", "hr",
"image", "img", "input", "keygen", "link", "menuitem", "meta", "param",
"source", "track", "wbr", "colgroup", "frameset", "head", "html", "table",
"tbody", "tfoot", "thead", "tr"].forEach(function(tag) {
testText(document.createElement(tag), "abc", "abc", "innerText on <" + tag + "> element");
});

View file

@ -0,0 +1,88 @@
<!DOCTYPE html>
<title>innerText setter test</title>
<script src="../../../../resources/testharness.js"></script>
<script src="../../../../resources/testharnessreport.js"></script>
<div id="container"></div>
<script>
// As of March 2017, WebKit and Blink have inconsistent results depending on
// rendered or not. setupTest() tests a rendered case, and setupTestDetached()
// tests a not-rendered case.
function setupTest(context, plain) {
var container = document.getElementById("container");
// context is either a string or an element node
if (typeof context === "string") {
container.innerHTML = context;
} else {
container.innerHTML = "";
container.appendChild(context);
}
var e = container.firstChild;
while (e && e.nodeType != Node.ELEMENT_NODE) {
e = e.nextSibling;
}
e.offsetWidth;
var oldChild = e.firstChild;
e.innerText = plain;
return [e, oldChild];
}
function setupTestDetached(context, plain) {
var detachedContainer = document.createElement("div");
// context is either a string or an element node
if (typeof context === "string") {
detachedContainer.innerHTML = context;
} else {
detachedContainer.innerHTML = "";
detachedContainer.appendChild(context);
}
var e = detachedContainer.firstChild;
while (e && e.nodeType != Node.ELEMENT_NODE) {
e = e.nextSibling;
}
var oldChild = e.firstChild;
e.innerText = plain;
return [e, oldChild];
}
function assertNewSingleTextNode(newChild, expectedText, oldChild) {
assert_not_equals(newChild, null, "Should have a child");
assert_equals(newChild.nodeType, Node.TEXT_NODE, "Child should be a text node");
assert_equals(newChild.nextSibling, null, "Should have only one child");
assert_equals(newChild.data, expectedText);
assert_not_equals(newChild, oldChild, "Child should be a *new* text node");
}
function assertNoEmptyTextChild(parent) {
for (var child = parent.firstChild; child; child = child.nextSibling) {
if (child.nodeType === Node.TEXT_NODE) {
assert_not_equals(child.data, "", "Should not have empty text nodes");
}
}
}
function testText(context, plain, expectedText, msg) {
test(function(){
var arr = setupTest(context, plain);
assertNewSingleTextNode(arr[0].firstChild, expectedText, arr[1]);
}, msg);
test(function() {
var arr = setupTestDetached(context, plain);
assertNewSingleTextNode(arr[0].firstChild, expectedText, arr[1]);
}, msg + ", detached");
}
function testHTML(context, plain, expectedHTML, msg) {
test(function(){
var e = setupTest(context, plain)[0];
assert_equals(e.innerHTML, expectedHTML);
assertNoEmptyTextChild(e);
}, msg);
test(function() {
var e = setupTestDetached(context, plain)[0];
assert_equals(e.innerHTML, expectedHTML);
assertNoEmptyTextChild(e);
}, msg + ", detached");
}
</script>
<script src="innertext-setter-tests.js"></script>

View file

@ -0,0 +1,42 @@
<!DOCTYPE html>
<title>innerText with white-space:pre-line</title>
<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1923829">
<script src="../../../../resources/testharness.js"></script>
<script src="../../../../resources/testharnessreport.js"></script>
<div id="a" style="white-space: pre-line">one&#10;two&#10;three&#10;four</div>
<div id="b" style="white-space: pre">one&#10;two&#10;three&#10;four</div>
<div id="c" style="white-space: pre-line">
one
two
<!-- comment -->
three
four
</div>
<div id="d" style="white-space: pre">
one
two
<!-- comment -->
three
four
</div>
<script>
test(() => {
assert_equals(a.innerText, b.innerText);
}, "innerText should be the same for the pre-line and pre examples");
test(() => {
function collapseWhitespace(s) {
return s.replace(/ +/g, ' ') // collapse runs of spaces
.replace(/ $/mg, '') // strip trailing spaces
.replace(/^ /mg, '') // strip leading spaces
.replace(/\n\n+/g, '\n') // collapse runs of newlines
.replace(/^\n/, ''); // remove any initial newline
}
assert_equals(c.innerText, collapseWhitespace(d.innerText));
}, "innerText has collapsed whitespace but preserved newlines with pre-line");
</script>

View file

@ -0,0 +1,16 @@
async_test(t => {
const div = document.body.appendChild(document.createElement("div"));
t.add_cleanup(() => div.remove());
const t1 = div.appendChild(new Text(""));
div.appendChild(new Text(""));
const t2 = div.appendChild(new Text(""));
const t3 = div.appendChild(new Text(""));
t.step_timeout(() => {
t1.data = "X";
t2.data = " ";
t3.data = "Y";
assert_equals(div.innerText, "X Y", "innerText");
assert_equals(div.outerText, "X Y", "outerText");
t.done();
}, 100);
}, "Ensure multiple text nodes get rendered properly");

View file

@ -0,0 +1,180 @@
<!DOCTYPE html>
<title>outerText setter test</title>
<script src="../../../../resources/testharness.js"></script>
<script src="../../../../resources/testharnessreport.js"></script>
<ul>
<li>A <span id="testReplacePrevious">B</span></li>
<li><span id="testReplaceFollowing">A</span> B</li>
<li>A <span id="testReplaceBoth">B</span> C</li>
<li><span id="testRemove">Testing</span> removing node using outerText.</li>
<li><span id="testNewlines">Replace this child with lots of newlines</span></li>
</ul>
<div id="container"></div>
<script>
"use strict";
test(() => {
const node = document.getElementById("testReplacePrevious");
const parent = node.parentNode;
node.outerText = "Replaced";
assert_equals(parent.innerHTML, "A Replaced");
assert_equals(parent.childNodes.length, 1, "It got merged with the previous text node");
}, "Replacing a node and merging with the previous text node");
test(() => {
const node = document.getElementById("testReplaceFollowing");
const parent = node.parentNode;
node.outerText = "Replaced";
assert_equals(parent.innerHTML, "Replaced B");
assert_equals(parent.childNodes.length, 1, "It got merged with the following text node");
}, "Replacing a node and merging with the following text node");
test(() => {
const node = document.getElementById("testReplaceBoth");
const parent = node.parentNode;
node.outerText = "Replaced";
assert_equals(parent.innerHTML, "A Replaced C");
assert_equals(parent.childNodes.length, 1, "It got merged with the previous and following text node");
}, "Replacing a node and merging with the previous and following text node");
test(t => {
const container = document.getElementById("container");
t.add_cleanup(() => { container.textContent = ""; });
container.append("A", "B", document.createElement("span"), "D", "E");
assert_equals(container.childNodes.length, 5, "Precondition check: five separate nodes");
const node = container.childNodes[2];
node.outerText = "Replaced";
assert_equals(container.innerHTML, "ABReplacedDE");
assert_equals(container.childNodes.length, 3, "It got merged with the previous and following text node");
assert_equals(container.childNodes[0].data, "A");
assert_equals(container.childNodes[1].data, "BReplacedD");
assert_equals(container.childNodes[2].data, "E");
}, "Only merges with the previous and following text nodes, does not completely normalize");
test(t => {
const container = document.getElementById("container");
t.add_cleanup(() => { container.textContent = ""; });
container.append(document.createElement("span"));
const node = container.childNodes[0];
node.outerText = "";
assert_equals(container.childNodes.length, 1, "Creates text node for the empty string");
assert_equals(container.childNodes[0].data, "");
}, "Empty string");
test(t => {
const container = document.getElementById("container");
t.add_cleanup(() => { container.textContent = ""; });
container.append("1", "2", document.createElement("span"), "3", "4");
const node = container.childNodes[2];
node.outerText = "";
assert_equals(container.childNodes.length, 3, "It got merged with the previous and following text node");
assert_equals(container.childNodes[0].data, "1");
assert_equals(container.childNodes[1].data, "23");
assert_equals(container.childNodes[2].data, "4");
}, "Empty string with surrounding text nodes");
test(t => {
const node = document.getElementById("testNewlines");
const parent = node.parentNode;
node.outerText = "\n\r\n\r";
assert_equals(parent.innerHTML, "<br><br><br>");
assert_equals(parent.childNodes.length, 3);
assert_equals(parent.childNodes[0].localName, "br", "node 1");
assert_equals(parent.childNodes[1].localName, "br", "node 2");
assert_equals(parent.childNodes[2].localName, "br", "node 3");
}, "Setting outerText to a bunch of newlines creates a bunch of <br>s with no text nodes");
test(() => {
const node = document.getElementById("testRemove");
const parent = node.parentNode;
node.outerText = "";
assert_equals(parent.innerHTML, " removing node using outerText.");
}, "Removing a node");
test(() => {
const node = document.createElement("span");
assert_throws_dom("NoModificationAllowedError", () => { node.outerText = ""; });
}, "Detached node");
testText("<div>", "abc", "abc", "Simplest possible test");
testHTML("<div>", "abc\ndef", "abc<br>def", "Newlines convert to <br> in non-white-space:pre elements");
testHTML("<pre>", "abc\ndef", "abc<br>def", "Newlines convert to <br> in <pre> element");
testHTML("<textarea>", "abc\ndef", "abc<br>def", "Newlines convert to <br> in <textarea> element");
testHTML("<div style='white-space:pre'>", "abc\ndef", "abc<br>def", "Newlines convert to <br> in white-space:pre element");
testHTML("<div>", "abc\rdef", "abc<br>def", "CRs convert to <br> in non-white-space:pre elements");
testHTML("<pre>", "abc\rdef", "abc<br>def", "CRs convert to <br> in <pre> element");
testHTML("<div>", "abc\r\ndef", "abc<br>def", "Newline/CR pair converts to <br> in non-white-space:pre element");
testHTML("<div>", "abc\n\ndef", "abc<br><br>def", "Newline/newline pair converts to two <br>s in non-white-space:pre element");
testHTML("<div>", "abc\r\rdef", "abc<br><br>def", "CR/CR pair converts to two <br>s in non-white-space:pre element");
testHTML("<div style='white-space:pre'>", "abc\rdef", "abc<br>def", "CRs convert to <br> in white-space:pre element");
testText("<div>", "abc<def", "abc<def", "< preserved");
testText("<div>", "abc>def", "abc>def", "> preserved");
testText("<div>", "abc&", "abc&", "& preserved");
testText("<div>", "abc\"def", "abc\"def", "\" preserved");
testText("<div>", "abc\'def", "abc\'def", "\' preserved");
testHTML("<svg>", "abc", "<svg></svg>", "outerText not supported on SVG elements");
testHTML("<math>", "abc", "<math></math>", "outerText not supported on MathML elements");
testText("<div>", "abc\0def", "abc\0def", "Null characters preserved");
testText("<div>", "abc\tdef", "abc\tdef", "Tabs preserved");
testText("<div>", " abc", " abc", "Leading whitespace preserved");
testText("<div>", "abc ", "abc ", "Trailing whitespace preserved");
testText("<div>", "abc def", "abc def", "Whitespace not compressed");
testText("<div>abc\n\n", "abc", "abc", "Existing text deleted");
testText("<div><br>", "abc", "abc", "Existing <br> deleted");
testHTML("<div>", "", "", "Assigning the empty string");
testHTML("<div>", null, "", "Assigning null");
testHTML("<div>", undefined, "undefined", "Assigning undefined");
testHTML("<div>", "\rabc", "<br>abc", "Start with CR");
testHTML("<div>", "\nabc", "<br>abc", "Start with LF");
testHTML("<div>", "\r\nabc", "<br>abc", "Start with CRLF");
testHTML("<div>", "abc\r", "abc<br>", "End with CR");
testHTML("<div>", "abc\n", "abc<br>", "End with LF");
testHTML("<div>", "abc\r\n", "abc<br>", "End with CRLF");
function testText(startingHTML, outerText, expected, description) {
test(t => {
const container = document.getElementById("container");
t.add_cleanup(() => { container.textContent = ""; });
container.innerHTML = startingHTML;
const elementToReplace = container.firstElementChild;
elementToReplace.outerText = outerText;
assert_equals(container.textContent, expected);
}, description);
}
function testHTML(startingHTML, outerText, expected, description) {
test(t => {
const container = document.getElementById("container");
t.add_cleanup(() => { container.textContent = ""; });
container.innerHTML = startingHTML;
const elementToReplace = container.firstElementChild;
elementToReplace.outerText = outerText;
assert_equals(container.innerHTML, expected);
}, description);
}
</script>