• 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
8UnserializableSpan = function() {};
9
10StatelessSerializableSpan = function() {};
11
12NonStatelessSerializableSpan = function(value) {
13  this.value = value;
14};
15
16/**
17 * @param {!Object} obj object containing the
18 *     serializable representation.
19 * @return {!Object} The Spannable.
20 */
21NonStatelessSerializableSpan.fromJson = function(obj) {
22  return new NonStatelessSerializableSpan(obj.value / 2);
23};
24
25/**
26 * @return {Object} the json serializable form.
27 */
28NonStatelessSerializableSpan.prototype.toJson = function() {
29  return {value: this.value * 2};
30};
31
32/**
33 * Test fixture.
34 * @constructor
35 * @extends {ChromeVoxUnitTestBase}
36 */
37function CvoxSpannableUnitTest() {}
38
39CvoxSpannableUnitTest.prototype = {
40  __proto__: ChromeVoxUnitTestBase.prototype,
41
42  /** @override */
43  closureModuleDeps: [
44    'cvox.Spannable',
45  ],
46
47  /** @override */
48  setUp: function() {
49    cvox.Spannable.registerStatelessSerializableSpan(
50        StatelessSerializableSpan, 'StatelessSerializableSpan');
51
52    cvox.Spannable.registerSerializableSpan(
53        NonStatelessSerializableSpan, 'NonStatelessSerializableSpan',
54        NonStatelessSerializableSpan.fromJson,
55        NonStatelessSerializableSpan.prototype.toJson);
56  }
57};
58
59TEST_F('CvoxSpannableUnitTest', 'ToStringUnannotated', function() {
60  assertEquals('', new cvox.Spannable().toString());
61  assertEquals('hello world', new cvox.Spannable('hello world').toString());
62});
63
64/** Tests that toString works correctly on annotated strings. */
65TEST_F('CvoxSpannableUnitTest', 'ToStringAnnotated', function() {
66  var spannable = new cvox.Spannable('Hello Google');
67  spannable.setSpan('http://www.google.com/', 6, 12);
68  assertEquals('Hello Google', spannable.toString());
69});
70
71/** Tests the length calculation. */
72TEST_F('CvoxSpannableUnitTest', 'GetLength', function() {
73  var spannable = new cvox.Spannable('Hello');
74  spannable.setSpan({}, 0, 3);
75  assertEquals(5, spannable.getLength());
76  spannable.append(' world');
77  assertEquals(11, spannable.getLength());
78  spannable.append(new cvox.Spannable(' from cvox.Spannable'));
79  assertEquals(31, spannable.getLength());
80});
81
82/** Tests that a span can be added and retrieved at the beginning. */
83TEST_F('CvoxSpannableUnitTest', 'SpanBeginning', function() {
84  var annotation = {};
85  var spannable = new cvox.Spannable('Hello world');
86  spannable.setSpan(annotation, 0, 5);
87  assertSame(annotation, spannable.getSpan(0));
88  assertSame(annotation, spannable.getSpan(3));
89  assertUndefined(spannable.getSpan(5));
90  assertUndefined(spannable.getSpan(8));
91});
92
93/** Tests that a span can be added and retrieved at the beginning. */
94TEST_F('CvoxSpannableUnitTest', 'SpanEnd', function() {
95  var annotation = {};
96  var spannable = new cvox.Spannable('Hello world');
97  spannable.setSpan(annotation, 6, 11);
98  assertUndefined(spannable.getSpan(3));
99  assertUndefined(spannable.getSpan(5));
100  assertSame(annotation, spannable.getSpan(6));
101  assertSame(annotation, spannable.getSpan(10));
102});
103
104/** Tests that a zero-length span is not retrieved. */
105TEST_F('CvoxSpannableUnitTest', 'SpanZeroLength', function() {
106  var annotation = {};
107  var spannable = new cvox.Spannable('Hello world');
108  spannable.setSpan(annotation, 3, 3);
109  assertUndefined(spannable.getSpan(2));
110  assertUndefined(spannable.getSpan(3));
111  assertUndefined(spannable.getSpan(4));
112});
113
114/** Tests that a removed span is not returned. */
115TEST_F('CvoxSpannableUnitTest', 'RemoveSpan', function() {
116  var annotation = {};
117  var spannable = new cvox.Spannable('Hello world');
118  spannable.setSpan(annotation, 0, 3);
119  assertSame(annotation, spannable.getSpan(1));
120  spannable.removeSpan(annotation);
121  assertUndefined(spannable.getSpan(1));
122});
123
124/** Tests that adding a span in one place removes it from another. */
125TEST_F('CvoxSpannableUnitTest', 'SetSpanMoves', function() {
126  var annotation = {};
127  var spannable = new cvox.Spannable('Hello world');
128  spannable.setSpan(annotation, 0, 3);
129  assertSame(annotation, spannable.getSpan(1));
130  assertUndefined(spannable.getSpan(4));
131  spannable.setSpan(annotation, 3, 6);
132  assertUndefined(spannable.getSpan(1));
133  assertSame(annotation, spannable.getSpan(4));
134});
135
136/** Tests that setSpan objects to out-of-range arguments. */
137TEST_F('CvoxSpannableUnitTest', 'SetSpanRangeError', function() {
138  var spannable = new cvox.Spannable('Hello world');
139
140  // Start index out of range.
141  assertException('expected range error', function() {
142    spannable.setSpan({}, -1, 0);
143  }, 'RangeError');
144
145  // End index out of range.
146  assertException('expected range error', function() {
147    spannable.setSpan({}, 0, 12);
148  }, 'RangeError');
149
150  // End before start.
151  assertException('expected range error', function() {
152    spannable.setSpan({}, 1, 0);
153  }, 'RangeError');
154});
155
156/**
157 * Tests that multiple spans can be retrieved at one point.
158 * The first one added which applies should be returned by getSpan.
159 */
160TEST_F('CvoxSpannableUnitTest', 'MultipleSpans', function() {
161  var annotation1 = { number: 1 };
162  var annotation2 = { number: 2 };
163  assertNotSame(annotation1, annotation2);
164  var spannable = new cvox.Spannable('Hello world');
165  spannable.setSpan(annotation1, 1, 4);
166  spannable.setSpan(annotation2, 2, 7);
167  assertSame(annotation1, spannable.getSpan(1));
168  assertThat([annotation1], eqJSON(spannable.getSpans(1)));
169  assertSame(annotation1, spannable.getSpan(3));
170  assertThat([annotation1, annotation2], eqJSON(spannable.getSpans(3)));
171  assertSame(annotation2, spannable.getSpan(6));
172  assertThat([annotation2], eqJSON(spannable.getSpans(6)));
173});
174
175/** Tests that appending appends the strings. */
176TEST_F('CvoxSpannableUnitTest', 'AppendToString', function() {
177  var spannable = new cvox.Spannable('Google');
178  assertEquals('Google', spannable.toString());
179  spannable.append(' Chrome');
180  assertEquals('Google Chrome', spannable.toString());
181  spannable.append(new cvox.Spannable('Vox'));
182  assertEquals('Google ChromeVox', spannable.toString());
183});
184
185/**
186 * Tests that appending Spannables combines annotations.
187 */
188TEST_F('CvoxSpannableUnitTest', 'AppendAnnotations', function() {
189  var annotation1 = { number: 1 };
190  var annotation2 = { number: 2 };
191  assertNotSame(annotation1, annotation2);
192  var left = new cvox.Spannable('hello');
193  left.setSpan(annotation1, 0, 3);
194  var right = new cvox.Spannable(' world');
195  right.setSpan(annotation2, 0, 3);
196  left.append(right);
197  assertSame(annotation1, left.getSpan(1));
198  assertSame(annotation2, left.getSpan(6));
199});
200
201/**
202 * Tests that a span's bounds can be retrieved.
203 */
204TEST_F('CvoxSpannableUnitTest', 'GetSpanStartAndEnd', function() {
205  var annotation = {};
206  var spannable = new cvox.Spannable('potato wedges');
207  spannable.setSpan(annotation, 8, 12);
208  assertEquals(8, spannable.getSpanStart(annotation));
209  assertEquals(12, spannable.getSpanEnd(annotation));
210});
211
212/**
213 * Tests that an absent span's bounds are reported correctly.
214 */
215TEST_F('CvoxSpannableUnitTest', 'GetSpanStartAndEndAbsent', function() {
216  var annotation = {};
217  var spannable = new cvox.Spannable('potato wedges');
218  assertUndefined(spannable.getSpanStart(annotation));
219  assertUndefined(spannable.getSpanEnd(annotation));
220});
221
222/**
223 * Test that a zero length span can still be found.
224 */
225TEST_F('CvoxSpannableUnitTest', 'GetSpanStartAndEndZeroLength', function() {
226  var annotation = {};
227  var spannable = new cvox.Spannable('potato wedges');
228  spannable.setSpan(annotation, 8, 8);
229  assertEquals(8, spannable.getSpanStart(annotation));
230  assertEquals(8, spannable.getSpanEnd(annotation));
231});
232
233/**
234 * Tests that == (but not ===) objects are treated distinctly when getting
235 * span bounds.
236 */
237TEST_F('CvoxSpannableUnitTest', 'GetSpanStartAndEndEquality', function() {
238  // Note that 0 == '' and '' == 0 in JavaScript.
239  var spannable = new cvox.Spannable('wat');
240  spannable.setSpan(0, 0, 0);
241  spannable.setSpan('', 1, 3);
242  assertEquals(0, spannable.getSpanStart(0));
243  assertEquals(0, spannable.getSpanEnd(0));
244  assertEquals(1, spannable.getSpanStart(''));
245  assertEquals(3, spannable.getSpanEnd(''));
246});
247
248/**
249 * Tests that substrings have the correct character sequence.
250 */
251TEST_F('CvoxSpannableUnitTest', 'Substring', function() {
252  var assertSubstringResult = function(expected, initial, start, opt_end) {
253    var spannable = new cvox.Spannable(initial);
254    var substring = spannable.substring(start, opt_end);
255    assertEquals(expected, substring.toString());
256  };
257  assertSubstringResult('Page', 'Google PageRank', 7, 11);
258  assertSubstringResult('Goog', 'Google PageRank', 0, 4);
259  assertSubstringResult('Rank', 'Google PageRank', 11, 15);
260  assertSubstringResult('Rank', 'Google PageRank', 11);
261});
262
263/**
264 * Tests that substring arguments are validated properly.
265 */
266TEST_F('CvoxSpannableUnitTest', 'SubstringRangeError', function() {
267  var assertRangeError = function(initial, start, opt_end) {
268    var spannable = new cvox.Spannable(initial);
269    assertException('expected range error', function() {
270      spannable.substring(start, opt_end);
271    }, 'RangeError');
272  };
273  assertRangeError('Google PageRank', -1, 5);
274  assertRangeError('Google PageRank', 0, 99);
275  assertRangeError('Google PageRank', 5, 2);
276});
277
278/**
279 * Tests that spans in the substring range are preserved.
280 */
281TEST_F('CvoxSpannableUnitTest', 'SubstringSpansIncluded', function() {
282  var assertSpanIncluded = function(expectedSpanStart, expectedSpanEnd,
283      initial, initialSpanStart, initialSpanEnd, start, opt_end) {
284    var annotation = {};
285    var spannable = new cvox.Spannable(initial);
286    spannable.setSpan(annotation, initialSpanStart, initialSpanEnd);
287    var substring = spannable.substring(start, opt_end);
288    assertEquals(expectedSpanStart, substring.getSpanStart(annotation));
289    assertEquals(expectedSpanEnd, substring.getSpanEnd(annotation));
290  };
291  assertSpanIncluded(1, 5, 'potato wedges', 8, 12, 7);
292  assertSpanIncluded(1, 5, 'potato wedges', 8, 12, 7, 13);
293  assertSpanIncluded(1, 5, 'potato wedges', 8, 12, 7, 12);
294  assertSpanIncluded(0, 4, 'potato wedges', 8, 12, 8, 12);
295  assertSpanIncluded(0, 3, 'potato wedges', 0, 3, 0);
296  assertSpanIncluded(0, 3, 'potato wedges', 0, 3, 0, 3);
297  assertSpanIncluded(0, 3, 'potato wedges', 0, 3, 0, 6);
298  assertSpanIncluded(0, 5, 'potato wedges', 8, 13, 8);
299  assertSpanIncluded(0, 5, 'potato wedges', 8, 13, 8, 13);
300  assertSpanIncluded(1, 6, 'potato wedges', 8, 13, 7, 13);
301
302  // Note: we should keep zero-length spans, even at the edges of the range.
303  assertSpanIncluded(0, 0, 'potato wedges', 0, 0, 0, 0);
304  assertSpanIncluded(0, 0, 'potato wedges', 0, 0, 0, 6);
305  assertSpanIncluded(1, 1, 'potato wedges', 8, 8, 7, 13);
306  assertSpanIncluded(6, 6, 'potato wedges', 6, 6, 0, 6);
307});
308
309/**
310 * Tests that spans outside the range are omitted.
311 * It's fine to keep zero-length spans at the ends, though.
312 */
313TEST_F('CvoxSpannableUnitTest', 'SubstringSpansExcluded', function() {
314  var assertSpanExcluded = function(initial, spanStart, spanEnd,
315      start, opt_end) {
316    var annotation = {};
317    var spannable = new cvox.Spannable(initial);
318    spannable.setSpan(annotation, spanStart, spanEnd);
319    var substring = spannable.substring(start, opt_end);
320    assertUndefined(substring.getSpanStart(annotation));
321    assertUndefined(substring.getSpanEnd(annotation));
322  };
323  assertSpanExcluded('potato wedges', 8, 12, 0, 6);
324  assertSpanExcluded('potato wedges', 7, 12, 0, 6);
325  assertSpanExcluded('potato wedges', 0, 6, 8);
326  assertSpanExcluded('potato wedges', 6, 6, 8);
327});
328
329/**
330 * Tests that spans which cross the boundary are clipped.
331 */
332TEST_F('CvoxSpannableUnitTest', 'SubstringSpansClipped', function() {
333  var assertSpanIncluded = function(expectedSpanStart, expectedSpanEnd,
334      initial, initialSpanStart, initialSpanEnd, start, opt_end) {
335    var annotation = {};
336    var spannable = new cvox.Spannable(initial);
337    spannable.setSpan(annotation, initialSpanStart, initialSpanEnd);
338    var substring = spannable.substring(start, opt_end);
339    assertEquals(expectedSpanStart, substring.getSpanStart(annotation));
340    assertEquals(expectedSpanEnd, substring.getSpanEnd(annotation));
341  };
342  assertSpanIncluded(0, 4, 'potato wedges', 7, 13, 8, 12);
343  assertSpanIncluded(0, 0, 'potato wedges', 0, 6, 0, 0);
344  assertSpanIncluded(0, 0, 'potato wedges', 0, 6, 6, 6);
345
346  // The first of the above should produce "edge".
347  assertEquals('edge',
348      new cvox.Spannable('potato wedges').substring(8, 12).toString());
349});
350
351/**
352 * Tests that whitespace is trimmed.
353 */
354TEST_F('CvoxSpannableUnitTest', 'Trim', function() {
355  var assertTrimResult = function(expected, initial) {
356    assertEquals(expected, new cvox.Spannable(initial).trim().toString());
357  };
358  assertTrimResult('John F. Kennedy', 'John F. Kennedy');
359  assertTrimResult('John F. Kennedy', '  John F. Kennedy');
360  assertTrimResult('John F. Kennedy', 'John F. Kennedy     ');
361  assertTrimResult('John F. Kennedy', '   \r\t   \nJohn F. Kennedy\n\n \n');
362  assertTrimResult('', '');
363  assertTrimResult('', '     \t\t    \n\r');
364});
365
366/**
367 * Tests that trim keeps, drops and clips spans.
368 */
369TEST_F('CvoxSpannableUnitTest', 'TrimSpans', function() {
370  var spannable = new cvox.Spannable(' \t Kennedy\n');
371  spannable.setSpan('tab', 1, 2);
372  spannable.setSpan('jfk', 3, 10);
373  spannable.setSpan('jfk-newline', 3, 11);
374  var trimmed = spannable.trim();
375  assertUndefined(trimmed.getSpanStart('tab'));
376  assertUndefined(trimmed.getSpanEnd('tab'));
377  assertEquals(0, trimmed.getSpanStart('jfk'));
378  assertEquals(7, trimmed.getSpanEnd('jfk'));
379  assertEquals(0, trimmed.getSpanStart('jfk-newline'));
380  assertEquals(7, trimmed.getSpanEnd('jfk-newline'));
381});
382
383/**
384 * Tests that when a string is all whitespace, we trim off the *end*.
385 */
386TEST_F('CvoxSpannableUnitTest', 'TrimAllWhitespace', function() {
387  var spannable = new cvox.Spannable('    ');
388  spannable.setSpan('cursor 1', 0, 0);
389  spannable.setSpan('cursor 2', 2, 2);
390  var trimmed = spannable.trim();
391  assertEquals(0, trimmed.getSpanStart('cursor 1'));
392  assertEquals(0, trimmed.getSpanEnd('cursor 1'));
393  assertUndefined(trimmed.getSpanStart('cursor 2'));
394  assertUndefined(trimmed.getSpanEnd('cursor 2'));
395});
396
397/**
398 * Tests finding a span which is an instance of a given class.
399 */
400TEST_F('CvoxSpannableUnitTest', 'GetSpanInstanceOf', function() {
401  function ExampleConstructorBase() {}
402  function ExampleConstructor1() {}
403  function ExampleConstructor2() {}
404  function ExampleConstructor3() {}
405  ExampleConstructor1.prototype = new ExampleConstructorBase();
406  ExampleConstructor2.prototype = new ExampleConstructorBase();
407  ExampleConstructor3.prototype = new ExampleConstructorBase();
408  var ex1 = new ExampleConstructor1();
409  var ex2 = new ExampleConstructor2();
410  var spannable = new cvox.Spannable('Hello world');
411  spannable.setSpan(ex1, 0, 0);
412  spannable.setSpan(ex2, 1, 1);
413  assertEquals(ex1, spannable.getSpanInstanceOf(ExampleConstructor1));
414  assertEquals(ex2, spannable.getSpanInstanceOf(ExampleConstructor2));
415  assertUndefined(spannable.getSpanInstanceOf(ExampleConstructor3));
416  assertEquals(ex1, spannable.getSpanInstanceOf(ExampleConstructorBase));
417});
418
419/** Tests trimming only left or right. */
420TEST_F('CvoxSpannableUnitTest', 'TrimLeftOrRight', function() {
421  var spannable = new cvox.Spannable('    ');
422  spannable.setSpan('cursor 1', 0, 0);
423  spannable.setSpan('cursor 2', 2, 2);
424  var trimmed = spannable.trimLeft();
425  assertEquals(0, trimmed.getSpanStart('cursor 1'));
426  assertEquals(0, trimmed.getSpanEnd('cursor 1'));
427  assertUndefined(trimmed.getSpanStart('cursor 2'));
428  assertUndefined(trimmed.getSpanEnd('cursor 2'));
429
430  var spannable2 = new cvox.Spannable('0  ');
431  spannable2.setSpan('cursor 1', 0, 0);
432  spannable2.setSpan('cursor 2', 2, 2);
433  var trimmed2 = spannable2.trimLeft();
434  assertEquals(0, trimmed2.getSpanStart('cursor 1'));
435  assertEquals(0, trimmed2.getSpanEnd('cursor 1'));
436  assertEquals(2, trimmed2.getSpanStart('cursor 2'));
437  assertEquals(2, trimmed2.getSpanEnd('cursor 2'));
438  trimmed2 = trimmed2.trimRight();
439  assertEquals(0, trimmed2.getSpanStart('cursor 1'));
440  assertEquals(0, trimmed2.getSpanEnd('cursor 1'));
441  assertUndefined(trimmed2.getSpanStart('cursor 2'));
442  assertUndefined(trimmed2.getSpanEnd('cursor 2'));
443
444  var spannable3 = new cvox.Spannable('  0');
445  spannable3.setSpan('cursor 1', 0, 0);
446  spannable3.setSpan('cursor 2', 2, 2);
447  var trimmed3 = spannable3.trimRight();
448  assertEquals(0, trimmed3.getSpanStart('cursor 1'));
449  assertEquals(0, trimmed3.getSpanEnd('cursor 1'));
450  assertEquals(2, trimmed3.getSpanStart('cursor 2'));
451  assertEquals(2, trimmed3.getSpanEnd('cursor 2'));
452  trimmed3 = trimmed3.trimLeft();
453  assertUndefined(trimmed3.getSpanStart('cursor 1'));
454  assertUndefined(trimmed3.getSpanEnd('cursor 1'));
455  assertEquals(0, trimmed3.getSpanStart('cursor 2'));
456  assertEquals(0, trimmed3.getSpanEnd('cursor 2'));
457});
458
459TEST_F('CvoxSpannableUnitTest', 'Serialize', function() {
460  var fresh = new cvox.Spannable('text');
461  var freshStatelessSerializable = new StatelessSerializableSpan();
462  var freshNonStatelessSerializable = new NonStatelessSerializableSpan(14);
463  fresh.setSpan(new UnserializableSpan(), 0, 1);
464  fresh.setSpan(freshStatelessSerializable, 0, 2);
465  fresh.setSpan(freshNonStatelessSerializable, 3, 4);
466  var thawn = cvox.Spannable.fromJson(fresh.toJson());
467  var thawnStatelessSerializable = thawn.getSpanInstanceOf(
468      StatelessSerializableSpan);
469  var thawnNonStatelessSerializable = thawn.getSpanInstanceOf(
470      NonStatelessSerializableSpan);
471  assertThat('text', eqJSON(thawn.toString()));
472  assertUndefined(thawn.getSpanInstanceOf(UnserializableSpan));
473  assertThat(
474      fresh.getSpanStart(freshStatelessSerializable),
475      eqJSON(thawn.getSpanStart(thawnStatelessSerializable)));
476  assertThat(
477      fresh.getSpanEnd(freshStatelessSerializable),
478      eqJSON(thawn.getSpanEnd(thawnStatelessSerializable)));
479  assertThat(freshNonStatelessSerializable,
480             eqJSON(thawnNonStatelessSerializable));
481});
482