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.
5 // Include test fixture.
6 GEN_INCLUDE(['../testing/chromevox_unittest_base.js']);
8 UnserializableSpan = function() {};
10 StatelessSerializableSpan = function() {};
12 NonStatelessSerializableSpan = function(value) {
17 * @param {!Object} obj object containing the
18 * serializable representation.
19 * @return {!Object} The Spannable.
21 NonStatelessSerializableSpan.fromJson = function(obj) {
22 return new NonStatelessSerializableSpan(obj.value / 2);
26 * @return {Object} the json serializable form.
28 NonStatelessSerializableSpan.prototype.toJson = function() {
29 return {value: this.value * 2};
35 * @extends {ChromeVoxUnitTestBase}
37 function CvoxSpannableUnitTest() {}
39 CvoxSpannableUnitTest.prototype = {
40 __proto__: ChromeVoxUnitTestBase.prototype,
49 cvox.Spannable.registerStatelessSerializableSpan(
50 StatelessSerializableSpan, 'StatelessSerializableSpan');
52 cvox.Spannable.registerSerializableSpan(
53 NonStatelessSerializableSpan, 'NonStatelessSerializableSpan',
54 NonStatelessSerializableSpan.fromJson,
55 NonStatelessSerializableSpan.prototype.toJson);
59 TEST_F('CvoxSpannableUnitTest', 'ToStringUnannotated', function() {
60 assertEquals('', new cvox.Spannable().toString());
61 assertEquals('hello world', new cvox.Spannable('hello world').toString());
64 /** Tests that toString works correctly on annotated strings. */
65 TEST_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());
71 /** Tests the length calculation. */
72 TEST_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());
82 /** Tests that a span can be added and retrieved at the beginning. */
83 TEST_F('CvoxSpannableUnitTest', 'SpanBeginning', function() {
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));
93 /** Tests that a span can be added and retrieved at the beginning. */
94 TEST_F('CvoxSpannableUnitTest', 'SpanEnd', function() {
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));
104 /** Tests that a zero-length span is not retrieved. */
105 TEST_F('CvoxSpannableUnitTest', 'SpanZeroLength', function() {
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));
114 /** Tests that a removed span is not returned. */
115 TEST_F('CvoxSpannableUnitTest', 'RemoveSpan', function() {
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));
124 /** Tests that adding a span in one place removes it from another. */
125 TEST_F('CvoxSpannableUnitTest', 'SetSpanMoves', function() {
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));
136 /** Tests that setSpan objects to out-of-range arguments. */
137 TEST_F('CvoxSpannableUnitTest', 'SetSpanRangeError', function() {
138 var spannable = new cvox.Spannable('Hello world');
140 // Start index out of range.
141 assertException('expected range error', function() {
142 spannable.setSpan({}, -1, 0);
145 // End index out of range.
146 assertException('expected range error', function() {
147 spannable.setSpan({}, 0, 12);
151 assertException('expected range error', function() {
152 spannable.setSpan({}, 1, 0);
157 * Tests that multiple spans can be retrieved at one point.
158 * The first one added which applies should be returned by getSpan.
160 TEST_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)));
175 /** Tests that appending appends the strings. */
176 TEST_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());
186 * Tests that appending Spannables combines annotations.
188 TEST_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);
197 assertSame(annotation1, left.getSpan(1));
198 assertSame(annotation2, left.getSpan(6));
202 * Tests that a span's bounds can be retrieved.
204 TEST_F('CvoxSpannableUnitTest', 'GetSpanStartAndEnd', function() {
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));
213 * Tests that an absent span's bounds are reported correctly.
215 TEST_F('CvoxSpannableUnitTest', 'GetSpanStartAndEndAbsent', function() {
217 var spannable = new cvox.Spannable('potato wedges');
218 assertUndefined(spannable.getSpanStart(annotation));
219 assertUndefined(spannable.getSpanEnd(annotation));
223 * Test that a zero length span can still be found.
225 TEST_F('CvoxSpannableUnitTest', 'GetSpanStartAndEndZeroLength', function() {
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));
234 * Tests that == (but not ===) objects are treated distinctly when getting
237 TEST_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(''));
249 * Tests that substrings have the correct character sequence.
251 TEST_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());
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);
264 * Tests that substring arguments are validated properly.
266 TEST_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);
273 assertRangeError('Google PageRank', -1, 5);
274 assertRangeError('Google PageRank', 0, 99);
275 assertRangeError('Google PageRank', 5, 2);
279 * Tests that spans in the substring range are preserved.
281 TEST_F('CvoxSpannableUnitTest', 'SubstringSpansIncluded', function() {
282 var assertSpanIncluded = function(expectedSpanStart, expectedSpanEnd,
283 initial, initialSpanStart, initialSpanEnd, start, opt_end) {
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));
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);
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);
310 * Tests that spans outside the range are omitted.
311 * It's fine to keep zero-length spans at the ends, though.
313 TEST_F('CvoxSpannableUnitTest', 'SubstringSpansExcluded', function() {
314 var assertSpanExcluded = function(initial, spanStart, spanEnd,
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));
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);
330 * Tests that spans which cross the boundary are clipped.
332 TEST_F('CvoxSpannableUnitTest', 'SubstringSpansClipped', function() {
333 var assertSpanIncluded = function(expectedSpanStart, expectedSpanEnd,
334 initial, initialSpanStart, initialSpanEnd, start, opt_end) {
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));
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);
346 // The first of the above should produce "edge".
348 new cvox.Spannable('potato wedges').substring(8, 12).toString());
352 * Tests that whitespace is trimmed.
354 TEST_F('CvoxSpannableUnitTest', 'Trim', function() {
355 var assertTrimResult = function(expected, initial) {
356 assertEquals(expected, new cvox.Spannable(initial).trim().toString());
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');
367 * Tests that trim keeps, drops and clips spans.
369 TEST_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'));
384 * Tests that when a string is all whitespace, we trim off the *end*.
386 TEST_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'));
398 * Tests finding a span which is an instance of a given class.
400 TEST_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));
419 /** Tests trimming only left or right. */
420 TEST_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'));
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'));
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'));
459 TEST_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));
474 fresh.getSpanStart(freshStatelessSerializable),
475 eqJSON(thawn.getSpanStart(thawnStatelessSerializable)));
477 fresh.getSpanEnd(freshStatelessSerializable),
478 eqJSON(thawn.getSpanEnd(thawnStatelessSerializable)));
479 assertThat(freshNonStatelessSerializable,
480 eqJSON(thawnNonStatelessSerializable));