test_spellcheck_after_edit.html (6589B)
1 <!doctype html> 2 <html> 3 <head> 4 <meta charset="utf-8"> 5 <title>Spellcheck result after edit</title> 6 <script src="/tests/SimpleTest/SimpleTest.js"></script> 7 <link rel="stylesheet" href="/tests/SimpleTest/test.css"> 8 </head> 9 <body> 10 <script> 11 let { maybeOnSpellCheck } = SpecialPowers.ChromeUtils.importESModule( 12 "resource://testing-common/AsyncSpellCheckTestHelper.sys.mjs" 13 ); 14 15 function waitForTick() { 16 return new Promise(resolve => 17 SimpleTest.executeSoon( 18 () => requestAnimationFrame( 19 () => requestAnimationFrame(resolve) 20 ) 21 ) 22 ); 23 } 24 25 async function waitForOnSpellCheck( 26 aSpellCheckSelection, 27 aEditingHost, 28 aWaitForNumberOfMisspelledWords, 29 aWhen 30 ) { 31 info(`Waiting for onSpellCheck (${aWhen})...`); 32 for (let retry = 0; retry < 100; retry++) { 33 await waitForTick(); 34 await new Promise(resolve => maybeOnSpellCheck(aEditingHost, resolve)); 35 if (aWaitForNumberOfMisspelledWords === 0) { 36 if (aSpellCheckSelection.rangeCount === 0) { 37 break; 38 } 39 } else if (aSpellCheckSelection.rangeCount >= aWaitForNumberOfMisspelledWords) { 40 break; 41 } 42 } 43 } 44 45 SimpleTest.waitForExplicitFinish(); 46 SimpleTest.waitForFocus(async () => { 47 /** 48 * test object should have: 49 * init function 50 * 51 * @param normalSel The normal selection for the editing host 52 * @param editingHost The editing host of the editor 53 * @return Number of misspelled word in the editor 54 * 55 * run function 56 * 57 * @param editingHost The editing host of the editor 58 * @return Expected number of misspelled word in the editor 59 * 60 * check function 61 * 62 * @param spellCheckSel The spellcheck selection for the editing host 63 * @param editingHost The editing host of the editor 64 */ 65 for (const test of [ 66 { 67 init: (normalSel, editingHost) => { 68 info("Staring to test spellcheck of misspelled word after joining paragraphs"); 69 editingHost.innerHTML = "<p>It is</p><p>what I want</p>"; 70 normalSel.collapse(editingHost.querySelector("p + p").firstChild, 0); 71 return 0; 72 }, 73 run: () => { 74 document.execCommand("delete"); 75 return 0; 76 }, 77 check: (spellCheckSel) => { 78 is( 79 spellCheckSel.rangeCount, 80 0, 81 "The joined misspelled word shouldn't be marked as misspelled word because caret is in the word" 82 ); 83 }, 84 }, 85 { 86 init: (normalSel, editingHost) => { 87 info("Staring to test spellcheck of correct word after joining paragraphs"); 88 editingHost.innerHTML = "<p>It's beco</p><p>ming nicer</p>"; 89 normalSel.collapse(editingHost.querySelector("p + p").firstChild, 0); 90 return 2; 91 }, 92 run: () => { 93 document.execCommand("delete"); 94 return 0; 95 }, 96 check: (spellCheckSel) => { 97 is( 98 spellCheckSel.rangeCount, 99 0, 100 "There shouldn't be misspelled word after joining separated word anyway" 101 ); 102 }, 103 }, 104 { 105 init: (normalSel, editingHost) => { 106 info("Staring to test spellcheck of correct words after splitting a paragraph"); 107 editingHost.innerHTML = "<p>It iswhat I want</p>"; 108 normalSel.collapse(editingHost.querySelector("p").firstChild, "It is".length); 109 return 1; 110 }, 111 run: () => { 112 document.execCommand("insertParagraph"); 113 return 0; 114 }, 115 check: (spellCheckSel) => { 116 is( 117 spellCheckSel.rangeCount, 118 0, 119 "No word should be marked as misspelled after split" 120 ); 121 }, 122 }, 123 { 124 init: (normalSel, editingHost) => { 125 info("Staring to test spellcheck of misspelled words after splitting a paragraph"); 126 editingHost.innerHTML = "<p>It's becoming nicer</p>"; 127 normalSel.collapse(editingHost.querySelector("p").firstChild, "It's beco".length); 128 return 0; 129 }, 130 run: () => { 131 document.execCommand("insertParagraph"); 132 return 1; 133 }, 134 check: (spellCheckSel, editingHost) => { 135 is( 136 spellCheckSel.rangeCount, 137 1, 138 "The split word in the first paragraph should be marked as misspelled, but the second paragraph's should be so because of caret is in it" 139 ); 140 if (!spellCheckSel.rangeCount) { 141 return; 142 } 143 is( 144 SpecialPowers.unwrap(spellCheckSel.getRangeAt(0).startContainer), 145 editingHost.querySelector("p").firstChild, 146 "First misspelled word should start in the first child of the first <p>" 147 ); 148 is( 149 SpecialPowers.unwrap(spellCheckSel.getRangeAt(0).endContainer), 150 editingHost.querySelector("p").firstChild, 151 "First misspelled word should end in the first child of the first <p>" 152 ); 153 is( 154 spellCheckSel.getRangeAt(0).startOffset, 155 "It's ".length, 156 "First misspelled word should start after 'It '" 157 ); 158 is( 159 spellCheckSel.getRangeAt(0).endOffset, 160 "It's beco".length, 161 "First misspelled word should end by after 'bec'" 162 ); 163 }, 164 }, 165 ]) { 166 const editingHost = document.createElement("div"); 167 editingHost.setAttribute("contenteditable", ""); 168 editingHost.setAttribute("spellcheck", "true"); 169 document.body.appendChild(editingHost); 170 editingHost.focus(); 171 const editor = 172 SpecialPowers.wrap(window).docShell.editingSession.getEditorForWindow(window); 173 const nsISelectionController = SpecialPowers.Ci.nsISelectionController; 174 const normalSel = editor.selectionController.getSelection( 175 nsISelectionController.SELECTION_NORMAL 176 ); 177 const spellCheckSel = editor.selectionController.getSelection( 178 nsISelectionController.SELECTION_SPELLCHECK 179 ); 180 const initialMisspelledWords = test.init(normalSel, editingHost); 181 await waitForOnSpellCheck( 182 spellCheckSel, editingHost, initialMisspelledWords, "before edit" 183 ); 184 await waitForTick(); 185 const expectedMisspelledWords = test.run(editingHost); 186 await waitForOnSpellCheck( 187 spellCheckSel, editingHost, expectedMisspelledWords, "after edit" 188 ); 189 test.check(spellCheckSel, editingHost); 190 editingHost.remove(); 191 await waitForTick(); 192 } 193 SimpleTest.finish(); 194 }); 195 </script> 196 </body> 197 </html>