<!doctype html> <html> <head> <meta charset="utf-8"> <meta name="variant" content="?method=backspace&block=div"> <meta name="variant" content="?method=backspace&block=p"> <meta name="variant" content="?method=backspace&block=blockquote"> <meta name="variant" content="?method=forwarddelete&block=div"> <meta name="variant" content="?method=forwarddelete&block=p"> <meta name="variant" content="?method=forwarddelete&block=blockquote"> <meta name="variant" content="?method=select-boundary&block=div"> <meta name="variant" content="?method=select-boundary&block=p"> <meta name="variant" content="?method=select-boundary&block=blockquote"> <title>Tests for joining pre and other block element</title> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script> <script src="/resources/testdriver.js"></script> <script src="/resources/testdriver-vendor.js"></script> <script src="/resources/testdriver-actions.js"></script> <script src="../include/editor-test-utils.js"></script> </head> <body> <div contenteditable></div> <script> "use strict"; const searchParams = new URLSearchParams(document.location.search); const testingBackspace = searchParams.get("method") == "backspace"; const testingSelectBoundary = searchParams.get("method") == "select-boundary"; const commandName = testingBackspace || testingSelectBoundary ? "delete" : "forwarddelete"; const editingHost = document.querySelector("div[contenteditable]"); const caretInLeft = (() => { if (testingSelectBoundary) { return "["; } return testingBackspace ? "" : "[]"; })(); const caretInRight = (() => { if (testingSelectBoundary) { return "]"; } return testingBackspace ? "[]" : ""; })(); const tag = searchParams.get("block"); // These expectations are odd because they don't preserve white-space style // coming from another element. However, this is traditional behavior so that // browsers should not change the behavior. const tests = [ { initialHTML: `<pre>abc${caretInLeft}</pre>` + `<${tag}>${caretInRight}def</${tag}>`, expectedHTML: [ "<pre>abcdef</pre>", ], }, { initialHTML: `<${tag}>abc${caretInLeft}</${tag}>` + `<pre>${caretInRight}def</pre>`, expectedHTML: [ `<${tag}>abcdef</${tag}>`, ], expectedHTMLWithStyledPre: [ `<${tag}>abc<span style="white-space:pre">def</span></${tag}>`, ], }, { initialHTML: `<pre>abc${caretInLeft}</pre>` + `<${tag}>${caretInRight}def<br>ghi</${tag}>`, expectedHTML: [ `<pre>abcdef</pre>` + `<${tag}>ghi</${tag}>`, ], }, { initialHTML: `<pre>abc${caretInLeft}</pre>` + `<${tag}>${caretInRight}def<div>ghi</div></${tag}>`, expectedHTML: [ "<pre>abcdef</pre>" + `<${tag}><div>ghi</div></${tag}>`, ], skip: tag == "p", }, { initialHTML: `<${tag}>abc${caretInLeft}</${tag}>` + `<pre>${caretInRight}def\nghi</pre>`, expectedHTML: [ `<${tag}>abcdef</${tag}>` + "<pre>ghi</pre>", ], expectedHTMLWithStyledPre: [ `<${tag}>abc<span style="white-space:pre">def</span></${tag}>` + "<pre>ghi</pre>", ], }, { initialHTML: `<${tag}>abc${caretInLeft}</${tag}>` + `<pre>${caretInRight}def<br>ghi</pre>`, expectedHTML: [ `<${tag}>abcdef</${tag}>` + "<pre>ghi</pre>", ], expectedHTMLWithStyledPre: [ `<${tag}>abc<span style="white-space:pre">def</span></${tag}>` + "<pre>ghi</pre>", ], }, { initialHTML: `<pre>abc${caretInLeft}</pre>` + `<${tag}><b>${caretInRight}def</b></${tag}>`, expectedHTML: [ "<pre>abc<b>def</b></pre>", ], }, { initialHTML: `<pre>abc${caretInLeft}</pre>` + `<${tag}><b>${caretInRight}def<br>ghi</b></${tag}>`, expectedHTML: [ "<pre>abc<b>def</b></pre>" + `<${tag}><b>ghi</b></${tag}>`, ], }, { initialHTML: `<${tag}>abc${caretInLeft}</${tag}>` + `<pre><b>${caretInRight}def\nghi</b></pre>`, expectedHTML: [ `<${tag}>abc<b>def</b></${tag}>` + "<pre><b>ghi</b></pre>", ], expectedHTMLWithStyledPre: [ `<${tag}>abc<b style="white-space:pre">def</b></${tag}>` + "<pre><b>ghi</b></pre>", `<${tag}>abc<span style="white-space:pre"><b>def</b></span></${tag}>` + "<pre><b>ghi</b></pre>", ], }, { initialHTML: `<${tag}>abc${caretInLeft}</${tag}>` + `<pre><b>${caretInRight}def<br>ghi</b></pre>`, expectedHTML: [ `<${tag}>abc<b>def</b></${tag}>` + "<pre><b>ghi</b></pre>", ], expectedHTMLWithStyledPre: [ `<${tag}>abc<b style="white-space:pre">def</b></${tag}>` + "<pre><b>ghi</b></pre>", `<${tag}>abc<span style="white-space:pre"><b>def</b></span></${tag}>` + "<pre><b>ghi</b></pre>", ], }, { initialHTML: `<${tag}>abc${caretInLeft}</${tag}>` + `<pre><b>${caretInRight}def</b>\nghi</pre>`, expectedHTML: [ `<${tag}>abc<b>def</b></${tag}>` + "<pre>ghi</pre>", ], expectedHTMLWithStyledPre: [ `<${tag}>abc<b style="white-space:pre">def</b></${tag}>` + "<pre>ghi</pre>", `<${tag}>abc<span style="white-space:pre"><b>def</b></span></${tag}>` + "<pre>ghi</pre>", ], }, { initialHTML: `<${tag}>abc${caretInLeft}</${tag}>` + `<pre><b>${caretInRight}def</b><br>ghi</pre>`, expectedHTML: [ `<${tag}>abc<b>def</b></${tag}>` + "<pre>ghi</pre>", ], expectedHTMLWithStyledPre: [ `<${tag}>abc<b style="white-space:pre">def</b></${tag}>` + "<pre>ghi</pre>", `<${tag}>abc<span style="white-space:pre"><b>def</b></span></${tag}>` + "<pre>ghi</pre>", ], }, { initialHTML: `<${tag}>abc${caretInLeft}</${tag}>` + `<pre><b>${caretInRight}def\n</b>ghi</pre>`, expectedHTML: [ `<${tag}>abc<b>def</b></${tag}>` + "<pre>ghi</pre>", ], expectedHTMLWithStyledPre: [ `<${tag}>abc<b style="white-space:pre">def</b></${tag}>` + `<pre>ghi</pre>`, `<${tag}>abc<span style="white-space:pre"><b>def</b></span></${tag}>` + "<pre>ghi</pre>", ], }, { initialHTML: `<${tag}>abc${caretInLeft}</${tag}>` + `<pre><b>${caretInRight}def<br></b>ghi</pre>`, expectedHTML: [ `<${tag}>abc<b>def</b></${tag}>` + "<pre>ghi</pre>", ], expectedHTMLWithStyledPre: [ `<${tag}>abc<b style="white-space:pre">def</b></${tag}>` + "<pre>ghi</pre>", `<${tag}>abc<span style="white-space:pre"><b>def</b></span></${tag}>` + "<pre>ghi</pre>", ], }, // One linefeed at start of <pre> should be ignored. // Note that if setupEditingHost() does not touch the text node in <pre>, // the leading line break is ignored, but if it touches the text node, // the value is set to as-is. Therefore, the following tests can work // with empty caretInRight value. { initialHTML: `<${tag}>abc${caretInLeft}</${tag}>` + `<pre>\ndef\nghi</pre>`, expectedHTML: [ `<${tag}>abcdef</${tag}>` + `<pre>ghi</pre>`, ], expectedHTMLWithStyledPre: [ `<${tag}>abc<span style="white-space:pre">def</span></${tag}>` + "<pre>ghi</pre>", ], skip: caretInRight !== "", }, // When there are two line breaks at start of <pre>, the first one should be // ignored by the parser but the second one should make empty first line. // Therefore, the first empty line should be removed. { initialHTML: `<${tag}>abc${caretInLeft}</${tag}>` + `<pre>\n\ndef\nghi</pre>`, expectedHTML: [ `<${tag}>abc</${tag}>` + "<pre>def\nghi</pre>", ], skip: caretInRight !== "", }, ]; const utils = new EditorTestUtils(editingHost); const betweenBlockAndPre = new RegExp(`</${tag}><pre>`); const betweenPreAndBlock = new RegExp(`</pre><${tag}>`); function putStyleElement() { const styleElement = document.createElement("style"); styleElement.textContent = "pre { white-space: pre; }"; document.head.appendChild(styleElement); } for (const specifyPreStyle of [false, true]) { for (const t of tests) { if (t.skip) { continue; } if (specifyPreStyle && !t.expectedHTMLWithStyledPre) { continue; } promise_test(async () => { if (specifyPreStyle) { putStyleElement(); } try { utils.setupEditingHost(t.initialHTML); await (testingBackspace ? utils.sendBackspaceKey() : utils.sendDeleteKey()); utils.normalizeStyleAttributeValues(); assert_in_array( editingHost.innerHTML, specifyPreStyle ? t.expectedHTMLWithStyledPre : t.expectedHTML, `white-space should${ !specifyPreStyle ? " not" : "" } be preserved by <span> elements` ); } finally { if (specifyPreStyle) { document.querySelector("style")?.remove(); } } }, `${commandName} at ${t.initialHTML.replace(/\n/g, "\\n")}${ specifyPreStyle ? " (with <style>pre { white-space: pre; }</style>)" : "" }`); // Repeat same tests with inserting a line break between the paragraphs. const initialHTMLWithLineBreak = t.initialHTML .replace(betweenBlockAndPre, `</${tag}>\n<pre>`) .replace(betweenPreAndBlock, `</pre>\n<${tag}>`); promise_test(async () => { if (specifyPreStyle) { putStyleElement(); } try { utils.setupEditingHost(initialHTMLWithLineBreak); await (testingBackspace ? utils.sendBackspaceKey() : utils.sendDeleteKey()); utils.normalizeStyleAttributeValues(); assert_in_array( editingHost.innerHTML, specifyPreStyle ? t.expectedHTMLWithStyledPre : t.expectedHTML, `white-space should${ !specifyPreStyle ? " not" : "" } be preserved by <span> elements (testing with a line break between paragraphs)` ); } finally { if (specifyPreStyle) { document.querySelector("style")?.remove(); } } }, `${commandName} at ${initialHTMLWithLineBreak.replace(/\n/g, "\\n")}${ specifyPreStyle ? " (with <style>pre { white-space: pre; }</style>)" : "" }`); } } </script> </body> </html>