plaintextspellchecker_test.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457
  1. // Copyright 2007 The Closure Library Authors. All Rights Reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS-IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. goog.provide('goog.ui.PlainTextSpellCheckerTest');
  15. goog.setTestOnly('goog.ui.PlainTextSpellCheckerTest');
  16. goog.require('goog.Timer');
  17. goog.require('goog.dom');
  18. goog.require('goog.events.KeyCodes');
  19. goog.require('goog.spell.SpellCheck');
  20. goog.require('goog.testing.events');
  21. goog.require('goog.testing.jsunit');
  22. goog.require('goog.ui.PlainTextSpellChecker');
  23. var missspelling = 'missspelling';
  24. var iggnore = 'iggnore';
  25. var vocabulary = ['test', 'words', 'a', 'few', missspelling, iggnore];
  26. // We don't use Math.random() to make test predictable. Math.random is not
  27. // repeatable, so a success on the dev machine != success in the lab (or on
  28. // other dev machines). This is the same pseudorandom logic that CRT rand()
  29. // uses.
  30. var rseed = 1;
  31. function random(range) {
  32. rseed = (rseed * 1103515245 + 12345) & 0xffffffff;
  33. return ((rseed >> 16) & 0x7fff) % range;
  34. }
  35. function localSpellCheckingFunction(words, spellChecker, callback) {
  36. var len = words.length;
  37. var results = [];
  38. for (var i = 0; i < len; i++) {
  39. var word = words[i];
  40. var found = false;
  41. // Last two words are considered misspellings
  42. for (var j = 0; j < vocabulary.length - 2; ++j) {
  43. if (vocabulary[j] == word) {
  44. found = true;
  45. break;
  46. }
  47. }
  48. if (found) {
  49. results.push([word, goog.spell.SpellCheck.WordStatus.VALID]);
  50. } else {
  51. results.push(
  52. [word, goog.spell.SpellCheck.WordStatus.INVALID, ['foo', 'bar']]);
  53. }
  54. }
  55. callback.call(spellChecker, results);
  56. }
  57. function generateRandomSpace() {
  58. var string = '';
  59. var nSpace = 1 + random(4);
  60. for (var i = 0; i < nSpace; ++i) {
  61. string += ' ';
  62. }
  63. return string;
  64. }
  65. function generateRandomString(maxWords, doQuotes) {
  66. var x = random(10);
  67. var string = '';
  68. if (doQuotes) {
  69. if (x == 0) {
  70. string = 'On xxxxx yyyy wrote:\n> ';
  71. } else if (x < 3) {
  72. string = '> ';
  73. }
  74. }
  75. var nWords = 1 + random(maxWords);
  76. for (var i = 0; i < nWords; ++i) {
  77. string += vocabulary[random(vocabulary.length)];
  78. string += generateRandomSpace();
  79. }
  80. return string;
  81. }
  82. var timerQueue = [];
  83. function processTimerQueue() {
  84. while (timerQueue.length > 0) {
  85. var fn = timerQueue.shift();
  86. fn();
  87. }
  88. }
  89. function localTimer(fn, delay, obj) {
  90. if (obj) {
  91. fn = goog.bind(fn, obj);
  92. }
  93. timerQueue.push(fn);
  94. return timerQueue.length;
  95. }
  96. function testPlainTextSpellCheckerNoQuotes() {
  97. var handler = new goog.spell.SpellCheck(localSpellCheckingFunction);
  98. var s = new goog.ui.PlainTextSpellChecker(handler);
  99. s.asyncWordsPerBatch_ = 100;
  100. var el = document.getElementById('test1');
  101. s.decorate(el);
  102. var text = '';
  103. for (var i = 0; i < 10; ++i) {
  104. text += generateRandomString(10, false) + '\n';
  105. }
  106. el.value = text;
  107. // Yes this looks bizarre. This is for '\n' processing.
  108. // They get converted to CRLF as part of the above statement.
  109. text = el.value;
  110. var timerSav = goog.Timer.callOnce;
  111. goog.Timer.callOnce = localTimer;
  112. s.check();
  113. processTimerQueue();
  114. s.ignoreWord(iggnore);
  115. processTimerQueue();
  116. s.check();
  117. processTimerQueue();
  118. s.resume();
  119. processTimerQueue();
  120. goog.Timer.callOnce = timerSav;
  121. assertEquals(
  122. 'Spell checker run should not change the underlying element.', text,
  123. el.value);
  124. s.dispose();
  125. }
  126. function testPlainTextSpellCheckerWithQuotes() {
  127. var handler = new goog.spell.SpellCheck(localSpellCheckingFunction);
  128. var s = new goog.ui.PlainTextSpellChecker(handler);
  129. s.asyncWordsPerBatch_ = 100;
  130. var el = document.getElementById('test2');
  131. s.decorate(el);
  132. var text = '';
  133. for (var i = 0; i < 10; ++i) {
  134. text += generateRandomString(10, true) + '\n';
  135. }
  136. el.value = text;
  137. // Yes this looks bizarre. This is for '\n' processing.
  138. // They get converted to CRLF as part of the above statement.
  139. text = el.value;
  140. var timerSav = goog.Timer.callOnce;
  141. goog.Timer.callOnce = localTimer;
  142. s.setExcludeMarker(new RegExp('\nOn .* wrote:\n(> .*\n)+|\n(> .*\n)', 'g'));
  143. s.check();
  144. processTimerQueue();
  145. s.ignoreWord(iggnore);
  146. processTimerQueue();
  147. s.check();
  148. processTimerQueue();
  149. s.resume();
  150. processTimerQueue();
  151. goog.Timer.callOnce = timerSav;
  152. assertEquals(
  153. 'Spell checker run should not change the underlying element.', text,
  154. el.value);
  155. s.dispose();
  156. }
  157. function testPlainTextSpellCheckerWordReplacement() {
  158. var handler = new goog.spell.SpellCheck(localSpellCheckingFunction);
  159. var s = new goog.ui.PlainTextSpellChecker(handler);
  160. s.asyncWordsPerBatch_ = 100;
  161. var el = document.getElementById('test3');
  162. s.decorate(el);
  163. var text = '';
  164. for (var i = 0; i < 10; ++i) {
  165. text += generateRandomString(10, false) + '\n';
  166. }
  167. el.value = text;
  168. var timerSav = goog.Timer.callOnce;
  169. goog.Timer.callOnce = localTimer;
  170. s.check();
  171. processTimerQueue();
  172. var container = s.overlay_;
  173. var wordEl = container.firstChild;
  174. while (wordEl) {
  175. if (goog.dom.getTextContent(wordEl) == missspelling) {
  176. break;
  177. }
  178. wordEl = wordEl.nextSibling;
  179. }
  180. if (!wordEl) {
  181. assertTrue(
  182. 'Cannot find the world that should have been here.' +
  183. 'Please revise the test',
  184. false);
  185. return;
  186. }
  187. s.activeWord_ = missspelling;
  188. s.activeElement_ = wordEl;
  189. var suggestions = s.getSuggestions_();
  190. s.replaceWord(wordEl, missspelling, 'foo');
  191. assertEquals(
  192. 'Should have set the original word attribute!',
  193. wordEl.getAttribute(goog.ui.AbstractSpellChecker.ORIGINAL_),
  194. missspelling);
  195. s.activeWord_ = goog.dom.getTextContent(wordEl);
  196. s.activeElement_ = wordEl;
  197. var newSuggestions = s.getSuggestions_();
  198. assertEquals(
  199. 'Suggestion list should still be present even if the word ' +
  200. 'is now correct!',
  201. suggestions, newSuggestions);
  202. s.resume();
  203. processTimerQueue();
  204. goog.Timer.callOnce = timerSav;
  205. s.dispose();
  206. }
  207. function testPlainTextSpellCheckerKeyboardNavigateNext() {
  208. var handler = new goog.spell.SpellCheck(localSpellCheckingFunction);
  209. var s = new goog.ui.PlainTextSpellChecker(handler);
  210. var el = document.getElementById('test4');
  211. s.decorate(el);
  212. var text = 'a unit test for keyboard test';
  213. el.value = text;
  214. var keyEventProperties = {};
  215. keyEventProperties.ctrlKey = true;
  216. keyEventProperties.shiftKey = false;
  217. var timerSav = goog.Timer.callOnce;
  218. goog.Timer.callOnce = localTimer;
  219. s.check();
  220. processTimerQueue();
  221. var container = s.overlay_;
  222. // First call just moves focus to first misspelled word.
  223. goog.testing.events.fireKeySequence(
  224. container, goog.events.KeyCodes.RIGHT, keyEventProperties);
  225. // Test moving from first to second misspelled word.
  226. var defaultExecuted = goog.testing.events.fireKeySequence(
  227. container, goog.events.KeyCodes.RIGHT, keyEventProperties);
  228. assertFalse(
  229. 'The default action should be prevented for the key event',
  230. defaultExecuted);
  231. assertEquals(
  232. 'The second misspelled word should have focus.', document.activeElement,
  233. container.children[1]);
  234. s.resume();
  235. processTimerQueue();
  236. goog.Timer.callOnce = timerSav;
  237. s.dispose();
  238. }
  239. function testPlainTextSpellCheckerKeyboardNavigateNextOnLastWord() {
  240. var handler = new goog.spell.SpellCheck(localSpellCheckingFunction);
  241. var s = new goog.ui.PlainTextSpellChecker(handler);
  242. var el = document.getElementById('test5');
  243. s.decorate(el);
  244. var text = 'a unit test for keyboard test';
  245. el.value = text;
  246. var keyEventProperties = {};
  247. keyEventProperties.ctrlKey = true;
  248. keyEventProperties.shiftKey = false;
  249. var timerSav = goog.Timer.callOnce;
  250. goog.Timer.callOnce = localTimer;
  251. s.check();
  252. processTimerQueue();
  253. var container = s.overlay_;
  254. // First call just moves focus to first misspelled word.
  255. goog.testing.events.fireKeySequence(
  256. container, goog.events.KeyCodes.RIGHT, keyEventProperties);
  257. goog.testing.events.fireKeySequence(
  258. container, goog.events.KeyCodes.RIGHT, keyEventProperties);
  259. goog.testing.events.fireKeySequence(
  260. container, goog.events.KeyCodes.RIGHT, keyEventProperties);
  261. // Test moving to the next invalid word.
  262. var defaultExecuted = goog.testing.events.fireKeySequence(
  263. container, goog.events.KeyCodes.RIGHT, keyEventProperties);
  264. assertFalse(
  265. 'The default action should be prevented for the key event',
  266. defaultExecuted);
  267. assertEquals(
  268. 'The third/last misspelled word should have focus.',
  269. document.activeElement, container.children[2]);
  270. s.resume();
  271. processTimerQueue();
  272. goog.Timer.callOnce = timerSav;
  273. s.dispose();
  274. }
  275. function testPlainTextSpellCheckerKeyboardNavigateOpenSuggestions() {
  276. var handler = new goog.spell.SpellCheck(localSpellCheckingFunction);
  277. var s = new goog.ui.PlainTextSpellChecker(handler);
  278. var el = document.getElementById('test6');
  279. s.decorate(el);
  280. var text = 'unit';
  281. el.value = text;
  282. var keyEventProperties = {};
  283. keyEventProperties.ctrlKey = true;
  284. keyEventProperties.shiftKey = false;
  285. var timerSav = goog.Timer.callOnce;
  286. goog.Timer.callOnce = localTimer;
  287. s.check();
  288. processTimerQueue();
  289. var container = s.overlay_;
  290. var suggestionMenu = s.getMenu();
  291. goog.testing.events.fireKeySequence(
  292. container, goog.events.KeyCodes.RIGHT, keyEventProperties);
  293. assertFalse(
  294. 'The suggestion menu should not be visible yet.',
  295. suggestionMenu.isVisible());
  296. keyEventProperties.ctrlKey = false;
  297. var defaultExecuted = goog.testing.events.fireKeySequence(
  298. container, goog.events.KeyCodes.DOWN, keyEventProperties);
  299. assertFalse(
  300. 'The default action should be prevented for the key event',
  301. defaultExecuted);
  302. assertTrue(
  303. 'The suggestion menu should be visible after the key event.',
  304. suggestionMenu.isVisible());
  305. s.resume();
  306. processTimerQueue();
  307. goog.Timer.callOnce = timerSav;
  308. s.dispose();
  309. }
  310. function testPlainTextSpellCheckerKeyboardNavigatePrevious() {
  311. var handler = new goog.spell.SpellCheck(localSpellCheckingFunction);
  312. var s = new goog.ui.PlainTextSpellChecker(handler);
  313. var el = document.getElementById('test7');
  314. s.decorate(el);
  315. var text = 'a unit test for keyboard test';
  316. el.value = text;
  317. var keyEventProperties = {};
  318. keyEventProperties.ctrlKey = true;
  319. keyEventProperties.shiftKey = false;
  320. var timerSav = goog.Timer.callOnce;
  321. goog.Timer.callOnce = localTimer;
  322. s.check();
  323. processTimerQueue();
  324. var container = s.overlay_;
  325. // Move to the third element, so we can test the move back to the second.
  326. goog.testing.events.fireKeySequence(
  327. container, goog.events.KeyCodes.RIGHT, keyEventProperties);
  328. goog.testing.events.fireKeySequence(
  329. container, goog.events.KeyCodes.RIGHT, keyEventProperties);
  330. goog.testing.events.fireKeySequence(
  331. container, goog.events.KeyCodes.RIGHT, keyEventProperties);
  332. // Test moving from third to second misspelled word.
  333. var defaultExecuted = goog.testing.events.fireKeySequence(
  334. container, goog.events.KeyCodes.LEFT, keyEventProperties);
  335. assertFalse(
  336. 'The default action should be prevented for the key event',
  337. defaultExecuted);
  338. assertEquals(
  339. 'The second misspelled word should have focus.', document.activeElement,
  340. container.children[1]);
  341. s.resume();
  342. processTimerQueue();
  343. goog.Timer.callOnce = timerSav;
  344. s.dispose();
  345. }
  346. function testPlainTextSpellCheckerKeyboardNavigatePreviousOnFirstWord() {
  347. var handler = new goog.spell.SpellCheck(localSpellCheckingFunction);
  348. var s = new goog.ui.PlainTextSpellChecker(handler);
  349. var el = document.getElementById('test8');
  350. s.decorate(el);
  351. var text = 'a unit test for keyboard test';
  352. el.value = text;
  353. var keyEventProperties = {};
  354. keyEventProperties.ctrlKey = true;
  355. keyEventProperties.shiftKey = false;
  356. var timerSav = goog.Timer.callOnce;
  357. goog.Timer.callOnce = localTimer;
  358. s.check();
  359. processTimerQueue();
  360. var container = s.overlay_;
  361. // Move to the first invalid word.
  362. goog.testing.events.fireKeySequence(
  363. container, goog.events.KeyCodes.RIGHT, keyEventProperties);
  364. // Test moving to the previous invalid word.
  365. var defaultExecuted = goog.testing.events.fireKeySequence(
  366. container, goog.events.KeyCodes.LEFT, keyEventProperties);
  367. assertFalse(
  368. 'The default action should be prevented for the key event',
  369. defaultExecuted);
  370. assertEquals(
  371. 'The first misspelled word should have focus.', document.activeElement,
  372. container.children[0]);
  373. s.resume();
  374. processTimerQueue();
  375. goog.Timer.callOnce = timerSav;
  376. s.dispose();
  377. }