join-pre-and-other-block.html (10391B)
1 <!doctype html> 2 <html> 3 <head> 4 <meta charset="utf-8"> 5 <meta name="variant" content="?method=backspace&block=div"> 6 <meta name="variant" content="?method=backspace&block=p"> 7 <meta name="variant" content="?method=backspace&block=blockquote"> 8 <meta name="variant" content="?method=forwarddelete&block=div"> 9 <meta name="variant" content="?method=forwarddelete&block=p"> 10 <meta name="variant" content="?method=forwarddelete&block=blockquote"> 11 <meta name="variant" content="?method=select-boundary&block=div"> 12 <meta name="variant" content="?method=select-boundary&block=p"> 13 <meta name="variant" content="?method=select-boundary&block=blockquote"> 14 <title>Tests for joining pre and other block element</title> 15 <script src="/resources/testharness.js"></script> 16 <script src="/resources/testharnessreport.js"></script> 17 <script src="/resources/testdriver.js"></script> 18 <script src="/resources/testdriver-vendor.js"></script> 19 <script src="/resources/testdriver-actions.js"></script> 20 <script src="../include/editor-test-utils.js"></script> 21 </head> 22 <body> 23 <div contenteditable></div> 24 <script> 25 "use strict"; 26 27 const searchParams = new URLSearchParams(document.location.search); 28 const testingBackspace = searchParams.get("method") == "backspace"; 29 const testingSelectBoundary = searchParams.get("method") == "select-boundary"; 30 const commandName = 31 testingBackspace || testingSelectBoundary ? "delete" : "forwarddelete"; 32 const editingHost = document.querySelector("div[contenteditable]"); 33 const caretInLeft = (() => { 34 if (testingSelectBoundary) { 35 return "["; 36 } 37 return testingBackspace ? "" : "[]"; 38 })(); 39 const caretInRight = (() => { 40 if (testingSelectBoundary) { 41 return "]"; 42 } 43 return testingBackspace ? "[]" : ""; 44 })(); 45 const tag = searchParams.get("block"); 46 47 // These expectations are odd because they don't preserve white-space style 48 // coming from another element. However, this is traditional behavior so that 49 // browsers should not change the behavior. 50 const tests = [ 51 { 52 initialHTML: 53 `<pre>abc${caretInLeft}</pre>` + 54 `<${tag}>${caretInRight}def</${tag}>`, 55 expectedHTML: [ 56 "<pre>abcdef</pre>", 57 ], 58 }, 59 { 60 initialHTML: 61 `<${tag}>abc${caretInLeft}</${tag}>` + 62 `<pre>${caretInRight}def</pre>`, 63 expectedHTML: [ 64 `<${tag}>abcdef</${tag}>`, 65 ], 66 expectedHTMLWithStyledPre: [ 67 `<${tag}>abc<span style="white-space:pre">def</span></${tag}>`, 68 ], 69 }, 70 { 71 initialHTML: 72 `<pre>abc${caretInLeft}</pre>` + 73 `<${tag}>${caretInRight}def<br>ghi</${tag}>`, 74 expectedHTML: [ 75 `<pre>abcdef</pre>` + 76 `<${tag}>ghi</${tag}>`, 77 ], 78 }, 79 { 80 initialHTML: 81 `<pre>abc${caretInLeft}</pre>` + 82 `<${tag}>${caretInRight}def<div>ghi</div></${tag}>`, 83 expectedHTML: [ 84 "<pre>abcdef</pre>" + 85 `<${tag}><div>ghi</div></${tag}>`, 86 ], 87 skip: tag == "p", 88 }, 89 { 90 initialHTML: 91 `<${tag}>abc${caretInLeft}</${tag}>` + 92 `<pre>${caretInRight}def\nghi</pre>`, 93 expectedHTML: [ 94 `<${tag}>abcdef</${tag}>` + 95 "<pre>ghi</pre>", 96 ], 97 expectedHTMLWithStyledPre: [ 98 `<${tag}>abc<span style="white-space:pre">def</span></${tag}>` + 99 "<pre>ghi</pre>", 100 ], 101 }, 102 { 103 initialHTML: 104 `<${tag}>abc${caretInLeft}</${tag}>` + 105 `<pre>${caretInRight}def<br>ghi</pre>`, 106 expectedHTML: [ 107 `<${tag}>abcdef</${tag}>` + 108 "<pre>ghi</pre>", 109 ], 110 expectedHTMLWithStyledPre: [ 111 `<${tag}>abc<span style="white-space:pre">def</span></${tag}>` + 112 "<pre>ghi</pre>", 113 ], 114 }, 115 { 116 initialHTML: 117 `<pre>abc${caretInLeft}</pre>` + 118 `<${tag}><b>${caretInRight}def</b></${tag}>`, 119 expectedHTML: [ 120 "<pre>abc<b>def</b></pre>", 121 ], 122 }, 123 { 124 initialHTML: 125 `<pre>abc${caretInLeft}</pre>` + 126 `<${tag}><b>${caretInRight}def<br>ghi</b></${tag}>`, 127 expectedHTML: [ 128 "<pre>abc<b>def</b></pre>" + 129 `<${tag}><b>ghi</b></${tag}>`, 130 ], 131 }, 132 { 133 initialHTML: 134 `<${tag}>abc${caretInLeft}</${tag}>` + 135 `<pre><b>${caretInRight}def\nghi</b></pre>`, 136 expectedHTML: [ 137 `<${tag}>abc<b>def</b></${tag}>` + 138 "<pre><b>ghi</b></pre>", 139 ], 140 expectedHTMLWithStyledPre: [ 141 `<${tag}>abc<b style="white-space:pre">def</b></${tag}>` + 142 "<pre><b>ghi</b></pre>", 143 `<${tag}>abc<span style="white-space:pre"><b>def</b></span></${tag}>` + 144 "<pre><b>ghi</b></pre>", 145 ], 146 }, 147 { 148 initialHTML: 149 `<${tag}>abc${caretInLeft}</${tag}>` + 150 `<pre><b>${caretInRight}def<br>ghi</b></pre>`, 151 expectedHTML: [ 152 `<${tag}>abc<b>def</b></${tag}>` + 153 "<pre><b>ghi</b></pre>", 154 ], 155 expectedHTMLWithStyledPre: [ 156 `<${tag}>abc<b style="white-space:pre">def</b></${tag}>` + 157 "<pre><b>ghi</b></pre>", 158 `<${tag}>abc<span style="white-space:pre"><b>def</b></span></${tag}>` + 159 "<pre><b>ghi</b></pre>", 160 ], 161 }, 162 { 163 initialHTML: 164 `<${tag}>abc${caretInLeft}</${tag}>` + 165 `<pre><b>${caretInRight}def</b>\nghi</pre>`, 166 expectedHTML: [ 167 `<${tag}>abc<b>def</b></${tag}>` + 168 "<pre>ghi</pre>", 169 ], 170 expectedHTMLWithStyledPre: [ 171 `<${tag}>abc<b style="white-space:pre">def</b></${tag}>` + 172 "<pre>ghi</pre>", 173 `<${tag}>abc<span style="white-space:pre"><b>def</b></span></${tag}>` + 174 "<pre>ghi</pre>", 175 ], 176 }, 177 { 178 initialHTML: 179 `<${tag}>abc${caretInLeft}</${tag}>` + 180 `<pre><b>${caretInRight}def</b><br>ghi</pre>`, 181 expectedHTML: [ 182 `<${tag}>abc<b>def</b></${tag}>` + 183 "<pre>ghi</pre>", 184 ], 185 expectedHTMLWithStyledPre: [ 186 `<${tag}>abc<b style="white-space:pre">def</b></${tag}>` + 187 "<pre>ghi</pre>", 188 `<${tag}>abc<span style="white-space:pre"><b>def</b></span></${tag}>` + 189 "<pre>ghi</pre>", 190 ], 191 }, 192 { 193 initialHTML: 194 `<${tag}>abc${caretInLeft}</${tag}>` + 195 `<pre><b>${caretInRight}def\n</b>ghi</pre>`, 196 expectedHTML: [ 197 `<${tag}>abc<b>def</b></${tag}>` + 198 "<pre>ghi</pre>", 199 ], 200 expectedHTMLWithStyledPre: [ 201 `<${tag}>abc<b style="white-space:pre">def</b></${tag}>` + 202 `<pre>ghi</pre>`, 203 `<${tag}>abc<span style="white-space:pre"><b>def</b></span></${tag}>` + 204 "<pre>ghi</pre>", 205 ], 206 }, 207 { 208 initialHTML: 209 `<${tag}>abc${caretInLeft}</${tag}>` + 210 `<pre><b>${caretInRight}def<br></b>ghi</pre>`, 211 expectedHTML: [ 212 `<${tag}>abc<b>def</b></${tag}>` + 213 "<pre>ghi</pre>", 214 ], 215 expectedHTMLWithStyledPre: [ 216 `<${tag}>abc<b style="white-space:pre">def</b></${tag}>` + 217 "<pre>ghi</pre>", 218 `<${tag}>abc<span style="white-space:pre"><b>def</b></span></${tag}>` + 219 "<pre>ghi</pre>", 220 ], 221 }, 222 // One linefeed at start of <pre> should be ignored. 223 // Note that if setupEditingHost() does not touch the text node in <pre>, 224 // the leading line break is ignored, but if it touches the text node, 225 // the value is set to as-is. Therefore, the following tests can work 226 // with empty caretInRight value. 227 { 228 initialHTML: 229 `<${tag}>abc${caretInLeft}</${tag}>` + 230 `<pre>\ndef\nghi</pre>`, 231 expectedHTML: [ 232 `<${tag}>abcdef</${tag}>` + 233 `<pre>ghi</pre>`, 234 ], 235 expectedHTMLWithStyledPre: [ 236 `<${tag}>abc<span style="white-space:pre">def</span></${tag}>` + 237 "<pre>ghi</pre>", 238 ], 239 skip: caretInRight !== "", 240 }, 241 // When there are two line breaks at start of <pre>, the first one should be 242 // ignored by the parser but the second one should make empty first line. 243 // Therefore, the first empty line should be removed. 244 { 245 initialHTML: 246 `<${tag}>abc${caretInLeft}</${tag}>` + 247 `<pre>\n\ndef\nghi</pre>`, 248 expectedHTML: [ 249 `<${tag}>abc</${tag}>` + 250 "<pre>def\nghi</pre>", 251 ], 252 skip: caretInRight !== "", 253 }, 254 ]; 255 const utils = new EditorTestUtils(editingHost); 256 257 const betweenBlockAndPre = new RegExp(`</${tag}><pre>`); 258 const betweenPreAndBlock = new RegExp(`</pre><${tag}>`); 259 function putStyleElement() { 260 const styleElement = document.createElement("style"); 261 styleElement.textContent = "pre { white-space: pre; }"; 262 document.head.appendChild(styleElement); 263 } 264 265 for (const specifyPreStyle of [false, true]) { 266 for (const t of tests) { 267 if (t.skip) { 268 continue; 269 } 270 if (specifyPreStyle && !t.expectedHTMLWithStyledPre) { 271 continue; 272 } 273 promise_test(async () => { 274 if (specifyPreStyle) { 275 putStyleElement(); 276 } 277 try { 278 utils.setupEditingHost(t.initialHTML); 279 await (testingBackspace ? utils.sendBackspaceKey() : utils.sendDeleteKey()); 280 utils.normalizeStyleAttributeValues(); 281 assert_in_array( 282 editingHost.innerHTML, 283 specifyPreStyle ? t.expectedHTMLWithStyledPre : t.expectedHTML, 284 `white-space should${ 285 !specifyPreStyle ? " not" : "" 286 } be preserved by <span> elements` 287 ); 288 } finally { 289 if (specifyPreStyle) { 290 document.querySelector("style")?.remove(); 291 } 292 } 293 }, `${commandName} at ${t.initialHTML.replace(/\n/g, "\\n")}${ 294 specifyPreStyle ? " (with <style>pre { white-space: pre; }</style>)" : "" 295 }`); 296 297 // Repeat same tests with inserting a line break between the paragraphs. 298 const initialHTMLWithLineBreak = 299 t.initialHTML 300 .replace(betweenBlockAndPre, `</${tag}>\n<pre>`) 301 .replace(betweenPreAndBlock, `</pre>\n<${tag}>`); 302 promise_test(async () => { 303 if (specifyPreStyle) { 304 putStyleElement(); 305 } 306 try { 307 utils.setupEditingHost(initialHTMLWithLineBreak); 308 await (testingBackspace ? utils.sendBackspaceKey() : utils.sendDeleteKey()); 309 utils.normalizeStyleAttributeValues(); 310 assert_in_array( 311 editingHost.innerHTML, 312 specifyPreStyle ? t.expectedHTMLWithStyledPre : t.expectedHTML, 313 `white-space should${ 314 !specifyPreStyle ? " not" : "" 315 } be preserved by <span> elements (testing with a line break between paragraphs)` 316 ); 317 } finally { 318 if (specifyPreStyle) { 319 document.querySelector("style")?.remove(); 320 } 321 } 322 }, `${commandName} at ${initialHTMLWithLineBreak.replace(/\n/g, "\\n")}${ 323 specifyPreStyle ? " (with <style>pre { white-space: pre; }</style>)" : "" 324 }`); 325 } 326 } 327 </script> 328 </body> 329 </html>