• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5// Include test fixture.
6GEN_INCLUDE(['../testing/chromevox_unittest_base.js']);
7
8/**
9 * A TTS class implementing speak and stop methods intended only for testing.
10 * @constructor
11 * @implements cvox.TtsInterface
12 */
13function TestTts() {
14  this.strings = [];
15}
16
17/**
18 * The strings that were spoken since the last call to get().
19 * @type {Array.<string>}
20 */
21TestTts.prototype.strings;
22
23/**
24 * Returns the list of strings spoken since the last time this method was
25 * called, and then clears the list.
26 * @return {Array.<string>} The list of strings.
27 */
28TestTts.prototype.get = function() {
29  var result = this.strings;
30  this.strings = [];
31  return result;
32};
33
34/** @override */
35TestTts.prototype.speak = function(text, queueMode, properties) {
36  this.strings.push(text);
37};
38
39/** @override */
40TestTts.prototype.isSpeaking = function() {
41  return false;
42};
43
44/** @override */
45TestTts.prototype.stop = function() {
46  // Do nothing.
47};
48
49/** @override */
50TestTts.prototype.increaseOrDecreaseProperty =
51    function(propertyName, increase) {
52  // Do nothing.
53};
54
55/**
56 * Stores the last braille content.
57 * @constructor
58 * @implements cvox.BrailleInterface
59 */
60function TestBraille() {
61  this.content = null;
62}
63
64/** @override */
65TestBraille.prototype.write = function(params) {
66  this.content = params;
67};
68
69/**
70 * Asserts the current braille content.
71 *
72 * @param {string} text Braille text.
73 * @param {number=} opt_start Selection start.
74 * @param {number=} opt_end Selection end.
75 */
76TestBraille.assertContent = function(text, opt_start, opt_end) {
77  var c = cvox.ChromeVox.braille.content;
78  assertTrue(c != null);
79  opt_start = opt_start !== undefined ? opt_start : -1;
80  opt_end = opt_end !== undefined ? opt_end : opt_start;
81  assertEquals(text, c.text.toString());
82  assertEquals(opt_start, c.startIndex);
83  assertEquals(opt_end, c.endIndex);
84};
85
86/**
87 * Test fixture.
88 * @constructor
89 * @extends {ChromeVoxUnitTestBase}
90 */
91function CvoxEditableTextUnitTest() {}
92
93CvoxEditableTextUnitTest.prototype = {
94  __proto__: ChromeVoxUnitTestBase.prototype,
95
96  /** @override */
97  closureModuleDeps: [
98    'cvox.ChromeVoxEditableHTMLInput',
99    'cvox.ChromeVoxEditableTextBase',
100    'cvox.ChromeVoxEventWatcher',
101    'cvox.TextChangeEvent',
102    'cvox.TtsInterface',
103    'cvox.TypingEcho',
104  ],
105
106  /** @override */
107  setUp: function() {
108    // TODO: These tests are all assuming we used the IBeam cursor.
109    // We need to add coverage for block cursor.
110    cvox.ChromeVoxEditableTextBase.useIBeamCursor = true;
111    cvox.ChromeVox.typingEcho = cvox.TypingEcho.CHARACTER_AND_WORD;
112    cvox.ChromeVoxEditableTextBase.eventTypingEcho = false;
113    cvox.ChromeVox.braille = new TestBraille();
114
115    /** Simple mock. */
116    cvox.ChromeVox.msgs = {};
117
118    /**
119     * Simply return the message id.
120     * @param {string} msg Message id.
121     * @return {string} Message id.
122     */
123    cvox.ChromeVox.msgs.getMsg = function(msg) {
124      return msg;
125    };
126  },
127
128  /**
129   * Sets up for a cursor movement test.
130   * @param {string} tagName Desired tag name, "input" or "textarea".
131   * @return {Object} object containing the editable element, and functions
132   *     to prepare, run the test, and tear down.
133   * @private
134   */
135  setUpForCursorTest_: function(tagName) {
136    var element, editable;
137    switch (tagName) {
138      case 'input':
139        element = document.createElement('input');
140        editable = new cvox.ChromeVoxEditableHTMLInput(element, new TestTts());
141        break;
142      case 'textarea':
143        element = document.createElement('textarea');
144        editable = new cvox.ChromeVoxEditableTextArea(element, new TestTts());
145        break;
146      default:
147        throw 'invalid tagName in setUpForCursorTest_';
148    }
149    document.body.appendChild(element);
150    element.focus();
151
152    var expect = function(str) {
153      assertEquals(element.selectionStart, element.selectionEnd);
154      assertEquals(str, element.value.substring(0, element.selectionStart) +
155          '|' + element.value.substring(element.selectionEnd));
156    };
157    return {
158      editable: editable,
159      expect: expect,
160      prepare: function(str) {
161        var position = str.indexOf('|');
162        var value = str.substring(0, position) + str.substring(position + 1);
163        element.value = value;
164        element.selectionStart = element.selectionEnd = position;
165        editable.update(true /* triggeredByUser */);
166        expect(str);
167      },
168      tearDown: function() {
169        document.body.removeChild(element);
170      }
171    };
172  }
173};
174
175TEST_F('CvoxEditableTextUnitTest', 'CursorNavigation', function() {
176  var tts = new TestTts();
177  var obj = new cvox.ChromeVoxEditableTextBase('Hello', 0, 0, false, tts);
178
179  obj.changed(new cvox.TextChangeEvent('Hello', 1, 1));
180  obj.changed(new cvox.TextChangeEvent('Hello', 2, 2));
181  obj.changed(new cvox.TextChangeEvent('Hello', 3, 3));
182  obj.changed(new cvox.TextChangeEvent('Hello', 4, 4));
183  obj.changed(new cvox.TextChangeEvent('Hello', 5, 5));
184  obj.changed(new cvox.TextChangeEvent('Hello', 4, 4));
185  obj.changed(new cvox.TextChangeEvent('Hello', 3, 3));
186  assertEqualStringArrays(['H', 'e', 'l', 'l', 'o',
187                            'o', 'l'], tts.get());
188  obj.changed(new cvox.TextChangeEvent('Hello', 0, 0));
189  obj.changed(new cvox.TextChangeEvent('Hello', 5, 5));
190  assertEqualStringArrays(['Hel', 'Hello'], tts.get());
191});
192
193/** Test typing words. */
194TEST_F('CvoxEditableTextUnitTest', 'TypingWords', function() {
195  var tts = new TestTts();
196  var obj = new cvox.ChromeVoxEditableTextBase('', 0, 0, false, tts);
197  obj.changed(new cvox.TextChangeEvent('H', 1, 1));
198  obj.changed(new cvox.TextChangeEvent('He', 2, 2));
199  obj.changed(new cvox.TextChangeEvent('Hel', 3, 3));
200  obj.changed(new cvox.TextChangeEvent('Hell', 4, 4));
201  obj.changed(new cvox.TextChangeEvent('Hello', 5, 5));
202  obj.changed(new cvox.TextChangeEvent('Hello,', 6, 6));
203  obj.changed(new cvox.TextChangeEvent('Hello, ', 7, 7));
204  obj.changed(new cvox.TextChangeEvent('Hello, W', 8, 8));
205  obj.changed(new cvox.TextChangeEvent('Hello, Wo', 9, 9));
206  obj.changed(new cvox.TextChangeEvent('Hello, Wor', 10, 10));
207  obj.changed(new cvox.TextChangeEvent('Hello, Worl', 11, 11));
208  obj.changed(new cvox.TextChangeEvent('Hello, World', 12, 12));
209  obj.changed(new cvox.TextChangeEvent('Hello, World.', 13, 13));
210  assertEqualStringArrays(['H', 'e', 'l', 'l', 'o', 'Hello,',
211                            ' ',
212                            'W', 'o', 'r', 'l', 'd', 'World.'],
213                           tts.get());
214
215  // Backspace
216  obj.changed(new cvox.TextChangeEvent('Hello, World', 12, 12));
217  obj.changed(new cvox.TextChangeEvent('Hello, Worl', 11, 11));
218  obj.changed(new cvox.TextChangeEvent('Hello, Wor', 10, 10));
219  assertEqualStringArrays(['.', 'd', 'l'], tts.get());
220
221  // Forward-delete
222  obj.changed(new cvox.TextChangeEvent('Hello, Wor', 9, 9));
223  obj.changed(new cvox.TextChangeEvent('Hello, Wor', 8, 8));
224  obj.changed(new cvox.TextChangeEvent('Hello, Wor', 7, 7));
225  obj.changed(new cvox.TextChangeEvent('Hello, or', 7, 7));
226  obj.changed(new cvox.TextChangeEvent('Hello, r', 7, 7));
227  obj.changed(new cvox.TextChangeEvent('Hello, ', 7, 7));
228  assertEqualStringArrays(['r', 'o', 'W', 'W', 'o', 'r'], tts.get());
229
230  // Clear all
231  obj.changed(new cvox.TextChangeEvent('', 0, 0));
232  assertEqualStringArrays(['Hello, , deleted'], tts.get());
233
234  // Paste / insert a whole word
235  obj.changed(new cvox.TextChangeEvent('Hello', 5, 5));
236  assertEqualStringArrays(['Hello'], tts.get());
237  obj.changed(new cvox.TextChangeEvent('Hello, World', 12, 12));
238  assertEqualStringArrays([', World'], tts.get());
239});
240
241/** Test selection. */
242TEST_F('CvoxEditableTextUnitTest', 'Selection', function() {
243  var tts = new TestTts();
244  var obj =
245      new cvox.ChromeVoxEditableTextBase('Hello, world.', 0, 0, false, tts);
246  obj.changed(new cvox.TextChangeEvent('Hello, world.', 0, 1));
247  obj.changed(new cvox.TextChangeEvent('Hello, world.', 0, 2));
248  obj.changed(new cvox.TextChangeEvent('Hello, world.', 0, 3));
249  obj.changed(new cvox.TextChangeEvent('Hello, world.', 0, 4));
250  obj.changed(new cvox.TextChangeEvent('Hello, world.', 0, 5));
251  obj.changed(new cvox.TextChangeEvent('Hello, world.', 0, 6));
252  assertEqualStringArrays(['H', 'selected',
253                            'e', 'added_to_selection',
254                            'l', 'added_to_selection',
255                            'l', 'added_to_selection',
256                            'o', 'added_to_selection',
257                            ',', 'added_to_selection'],
258                           tts.get());
259  obj.changed(new cvox.TextChangeEvent('Hello, world.', 0, 12));
260  assertEqualStringArrays([' world', 'added_to_selection'],
261                           tts.get());
262  obj.changed(new cvox.TextChangeEvent('Hello, world.', 1, 12));
263  assertEqualStringArrays(['H', 'removed_from_selection'],
264                           tts.get());
265  obj.changed(new cvox.TextChangeEvent('Hello, world.', 2, 5));
266  assertEqualStringArrays(['llo', 'selected'],
267                           tts.get());
268  obj.changed(new cvox.TextChangeEvent('Hello, world.', 2, 2));
269  assertEqualStringArrays(['Unselected'],
270                           tts.get());
271});
272
273
274/** Test multi-line text. */
275TEST_F('CvoxEditableTextUnitTest', 'MultiLineText', function() {
276  var str = 'This string\nspans\nfive lines.\n  \n';
277  var tts = new TestTts();
278  var obj = new cvox.ChromeVoxEditableTextBase(str, 0, 0, false, tts);
279  obj.multiline = true;
280  obj.getLineIndex = function(index) {
281    if (index >= 33) {
282      return 4;
283    } else if (index >= 30) {
284      return 3;
285    } else if (index >= 18) {
286      return 2;
287    } else if (index >= 12) {
288      return 1;
289    } else {
290      return 0;
291    }
292  };
293  obj.getLineStart = function(index) {
294    return [0, 12, 18, 30, 33][index];
295  };
296  obj.getLineEnd = function(index) {
297    return [11, 17, 29, 32, 33][index];
298  };
299  assertEquals('This string', obj.getLine(0));
300  obj.changed(new cvox.TextChangeEvent(str, 12, 12));
301  assertEqualStringArrays(['spans'], tts.get());
302  TestBraille.assertContent('spans', 0);
303  obj.changed(new cvox.TextChangeEvent(str, 18, 18));
304  assertEqualStringArrays(['five lines.'], tts.get());
305  TestBraille.assertContent('five lines.', 0);
306  obj.changed(new cvox.TextChangeEvent(str, 30, 30));
307  assertEqualStringArrays(['text_box_whitespace'], tts.get());
308  TestBraille.assertContent('  ', 0);
309  obj.changed(new cvox.TextChangeEvent(str, 33, 33));
310  assertEqualStringArrays(['text_box_blank'], tts.get());
311  TestBraille.assertContent('', 0);
312  obj.changed(new cvox.TextChangeEvent(str, 0, 1));
313  assertEqualStringArrays(['T', 'selected'], tts.get());
314  TestBraille.assertContent('This string', 0, 1);
315  obj.changed(new cvox.TextChangeEvent(str, 0, 12));
316  assertEqualStringArrays(['his string\n', 'added_to_selection'],
317      tts.get());
318  // Newline stripped, thus 11, not 12.
319  TestBraille.assertContent('This string', 0, 11);
320  obj.changed(new cvox.TextChangeEvent(str, 0, str.length));
321  assertEqualStringArrays([str.substr(12), 'added_to_selection'],
322      tts.get());
323  TestBraille.assertContent('This string', 0, 11);
324  obj.changed(new cvox.TextChangeEvent(str, 12, 19));
325  assertEqualStringArrays(['spans\nf', 'selected'], tts.get());
326  TestBraille.assertContent('spans', 0, 5);
327});
328
329
330/**
331 * Test autocomplete; suppose a user is typing "google.com/firefox" into an
332 * address bar, and it's being autocompleted. Sometimes it's autocompleted
333 * as they type, sometimes there's a short delay.
334 */
335TEST_F('CvoxEditableTextUnitTest', 'Autocomplete', function() {
336  var tts = new TestTts();
337  var obj = new cvox.ChromeVoxEditableTextBase('', 0, 0, false, tts);
338
339  // User types 'g'
340  obj.changed(new cvox.TextChangeEvent('g', 1, 1));
341  assertEqualStringArrays(['g'], tts.get());
342
343  // The rest of 'google.com' is autocompleted and automatically selected.
344  obj.changed(new cvox.TextChangeEvent('google.com', 1, 10));
345  assertEqualStringArrays(['oogle.com, oogle.com'], tts.get());
346
347  // The user doesn't realize it and types a few more characters of 'google.com'
348  // and this changes the selection (unselecting) as the user types them.
349  obj.changed(new cvox.TextChangeEvent('google.com', 2, 10));
350  assertEqualStringArrays(['o', 'ogle.com'], tts.get());
351  obj.changed(new cvox.TextChangeEvent('google.com', 3, 10));
352  assertEqualStringArrays(['o', 'gle.com'], tts.get());
353  obj.changed(new cvox.TextChangeEvent('google.com', 4, 10));
354  assertEqualStringArrays(['g', 'le.com'], tts.get());
355
356  // The user presses right-arrow, which fully unselects the remaining text.
357  obj.changed(new cvox.TextChangeEvent('google.com', 10, 10));
358  assertEqualStringArrays(['Unselected'], tts.get());
359
360  // The user types '/'
361  obj.changed(new cvox.TextChangeEvent('google.com/', 11, 11));
362  assertEqualStringArrays(['com/'], tts.get());
363
364  // The user types 'f', and 'finance' is autocompleted
365  obj.changed(new cvox.TextChangeEvent('google.com/finance', 12, 18));
366  assertEqualStringArrays(['finance, inance'], tts.get());
367
368  // The user types 'i'
369  obj.changed(new cvox.TextChangeEvent('google.com/finance', 13, 18));
370  assertEqualStringArrays(['i', 'nance'], tts.get());
371
372  // The user types 'r', now 'firefox' is autocompleted
373  obj.changed(new cvox.TextChangeEvent('google.com/firefox', 14, 18));
374  assertEqualStringArrays(['refox, efox'], tts.get());
375
376  // The user presses right-arrow to accept the completion.
377  obj.changed(new cvox.TextChangeEvent('google.com/firefox', 18, 18));
378  assertEqualStringArrays(['Unselected'], tts.get());
379});
380
381
382/**
383 * Test a few common scenarios where text is replaced.
384 */
385TEST_F('CvoxEditableTextUnitTest', 'ReplacingText', function() {
386  // Initial value is Alabama.
387  var tts = new TestTts();
388  var obj = new cvox.ChromeVoxEditableTextBase('Alabama', 0, 0, false, tts);
389
390  // Entire text replaced with Alaska.
391  obj.changed(new cvox.TextChangeEvent('Alaska', 0, 0));
392  assertEqualStringArrays(['Alaska'], tts.get());
393
394  // Entire text selected.
395  obj.changed(new cvox.TextChangeEvent('Alaska', 0, 6));
396  assertEqualStringArrays(['Alaska', 'selected'], tts.get());
397
398  // Entire text replaced with Arizona.
399  obj.changed(new cvox.TextChangeEvent('Arizona', 7, 7));
400  assertEqualStringArrays(['Arizona'], tts.get());
401
402  // Entire text selected.
403  obj.changed(new cvox.TextChangeEvent('Arizona', 0, 7));
404  assertEqualStringArrays(['Arizona', 'selected'], tts.get());
405
406  // Click between 'r' and 'i'.
407  obj.changed(new cvox.TextChangeEvent('Arizona', 2, 2));
408  assertEqualStringArrays(['Unselected'], tts.get());
409
410  // Next character removed from selection.
411  obj.changed(new cvox.TextChangeEvent('Arizona', 2, 7));
412  assertEqualStringArrays(['izona', 'selected'], tts.get());
413
414  // Selection replaced with "kansas" to make Arkansas.  This time it
415  // says "kansas" because the deleted text was selected.
416  obj.changed(new cvox.TextChangeEvent('Arkansas', 8, 8));
417  assertEqualStringArrays(['kansas'], tts.get());
418});
419
420
421/**
422 * Test feedback when text changes in a long sentence.
423 */
424TEST_F('CvoxEditableTextUnitTest', 'ReplacingLongText', function() {
425  var tts = new TestTts();
426  var obj = new cvox.ChromeVoxEditableTextBase(
427    'I love deadlines. I like the whooshing sound they make as they fly by.',
428    0, 0, false, tts);
429
430  // Change the whole sentence without moving the cursor. It should speak
431  // only the part that changed, but it should speak whole words.
432  obj.changed(new cvox.TextChangeEvent(
433    'I love deadlines. I love the whooshing sounds they make as they fly by.',
434    0, 0));
435  assertEqualStringArrays(['love the whooshing sounds'], tts.get());
436});
437
438/** Tests character echo. */
439TEST_F('CvoxEditableTextUnitTest', 'CharacterEcho', function() {
440  cvox.ChromeVox.typingEcho = cvox.TypingEcho.CHARACTER;
441  var tts = new TestTts();
442  var obj = new cvox.ChromeVoxEditableTextBase('', 0, 0, false, tts);
443  obj.changed(new cvox.TextChangeEvent('H', 1, 1));
444  obj.changed(new cvox.TextChangeEvent('He', 2, 2));
445  obj.changed(new cvox.TextChangeEvent('Hel', 3, 3));
446  obj.changed(new cvox.TextChangeEvent('Hell', 4, 4));
447  obj.changed(new cvox.TextChangeEvent('Hello', 5, 5));
448  obj.changed(new cvox.TextChangeEvent('Hello,', 6, 6));
449  obj.changed(new cvox.TextChangeEvent('Hello, ', 7, 7));
450  obj.changed(new cvox.TextChangeEvent('Hello, W', 8, 8));
451  obj.changed(new cvox.TextChangeEvent('Hello, Wo', 9, 9));
452  obj.changed(new cvox.TextChangeEvent('Hello, Wor', 10, 10));
453  obj.changed(new cvox.TextChangeEvent('Hello, Worl', 11, 11));
454  obj.changed(new cvox.TextChangeEvent('Hello, World', 12, 12));
455  obj.changed(new cvox.TextChangeEvent('Hello, World.', 13, 13));
456  assertEqualStringArrays(
457      ['H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '.'],
458       tts.get());
459});
460
461
462/** Tests word echo. */
463TEST_F('CvoxEditableTextUnitTest', 'WordEcho', function() {
464  cvox.ChromeVox.typingEcho = cvox.TypingEcho.WORD;
465  var tts = new TestTts();
466  var obj = new cvox.ChromeVoxEditableTextBase('', 0, 0, false, tts);
467  obj.changed(new cvox.TextChangeEvent('H', 1, 1));
468  obj.changed(new cvox.TextChangeEvent('He', 2, 2));
469  obj.changed(new cvox.TextChangeEvent('Hel', 3, 3));
470  obj.changed(new cvox.TextChangeEvent('Hell', 4, 4));
471  obj.changed(new cvox.TextChangeEvent('Hello', 5, 5));
472  obj.changed(new cvox.TextChangeEvent('Hello,', 6, 6));
473  obj.changed(new cvox.TextChangeEvent('Hello, ', 7, 7));
474  obj.changed(new cvox.TextChangeEvent('Hello, W', 8, 8));
475  obj.changed(new cvox.TextChangeEvent('Hello, Wo', 9, 9));
476  obj.changed(new cvox.TextChangeEvent('Hello, Wor', 10, 10));
477  obj.changed(new cvox.TextChangeEvent('Hello, Worl', 11, 11));
478  obj.changed(new cvox.TextChangeEvent('Hello, World', 12, 12));
479  obj.changed(new cvox.TextChangeEvent('Hello, World.', 13, 13));
480  assertEqualStringArrays(
481      ['Hello,', 'World.'],
482       tts.get());
483});
484
485
486/** Tests no echo. */
487TEST_F('CvoxEditableTextUnitTest', 'NoEcho', function() {
488  cvox.ChromeVox.typingEcho = cvox.TypingEcho.NONE;
489  var tts = new TestTts();
490  var obj = new cvox.ChromeVoxEditableTextBase('', 0, 0, false, tts);
491  obj.changed(new cvox.TextChangeEvent('H', 1, 1));
492  obj.changed(new cvox.TextChangeEvent('He', 2, 2));
493  obj.changed(new cvox.TextChangeEvent('Hel', 3, 3));
494  obj.changed(new cvox.TextChangeEvent('Hell', 4, 4));
495  obj.changed(new cvox.TextChangeEvent('Hello', 5, 5));
496  obj.changed(new cvox.TextChangeEvent('Hello,', 6, 6));
497  obj.changed(new cvox.TextChangeEvent('Hello, ', 7, 7));
498  obj.changed(new cvox.TextChangeEvent('Hello, W', 8, 8));
499  obj.changed(new cvox.TextChangeEvent('Hello, Wo', 9, 9));
500  obj.changed(new cvox.TextChangeEvent('Hello, Wor', 10, 10));
501  obj.changed(new cvox.TextChangeEvent('Hello, Worl', 11, 11));
502  obj.changed(new cvox.TextChangeEvent('Hello, World', 12, 12));
503  obj.changed(new cvox.TextChangeEvent('Hello, World.', 13, 13));
504  assertEqualStringArrays(
505      [],
506       tts.get());
507});
508
509/** Tests cursor movement in an input field by character. */
510TEST_F('CvoxEditableTextUnitTest', 'CursorMovementByCharacter', function() {
511  var test = this.setUpForCursorTest_('input');
512  var editable = test.editable, prepare = test.prepare, expect = test.expect;
513  try {
514    // Moving near the beginning of the text.
515    prepare('|"Hello," says Sally.');
516    editable.moveCursorToPreviousCharacter();
517    expect('|"Hello," says Sally.');
518    editable.moveCursorToNextCharacter();
519    expect('"|Hello," says Sally.');
520    editable.moveCursorToNextCharacter();
521    expect('"H|ello," says Sally.');
522
523    // Moving near the end of the text.
524    prepare('"Hello," says Sally|.');
525    editable.moveCursorToPreviousCharacter();
526    expect('"Hello," says Sall|y.');
527    editable.moveCursorToNextCharacter();
528    expect('"Hello," says Sally|.');
529    editable.moveCursorToNextCharacter();
530    expect('"Hello," says Sally.|');
531    editable.moveCursorToNextCharacter();
532    expect('"Hello," says Sally.|');
533  } finally {
534    test.tearDown();
535  }
536});
537
538/** Tests cursor movement in an input field by word. */
539TEST_F('CvoxEditableTextUnitTest', 'CursorMovementByWord', function() {
540  var test = this.setUpForCursorTest_('input');
541  var editable = test.editable, prepare = test.prepare, expect = test.expect;
542  try {
543    // Moving forward.
544    prepare('"He|llo," says Sally.');
545    editable.moveCursorToNextWord();
546    expect('"Hello|," says Sally.');
547    editable.moveCursorToNextWord();
548    expect('"Hello," says| Sally.');
549    editable.moveCursorToNextWord();
550    expect('"Hello," says Sally|.');
551    editable.moveCursorToNextWord();
552    expect('"Hello," says Sally.|');
553    editable.moveCursorToNextWord();
554    expect('"Hello," says Sally.|');
555
556    // Moving backward.
557    prepare('"Hello," says S|ally.');
558    editable.moveCursorToPreviousWord();
559    expect('"Hello," says |Sally.');
560    editable.moveCursorToPreviousWord();
561    expect('"Hello," |says Sally.');
562    editable.moveCursorToPreviousWord();
563    expect('"|Hello," says Sally.');
564    editable.moveCursorToPreviousWord();
565    expect('|"Hello," says Sally.');
566    editable.moveCursorToPreviousWord();
567    expect('|"Hello," says Sally.');
568  } finally {
569    test.tearDown();
570  }
571});
572
573/** Tests that character and word movement still work in <textarea>. */
574TEST_F('CvoxEditableTextUnitTest', 'CursorMovementTextArea', function() {
575  var test = this.setUpForCursorTest_('textarea');
576  var editable = test.editable, prepare = test.prepare, expect = test.expect;
577  try {
578    prepare('|Hello, Larry.\nHello, Sergey.');
579    editable.moveCursorToNextCharacter();
580    expect('H|ello, Larry.\nHello, Sergey.');
581    editable.moveCursorToNextWord();
582    expect('Hello|, Larry.\nHello, Sergey.');
583    editable.moveCursorToNextWord();
584    expect('Hello, Larry|.\nHello, Sergey.');
585    editable.moveCursorToNextWord();
586    expect('Hello, Larry.\nHello|, Sergey.');
587    editable.moveCursorToNextCharacter();
588    expect('Hello, Larry.\nHello,| Sergey.');
589    editable.moveCursorToPreviousWord();
590    expect('Hello, Larry.\n|Hello, Sergey.');
591    editable.moveCursorToPreviousCharacter();
592    expect('Hello, Larry.|\nHello, Sergey.');
593  } finally {
594    test.tearDown();
595  }
596});
597
598/** Tests that line navigation works. */
599TEST_F('CvoxEditableTextUnitTest', 'CursorMovementByLine', function() {
600  var test = this.setUpForCursorTest_('textarea');
601  var editable = test.editable, prepare = test.prepare, expect = test.expect;
602  try {
603    prepare('123\n1234\n1234|5\n\nHi');
604    editable.moveCursorToPreviousLine();
605    expect('123\n1234|\n12345\n\nHi');
606    editable.moveCursorToPreviousLine();
607    expect('123|\n1234\n12345\n\nHi');
608    editable.moveCursorToNextLine();
609    expect('123\n123|4\n12345\n\nHi');
610    editable.moveCursorToNextLine();
611    expect('123\n1234\n123|45\n\nHi');
612    editable.moveCursorToNextLine();
613    expect('123\n1234\n12345\n|\nHi');
614    editable.moveCursorToNextLine();
615    expect('123\n1234\n12345\n\n|Hi');
616    editable.moveCursorToNextLine();
617    expect('123\n1234\n12345\n\nHi|');
618
619    prepare('foo|bar');
620    editable.moveCursorToPreviousLine();
621    expect('|foobar');
622    editable.moveCursorToPreviousLine();
623    expect('|foobar');
624    editable.moveCursorToNextLine();
625    expect('foobar|');
626    editable.moveCursorToNextLine();
627    expect('foobar|');
628  } finally {
629    test.tearDown();
630  }
631});
632
633/** Tests that paragraph navigation works. */
634TEST_F('CvoxEditableTextUnitTest', 'CursorMovementByParagraph', function() {
635  var test = this.setUpForCursorTest_('textarea');
636  var editable = test.editable, prepare = test.prepare, expect = test.expect;
637  try {
638    prepare('Para|graph 1\nParagraph 2\nParagraph 3');
639    editable.moveCursorToNextParagraph();
640    expect('Paragraph 1\n|Paragraph 2\nParagraph 3');
641    editable.moveCursorToNextParagraph();
642    expect('Paragraph 1\nParagraph 2\n|Paragraph 3');
643    editable.moveCursorToNextParagraph();
644    expect('Paragraph 1\nParagraph 2\nParagraph 3|');
645    editable.moveCursorToPreviousParagraph();
646    expect('Paragraph 1\nParagraph 2\n|Paragraph 3');
647    editable.moveCursorToPreviousParagraph();
648    expect('Paragraph 1\n|Paragraph 2\nParagraph 3');
649    editable.moveCursorToPreviousParagraph();
650    expect('|Paragraph 1\nParagraph 2\nParagraph 3');
651  } finally {
652    test.tearDown();
653  }
654});
655
656/** Tests normalization of TextChangeEvent's */
657TEST_F('CvoxEditableTextUnitTest', 'TextChangeEvent', function() {
658  var event1 = new cvox.TextChangeEvent('foo', 0, 1, true);
659  var event2 = new cvox.TextChangeEvent('foo', 1, 0, true);
660  var event3 = new cvox.TextChangeEvent('foo', 1, 1, true);
661
662  assertEquals(0, event1.start);
663  assertEquals(1, event1.end);
664
665  assertEquals(0, event2.start);
666  assertEquals(1, event2.end);
667
668  assertEquals(1, event3.start);
669  assertEquals(1, event3.end);
670});
671