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: 'value', start: 0, end: 2},
151 // The name is an empty string.
152 {value: 'name', start: 3, end: 3}
153 ]}, o.speechOutputForTest);
156 [{value: new Output.NodeSpan(el), start: 0, end: 2}],
161 TEST_F('OutputE2ETest', 'Headings', function() {
162 this.runWithLoadedTree(function() {/*!
163 <h1>a</h1><h2>b</h2><h3>c</h3><h4>d</h4><h5>e</h5><h6>f</h6>
164 <h1><a href="a.com">b</a></h1> */},
166 var el = root.firstChild;
167 for (var i = 1; i <= 6; ++i) {
168 var range = cursors.Range.fromNode(el);
169 var o = new Output().withSpeechAndBraille(range, null, 'navigate');
170 var letter = String.fromCharCode('a'.charCodeAt(0) + i -1);
171 assertEqualsJSON({string_: 'Heading ' + i + '|' + letter, 'spans_': [
173 {value: 'nameOrDescendants', start: 10, end: 11}
174 ]}, o.speechOutputForTest);
176 'h' + i + ' ' + letter,
177 [{value: new Output.NodeSpan(el), start: 0, end: 4}],
182 range = cursors.Range.fromNode(el);
183 o = new Output().withSpeechAndBraille(range, null, 'navigate');
184 assertEqualsJSON({string_: 'Heading 1|b|Link', 'spans_': [
186 {value: 'name', start: 10, end: 11},
187 {value: {earconId: "LINK"}, start: 10, end: 11},
188 {value: 'role', start: 12, end: 16}
189 ]}, o.speechOutputForTest);
192 [{value: new Output.NodeSpan(el), start: 0, end: 2},
193 {value: new Output.NodeSpan(el.firstChild), start: 3, end: 8}],
198 TEST_F('OutputE2ETest', 'Audio', function() {
199 this.runWithLoadedTree('<audio src="foo.mp3" controls></audio>',
201 var el = root.firstChild.firstChild.firstChild.firstChild;
202 var range = cursors.Range.fromNode(el);
203 var o = new Output().withSpeechAndBraille(range, null, 'navigate');
205 {string_: 'audio|Tool bar|play||begin playback|Button',
207 // Entered container toolbar.
210 [{value: 'name', start: 15, end: 19},
213 {value: {earconId: "BUTTON"}, start: 15, end: 19},
215 {value: 'value', start: 20, end: 20},
216 {value: 'help', start: 21, end: 35},
217 {value: 'role', start: 36, end: 42}]
218 }, o.speechOutputForTest);
220 'audio tlbar play begin playback btn',
221 [{value: new Output.NodeSpan(el.parent), start: 0, end: 11},
222 {value: new Output.NodeSpan(el), start: 12, end: 35}],
226 var prevRange = range;
227 range = cursors.Range.fromNode(el);
228 var o = new Output().withSpeechAndBraille(range, prevRange, 'navigate');
229 assertEqualsJSON({string_: '0, , slider|audio time scrubber',
231 [{value: 'help', start: 12, end: 31}]
232 }, o.speechOutputForTest);
233 // TODO(plundblad): Investigate this.
235 '0, , slider audio time scrubber',
236 [{value: new Output.NodeSpan(el), start: 0, end: 31}],
241 TEST_F('OutputE2ETest', 'Input', function() {
242 this.runWithLoadedTree(
243 '<input type="text"></input>' +
244 '<input type="email"></input>' +
245 '<input type="password"></input>' +
246 '<input type="tel"></input>' +
247 '<input type="number"></input>' +
248 '<input type="time"></input>' +
249 '<input type="date"></input>',
251 var expected = {string_: '', 'spans_': [
252 {value: 'name', start: 0, end: 0},
255 {value: {earconId: 'EDITABLE_TEXT'}, start: 0, end: 0},
258 {value: {startIndex: 0, endIndex: 0}, start: 1, end: 1},
260 {value: 'value', start: 1, end: 1}
263 var expectedSpeechValues = [
265 '||Edit text, email entry',
266 '||Password edit text',
267 '||Edit text, number entry',
268 {string_: '||Spin button', spans_: [{value: 'name', start: 0, end: 0},
269 {value: {earconId:'LISTBOX'}, start: 0, end: 0},
270 {value: {startIndex: 0, endIndex: 0}, start: 1, end: 1},
271 {value: 'value', start: 1, end: 1},
272 {value: 'role', start: 2, end: 13}]},
273 {string_: '||Time control', spans_: [{value: 'name', start: 0, end: 0},
274 {value: 'value', start: 1, end: 1},
275 {value: 'role', start: 2, end: 14}]},
276 {string_: '||Date control', spans_: [{value: 'name', start: 0, end: 0},
277 {value: 'value', start: 1, end: 1},
278 {value: 'role', start: 2, end: 14}]}
280 // TODO(plundblad): Some of these are wrong, there should be an initial
281 // space for the cursor in edit fields.
282 var expectedBrailleValues = [
291 assertEquals(expectedSpeechValues.length, expectedBrailleValues.length);
293 var el = root.firstChild.firstChild;
294 expectedSpeechValues.forEach(function(expectedValue) {
295 var range = cursors.Range.fromNode(el);
296 var o = new Output().withSpeechAndBraille(range, null, 'navigate');
297 if (typeof expectedValue == 'object') {
298 assertEqualsJSON(expectedValue, o.speechOutputForTest);
300 expected.string_ = expectedValue;
301 assertEqualsJSON(expected, o.speechOutputForTest);
306 el = root.firstChild.firstChild;
307 expectedBrailleValues.forEach(function(expectedValue) {
308 var range = cursors.Range.fromNode(el);
309 var o = new Output().withBraille(range, null, 'navigate');
310 if (typeof expectedValue === 'string') {
313 [{value: {startIndex: 0, endIndex: 0}, start: 0, end: 0},
314 {value: new Output.NodeSpan(el), start: 0,
315 end: expectedValue.length}],
319 expectedValue.string_,
320 [{value: new Output.NodeSpan(el), start: 0,
321 end: expectedValue.string_.length}],
329 TEST_F('OutputE2ETest', 'List', function() {
330 this.runWithLoadedTree(
331 '<ul><li>a<li>b<li>c</ul>',
333 var el = root.firstChild.firstChild;
334 var range = cursors.Range.fromNode(el);
335 var o = new Output().withSpeechAndBraille(range, null, 'navigate');
336 assertEqualsJSON({string_: 'list|with 3 items|a||List item', spans_: [
337 {value: 'name', start: 18, end: 19},
338 {value: {earconId:'LIST_ITEM'}, start: 18, end: 19},
339 {value: 'value', start:20, end: 20},
340 {value: 'role', start: 21, end: 30}
341 ]}, o.speechOutputForTest);
342 // TODO(plundblad): This output is wrong. Add special handling for
346 [{value: new Output.NodeSpan(el.parent), start: 0, end: 7},
347 {value: new Output.NodeSpan(el), start: 8, end: 16}],
352 TEST_F('OutputE2ETest', 'Tree', function() {
353 this.runWithLoadedTree(function() {/*!
355 <li aria-expanded="true" role="treeitem">a
356 <li role="treeitem">b
357 <li aria-expanded="false" role="treeitem">c
361 var el = root.firstChild.children[0].firstChild;
362 var range = cursors.Range.fromNode(el);
363 var o = new Output().withSpeechAndBraille(range, null, 'navigate');
365 {string_: '|Tree|with 3 items|Tree item|Expanded| 1 of 3 | level 1 |a|',
370 {value: 'role','start': 19, end: 28},
371 {value: 'state', start: 29, end: 37},
374 {value: 'value', start: 57, end: 58},
375 {value: 'name', start: 59, end: 59}
376 ]}, o.speechOutputForTest);
377 // TODO(plundblad): Braille output is wrong.
379 'tree +3 tritm - 1/3 level 1 a',
380 [{value: new Output.NodeSpan(el.parent.parent), start: 0, end: 7},
381 {value: new Output.NodeSpan(el.parent), start: 8, end: 29},
382 {value: new Output.NodeSpan(el), start: 30, end: 31}],
385 el = root.firstChild.children[1].firstChild;
386 var range = cursors.Range.fromNode(el);
387 var o = new Output().withSpeechAndBraille(range, null, 'navigate');
389 {string_: '|Tree|with 3 items|Tree item| 2 of 3 | level 1 |b|',
394 {value: 'role','start': 19, end: 28},
397 {value: 'value', start: 48, end: 49},
398 {value: 'name', start: 50, end: 50}
399 ]}, o.speechOutputForTest);
400 // TODO(plundblad): Braille output is wrong.
402 'tree +3 tritm 2/3 level 1 b',
403 [{value: new Output.NodeSpan(el.parent.parent), start: 0, end: 7},
404 {value: new Output.NodeSpan(el.parent), start: 8, end: 27},
405 {value: new Output.NodeSpan(el), start: 28, end: 29}],
408 el = root.firstChild.children[2].firstChild;
409 var range = cursors.Range.fromNode(el);
410 var o = new Output().withSpeechAndBraille(range, null, 'navigate');
412 {string_: '|Tree|with 3 items|Tree item|Collapsed| 3 of 3 | level 1 |c|',
417 {value: 'role','start': 19, end: 28},
418 {value: 'state', start: 29, end: 38},
421 {value: 'value', start: 58, end: 59},
422 {value: 'name', start: 60, end: 60}
423 ]}, o.speechOutputForTest);
424 // TODO(plundblad): Braille output is wrong.
426 'tree +3 tritm + 3/3 level 1 c',
427 [{value: new Output.NodeSpan(el.parent.parent), start: 0, end: 7},
428 {value: new Output.NodeSpan(el.parent), start: 8, end: 29},
429 {value: new Output.NodeSpan(el), start: 30, end: 31}],
434 TEST_F('OutputE2ETest', 'Menu', function() {
435 this.runWithLoadedTree(function() {/*!
437 <div role="menuitem">a</div>
441 var el = root.firstChild.firstChild;
442 var range = cursors.Range.fromNode(el);
443 var o = new Output().withSpeechAndBraille(range, null, 'navigate');
444 assertEqualsJSON({string_:
445 '|Menu|with 1 item|a|Menu item| 1 of 1 ', spans_: [
446 {value: 'name', start: 18, end: 19},
447 {value: 'role', start:20, end: 29}
448 ]}, o.speechOutputForTest);
450 'mnu +1 a mnuitm 1/1',
451 [{value: new Output.NodeSpan(el.parent), start: 0, end: 6},
452 {value: new Output.NodeSpan(el), start: 7, end: 19}],
457 TEST_F('OutputE2ETest', 'ListBox', function() {
458 this.runWithLoadedTree(function() {/*!
465 var el = root.firstChild.firstChild.firstChild;
466 var range = cursors.Range.fromNode(el);
467 var o = new Output().withSpeechAndBraille(range, null, 'navigate');
468 assertEqualsJSON({string_: '|List box|with 2 items||List item| 1 of 2 ',
472 {value: {earconId:'LISTBOX'}, start: 0, end: 0},
474 {value: 'name', start: 23, end: 23},
477 {value: {earconId: 'LIST_ITEM'}, start: 23, end: 23},
479 {value: 'role', start:24, end: 33}
480 ]}, o.speechOutputForTest);
482 'lstbx +2 lstitm 1/2',
483 [{value: new Output.NodeSpan(el.parent), start: 0, end: 8},
484 {value: new Output.NodeSpan(el), start: 9, end: 19}],
489 SYNC_TEST_F('OutputE2ETest', 'MessageIdAndEarconValidity', function() {
490 for (var key in Output.ROLE_INFO_) {
491 var value = Output.ROLE_INFO_[key];
492 cvox.ChromeVox.msgs.getMsg(value.msgId);
493 cvox.ChromeVox.msgs.getMsg(value.msgId + '_brl');
494 assertFalse(/[A-Z]+/.test(value.msgId));
496 assertNotNullNorUndefined(cvox.Earcon[value.earconId]);
498 for (var key in Output.STATE_INFO_) {
499 var value = Output.STATE_INFO_[key];
500 for (innerKey in value) {
501 var innerValue = value[innerKey];
502 cvox.ChromeVox.msgs.getMsg(innerValue.msgId);
503 cvox.ChromeVox.msgs.getMsg(innerValue.msgId + '_brl');
504 assertFalse(/[A-Z]+/.test(innerValue.msgId));
505 if (innerValue.earconId)
506 assertNotNullNorUndefined(cvox.Earcon[innerValue.earconId]);