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 GEN_INCLUDE(['../../testing/assert_additions.js']);
6 GEN_INCLUDE(['../../testing/chromevox_next_e2e_test_base.js']);
9 * Gets the braille output and asserts that it matches expected values.
10 * Annotations in the output that are primitive strings are ignored.
12 function checkBrailleOutput(expectedText, expectedSpans, output) {
13 var actualOutput = output.brailleOutputForTest;
14 // Remove string annotations. These are tested in the speech output and
15 // there's no need to clutter the tests with the corresponding braille
17 var actualSpans = actualOutput.spans_.filter(function(span) {
18 return (typeof span.value !== 'string');
20 assertEquals(expectedText, actualOutput.toString());
22 function describeSpan(span) {
23 var obj = {value: span.value, start: span.start, end: span.end};
24 if (obj.value instanceof Output.NodeSpan) {
25 obj.value.node = (obj.value.node.name || '') + ' ' +
26 obj.value.node.toString();
28 return JSON.stringify(obj);
31 function describeActualSpans() {
32 return '\nAll actual spans:\n' + actualSpans.map(describeSpan).join('\n');
35 for (var i = 0, max = Math.max(expectedSpans.length, actualSpans.length);
37 var expectedSpan = expectedSpans[i];
38 var actualSpan = actualSpans[i];
40 throw Error('Unexpected span in ' + expectedText + ': ' +
41 describeSpan(actualSpan) + describeActualSpans());
43 throw Error('Missing expected span in ' + expectedText + ': ' +
44 describeSpan(expectedSpan) + describeActualSpans());
46 if (expectedSpan.start !== actualSpan.start ||
47 expectedSpan.end !== actualSpan.end) {
49 } else if (expectedSpan.value instanceof Output.NodeSpan &&
50 (!(actualSpan.value instanceof Output.NodeSpan) ||
51 expectedSpan.value.node !== actualSpan.value.node)) {
54 equal = (JSON.stringify(expectedSpan.value) ===
55 JSON.stringify(actualSpan.value));
58 throw Error('Spans differ in ' + expectedText + ':\n' +
59 'Expected: ' + describeSpan(expectedSpan) + '\n' +
60 'Got : ' + describeSpan(actualSpan) + describeActualSpans());
66 * Test fixture for output.js.
68 * @extends {ChromeVoxNextE2ETestBase}
70 function OutputE2ETest() {
71 ChromeVoxNextE2ETest.call(this);
74 OutputE2ETest.prototype = {
75 __proto__: ChromeVoxNextE2ETest.prototype,
79 window.Dir = AutomationUtil.Dir;
83 TEST_F('OutputE2ETest', 'Links', function() {
84 this.runWithLoadedTree('<a href="#">Click here</a>',
86 var el = root.firstChild.firstChild;
87 var range = cursors.Range.fromNode(el);
88 var o = new Output().withSpeechAndBraille(range, null, 'navigate');
89 assertEqualsJSON({string_: 'Click here|Link', 'spans_': [
91 {value: 'name', start: 0, end: 10},
93 // Link earcon (based on the name).
94 {value: {earconId: 'LINK'}, start: 0, end: 10},
96 {value: 'role', start: 11, end: 15}
97 ]}, o.speechOutputForTest);
100 [{value: new Output.NodeSpan(el), start: 0, end: 14}],
105 TEST_F('OutputE2ETest', 'Checkbox', function() {
106 this.runWithLoadedTree('<input type="checkbox">',
108 var el = root.firstChild.firstChild;
109 var range = cursors.Range.fromNode(el);
110 var o = new Output().withSpeechAndBraille(range, null, 'navigate');
111 assertEqualsJSON({string_: '|Check box|not checked', 'spans_': [
113 {value: 'name', start: 0, end: 0},
114 {value: 'role', start: 1, end: 10},
115 {value: 'state', start: 11, end: 22},
117 // Checkbox earcon (based on the state).
118 {value: {earconId: 'CHECK_OFF'}, start: 11, end: 22}
119 ]}, o.speechOutputForTest);
122 [{value: new Output.NodeSpan(el), start: 0, end: 7}],
127 TEST_F('OutputE2ETest', 'InLineTextBoxValueGetsIgnored', function() {
128 this.runWithLoadedTree('<p>OK',
130 var el = root.firstChild.firstChild.firstChild;
131 assertEquals('inlineTextBox', el.role);
132 var range = cursors.Range.fromNode(el);
133 var o = new Output().withSpeechAndBraille(range, null, 'navigate');
134 assertEqualsJSON({string_: 'OK', 'spans_': [
136 {value: 'name', start: 0, end: 2}
137 ]}, o.speechOutputForTest);
140 [{value: new Output.NodeSpan(el), start: 0, end: 2}],
143 el = root.firstChild.firstChild;
144 assertEquals('staticText', el.role);
145 range = cursors.Range.fromNode(el);
146 o = new Output().withSpeechAndBraille(range, null, 'navigate');
147 assertEqualsJSON({string_: 'OK', 'spans_': [
149 {value: 'name', start: 0, end: 2}
150 ]}, o.speechOutputForTest);
153 [{value: new Output.NodeSpan(el), start: 0, end: 2}],
158 TEST_F('OutputE2ETest', 'Headings', function() {
159 this.runWithLoadedTree(function() {/*!
160 <h1>a</h1><h2>b</h2><h3>c</h3><h4>d</h4><h5>e</h5><h6>f</h6>
161 <h1><a href="a.com">b</a></h1> */},
163 var el = root.firstChild;
164 for (var i = 1; i <= 6; ++i) {
165 var range = cursors.Range.fromNode(el);
166 var o = new Output().withSpeechAndBraille(range, null, 'navigate');
167 var letter = String.fromCharCode('a'.charCodeAt(0) + i -1);
168 assertEqualsJSON({string_: 'Heading ' + i + '|' + letter, 'spans_': [
170 {value: 'nameOrDescendants', start: 10, end: 11}
171 ]}, o.speechOutputForTest);
173 'h' + i + ' ' + letter,
174 [{value: new Output.NodeSpan(el), start: 0, end: 4}],
179 range = cursors.Range.fromNode(el);
180 o = new Output().withSpeechAndBraille(range, null, 'navigate');
181 assertEqualsJSON({string_: 'Heading 1|b|Link', 'spans_': [
183 {value: 'name', start: 10, end: 11},
184 {value: {earconId: "LINK"}, start: 10, end: 11},
185 {value: 'role', start: 12, end: 16}
186 ]}, o.speechOutputForTest);
189 [{value: new Output.NodeSpan(el), start: 0, end: 2},
190 {value: new Output.NodeSpan(el.firstChild), start: 3, end: 8}],
195 TEST_F('OutputE2ETest', 'Audio', function() {
196 this.runWithLoadedTree('<audio src="foo.mp3" controls></audio>',
198 var el = root.firstChild.firstChild.firstChild.firstChild;
199 var range = cursors.Range.fromNode(el);
200 var o = new Output().withSpeechAndBraille(range, null, 'navigate');
202 {string_: 'audio|Tool bar|play||begin playback|Button',
204 // Entered container toolbar.
207 [{value: 'name', start: 15, end: 19},
210 {value: {earconId: "BUTTON"}, start: 15, end: 19},
212 {value: 'value', start: 20, end: 20},
213 {value: 'help', start: 21, end: 35},
214 {value: 'role', start: 36, end: 42}]
215 }, o.speechOutputForTest);
217 'audio tlbar play begin playback btn',
218 [{value: new Output.NodeSpan(el.parent), start: 0, end: 11},
219 {value: new Output.NodeSpan(el), start: 12, end: 35}],
223 var prevRange = range;
224 range = cursors.Range.fromNode(el);
225 var o = new Output().withSpeechAndBraille(range, prevRange, 'navigate');
226 assertEqualsJSON({string_: '0, , slider|audio time scrubber',
228 [{value: 'help', start: 12, end: 31}]
229 }, o.speechOutputForTest);
230 // TODO(plundblad): Investigate this.
232 '0, , slider audio time scrubber',
233 [{value: new Output.NodeSpan(el), start: 0, end: 31}],
238 TEST_F('OutputE2ETest', 'Input', function() {
239 this.runWithLoadedTree(
240 '<input type="text"></input>' +
241 '<input type="email"></input>' +
242 '<input type="password"></input>' +
243 '<input type="tel"></input>' +
244 '<input type="number"></input>' +
245 '<input type="time"></input>' +
246 '<input type="date"></input>',
248 var expected = {string_: '', 'spans_': [
249 {value: 'name', start: 0, end: 0},
252 {value: {earconId: 'EDITABLE_TEXT'}, start: 0, end: 0},
255 {value: {startIndex: 0, endIndex: 0}, start: 1, end: 1},
257 {value: 'value', start: 1, end: 1}
260 var expectedSpeechValues = [
262 '||Edit text, email entry',
263 '||Password edit text',
264 '||Edit text, number entry',
265 {string_: '||Spin button', spans_: [{value: 'name', start: 0, end: 0},
266 {value: {earconId:'LISTBOX'}, start: 0, end: 0},
267 {value: {startIndex: 0, endIndex: 0}, start: 1, end: 1},
268 {value: 'value', start: 1, end: 1},
269 {value: 'role', start: 2, end: 13}]},
270 {string_: '||Time control', spans_: [{value: 'name', start: 0, end: 0},
271 {value: 'value', start: 1, end: 1},
272 {value: 'role', start: 2, end: 14}]},
273 {string_: '||Date control', spans_: [{value: 'name', start: 0, end: 0},
274 {value: 'value', start: 1, end: 1},
275 {value: 'role', start: 2, end: 14}]}
277 // TODO(plundblad): Some of these are wrong, there should be an initial
278 // space for the cursor in edit fields.
279 var expectedBrailleValues = [
288 assertEquals(expectedSpeechValues.length, expectedBrailleValues.length);
290 var el = root.firstChild.firstChild;
291 expectedSpeechValues.forEach(function(expectedValue) {
292 var range = cursors.Range.fromNode(el);
293 var o = new Output().withSpeechAndBraille(range, null, 'navigate');
294 if (typeof expectedValue == 'object') {
295 assertEqualsJSON(expectedValue, o.speechOutputForTest);
297 expected.string_ = expectedValue;
298 assertEqualsJSON(expected, o.speechOutputForTest);
303 el = root.firstChild.firstChild;
304 expectedBrailleValues.forEach(function(expectedValue) {
305 var range = cursors.Range.fromNode(el);
306 var o = new Output().withBraille(range, null, 'navigate');
307 if (typeof expectedValue === 'string') {
310 [{value: {startIndex: 0, endIndex: 0}, start: 0, end: 0},
311 {value: new Output.NodeSpan(el), start: 0,
312 end: expectedValue.length}],
316 expectedValue.string_,
317 [{value: new Output.NodeSpan(el), start: 0,
318 end: expectedValue.string_.length}],
326 TEST_F('OutputE2ETest', 'List', function() {
327 this.runWithLoadedTree(
328 '<ul><li>a<li>b<li>c</ul>',
330 var el = root.firstChild.firstChild;
331 var range = cursors.Range.fromNode(el);
332 var o = new Output().withSpeechAndBraille(range, null, 'navigate');
333 assertEqualsJSON({string_: 'list|with 3 items|a||List item', spans_: [
334 {value: 'name', start: 18, end: 19},
335 {value: {earconId:'LIST_ITEM'}, start: 18, end: 19},
336 {value: 'value', start:20, end: 20},
337 {value: 'role', start: 21, end: 30}
338 ]}, o.speechOutputForTest);
339 // TODO(plundblad): This output is wrong. Add special handling for
343 [{value: new Output.NodeSpan(el.parent), start: 0, end: 7},
344 {value: new Output.NodeSpan(el), start: 8, end: 16}],
349 TEST_F('OutputE2ETest', 'Tree', function() {
350 this.runWithLoadedTree(function() {/*!
352 <li aria-expanded="true" role="treeitem">a
353 <li role="treeitem">b
354 <li aria-expanded="false" role="treeitem">c
358 var el = root.firstChild.children[0].firstChild;
359 var range = cursors.Range.fromNode(el);
360 var o = new Output().withSpeechAndBraille(range, null, 'navigate');
362 {string_: '|Tree|with 3 items|Tree item|Expanded| 1 of 3 | level 1 |a',
367 {value: 'role','start': 19, end: 28},
368 {value: 'state', start: 29, end: 37},
371 {value: 'name', start: 57, end: 58}
372 ]}, o.speechOutputForTest);
373 // TODO(plundblad): Braille output is wrong.
375 'tree +3 tritm - 1/3 level 1 a',
376 [{value: new Output.NodeSpan(el.parent.parent), start: 0, end: 7},
377 {value: new Output.NodeSpan(el.parent), start: 8, end: 29},
378 {value: new Output.NodeSpan(el), start: 30, end: 31}],
381 el = root.firstChild.children[1].firstChild;
382 var range = cursors.Range.fromNode(el);
383 var o = new Output().withSpeechAndBraille(range, null, 'navigate');
385 {string_: '|Tree|with 3 items|Tree item| 2 of 3 | level 1 |b',
390 {value: 'role','start': 19, end: 28},
393 {value: 'name', start: 48, end: 49}
394 ]}, o.speechOutputForTest);
395 // TODO(plundblad): Braille output is wrong.
397 'tree +3 tritm 2/3 level 1 b',
398 [{value: new Output.NodeSpan(el.parent.parent), start: 0, end: 7},
399 {value: new Output.NodeSpan(el.parent), start: 8, end: 27},
400 {value: new Output.NodeSpan(el), start: 28, end: 29}],
403 el = root.firstChild.children[2].firstChild;
404 var range = cursors.Range.fromNode(el);
405 var o = new Output().withSpeechAndBraille(range, null, 'navigate');
407 {string_: '|Tree|with 3 items|Tree item|Collapsed| 3 of 3 | level 1 |c',
412 {value: 'role','start': 19, end: 28},
413 {value: 'state', start: 29, end: 38},
416 {value: 'name', start: 58, end: 59}
417 ]}, o.speechOutputForTest);
418 // TODO(plundblad): Braille output is wrong.
420 'tree +3 tritm + 3/3 level 1 c',
421 [{value: new Output.NodeSpan(el.parent.parent), start: 0, end: 7},
422 {value: new Output.NodeSpan(el.parent), start: 8, end: 29},
423 {value: new Output.NodeSpan(el), start: 30, end: 31}],
428 TEST_F('OutputE2ETest', 'Menu', function() {
429 this.runWithLoadedTree(function() {/*!
431 <div role="menuitem">a</div>
435 var el = root.firstChild.firstChild;
436 var range = cursors.Range.fromNode(el);
437 var o = new Output().withSpeechAndBraille(range, null, 'navigate');
438 assertEqualsJSON({string_:
439 '|Menu|with 1 item|a|Menu item| 1 of 1 ', spans_: [
440 {value: 'name', start: 18, end: 19},
441 {value: 'role', start:20, end: 29}
442 ]}, o.speechOutputForTest);
444 'mnu +1 a mnuitm 1/1',
445 [{value: new Output.NodeSpan(el.parent), start: 0, end: 6},
446 {value: new Output.NodeSpan(el), start: 7, end: 19}],
451 TEST_F('OutputE2ETest', 'ListBox', function() {
452 this.runWithLoadedTree(function() {/*!
459 var el = root.firstChild.firstChild.firstChild;
460 var range = cursors.Range.fromNode(el);
461 var o = new Output().withSpeechAndBraille(range, null, 'navigate');
462 assertEqualsJSON({string_: '|List box|with 2 items||List item| 1 of 2 ',
466 {value: {earconId:'LISTBOX'}, start: 0, end: 0},
468 {value: 'name', start: 23, end: 23},
471 {value: {earconId: 'LIST_ITEM'}, start: 23, end: 23},
473 {value: 'role', start:24, end: 33}
474 ]}, o.speechOutputForTest);
476 'lstbx +2 lstitm 1/2',
477 [{value: new Output.NodeSpan(el.parent), start: 0, end: 8},
478 {value: new Output.NodeSpan(el), start: 9, end: 19}],
483 SYNC_TEST_F('OutputE2ETest', 'MessageIdAndEarconValidity', function() {
484 for (var key in Output.ROLE_INFO_) {
485 var value = Output.ROLE_INFO_[key];
486 cvox.ChromeVox.msgs.getMsg(value.msgId);
487 cvox.ChromeVox.msgs.getMsg(value.msgId + '_brl');
488 assertFalse(/[A-Z]+/.test(value.msgId));
490 assertNotNullNorUndefined(cvox.Earcon[value.earconId]);
492 for (var key in Output.STATE_INFO_) {
493 var value = Output.STATE_INFO_[key];
494 for (innerKey in value) {
495 var innerValue = value[innerKey];
496 cvox.ChromeVox.msgs.getMsg(innerValue.msgId);
497 cvox.ChromeVox.msgs.getMsg(innerValue.msgId + '_brl');
498 assertFalse(/[A-Z]+/.test(innerValue.msgId));
499 if (innerValue.earconId)
500 assertNotNullNorUndefined(cvox.Earcon[innerValue.earconId]);