FormControlRange-offset.html (5197B)
1 <!DOCTYPE html> 2 <meta charset="utf-8"> 3 <script src="/resources/testharness.js"></script> 4 <script src="/resources/testharnessreport.js"></script> 5 <body></body> 6 <script> 7 const controls = ["textarea", "input"]; 8 9 function setupControl(control, value) { 10 document.body.innerHTML = (control === "input") ? '<input type="text">' : '<textarea></textarea>'; 11 const element = document.body.firstElementChild; 12 element.value = value; 13 return element; 14 } 15 16 controls.forEach(control => { 17 test(() => { 18 const element = setupControl(control, "A"); 19 const range = new FormControlRange(); 20 21 [[0, 0, "", true], [0, 1, "A", false], [1, 1, "", true]].forEach(([start, end, text, collapsed]) => { 22 range.setFormControlRange(element, start, end); 23 assert_equals(range.toString(), text); 24 assert_equals(range.collapsed, collapsed); 25 }); 26 }, `FormControlRange with single character in ${control}.`); 27 28 test(() => { 29 const element = setupControl(control, ""); 30 const range = new FormControlRange(); 31 32 range.setFormControlRange(element, 0, 0); 33 assert_equals(range.toString(), ""); 34 assert_true(range.collapsed); 35 }, `FormControlRange with empty control in ${control}.`); 36 37 test(() => { 38 const element = setupControl(control, "Hello World"); 39 const range = new FormControlRange(); 40 41 [[0, 5, "Hello"], [5, 6, " "], [6, 11, "World"], [11, 11, ""]].forEach(([start, end, expected]) => { 42 range.setFormControlRange(element, start, end); 43 assert_equals(range.toString(), expected); 44 }); 45 }, `FormControlRange boundary positions in ${control}.`); 46 47 test(() => { 48 const testCases = [ 49 { 50 // Emoji π with length 2 in UTF-16 ('\uD83D\uDE0A'). 51 text: '\u{1F60A}', 52 ranges: [[0, 0, ""], [0, 2, "π"], [2, 2, ""], [0, 1, "\uD83D"], [1, 2, "\uDE0A"]] 53 }, 54 { 55 // ZWJ emoji (π¨βπ©βπ§) with length 8 in UTF-16 ('\uD83D\uDC68\u200D\uD83D\uDC69\u200D\uD83D\uDC67'). 56 text: '\u{1F468}\u{200D}\u{1F469}\u{200D}\u{1F467}', 57 ranges: [[0, 0, ""], [0, 8, "π¨βπ©βπ§"], [8, 8, ""], [0, 2, "π¨"], [3, 5, "π©"], [6, 8, "π§"], 58 [1, 2, "\uDC68"], [2, 3, "\u200D"]] 59 }, 60 { 61 // Mixed ASCII and emoji π. 62 text: 'Hello\u{1F60A}World', 63 ranges: [[0, 5, "Hello"], [5, 7, "π"], [7, 12, "World"], [0, 12, "HelloπWorld"], [3, 6, "lo\uD83D"], 64 [6, 9, "\uDE0AWo"], [4, 8, "oπW"]] 65 } 66 ]; 67 68 testCases.forEach(testCase => { 69 const element = setupControl(control, testCase.text); 70 const range = new FormControlRange(); 71 testCase.ranges.forEach(([start, end, expected]) => { 72 range.setFormControlRange(element, start, end); 73 assert_equals(range.toString(), expected); 74 }); 75 }); 76 }, `FormControlRange with Unicode characters in ${control}.`); 77 }); 78 79 80 test(() => { 81 // Test textarea with mixed whitespace in light DOM. 82 document.body.innerHTML = '<textarea> \n\t Line1\n Line2 \t\n </textarea>'; 83 const textarea = document.body.firstElementChild; 84 const range = new FormControlRange(); 85 86 // Whitespace is preserved in textarea.value. 87 range.setFormControlRange(textarea, 0, textarea.value.length); 88 assert_equals(range.toString(), textarea.value); 89 assert_true(range.toString().includes('\n')); 90 assert_true(range.toString().includes('\t')); 91 }, "FormControlRange preserves whitespace in textarea light DOM."); 92 93 test(() => { 94 document.body.innerHTML = '<textarea>Hello World</textarea>'; 95 const textarea = document.body.firstElementChild; 96 97 // Create a complex DOM structure inside the textarea. 98 // Child nodes don't affect the textarea's .value, only text content does. 99 textarea.append(document.createElement("video")); 100 textarea.append(document.createElement("iframe")); 101 102 const span = document.createElement("span"); 103 span.innerHTML = "more text"; 104 textarea.append(span); 105 106 textarea.append(document.createTextNode("Some rendered content")); 107 textarea.append(document.createTextNode("Some more rendered content")); 108 109 const range = new FormControlRange(); 110 111 // FormControlRange uses element.value, which includes text nodes but ignores element children. 112 assert_equals(textarea.value, "Hello WorldSome rendered contentSome more rendered content"); 113 range.setFormControlRange(textarea, 0, textarea.value.length); 114 assert_equals(range.toString(), "Hello WorldSome rendered contentSome more rendered content"); 115 }, "FormControlRange should work correctly with weirdly formed light DOM structure."); 116 117 test(() => { 118 // Test input with child nodes (which shouldn't exist but might) 119 document.body.innerHTML = '<input type="text" value="Original">'; 120 const input = document.body.firstElementChild; 121 122 // Malformed: add child nodes to input (shouldn't affect value). 123 input.appendChild(document.createTextNode("Ignored")); 124 input.appendChild(document.createElement("span")).textContent = "AlsoIgnored"; 125 126 const range = new FormControlRange(); 127 range.setFormControlRange(input, 0, input.value.length); 128 129 // Input.value is unaffected by child nodes. 130 assert_equals(range.toString(), "Original"); 131 }, "FormControlRange ignores malformed input child nodes."); 132 </script>