Initial commit of new ChromeVox earcon engine.
[chromium-blink-merge.git] / chrome / browser / resources / chromeos / chromevox / cvox2 / background / output_test.extjs
blob07f494744fbfcc5bee870328942d21df0ea42ed9
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']);
8 /**
9  * Gets the braille output and asserts that it matches expected values.
10  * Annotations in the output that are primitive strings are ignored.
11  */
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
16   // annotations.
17   var actualSpans = actualOutput.spans_.filter(function(span) {
18     return (typeof span.value !== 'string');
19   });
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();
27     }
28     return JSON.stringify(obj);
29   }
31   function describeActualSpans() {
32     return '\nAll actual spans:\n' + actualSpans.map(describeSpan).join('\n');
33   }
35   for (var i = 0, max = Math.max(expectedSpans.length, actualSpans.length);
36        i < max; ++i) {
37     var expectedSpan = expectedSpans[i];
38     var actualSpan = actualSpans[i];
39     if (!expectedSpan)
40       throw Error('Unexpected span in ' + expectedText + ': ' +
41           describeSpan(actualSpan) + describeActualSpans());
42     if (!actualSpan)
43       throw Error('Missing expected span in ' + expectedText + ': ' +
44           describeSpan(expectedSpan) + describeActualSpans());
45     var equal = true;
46     if (expectedSpan.start !== actualSpan.start ||
47         expectedSpan.end !== actualSpan.end) {
48       equal = false;
49     } else if (expectedSpan.value instanceof Output.NodeSpan &&
50         (!(actualSpan.value instanceof Output.NodeSpan) ||
51         expectedSpan.value.node !== actualSpan.value.node)) {
52       equal = false;
53     } else {
54       equal = (JSON.stringify(expectedSpan.value) ===
55           JSON.stringify(actualSpan.value));
56     }
57     if (!equal) {
58       throw Error('Spans differ in ' + expectedText + ':\n' +
59           'Expected: ' + describeSpan(expectedSpan) + '\n' +
60           'Got     : ' + describeSpan(actualSpan) + describeActualSpans());
61     }
62   }
65 /**
66  * Test fixture for output.js.
67  * @constructor
68  * @extends {ChromeVoxNextE2ETestBase}
69  */
70 function OutputE2ETest() {
71   ChromeVoxNextE2ETest.call(this);
74 OutputE2ETest.prototype = {
75   __proto__: ChromeVoxNextE2ETest.prototype,
77   /** @override */
78   setUp: function() {
79     window.Dir = AutomationUtil.Dir;
80   }
83 TEST_F('OutputE2ETest', 'Links', function() {
84   this.runWithLoadedTree('<a href="#">Click here</a>',
85     function(root) {
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_': [
90         // Attributes.
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);
98       checkBrailleOutput(
99           'Click here lnk',
100           [{value: new Output.NodeSpan(el), start: 0, end: 14}],
101           o);
102   });
105 TEST_F('OutputE2ETest', 'Checkbox', function() {
106   this.runWithLoadedTree('<input type="checkbox">',
107     function(root) {
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_': [
112         // Attributes.
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);
120       checkBrailleOutput(
121           'chk ( )',
122           [{value: new Output.NodeSpan(el), start: 0, end: 7}],
123           o);
124   });
127 TEST_F('OutputE2ETest', 'InLineTextBoxValueGetsIgnored', function() {
128   this.runWithLoadedTree('<p>OK',
129     function(root) {
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_': [
135         // Attributes.
136         {value: 'name', start: 0, end: 2}
137       ]}, o.speechOutputForTest);
138       checkBrailleOutput(
139           'OK',
140           [{value: new Output.NodeSpan(el), start: 0, end: 2}],
141           o);
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_': [
148         // Attributes.
149         {value: 'name', start: 0, end: 2}
150       ]}, o.speechOutputForTest);
151       checkBrailleOutput(
152           'OK',
153           [{value: new Output.NodeSpan(el), start: 0, end: 2}],
154           o);
155   });
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> */},
162     function(root) {
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_': [
169           // Attributes.
170           {value: 'nameOrDescendants', start: 10, end: 11}
171         ]}, o.speechOutputForTest);
172         checkBrailleOutput(
173             'h' + i + ' ' + letter,
174             [{value: new Output.NodeSpan(el), start: 0, end: 4}],
175             o);
176         el = el.nextSibling;
177       }
179       range = cursors.Range.fromNode(el);
180       o = new Output().withSpeechAndBraille(range, null, 'navigate');
181       assertEqualsJSON({string_: 'Heading 1|b|Link', 'spans_': [
182         // Link.
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);
187       checkBrailleOutput(
188           'h1 b lnk',
189           [{value: new Output.NodeSpan(el), start: 0, end: 2},
190            {value: new Output.NodeSpan(el.firstChild), start: 3, end: 8}],
191           o);
192   });
195 TEST_F('OutputE2ETest', 'Audio', function() {
196   this.runWithLoadedTree('<audio src="foo.mp3" controls></audio>',
197     function(root) {
198       var el = root.firstChild.firstChild.firstChild.firstChild;
199       var range = cursors.Range.fromNode(el);
200       var o = new Output().withSpeechAndBraille(range, null, 'navigate');
201       assertEqualsJSON(
202           {string_: 'audio|Tool bar|play||begin playback|Button',
203            spans_:
204               // Entered container toolbar.
206               // Button.
207               [{value: 'name', start: 15, end: 19},
209               // Button earcon.
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);
216       checkBrailleOutput(
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}],
220           o);
222       el = el.nextSibling;
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',
227           spans_:
228               [{value: 'help', start: 12, end: 31}]
229           }, o.speechOutputForTest);
230       // TODO(plundblad): Investigate this.
231       checkBrailleOutput(
232           '0, , slider audio time scrubber',
233           [{value: new Output.NodeSpan(el), start: 0, end: 31}],
234           o);
235   });
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>',
247     function(root) {
248       var expected = {string_: '', 'spans_': [
249               {value: 'name', start: 0, end: 0},
251               // Earcon
252               {value: {earconId: 'EDITABLE_TEXT'}, start: 0, end: 0},
254               // Selection span.
255               {value: {startIndex: 0, endIndex: 0}, start: 1, end: 1},
257               {value: 'value', start: 1, end: 1}
258       ]};
260       var expectedSpeechValues = [
261         '||Edit text',
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}]}
276       ];
277       // TODO(plundblad): Some of these are wrong, there should be an initial
278       // space for the cursor in edit fields.
279       var expectedBrailleValues = [
280         ' ed',
281         ' @ed',
282         ' pwded',
283         ' #ed',
284         ' spnbtn',
285         {string_: 'time'},
286         {string_: 'date'}
287       ];
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);
296         } else {
297           expected.string_ = expectedValue;
298           assertEqualsJSON(expected, o.speechOutputForTest);
299         }
300         el = el.nextSibling;
301       });
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') {
308           checkBrailleOutput(
309               expectedValue,
310               [{value: {startIndex: 0, endIndex: 0}, start: 0, end: 0},
311                {value: new Output.NodeSpan(el), start: 0,
312                 end: expectedValue.length}],
313               o);
314         } else {
315           checkBrailleOutput(
316               expectedValue.string_,
317               [{value: new Output.NodeSpan(el), start: 0,
318                 end: expectedValue.string_.length}],
319               o);
320         }
321         el = el.nextSibling;
322       });
323   });
326 TEST_F('OutputE2ETest', 'List', function() {
327   this.runWithLoadedTree(
328       '<ul><li>a<li>b<li>c</ul>',
329     function(root) {
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
340       // braille here.
341       checkBrailleOutput(
342           'list +3 a lstitm',
343           [{value: new Output.NodeSpan(el.parent), start: 0, end: 7},
344            {value: new Output.NodeSpan(el), start: 8, end: 16}],
345           o);
346   });
349 TEST_F('OutputE2ETest', 'Tree', function() {
350   this.runWithLoadedTree(function() {/*!
351     <ul role="tree">
352       <li aria-expanded="true" role="treeitem">a
353       <li role="treeitem">b
354       <li aria-expanded="false" role="treeitem">c
355     </ul>
356   */},
357   function(root) {
358     var el = root.firstChild.children[0].firstChild;
359     var range = cursors.Range.fromNode(el);
360     var o = new Output().withSpeechAndBraille(range, null, 'navigate');
361     assertEqualsJSON(
362         {string_: '|Tree|with 3 items|Tree item|Expanded| 1 of 3 | level 1 |a',
363         spans_: [
364           // Enter rule.
366           // TreeItem.
367           {value: 'role','start': 19, end: 28},
368           {value: 'state', start: 29, end: 37},
370           // InLineTextBox.
371           {value: 'name', start: 57, end: 58}
372     ]}, o.speechOutputForTest);
373     // TODO(plundblad): Braille output is wrong.
374     checkBrailleOutput(
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}],
379         o);
381     el = root.firstChild.children[1].firstChild;
382     var range = cursors.Range.fromNode(el);
383     var o = new Output().withSpeechAndBraille(range, null, 'navigate');
384     assertEqualsJSON(
385         {string_: '|Tree|with 3 items|Tree item| 2 of 3 | level 1 |b',
386         spans_: [
387           // Enter rule.
389           // TreeItem.
390           {value: 'role','start': 19, end: 28},
392           // InLineTextBox.
393           {value: 'name', start: 48, end: 49}
394     ]}, o.speechOutputForTest);
395     // TODO(plundblad): Braille output is wrong.
396     checkBrailleOutput(
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}],
401         o);
403     el = root.firstChild.children[2].firstChild;
404     var range = cursors.Range.fromNode(el);
405     var o = new Output().withSpeechAndBraille(range, null, 'navigate');
406     assertEqualsJSON(
407         {string_: '|Tree|with 3 items|Tree item|Collapsed| 3 of 3 | level 1 |c',
408         spans_: [
409           // Enter rule.
411           // TreeItem.
412           {value: 'role','start': 19, end: 28},
413           {value: 'state', start: 29, end: 38},
415           // InLineTextBox.
416           {value: 'name', start: 58, end: 59}
417     ]}, o.speechOutputForTest);
418     // TODO(plundblad): Braille output is wrong.
419     checkBrailleOutput(
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}],
424         o);
425   });
428 TEST_F('OutputE2ETest', 'Menu', function() {
429   this.runWithLoadedTree(function() {/*!
430     <div role="menu">
431       <div role="menuitem">a</div>
432     </div>
433   */},
434   function(root) {
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);
443     checkBrailleOutput(
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}],
447         o);
448   });
451 TEST_F('OutputE2ETest', 'ListBox', function() {
452   this.runWithLoadedTree(function() {/*!
453     <select multiple>
454       <option>1</option>
455       <option>2</option>
456     </select>
457   */},
458   function(root) {
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 ',
463         spans_: [
464           // ListBox.
465           // Earcon.
466           {value: {earconId:'LISTBOX'}, start: 0, end: 0},
468           {value: 'name', start: 23, end: 23},
470           // Earcon.
471           {value: {earconId: 'LIST_ITEM'}, start: 23, end: 23},
473           {value: 'role', start:24, end: 33}
474     ]}, o.speechOutputForTest);
475     checkBrailleOutput(
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}],
479         o);
480   });
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));
489     if (value.earconId)
490       assertNotNullNorUndefined(cvox.Earcon[value.earconId]);
491   }
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]);
501     }
502   }