FormControlRange-interactive-overlap-and-selection.html (3934B)
1 <!DOCTYPE html> 2 <meta charset="utf-8"> 3 <script src="/resources/testharness.js"></script> 4 <script src="/resources/testharnessreport.js"></script> 5 <script src="/resources/testdriver.js"></script> 6 <script src="/resources/testdriver-vendor.js"></script> 7 <script src="/resources/testdriver-actions.js"></script> 8 <body></body> 9 <script> 10 'use strict'; 11 12 const KEY_BACKSPACE = '\uE003'; 13 const KEY_DELETE = '\uE017'; 14 const controls = ['input', 'textarea']; 15 16 function setup(control, value) { 17 document.body.innerHTML = control === 'input' ? '<input type="text">' : '<textarea></textarea>'; 18 const element = document.body.firstElementChild; 19 element.value = value; 20 element.focus(); 21 return element; 22 } 23 24 async function typeKeys(element, text) { await test_driver.send_keys(element, text); } 25 26 function makeRange(element, start, end) { 27 const range = new FormControlRange(); 28 range.setFormControlRange(element, start, end); 29 return range; 30 } 31 32 controls.forEach(control => { 33 [ 34 // Selection partially overlaps the start of the live range: deletion shrinks and shifts start. 35 { name:'partial overlap at start (delete selection)', 36 init:{ value:'ABCDE', rangeStart:2, rangeEnd:4, selA:1, selB:3, key:KEY_BACKSPACE }, 37 expect:{ value:'ADE', start:1, end:2, text:'D' } }, 38 // Selection partially overlaps the end of the live range: deletion contracts end. 39 { name:'partial overlap at end (delete selection)', 40 init:{ value:'ABCDE', rangeStart:1, rangeEnd:3, selA:2, selB:4, key:KEY_BACKSPACE }, 41 expect:{ value:'ABE', start:1, end:2, text:'B' } }, 42 ].forEach(tc => { 43 promise_test(async t => { 44 const element = setup(control, tc.init.value); 45 const range = makeRange(element, tc.init.rangeStart, tc.init.rangeEnd); 46 element.setSelectionRange(tc.init.selA, tc.init.selB); 47 if (tc.init.key) { 48 await typeKeys(element, tc.init.key); 49 } else if (tc.init.text) { 50 await typeKeys(element, tc.init.text); 51 } 52 assert_equals(element.value, tc.expect.value, 'post-edit element.value'); 53 assert_equals(range.startOffset, tc.expect.start, 'range start'); 54 assert_equals(range.endOffset, tc.expect.end, 'range end'); 55 assert_equals(range.toString(), tc.expect.text, 'range text'); 56 }, `Overlap: ${tc.name} (${control}).`); 57 }); 58 59 [ 60 { name:'delete selection exactly equal to range (collapse)', 61 init:{ value:'ABCDE', rangeStart:1, rangeEnd:3, selA:1, selB:3, key:KEY_BACKSPACE }, 62 expect:{ value:'ADE', start:1, end:1, text:'' } }, 63 { name:'delete subset inside range (contract end)', 64 init:{ value:'ABCDE', rangeStart:1, rangeEnd:4, selA:2, selB:3, key:KEY_BACKSPACE }, 65 expect:{ value:'ABDE', start:1, end:3, text:'BD' } }, 66 ].forEach(tc => { 67 promise_test(async t => { 68 const element = setup(control, tc.init.value); 69 const range = makeRange(element, tc.init.rangeStart, tc.init.rangeEnd); 70 element.setSelectionRange(tc.init.selA, tc.init.selB); 71 await typeKeys(element, tc.init.key); 72 assert_equals(element.value, tc.expect.value, 'post-delete value'); 73 assert_equals(range.startOffset, tc.expect.start, 'start after delete'); 74 assert_equals(range.endOffset, tc.expect.end, 'end after delete'); 75 assert_equals(range.toString(), tc.expect.text, 'text after delete'); 76 if (tc.expect.start === tc.expect.end) { 77 assert_true(range.collapsed, 'range collapsed'); 78 } 79 }, `Selection deletion: ${tc.name} (${control}).`); 80 }); 81 82 promise_test(async t => { 83 const element = setup(control, 'ABCDE'); 84 const range = makeRange(element, 2, 4); 85 element.setSelectionRange(1, 1); 86 await typeKeys(element, KEY_DELETE); 87 assert_equals(element.value, 'ACDE'); 88 assert_equals(range.startOffset, 1); 89 assert_equals(range.endOffset, 3); 90 assert_equals(range.toString(), 'CD'); 91 }, `Boundary forward delete shifts range left (${control}).`); 92 93 }); 94 </script>