blob: ac4895d86fa22f66486307a3b822fc76a85113bb [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Include test fixture.
GEN_INCLUDE(['../testing/chromevox_unittest_base.js']);
UnserializableSpan = function() {};
StatelessSerializableSpan = function() {};
NonStatelessSerializableSpan = function(value) {
this.value = value;
};
/**
* @param {!Object} obj object containing the
* serializable representation.
* @return {!Object} The Spannable.
*/
NonStatelessSerializableSpan.fromJson = function(obj) {
return new NonStatelessSerializableSpan(obj.value / 2);
};
/**
* @return {Object} the json serializable form.
*/
NonStatelessSerializableSpan.prototype.toJson = function() {
return {value: this.value * 2};
};
/**
* @param {Spannable} spannable
* @param {*} annotation
*/
function assertSpanNotFound(spannable, annotation) {
assertFalse(spannable.hasSpan(annotation));
assertException(
'Span ' + annotation + ' shouldn\'t be in spannable ' + spannable,
function() {
spannable.getSpanStart(annotation);
},
'Error');
assertException(
'Span ' + annotation + ' shouldn\'t be in spannable ' + spannable,
function() {
spannable.getSpanEnd(annotation);
},
'Error');
assertException(
'Span ' + annotation + ' shouldn\'t be in spannable ' + spannable,
function() {
spannable.getSpanLength(annotation);
},
'Error');
}
/**
* Test fixture.
* @constructor
* @extends {ChromeVoxUnitTestBase}
*/
function ChromeVoxSpannableUnitTest() {}
ChromeVoxSpannableUnitTest.prototype = {
__proto__: ChromeVoxUnitTestBase.prototype,
/** @override */
closureModuleDeps: [
'Spannable',
],
/** @override */
setUp: function() {
Spannable.registerStatelessSerializableSpan(
StatelessSerializableSpan, 'StatelessSerializableSpan');
Spannable.registerSerializableSpan(
NonStatelessSerializableSpan, 'NonStatelessSerializableSpan',
NonStatelessSerializableSpan.fromJson,
NonStatelessSerializableSpan.prototype.toJson);
}
};
TEST_F('ChromeVoxSpannableUnitTest', 'ToStringUnannotated', function() {
assertEquals('', new Spannable().toString());
assertEquals('hello world', new Spannable('hello world').toString());
});
/** Tests that toString works correctly on annotated strings. */
TEST_F('ChromeVoxSpannableUnitTest', 'ToStringAnnotated', function() {
var spannable = new Spannable('Hello Google');
spannable.setSpan('http://www.google.com/', 6, 12);
assertEquals('Hello Google', spannable.toString());
});
/** Tests the length calculation. */
TEST_F('ChromeVoxSpannableUnitTest', 'LengthProperty', function() {
var spannable = new Spannable('Hello');
spannable.setSpan({}, 0, 3);
assertEquals(5, spannable.length);
spannable.append(' world');
assertEquals(11, spannable.length);
spannable.append(new Spannable(' from Spannable'));
assertEquals(26, spannable.length);
});
/** Tests that a span can be added and retrieved at the beginning. */
TEST_F('ChromeVoxSpannableUnitTest', 'SpanBeginning', function() {
var annotation = {};
var spannable = new Spannable('Hello world');
spannable.setSpan(annotation, 0, 5);
assertTrue(spannable.hasSpan(annotation));
assertSame(annotation, spannable.getSpan(0));
assertSame(annotation, spannable.getSpan(3));
assertUndefined(spannable.getSpan(5));
assertUndefined(spannable.getSpan(8));
});
/** Tests that a span can be added and retrieved at the beginning. */
TEST_F('ChromeVoxSpannableUnitTest', 'SpanEnd', function() {
var annotation = {};
var spannable = new Spannable('Hello world');
spannable.setSpan(annotation, 6, 11);
assertTrue(spannable.hasSpan(annotation));
assertUndefined(spannable.getSpan(3));
assertUndefined(spannable.getSpan(5));
assertSame(annotation, spannable.getSpan(6));
assertSame(annotation, spannable.getSpan(10));
});
/** Tests that a zero-length span is not retrieved. */
TEST_F('ChromeVoxSpannableUnitTest', 'SpanZeroLength', function() {
var annotation = {};
var spannable = new Spannable('Hello world');
spannable.setSpan(annotation, 3, 3);
assertTrue(spannable.hasSpan(annotation));
assertUndefined(spannable.getSpan(2));
assertUndefined(spannable.getSpan(3));
assertUndefined(spannable.getSpan(4));
});
/** Tests that a removed span is not returned. */
TEST_F('ChromeVoxSpannableUnitTest', 'RemoveSpan', function() {
var annotation = {};
var spannable = new Spannable('Hello world');
spannable.setSpan(annotation, 0, 3);
assertSame(annotation, spannable.getSpan(1));
spannable.removeSpan(annotation);
assertFalse(spannable.hasSpan(annotation));
assertUndefined(spannable.getSpan(1));
});
/** Tests that adding a span in one place removes it from another. */
TEST_F('ChromeVoxSpannableUnitTest', 'SetSpanMoves', function() {
var annotation = {};
var spannable = new Spannable('Hello world');
spannable.setSpan(annotation, 0, 3);
assertSame(annotation, spannable.getSpan(1));
assertUndefined(spannable.getSpan(4));
spannable.setSpan(annotation, 3, 6);
assertUndefined(spannable.getSpan(1));
assertSame(annotation, spannable.getSpan(4));
});
/** Tests that setSpan objects to out-of-range arguments. */
TEST_F('ChromeVoxSpannableUnitTest', 'SetSpanRangeError', function() {
var spannable = new Spannable('Hello world');
// Start index out of range.
assertException('expected range error', function() {
spannable.setSpan({}, -1, 0);
}, 'RangeError');
// End index out of range.
assertException('expected range error', function() {
spannable.setSpan({}, 0, 12);
}, 'RangeError');
// End before start.
assertException('expected range error', function() {
spannable.setSpan({}, 1, 0);
}, 'RangeError');
});
/**
* Tests that multiple spans can be retrieved at one point.
* The first one added which applies should be returned by getSpan.
*/
TEST_F('ChromeVoxSpannableUnitTest', 'MultipleSpans', function() {
var annotation1 = { number: 1 };
var annotation2 = { number: 2 };
assertNotSame(annotation1, annotation2);
var spannable = new Spannable('Hello world');
spannable.setSpan(annotation1, 1, 4);
spannable.setSpan(annotation2, 2, 7);
assertSame(annotation1, spannable.getSpan(1));
assertThat([annotation1], eqJSON(spannable.getSpans(1)));
assertSame(annotation1, spannable.getSpan(3));
assertThat([annotation1, annotation2], eqJSON(spannable.getSpans(3)));
assertSame(annotation2, spannable.getSpan(6));
assertThat([annotation2], eqJSON(spannable.getSpans(6)));
});
/** Tests that appending appends the strings. */
TEST_F('ChromeVoxSpannableUnitTest', 'AppendToString', function() {
var spannable = new Spannable('Google');
assertEquals('Google', spannable.toString());
spannable.append(' Chrome');
assertEquals('Google Chrome', spannable.toString());
spannable.append(new Spannable('Vox'));
assertEquals('Google ChromeVox', spannable.toString());
});
/**
* Tests that appending Spannables combines annotations.
*/
TEST_F('ChromeVoxSpannableUnitTest', 'AppendAnnotations', function() {
var annotation1 = { number: 1 };
var annotation2 = { number: 2 };
assertNotSame(annotation1, annotation2);
var left = new Spannable('hello');
left.setSpan(annotation1, 0, 3);
var right = new Spannable(' world');
right.setSpan(annotation2, 0, 3);
left.append(right);
assertSame(annotation1, left.getSpan(1));
assertSame(annotation2, left.getSpan(6));
});
/**
* Tests that a span's bounds can be retrieved.
*/
TEST_F('ChromeVoxSpannableUnitTest', 'GetSpanStartAndEndAndLength', function() {
var annotation = {};
var spannable = new Spannable('potato wedges');
spannable.setSpan(annotation, 8, 12);
assertEquals(8, spannable.getSpanStart(annotation));
assertEquals(12, spannable.getSpanEnd(annotation));
assertEquals(4, spannable.getSpanLength(annotation));
});
/**
* Tests that an absent span's bounds are reported correctly.
*/
TEST_F('ChromeVoxSpannableUnitTest', 'GetSpanStartAndEndAndLengthAbsent',
function() {
var annotation = {};
var spannable = new Spannable('potato wedges');
assertSpanNotFound(spannable, annotation);
});
/**
* Test that a zero length span can still be found.
*/
TEST_F('ChromeVoxSpannableUnitTest', 'GetSpanStartAndEndAndLengthZeroLength',
function() {
var annotation = {};
var spannable = new Spannable('potato wedges');
spannable.setSpan(annotation, 8, 8);
assertEquals(8, spannable.getSpanStart(annotation));
assertEquals(8, spannable.getSpanEnd(annotation));
assertEquals(0, spannable.getSpanLength(annotation));
});
/**
* Tests that == (but not ===) objects are treated distinctly when getting
* span bounds.
*/
TEST_F('ChromeVoxSpannableUnitTest', 'GetSpanStartAndEndEquality', function() {
// Note that 0 == '' and '' == 0 in JavaScript.
var spannable = new Spannable('wat');
spannable.setSpan(0, 0, 0);
spannable.setSpan('', 1, 3);
assertEquals(0, spannable.getSpanStart(0));
assertEquals(0, spannable.getSpanEnd(0));
assertEquals(1, spannable.getSpanStart(''));
assertEquals(3, spannable.getSpanEnd(''));
});
/**
* Tests that substrings have the correct character sequence.
*/
TEST_F('ChromeVoxSpannableUnitTest', 'Substring', function() {
var assertSubstringResult = function(expected, initial, start, opt_end) {
var spannable = new Spannable(initial);
var substring = spannable.substring(start, opt_end);
assertEquals(expected, substring.toString());
};
assertSubstringResult('Page', 'Google PageRank', 7, 11);
assertSubstringResult('Goog', 'Google PageRank', 0, 4);
assertSubstringResult('Rank', 'Google PageRank', 11, 15);
assertSubstringResult('Rank', 'Google PageRank', 11);
});
/**
* Tests that substring arguments are validated properly.
*/
TEST_F('ChromeVoxSpannableUnitTest', 'SubstringRangeError', function() {
var assertRangeError = function(initial, start, opt_end) {
var spannable = new Spannable(initial);
assertException('expected range error', function() {
spannable.substring(start, opt_end);
}, 'RangeError');
};
assertRangeError('Google PageRank', -1, 5);
assertRangeError('Google PageRank', 0, 99);
assertRangeError('Google PageRank', 5, 2);
});
/**
* Tests that spans in the substring range are preserved.
*/
TEST_F('ChromeVoxSpannableUnitTest', 'SubstringSpansIncluded', function() {
var assertSpanIncluded = function(expectedSpanStart, expectedSpanEnd,
initial, initialSpanStart, initialSpanEnd, start, opt_end) {
var annotation = {};
var spannable = new Spannable(initial);
spannable.setSpan(annotation, initialSpanStart, initialSpanEnd);
var substring = spannable.substring(start, opt_end);
assertTrue(substring.hasSpan(annotation));
assertEquals(expectedSpanStart, substring.getSpanStart(annotation));
assertEquals(expectedSpanEnd, substring.getSpanEnd(annotation));
};
assertSpanIncluded(1, 5, 'potato wedges', 8, 12, 7);
assertSpanIncluded(1, 5, 'potato wedges', 8, 12, 7, 13);
assertSpanIncluded(1, 5, 'potato wedges', 8, 12, 7, 12);
assertSpanIncluded(0, 4, 'potato wedges', 8, 12, 8, 12);
assertSpanIncluded(0, 3, 'potato wedges', 0, 3, 0);
assertSpanIncluded(0, 3, 'potato wedges', 0, 3, 0, 3);
assertSpanIncluded(0, 3, 'potato wedges', 0, 3, 0, 6);
assertSpanIncluded(0, 5, 'potato wedges', 8, 13, 8);
assertSpanIncluded(0, 5, 'potato wedges', 8, 13, 8, 13);
assertSpanIncluded(1, 6, 'potato wedges', 8, 13, 7, 13);
// Note: we should keep zero-length spans, even at the edges of the range.
assertSpanIncluded(0, 0, 'potato wedges', 0, 0, 0, 0);
assertSpanIncluded(0, 0, 'potato wedges', 0, 0, 0, 6);
assertSpanIncluded(1, 1, 'potato wedges', 8, 8, 7, 13);
assertSpanIncluded(6, 6, 'potato wedges', 6, 6, 0, 6);
});
/**
* Tests that spans outside the range are omitted.
* It's fine to keep zero-length spans at the ends, though.
*/
TEST_F('ChromeVoxSpannableUnitTest', 'SubstringSpansExcluded', function() {
var assertSpanExcluded = function(initial, spanStart, spanEnd,
start, opt_end) {
var annotation = {};
var spannable = new Spannable(initial);
spannable.setSpan(annotation, spanStart, spanEnd);
var substring = spannable.substring(start, opt_end);
assertSpanNotFound(substring, annotation);
};
assertSpanExcluded('potato wedges', 8, 12, 0, 6);
assertSpanExcluded('potato wedges', 7, 12, 0, 6);
assertSpanExcluded('potato wedges', 0, 6, 8);
assertSpanExcluded('potato wedges', 6, 6, 8);
});
/**
* Tests that spans which cross the boundary are clipped.
*/
TEST_F('ChromeVoxSpannableUnitTest', 'SubstringSpansClipped', function() {
var assertSpanIncluded = function(expectedSpanStart, expectedSpanEnd,
initial, initialSpanStart, initialSpanEnd, start, opt_end) {
var annotation = {};
var spannable = new Spannable(initial);
spannable.setSpan(annotation, initialSpanStart, initialSpanEnd);
var substring = spannable.substring(start, opt_end);
assertEquals(expectedSpanStart, substring.getSpanStart(annotation));
assertEquals(expectedSpanEnd, substring.getSpanEnd(annotation));
};
assertSpanIncluded(0, 4, 'potato wedges', 7, 13, 8, 12);
assertSpanIncluded(0, 0, 'potato wedges', 0, 6, 0, 0);
assertSpanIncluded(0, 0, 'potato wedges', 0, 6, 6, 6);
// The first of the above should produce "edge".
assertEquals('edge',
new Spannable('potato wedges').substring(8, 12).toString());
});
/**
* Tests that whitespace is trimmed.
*/
TEST_F('ChromeVoxSpannableUnitTest', 'Trim', function() {
var assertTrimResult = function(expected, initial) {
assertEquals(expected, new Spannable(initial).trim().toString());
};
assertTrimResult('John F. Kennedy', 'John F. Kennedy');
assertTrimResult('John F. Kennedy', ' John F. Kennedy');
assertTrimResult('John F. Kennedy', 'John F. Kennedy ');
assertTrimResult('John F. Kennedy', ' \r\t \nJohn F. Kennedy\n\n \n');
assertTrimResult('', '');
assertTrimResult('', ' \t\t \n\r');
});
/**
* Tests that trim keeps, drops and clips spans.
*/
TEST_F('ChromeVoxSpannableUnitTest', 'TrimSpans', function() {
var spannable = new Spannable(' \t Kennedy\n');
spannable.setSpan('tab', 1, 2);
spannable.setSpan('jfk', 3, 10);
spannable.setSpan('jfk-newline', 3, 11);
var trimmed = spannable.trim();
assertSpanNotFound(trimmed, 'tab');
assertEquals(0, trimmed.getSpanStart('jfk'));
assertEquals(7, trimmed.getSpanEnd('jfk'));
assertEquals(0, trimmed.getSpanStart('jfk-newline'));
assertEquals(7, trimmed.getSpanEnd('jfk-newline'));
});
/**
* Tests that when a string is all whitespace, we trim off the *end*.
*/
TEST_F('ChromeVoxSpannableUnitTest', 'TrimAllWhitespace', function() {
var spannable = new Spannable(' ');
spannable.setSpan('cursor 1', 0, 0);
spannable.setSpan('cursor 2', 2, 2);
var trimmed = spannable.trim();
assertEquals(0, trimmed.getSpanStart('cursor 1'));
assertEquals(0, trimmed.getSpanEnd('cursor 1'));
assertSpanNotFound(trimmed, 'cursor 2');
});
/**
* Tests finding a span which is an instance of a given class.
*/
TEST_F('ChromeVoxSpannableUnitTest', 'GetSpanInstanceOf', function() {
function ExampleConstructorBase() {}
function ExampleConstructor1() {}
function ExampleConstructor2() {}
function ExampleConstructor3() {}
ExampleConstructor1.prototype = new ExampleConstructorBase();
ExampleConstructor2.prototype = new ExampleConstructorBase();
ExampleConstructor3.prototype = new ExampleConstructorBase();
var ex1 = new ExampleConstructor1();
var ex2 = new ExampleConstructor2();
var spannable = new Spannable('Hello world');
spannable.setSpan(ex1, 0, 0);
spannable.setSpan(ex2, 1, 1);
assertEquals(ex1, spannable.getSpanInstanceOf(ExampleConstructor1));
assertEquals(ex2, spannable.getSpanInstanceOf(ExampleConstructor2));
assertUndefined(spannable.getSpanInstanceOf(ExampleConstructor3));
assertEquals(ex1, spannable.getSpanInstanceOf(ExampleConstructorBase));
});
/** Tests trimming only left or right. */
TEST_F('ChromeVoxSpannableUnitTest', 'TrimLeftOrRight', function() {
var spannable = new Spannable(' ');
spannable.setSpan('cursor 1', 0, 0);
spannable.setSpan('cursor 2', 2, 2);
var trimmed = spannable.trimLeft();
assertEquals(0, trimmed.getSpanStart('cursor 1'));
assertEquals(0, trimmed.getSpanEnd('cursor 1'));
assertSpanNotFound(trimmed, 'cursor 2');
var spannable2 = new Spannable('0 ');
spannable2.setSpan('cursor 1', 0, 0);
spannable2.setSpan('cursor 2', 2, 2);
var trimmed2 = spannable2.trimLeft();
assertEquals(0, trimmed2.getSpanStart('cursor 1'));
assertEquals(0, trimmed2.getSpanEnd('cursor 1'));
assertEquals(2, trimmed2.getSpanStart('cursor 2'));
assertEquals(2, trimmed2.getSpanEnd('cursor 2'));
trimmed2 = trimmed2.trimRight();
assertEquals(0, trimmed2.getSpanStart('cursor 1'));
assertEquals(0, trimmed2.getSpanEnd('cursor 1'));
assertSpanNotFound(trimmed2, 'cursor 2');
var spannable3 = new Spannable(' 0');
spannable3.setSpan('cursor 1', 0, 0);
spannable3.setSpan('cursor 2', 2, 2);
var trimmed3 = spannable3.trimRight();
assertEquals(0, trimmed3.getSpanStart('cursor 1'));
assertEquals(0, trimmed3.getSpanEnd('cursor 1'));
assertEquals(2, trimmed3.getSpanStart('cursor 2'));
assertEquals(2, trimmed3.getSpanEnd('cursor 2'));
trimmed3 = trimmed3.trimLeft();
assertSpanNotFound(trimmed3, 'cursor 1');
assertEquals(0, trimmed3.getSpanStart('cursor 2'));
assertEquals(0, trimmed3.getSpanEnd('cursor 2'));
});
TEST_F('ChromeVoxSpannableUnitTest', 'Serialize', function() {
var fresh = new Spannable('text');
var freshStatelessSerializable = new StatelessSerializableSpan();
var freshNonStatelessSerializable = new NonStatelessSerializableSpan(14);
fresh.setSpan(new UnserializableSpan(), 0, 1);
fresh.setSpan(freshStatelessSerializable, 0, 2);
fresh.setSpan(freshNonStatelessSerializable, 3, 4);
var thawn = Spannable.fromJson(fresh.toJson());
var thawnStatelessSerializable = thawn.getSpanInstanceOf(
StatelessSerializableSpan);
var thawnNonStatelessSerializable = thawn.getSpanInstanceOf(
NonStatelessSerializableSpan);
assertThat('text', eqJSON(thawn.toString()));
assertUndefined(thawn.getSpanInstanceOf(UnserializableSpan));
assertThat(
fresh.getSpanStart(freshStatelessSerializable),
eqJSON(thawn.getSpanStart(thawnStatelessSerializable)));
assertThat(
fresh.getSpanEnd(freshStatelessSerializable),
eqJSON(thawn.getSpanEnd(thawnStatelessSerializable)));
assertThat(freshNonStatelessSerializable,
eqJSON(thawnNonStatelessSerializable));
});
TEST_F('ChromeVoxSpannableUnitTest', 'GetSpanIntervals', function() {
function Foo() {}
function Bar() {}
var ms = new MultiSpannable('f12b45f78b01');
var foo = new Foo();
var bar = new Bar();
ms.setSpan(foo, 0, 3);
ms.setSpan(bar, 3, 6);
ms.setSpan(foo, 6, 9);
ms.setSpan(bar, 9, 12);
assertEquals(2, ms.getSpansInstanceOf(Foo).length);
assertEquals(2, ms.getSpansInstanceOf(Bar).length);
var fooIntervals = ms.getSpanIntervals(foo);
assertEquals(2, fooIntervals.length);
assertEquals(0, fooIntervals[0].start);
assertEquals(3, fooIntervals[0].end);
assertEquals(6, fooIntervals[1].start);
assertEquals(9, fooIntervals[1].end);
var barIntervals = ms.getSpanIntervals(bar);
assertEquals(2, barIntervals.length);
assertEquals(3, barIntervals[0].start);
assertEquals(6, barIntervals[0].end);
assertEquals(9, barIntervals[1].start);
assertEquals(12, barIntervals[1].end);
});