123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372 |
- // Copyright 2007 The Closure Library Authors. All Rights Reserved.
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS-IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- goog.provide('goog.ui.RichTextSpellCheckerTest');
- goog.setTestOnly('goog.ui.RichTextSpellCheckerTest');
- goog.require('goog.dom.Range');
- goog.require('goog.dom.TagName');
- goog.require('goog.dom.classlist');
- goog.require('goog.events.KeyCodes');
- goog.require('goog.object');
- goog.require('goog.spell.SpellCheck');
- goog.require('goog.testing.MockClock');
- goog.require('goog.testing.events');
- goog.require('goog.testing.jsunit');
- goog.require('goog.ui.RichTextSpellChecker');
- var VOCABULARY = ['test', 'words', 'a', 'few'];
- var SUGGESTIONS = ['foo', 'bar'];
- var EXCLUDED_DATA = ['DIV.goog-quote', 'goog-comment', 'SPAN.goog-note'];
- /**
- * Delay in ms needed for the spell check word lookup to finish. Finishing the
- * lookup also finishes the spell checking.
- * @see goog.spell.SpellCheck.LOOKUP_DELAY_
- */
- var SPELL_CHECK_LOOKUP_DELAY = 100;
- var TEST_TEXT1 = 'this test is longer than a few words now';
- var TEST_TEXT2 = 'test another simple text with misspelled words';
- var TEST_TEXT3 = 'test another simple text with misspelled words' +
- '<b class="goog-quote">test another simple text with misspelled words<u> ' +
- 'test another simple text with misspelled words<del class="goog-quote"> ' +
- 'test another simple text with misspelled words<i>this test is longer ' +
- 'than a few words now</i>test another simple text with misspelled words ' +
- '<i>this test is longer than a few words now</i></del>test another ' +
- 'simple text with misspelled words<del class="goog-quote">test another ' +
- 'simple text with misspelled words<i>this test is longer than a few ' +
- 'words now</i>test another simple text with misspelled words<i>this test ' +
- 'is longer than a few words now</i></del></u>test another simple text ' +
- 'with misspelled words<u>test another simple text with misspelled words' +
- '<del class="goog-quote">test another simple text with misspelled words' +
- '<i> thistest is longer than a few words now</i>test another simple text ' +
- 'with misspelled words<i>this test is longer than a few words ' +
- 'now</i></del>test another simple text with misspelled words' +
- '<del class="goog-quote">test another simple text with misspelled words' +
- '<i>this test is longer than a few words now</i>test another simple text ' +
- 'with misspelled words<i>this test is longer than a few words ' +
- 'now</i></del></u></b>';
- var spellChecker;
- var handler;
- var mockClock;
- function setUp() {
- mockClock = new goog.testing.MockClock(true /* install */);
- handler = new goog.spell.SpellCheck(localSpellCheckingFunction);
- spellChecker = new goog.ui.RichTextSpellChecker(handler);
- }
- function tearDown() {
- spellChecker.dispose();
- handler.dispose();
- mockClock.dispose();
- }
- function waitForSpellCheckToFinish() {
- mockClock.tick(SPELL_CHECK_LOOKUP_DELAY);
- }
- /**
- * Function to use for word lookup by the spell check handler. This function is
- * supplied as a constructor parameter for the spell check handler.
- * @param {!Array<string>} words Unknown words that need to be looked up.
- * @param {!goog.spell.SpellCheck} spellChecker The spell check handler.
- * @param {function(!Array.)} callback The lookup callback
- * function.
- */
- function localSpellCheckingFunction(words, spellChecker, callback) {
- var len = words.length;
- var results = [];
- for (var i = 0; i < len; i++) {
- var word = words[i];
- var found = false;
- for (var j = 0; j < VOCABULARY.length; ++j) {
- if (VOCABULARY[j] == word) {
- found = true;
- break;
- }
- }
- if (found) {
- results.push([word, goog.spell.SpellCheck.WordStatus.VALID]);
- } else {
- results.push(
- [word, goog.spell.SpellCheck.WordStatus.INVALID, SUGGESTIONS]);
- }
- }
- callback.call(spellChecker, results);
- }
- function testDocumentIntegrity() {
- var el = document.getElementById('test1');
- spellChecker.decorate(el);
- el.appendChild(document.createTextNode(TEST_TEXT3));
- var el2 = el.cloneNode(true);
- spellChecker.setExcludeMarker('goog-quote');
- spellChecker.check();
- waitForSpellCheckToFinish();
- spellChecker.ignoreWord('iggnore');
- waitForSpellCheckToFinish();
- spellChecker.check();
- waitForSpellCheckToFinish();
- spellChecker.resume();
- waitForSpellCheckToFinish();
- assertEquals(
- 'Spell checker run should not change the underlying element.',
- el2.innerHTML, el.innerHTML);
- }
- function testExcludeMarkers() {
- var el = document.getElementById('test1');
- spellChecker.decorate(el);
- spellChecker.setExcludeMarker(
- ['DIV.goog-quote', 'goog-comment', 'SPAN.goog-note']);
- assertArrayEquals(
- ['goog-quote', 'goog-comment', 'goog-note'], spellChecker.excludeMarker);
- assertArrayEquals(
- [String(goog.dom.TagName.DIV), undefined, String(goog.dom.TagName.SPAN)],
- spellChecker.excludeTags);
- el.innerHTML = '<div class="goog-quote">misspelling</div>' +
- '<div class="goog-yes">misspelling</div>' +
- '<div class="goog-note">misspelling</div>' +
- '<div class="goog-comment">misspelling</div>' +
- '<span>misspelling<span>';
- spellChecker.check();
- waitForSpellCheckToFinish();
- assertEquals(3, spellChecker.getLastIndex());
- }
- function testBiggerDocument() {
- var el = document.getElementById('test2');
- spellChecker.decorate(el);
- el.appendChild(document.createTextNode(TEST_TEXT3));
- var el2 = el.cloneNode(true);
- spellChecker.check();
- waitForSpellCheckToFinish();
- spellChecker.resume();
- waitForSpellCheckToFinish();
- assertEquals(
- 'Spell checker run should not change the underlying element.',
- el2.innerHTML, el.innerHTML);
- }
- function testElementOverflow() {
- var el = document.getElementById('test3');
- spellChecker.decorate(el);
- el.appendChild(document.createTextNode(TEST_TEXT3));
- var el2 = el.cloneNode(true);
- spellChecker.check();
- waitForSpellCheckToFinish();
- spellChecker.check();
- waitForSpellCheckToFinish();
- spellChecker.resume();
- waitForSpellCheckToFinish();
- assertEquals(
- 'Spell checker run should not change the underlying element.',
- el2.innerHTML, el.innerHTML);
- }
- function testKeyboardNavigateNext() {
- var el = document.getElementById('test4');
- spellChecker.decorate(el);
- var text = 'a unit test for keyboard test';
- el.appendChild(document.createTextNode(text));
- var keyEventProperties =
- goog.object.create('ctrlKey', true, 'shiftKey', false);
- spellChecker.check();
- waitForSpellCheckToFinish();
- // First call just moves focus to first misspelled word.
- goog.testing.events.fireKeySequence(
- el, goog.events.KeyCodes.RIGHT, keyEventProperties);
- // Test moving from first to second misspelled word.
- var defaultExecuted = goog.testing.events.fireKeySequence(
- el, goog.events.KeyCodes.RIGHT, keyEventProperties);
- assertFalse(
- 'The default action should be prevented for the key event',
- defaultExecuted);
- assertCursorAtElement(spellChecker.makeElementId(2));
- spellChecker.resume();
- }
- function testKeyboardNavigateNextOnLastWord() {
- var el = document.getElementById('test5');
- spellChecker.decorate(el);
- var text = 'a unit test for keyboard test';
- el.appendChild(document.createTextNode(text));
- var keyEventProperties =
- goog.object.create('ctrlKey', true, 'shiftKey', false);
- spellChecker.check();
- waitForSpellCheckToFinish();
- // Move to the last invalid word.
- goog.testing.events.fireKeySequence(
- el, goog.events.KeyCodes.RIGHT, keyEventProperties);
- goog.testing.events.fireKeySequence(
- el, goog.events.KeyCodes.RIGHT, keyEventProperties);
- goog.testing.events.fireKeySequence(
- el, goog.events.KeyCodes.RIGHT, keyEventProperties);
- // Test moving to the next invalid word. Should have no effect.
- var defaultExecuted = goog.testing.events.fireKeySequence(
- el, goog.events.KeyCodes.RIGHT, keyEventProperties);
- assertFalse(
- 'The default action should be prevented for the key event',
- defaultExecuted);
- assertCursorAtElement(spellChecker.makeElementId(3));
- spellChecker.resume();
- }
- function testKeyboardNavigateOpenSuggestions() {
- var el = document.getElementById('test6');
- spellChecker.decorate(el);
- var text = 'unit';
- el.appendChild(document.createTextNode(text));
- var keyEventProperties =
- goog.object.create('ctrlKey', true, 'shiftKey', false);
- spellChecker.check();
- waitForSpellCheckToFinish();
- var suggestionMenu = spellChecker.getMenu();
- goog.testing.events.fireKeySequence(
- el, goog.events.KeyCodes.RIGHT, keyEventProperties);
- assertFalse(
- 'The suggestion menu should not be visible yet.',
- suggestionMenu.isVisible());
- keyEventProperties.ctrlKey = false;
- var defaultExecuted = goog.testing.events.fireKeySequence(
- el, goog.events.KeyCodes.DOWN, keyEventProperties);
- assertFalse(
- 'The default action should be prevented for the key event',
- defaultExecuted);
- assertTrue(
- 'The suggestion menu should be visible after the key event.',
- suggestionMenu.isVisible());
- spellChecker.resume();
- }
- function testKeyboardNavigatePrevious() {
- var el = document.getElementById('test7');
- spellChecker.decorate(el);
- var text = 'a unit test for keyboard test';
- el.appendChild(document.createTextNode(text));
- var keyEventProperties =
- goog.object.create('ctrlKey', true, 'shiftKey', false);
- spellChecker.check();
- waitForSpellCheckToFinish();
- // Move to the third element, so we can test the move back to the second.
- goog.testing.events.fireKeySequence(
- el, goog.events.KeyCodes.RIGHT, keyEventProperties);
- goog.testing.events.fireKeySequence(
- el, goog.events.KeyCodes.RIGHT, keyEventProperties);
- goog.testing.events.fireKeySequence(
- el, goog.events.KeyCodes.RIGHT, keyEventProperties);
- var defaultExecuted = goog.testing.events.fireKeySequence(
- el, goog.events.KeyCodes.LEFT, keyEventProperties);
- assertFalse(
- 'The default action should be prevented for the key event',
- defaultExecuted);
- assertCursorAtElement(spellChecker.makeElementId(2));
- spellChecker.resume();
- }
- function testKeyboardNavigatePreviousOnLastWord() {
- var el = document.getElementById('test8');
- spellChecker.decorate(el);
- var text = 'a unit test for keyboard test';
- el.appendChild(document.createTextNode(text));
- var keyEventProperties =
- goog.object.create('ctrlKey', true, 'shiftKey', false);
- spellChecker.check();
- waitForSpellCheckToFinish();
- // Move to the first invalid word.
- goog.testing.events.fireKeySequence(
- el, goog.events.KeyCodes.RIGHT, keyEventProperties);
- // Test moving to the previous invalid word. Should have no effect.
- var defaultExecuted = goog.testing.events.fireKeySequence(
- el, goog.events.KeyCodes.LEFT, keyEventProperties);
- assertFalse(
- 'The default action should be prevented for the key event',
- defaultExecuted);
- assertCursorAtElement(spellChecker.makeElementId(1));
- spellChecker.resume();
- }
- function assertCursorAtElement(expectedId) {
- var range = goog.dom.Range.createFromWindow();
- if (isCaret(range)) {
- if (isMisspelledWordElement(range.getStartNode())) {
- var focusedElementId = range.getStartNode().id;
- }
- // In Chrome a cursor at the start of a misspelled word will appear to be at
- // the end of the text node preceding it.
- if (isCursorAtEndOfStartNode(range) &&
- range.getStartNode().nextSibling != null &&
- isMisspelledWordElement(range.getStartNode().nextSibling)) {
- var focusedElementId = range.getStartNode().nextSibling.id;
- }
- }
- assertEquals(
- 'The cursor is not at the expected misspelled word.', expectedId,
- focusedElementId);
- }
- function isCaret(range) {
- return range.getStartNode() == range.getEndNode();
- }
- function isMisspelledWordElement(element) {
- return goog.dom.classlist.contains(element, 'goog-spellcheck-word');
- }
- function isCursorAtEndOfStartNode(range) {
- return range.getStartNode().length == range.getStartOffset();
- }
|