input-events-get-target-ranges.html (5831B)
1 <!DOCTYPE html> 2 <meta charset="utf-8"> 3 <title>InputEvent.getTargetRanges() behavior</title> 4 <script src="/resources/testharness.js"></script> 5 <script src="/resources/testharnessreport.js"></script> 6 <script src="/resources/testdriver.js"></script> 7 <script src="/resources/testdriver-actions.js"></script> 8 <script src="/resources/testdriver-vendor.js"></script> 9 <p>To manually run this test, please follow the steps below:<br/> 10 1. Place caret at the end of 'hel<i>lo wo</i><b>rld</b>'.<br/> 11 2. Press Ctrl-Backspace (Alt-Backspace on macOS) to delete word backwards.<br/> 12 3. Place caret at the end of 'test2' => Press 'a' key.<br/> 13 4. Select 'test2a' => Press 'b' key.<br/> 14 5. Select 'b' => Bold text through context menu or Command-b on macOS.<br/> 15 6. Place caret at the end of 'test3' => Press 'a' key => Press Backspace key.<br/> 16 <br/> 17 If a "PASS" result appears the test passes, otherwise it fails</p> 18 <p id="test1_editable" contenteditable>hel<i>lo wo</i><b>rld</b></p> 19 <p id="test2_editable" contenteditable>test2</p> 20 <textarea id="test3_plain">test3</textarea> 21 <script> 22 function resolveWhen(condition) { 23 return new Promise((resolve, reject) => { 24 function tick() { 25 if (condition()) 26 resolve(); 27 else 28 requestAnimationFrame(tick.bind(this)); 29 } 30 tick(); 31 }); 32 } 33 34 let modifier_key = "\uE009"; 35 if(navigator.platform.includes('Mac')) 36 modifier_key = "\uE03D"; 37 const commands = { 38 COPY: 'copy', 39 CUT: 'cut', 40 PASTE: 'paste', 41 SELECTALL: 'select all', 42 DELETEALL: 'delete all', 43 BOLD: 'bold', 44 } 45 const backspace = "\uE003"; 46 47 function clickOnTarget(target) { 48 return new test_driver.Actions() 49 .pointerMove(0, 0, {origin: target}) 50 .pointerDown() 51 .pointerUp() 52 .send(); 53 } 54 55 function sendTextCommand(command) { 56 let command_key = ""; 57 if(command == "copy") 58 command_key = "c"; 59 else if (command == "cut") 60 command_key = "x"; 61 else if (command == "paste") 62 command_key = "v"; 63 else if (command == "select all") 64 command_key = "a"; 65 else if (command == "delete all") 66 command_key = backspace; 67 else if (command == "bold") 68 command_key = "b"; 69 return new test_driver.Actions() 70 .keyDown(modifier_key) 71 .keyDown(command_key) 72 .keyUp(command_key) 73 .keyUp(modifier_key) 74 .send(); 75 } 76 77 function sendTextCommandAtTarget(target, command) { 78 return clickOnTarget(target).then(() => { 79 return sendTextCommand(command); 80 }); 81 } 82 83 function addTextAtTarget(target, char) { 84 return test_driver.send_keys(target, char); 85 } 86 87 promise_test(async test => { 88 const test1_editable = document.getElementById('test1_editable'); 89 let lastBeforeInput; 90 test1_editable.addEventListener('beforeinput', test.step_func(function() { 91 assert_equals(event.inputType, 'deleteWordBackward'); 92 const ranges = event.getTargetRanges(); 93 assert_equals(ranges.length, 1); 94 const range = ranges[0]; 95 assert_true(range instanceof StaticRange); 96 assert_equals(range.startOffset, 3); 97 assert_equals(range.startContainer.textContent, 'lo wo'); 98 assert_equals(range.endOffset, 3); 99 assert_equals(range.endContainer.textContent, 'rld'); 100 assert_equals(test1_editable.innerHTML, 'hel<i>lo wo</i><b>rld</b>'); 101 lastBeforeInput = event; 102 })); 103 104 test1_editable.addEventListener('input', test.step_func(function() { 105 assert_equals(event.inputType, 'deleteWordBackward'); 106 assert_equals(test1_editable.innerHTML, 'hel<i>lo </i>'); 107 assert_equals(lastBeforeInput.inputType, 'deleteWordBackward'); 108 assert_equals(lastBeforeInput.getTargetRanges().length, 0, 109 'getTargetRanges() should be empty after the event has finished dispatching.'); 110 })); 111 112 await sendTextCommandAtTarget(test1_editable, commands.DELETEALL); 113 await resolveWhen(() => { return test1_editable.innerHTML == 'hel<i>lo </i>' }); 114 }, 'getTargetRanges() returns correct range and cleared after dispatch.'); 115 116 promise_test(async test => { 117 const expectedEventLog = ['test2-5-test2-5', 'test2a-0-test2a-6', 'b-0-b-1']; 118 const actualEventLog = []; 119 120 const test2_editable = document.getElementById('test2_editable'); 121 test2_editable.addEventListener('beforeinput', test.step_func(function() { 122 const ranges = event.getTargetRanges(); 123 assert_equals(ranges.length, 1); 124 const range = ranges[0]; 125 actualEventLog.push( 126 `${range.startContainer.textContent}-${range.startOffset}-${range.endContainer.textContent}-${range.endOffset}`); 127 })); 128 129 await addTextAtTarget(test2_editable, "a"); 130 await sendTextCommandAtTarget(test2_editable, commands.SELECTALL); 131 await addTextAtTarget(test2_editable, "b"); 132 await sendTextCommandAtTarget(test2_editable, commands.SELECTALL); 133 await sendTextCommand(commands.BOLD); 134 await resolveWhen(() => { return actualEventLog.length == expectedEventLog.length }); 135 assert_array_equals(actualEventLog, expectedEventLog, 136 `Expected: ${expectedEventLog}; Actual: ${actualEventLog}.`); 137 }, 'Actions other than deletion should have current selection as target ranges.'); 138 139 promise_test(async test => { 140 const test3_plain = document.getElementById('test3_plain'); 141 let event_type; 142 test3_plain.addEventListener('beforeinput', test.step_func(function() { 143 assert_equals(event.getTargetRanges().length, 0, 144 'getTargetRanges() should return empty array on textarea.'); 145 146 if (event.inputType === 'deleteContentBackward') 147 event_type = event.inputType; 148 })); 149 150 await addTextAtTarget(test3_plain, "a"); 151 await addTextAtTarget(test3_plain, backspace); 152 await resolveWhen(() => { return event_type == 'deleteContentBackward' }); 153 }, 'Textarea should have empty target range.'); 154 </script>