Index: chrome/browser/resources/chromeos/chromevox/chromevox/injected/navigation_manager_test.unitjs |
diff --git a/chrome/browser/resources/chromeos/chromevox/chromevox/injected/navigation_manager_test.unitjs b/chrome/browser/resources/chromeos/chromevox/chromevox/injected/navigation_manager_test.unitjs |
new file mode 100644 |
index 0000000000000000000000000000000000000000..3af5bd2bdade9436c738c075e051678b57b215f0 |
--- /dev/null |
+++ b/chrome/browser/resources/chromeos/chromevox/chromevox/injected/navigation_manager_test.unitjs |
@@ -0,0 +1,1396 @@ |
+// 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']); |
+ |
+/** |
+ * Test fixture. |
+ * @constructor |
+ * @extends {ChromeVoxUnitTestBase} |
+ */ |
+function CvoxNavigationManagerUnitTest() {} |
+ |
+CvoxNavigationManagerUnitTest.prototype = { |
+ __proto__: ChromeVoxUnitTestBase.prototype, |
+ |
+ /** @override */ |
+ isAsync: true, |
+ |
+ /** @override */ |
+ closureModuleDeps: [ |
+ 'cvox.ChromeVoxTester', |
+ 'cvox.NavigationShifter', |
+ 'cvox.TestTts', |
+ ], |
+ |
+ /** @override */ |
+ setUp: function() { |
+ cvox.ChromeVoxTester.setUp(document); |
+ }, |
+ |
+ /** @override */ |
+ tearDown: function() { |
+ cvox.ChromeVoxTester.tearDown(document); |
+ }, |
+ |
+ /** |
+ * Check a sequence of navigations. |
+ * |
+ * @param {Array.<string>} strategies A list of nav strategies to try. |
+ * @param {Array.<Object>} commandsAndExpectations An array of objects, |
+ * each one of which should contain: |
+ * 'command': The UserCommands command to execute. |
+ * 'text': The expected text of the node that's navigated to. |
+ * 'annotation': The expected annotation of the node navigated to. |
+ */ |
+ checkNavSequence: function(strategies, commandsAndExpectations) { |
+ if (strategies.length == 0) |
+ testDone(); |
+ |
+ var strategy = strategies.shift(); |
+ this.waitForCalm(cvox.ChromeVoxTester.setStrategy, strategy) |
+ .waitForCalm(cvox.ChromeVoxTester.syncToFirstNode); |
+ |
+ var depth = 0; |
+ commandsAndExpectations.forEach((function(ce) { |
+ this.waitForCalm(function() { |
+ if (ce.command) { |
+ this.userCommand(ce.command); |
+ depth = 0; |
+ } |
+ }); |
+ this.waitForCalm(function() { |
+ if (ce.context) |
+ this.assertNodeContext(ce.context, depth); |
+ if (ce.text) |
+ this.assertNodeText(ce.text, depth); |
+ if (ce.userValue) |
+ this.assertNodeUserValue(ce.userValue, depth); |
+ if (ce.annotation) |
+ this.assertNodeAnnotation(ce.annotation, depth); |
+ depth++; |
+ }); |
+ }).bind(this)); |
+ this.waitForCalm(function() { |
+ this.checkNavSequence(strategies, commandsAndExpectations); |
+ }); |
+ }, |
+ |
+ currentDescription_: function(opt_depth) { |
+ var depth = opt_depth || 0; |
+ return cvox.ChromeVox.navigationManager.getDescription()[depth] || |
+ new cvox.NavDescription({text: ''}); |
+ }, |
+ |
+ assertTextEquals: function(expected, actual) { |
+ try { |
+ assertEquals(expected, actual); |
+ } catch (e) { |
+ throw new Error('Expecting ' + expected + ' Actual ' + actual); |
+ } |
+ }, |
+ |
+ assertNodeText: function(expectedText, opt_depth) { |
+ this.assertTextEquals(expectedText, |
+ this.currentDescription_(opt_depth).text); |
+ return this; |
+ }, |
+ |
+ assertNodeAnnotation: function(expectedAnnotation, opt_depth) { |
+ this.assertTextEquals(expectedAnnotation, |
+ this.currentDescription_(opt_depth).annotation); |
+ return this; |
+ }, |
+ |
+ assertNodeContext: function(expectedContext, opt_depth) { |
+ this.assertTextEquals(expectedContext, |
+ this.currentDescription_(opt_depth).context); |
+ return this; |
+ }, |
+ |
+ assertNodeUserValue: function(expectedUserValue, opt_depth) { |
+ this.assertTextEquals(expectedUserValue, |
+ this.currentDescription_(opt_depth).userValue); |
+ return this; |
+ } |
+}; |
+ |
+TEST_F('CvoxNavigationManagerUnitTest', 'SimpleStaticHTML', function() { |
+ this.loadHtml( |
+ '<div>' + |
+ '<p id="before">Before</p>' + |
+ '<h1>FirstHeading</h1>' + |
+ '<h2>SecondHeading</h2>' + |
+ '</div>'); |
+ this.checkNavSequence( |
+ ['lineardom', 'smart', 'selection'], |
+ [ |
+ { 'command': 'forward', |
+ 'text': 'FirstHeading', |
+ 'annotation': 'Heading 1' |
+ }, |
+ { 'command': 'forward', |
+ 'text': 'SecondHeading', |
+ 'annotation': 'Heading 2' |
+ } |
+ ]); |
+}); |
+ |
+ |
+/** |
+ * Test navigation of simple static HTML with some control elements to validate |
+ * the text and annotation returned when doing navigation. |
+ */ |
+TEST_F('CvoxNavigationManagerUnitTest', 'ControlElements', function() { |
+ this.loadHtml( |
+ '<div>' + |
+ '<p id="before">Before</p>' + |
+ '<p>Some text</p>' + |
+ '<input type="text"/>' + |
+ '<p>Some more text</p>' + |
+ '<input type="button"/>' + |
+ '</div>'); |
+ this.checkNavSequence( |
+ ['lineardom', 'smart', 'selection'], |
+ [ |
+ { 'command': 'forward', |
+ 'text': 'Some text', |
+ 'annotation': '' |
+ }, |
+ { 'command': 'forward', |
+ 'text': '', |
+ 'annotation': 'Edit text' |
+ }, |
+ { 'command': 'forward', |
+ 'text': 'Some more text', |
+ 'annotation': '' |
+ }, |
+ { 'command': 'forward', |
+ 'text': '', |
+ 'annotation': 'Button' |
+ } |
+ ]); |
+}); |
+ |
+ |
+/** |
+ * Test navigation of simple static HTML with some control elements inside a |
+ * fieldset to validate the text and annotation returned when doing |
+ * navigation. |
+ */ |
+TEST_F('CvoxNavigationManagerUnitTest', 'ControlElementsWithFieldset', function() { |
+ this.loadHtml( |
+ '<div>' + |
+ '<p id="before">Before</p>' + |
+ '<p>Some text</p>' + |
+ '<fieldset id="Fieldset">' + |
+ '<legend>This is a legend inside a fieldset</legend>' + |
+ '<input type="text"/>' + |
+ '<p>Some more text</p>' + |
+ '<input type="button"/>' + |
+ '</fieldset>' + |
+ '</div>'); |
+ this.checkNavSequence( |
+ ['lineardom', 'selection'], |
+ [ |
+ { 'command': 'forward', |
+ 'text': 'Some text', |
+ 'annotation': '' |
+ }, |
+ { 'command': 'forward', |
+ 'text': 'This is a legend inside a fieldset' |
+ }, |
+ { 'command': 'forward', |
+ 'annotation': 'Edit text' |
+ }, |
+ { 'command': 'forward', |
+ 'text': 'Some more text', |
+ 'annotation': '' |
+ }, |
+ { 'command': 'forward', |
+ 'text': '', |
+ 'annotation': 'Button' |
+ } |
+ ]); |
+}); |
+ |
+ |
+/** |
+ * Test skip to next/prev element navigation. |
+ */ |
+TEST_F('CvoxNavigationManagerUnitTest', 'SkipNavigation', function() { |
+ this.loadHtml( |
+ '<div>' + |
+ '<p id="before">Before</p>' + |
+ '<h1> <i>FirstHeading</i> </h1>' + |
+ 'asdf' + |
+ '<p>Here is some text</p>' + |
+ 'asdf' + |
+ '<h2>SecondHeading</h2>' + |
+ '</div>'); |
+ |
+ cvox.ChromeVoxUserCommands.enableCommandDispatchingToPage = false; |
+ |
+ this.checkNavSequence( |
+ ['smart'], |
+ [ |
+ { 'command': 'forward', |
+ 'text': 'FirstHeading', |
+ 'annotation': 'Heading 1' |
+ }, |
+ { 'command': 'forward', |
+ 'text': 'asdf', |
+ 'annotation': '' |
+ }, |
+ { 'command': 'nextHeading', |
+ 'text': 'SecondHeading', |
+ 'annotation': 'Heading 2' |
+ }, |
+ { 'command': 'previousHeading', |
+ 'text': 'FirstHeading', |
+ 'annotation': 'Heading 1' |
+ } |
+ ]); |
+}); |
+ |
+ |
+/** |
+ * Test finding the next heading. |
+ */ |
+TEST_F('CvoxNavigationManagerUnitTest', 'FindNextHeading', function() { |
+ this.loadHtml( |
+ '<div>' + |
+ '<p id="before">Before</p>' + |
+ 'Some text.' + |
+ '<h1>A heading</h1>' + |
+ 'More text after the heading.' + |
+ '<p>Even more text after the heading.</p>' + |
+ '<p id="after">After</p>' + |
+ '</div>'); |
+ cvox.ChromeVox.navigationManager.ignoreIframesNoMatterWhat(); |
+ this.waitForCalm(cvox.ChromeVoxTester.setStrategy, 'lineardom'); |
+ this.waitForCalm(cvox.ChromeVoxTester.syncToFirstNode); |
+ |
+ this.waitForCalm(this.userCommand, 'forward'); |
+ this.waitForCalm(this.assertSpoken, 'Some text.'); |
+ |
+ this.waitForCalm(this.userCommand, 'nextHeading'); |
+ this.waitForCalm(this.assertSpoken, 'A heading Heading 1'); |
+ |
+ this.waitForCalm(this.userCommand, 'nextHeading'); |
+ this.waitForCalm(this.assertSpoken, |
+ 'Wrapped to top A heading Heading 1'); |
+ |
+ this.waitForCalm(this.userCommand, 'forward'); |
+ this.waitForCalm(this.assertSpoken, 'More text after the heading.'); |
+ |
+ this.waitForCalm(this.userCommand, 'previousHeading'); |
+ this.waitForCalm(this.assertSpoken, 'A heading Heading 1'); |
+ |
+ this.waitForCalm(this.userCommand, 'previousHeading'); |
+ this.waitForCalm(this.assertSpoken, |
+ 'Wrapped to bottom A heading Heading 1'); |
+ |
+ this.waitForCalm(this.userCommand, 'backward'); |
+ this.waitForCalm(this.assertSpoken, 'Some text.'); |
+ |
+ this.waitForCalm(cvox.ChromeVoxTester.setStrategy, 'smart'); |
+ this.waitForCalm(cvox.ChromeVoxTester.syncToFirstNode); |
+ |
+ this.waitForCalm(this.userCommand, 'forward'); |
+ this.waitForCalm(this.assertSpoken, 'Some text.'); |
+ |
+ this.waitForCalm(this.userCommand, 'nextHeading'); |
+ this.waitForCalm(this.assertSpoken, 'A heading Heading 1'); |
+ |
+ this.waitForCalm(this.userCommand, 'nextHeading'); |
+ this.waitForCalm(this.assertSpoken, |
+ 'Wrapped to top A heading Heading 1'); |
+ |
+ this.waitForCalm(this.userCommand, 'forward'); |
+ this.waitForCalm(this.assertSpoken, 'More text after the heading.'); |
+ |
+ this.waitForCalm(this.userCommand, 'previousHeading'); |
+ this.waitForCalm(this.assertSpoken, 'A heading Heading 1'); |
+ |
+ this.waitForCalm(this.userCommand, 'previousHeading'); |
+ this.waitForCalm(this.assertSpoken, |
+ 'Wrapped to bottom A heading Heading 1'); |
+ |
+ this.waitForCalm(this.userCommand, 'backward'); |
+ this.waitForCalm(this.assertSpoken, 'Some text.'); |
+ |
+ this.waitForCalm(testDone); |
+}); |
+ |
+ |
+/** |
+ * Test navigation of HTML and ARIA lists. |
+ */ |
+TEST_F('CvoxNavigationManagerUnitTest', 'ListLinearNav', function() { |
+ this.loadHtml( |
+ '<div>' + |
+ '<p id="before">Before</p>' + |
+ '<ul>' + |
+ '<li>First' + |
+ '<li><div>Second</div></li>' + |
+ '<div role="listitem"><a href="#">Third</a></div>' + |
+ '</ul>' + |
+ '<div role="list">' + |
+ '<div role="listitem"><a href="#">First</a></div>' + |
+ '<li>Second' + |
+ '</div>' + |
+ '<p id="after">After</p>' + |
+ '</div>'); |
+ this.checkNavSequence( |
+ ['lineardom'], |
+ [ |
+ { 'command': 'forward', |
+ 'context': 'List with 3 items', |
+ 'text': 'First', |
+ 'annotation': 'List item' |
+ }, |
+ { 'command': 'forward', |
+ 'context': '', |
+ 'text': 'Second', |
+ 'annotation': 'List item' |
+ }, |
+ { 'command': 'forward', |
+ 'context': '', |
+ 'text': 'Third', |
+ 'annotation': 'Internal link List item' |
+ }, |
+ { 'command': 'forward', |
+ 'context': 'List with 2 items', |
+ 'text': 'First', |
+ 'annotation': 'Internal link List item' |
+ }, |
+ { 'command': 'forward', |
+ 'context': '', |
+ 'text': 'Second', |
+ 'annotation': 'List item' |
+ }, |
+ { 'command': 'forward', |
+ 'context': '', |
+ 'text': 'After', |
+ 'annotation': '' |
+ }, |
+ { 'command': 'backward', |
+ 'context': 'List with 2 items', |
+ 'text': 'Second', |
+ 'annotation': 'List item' |
+ }, |
+ { 'command': 'backward', |
+ 'context': '', |
+ 'text': 'First', |
+ 'annotation': 'Internal link List item' |
+ } |
+ ]); |
+}); |
+ |
+ |
+/** |
+ * Test navigation of HTML and ARIA lists. |
+ */ |
+TEST_F('CvoxNavigationManagerUnitTest', 'ListSmartNav', function() { |
+ this.loadHtml( |
+ '<div>' + |
+ '<p id="before">Before</p>' + |
+ '<ul>' + |
+ '<li>First</li>' + |
+ '<li>Second</li>' + |
+ '<div role="listitem">Third</div>' + |
+ '</ul>' + |
+ '<div role="list">' + |
+ '<div role="listitem"><a href="#">First</a></div>' + |
+ '<li>Second' + |
+ '</div>' + |
+ '<p id="after">After</p>' + |
+ '</div>'); |
+ this.checkNavSequence( |
+ ['smart'], |
+ [ |
+ { 'command': 'forward', |
+ 'context': 'List with 3 items', |
+ 'text': 'First', |
+ 'annotation': 'List item' |
+ }, |
+ { 'text': 'Second', |
+ 'annotation': 'List item' |
+ }, |
+ { 'text': 'Third', |
+ 'annotation': 'List item' |
+ }, |
+ { 'command': 'forward', |
+ 'context': 'List with 2 items', |
+ 'text': 'First', |
+ 'annotation': 'Internal link List item' |
+ }, |
+ { 'text': 'Second', |
+ 'annotation': 'List item' |
+ }, |
+ { 'command': 'forward', |
+ 'text': 'After' |
+ }, |
+ { 'command': 'backward', |
+ 'context': 'List with 2 items', |
+ 'text': 'First', |
+ 'annotation': 'Internal link List item' |
+ }, |
+ { 'text': 'Second', |
+ 'annotation': 'List item' |
+ } |
+ ]); |
+}); |
+ |
+ |
+/** |
+ * Test smart navigation of link collections. |
+ */ |
+TEST_F('CvoxNavigationManagerUnitTest', 'LinkCollectionSmartNav', function() { |
+ this.loadHtml( |
+ '<div>' + |
+ '<p id="before">Before</p>' + |
+ '<p>' + |
+ '<a href="#">First</a>' + |
+ '<a href="#">Second</a>' + |
+ '<a href="#">Third</a>' + |
+ '</p>' + |
+ '<p>' + |
+ '<a href="#">First</a> and' + |
+ '<a href="#">Second</a> and' + |
+ '<a href="#">Third</a>' + |
+ '</p>' + |
+ '<p id="after">After</p>' + |
+ '</div>'); |
+ |
+ // TODO: The way we implemented this feature breaks the i18n_check. |
+ if (cvox.I18nCheck) |
+ return; |
+ |
+ this.checkNavSequence( |
+ ['smart'], |
+ [ |
+ { 'command': 'forward', |
+ 'annotation': 'Link collection with 3 items' |
+ }, |
+ { 'text': 'First' |
+ }, |
+ { 'text': 'Second' |
+ }, |
+ { 'text': 'Third' |
+ }, |
+ { 'command': 'forward', |
+ 'text': 'First', |
+ 'annotation': 'Internal link' |
+ }, |
+ { 'text': 'and' |
+ }, |
+ { 'text': 'Second', |
+ 'annotation': 'Internal link' |
+ }, |
+ { 'text': 'and' |
+ }, |
+ { 'text': 'Third', |
+ 'annotation': 'Internal link' |
+ }, |
+ { 'command': 'forward', |
+ 'text': 'After' |
+ } |
+ ]); |
+}); |
+ |
+ |
+/** |
+ * Test navigation of a control followed by its label. |
+ */ |
+TEST_F('CvoxNavigationManagerUnitTest', 'ControlThenLabel', function() { |
+ this.loadHtml( |
+ '<div>' + |
+ '<p id="before">Before</p>' + |
+ '<input type="checkbox" id="aaa">' + |
+ '<label for="aaa">Alpha</label>' + |
+ '<p id="after">After</p>' + |
+ '</div>'); |
+ this.checkNavSequence( |
+ ['lineardom'], |
+ [ |
+ { 'command': 'forward', |
+ 'text': 'Alpha', |
+ 'annotation': 'Check box not checked' |
+ }, |
+ { 'command': 'forward', |
+ 'text': 'After', |
+ 'annotation': '' |
+ }, |
+ { 'command': 'backward', |
+ 'text': 'Alpha', |
+ 'annotation': 'Check box not checked' |
+ }, |
+ { 'command': 'backward', |
+ 'text': 'Before', |
+ 'annotation': '' |
+ } |
+ ]); |
+}); |
+ |
+ |
+/** |
+ * Test navigation of a label followed by its control. |
+ */ |
+TEST_F('CvoxNavigationManagerUnitTest', 'LabelThenControl', function() { |
+ this.loadHtml( |
+ '<div>' + |
+ '<p id="before">Before</p>' + |
+ '<label for="bbb">Beta</label>' + |
+ '<input type="checkbox" id="bbb">' + |
+ '<p id="after">After</p>' + |
+ '</div>'); |
+ this.checkNavSequence( |
+ ['lineardom'], |
+ [ |
+ { 'command': 'forward', |
+ 'text': 'Beta', |
+ 'annotation': 'Check box not checked' |
+ }, |
+ { 'command': 'forward', |
+ 'text': 'After', |
+ 'annotation': '' |
+ }, |
+ { 'command': 'backward', |
+ 'text': 'Beta', |
+ 'annotation': 'Check box not checked' |
+ }, |
+ { 'command': 'backward', |
+ 'text': 'Before', |
+ 'annotation': '' |
+ } |
+ ]); |
+}); |
+ |
+ |
+/** |
+ * Test navigation of a control inside a label element (yes, this is |
+ * valid HTML and should be supported). |
+ */ |
+TEST_F('CvoxNavigationManagerUnitTest', 'ControlInsideLabel', function() { |
+ this.loadHtml( |
+ '<div>' + |
+ '<p id="before">Before</p>' + |
+ '<label>' + |
+ 'First name:' + |
+ '<input type="text" name="name" value="Linus">' + |
+ '</label>' + |
+ '<p>' + |
+ '<label>Remember me' + |
+ '<input type="checkbox" id="remember" />' + |
+ '</label>' + |
+ '</p>' + |
+ '<p id="after">After</p>' + |
+ '</div>'); |
+ this.checkNavSequence( |
+ ['lineardom'], |
+ [ |
+ { 'command': 'forward', |
+ 'text': 'First name:', |
+ 'userValue': 'Linus', |
+ 'annotation': 'Edit text' |
+ }, |
+ { 'command': 'forward', |
+ 'text': 'Remember me', |
+ 'annotation': 'Check box not checked' |
+ }, |
+ { 'command': 'forward', |
+ 'text': 'After', |
+ 'annotation': '' |
+ }, |
+ { 'command': 'backward', |
+ 'text': 'Remember me', |
+ 'annotation': 'Check box not checked' |
+ }, |
+ { 'command': 'backward', |
+ 'text': 'First name:', |
+ 'userValue': 'Linus', |
+ 'annotation': 'Edit text' |
+ }, |
+ { 'command': 'backward', |
+ 'text': 'Before', |
+ 'annotation': '' |
+ } |
+ ]); |
+}); |
+ |
+ |
+/** |
+ * Test navigation of two controls inside a single label element - this |
+ * is nonstandard but we should make sure we don't totally fail! |
+ */ |
+TEST_F('CvoxNavigationManagerUnitTest', 'TwoControlsInsideLabel', function() { |
+ this.loadHtml( |
+ '<div>' + |
+ '<p id="before">Before</p>' + |
+ '<label>' + |
+ 'LabelText' + |
+ '<input type="text" value="Value">' + |
+ '<input type="checkbox">' + |
+ '</label>' + |
+ '<p id="after">After</p>' + |
+ '</div>'); |
+ this.checkNavSequence( |
+ ['lineardom'], |
+ [ |
+ { 'command': 'forward', |
+ 'text': 'LabelText', |
+ 'userValue': 'Value', |
+ 'annotation': 'Edit text' |
+ }, |
+ { 'command': 'forward', |
+ 'text': 'LabelText', |
+ 'annotation': 'Check box not checked' |
+ }, |
+ { 'command': 'forward', |
+ 'text': 'After', |
+ 'annotation': '' |
+ }, |
+ { 'command': 'backward', |
+ 'text': 'LabelText', |
+ 'annotation': 'Check box not checked' |
+ }, |
+ { 'command': 'backward', |
+ 'text': 'LabelText', |
+ 'userValue': 'Value', |
+ 'annotation': 'Edit text' |
+ }, |
+ { 'command': 'backward', |
+ 'text': 'Before', |
+ 'annotation': '' |
+ } |
+ ]); |
+}); |
+ |
+ |
+/** |
+ * Test invalid labels - if a label doesn't point to a control, it |
+ * should just be read as if it wasn't a label. |
+ */ |
+TEST_F('CvoxNavigationManagerUnitTest', 'InvalidLabels', function() { |
+ this.loadHtml( |
+ '<div>' + |
+ '<p id="before">Before</p>' + |
+ '<label for="xxx">Label for nonexistent control</label>' + |
+ '<label>Label with no associated control</label>' + |
+ '<p id="after">After</p>' + |
+ '</div>'); |
+ this.checkNavSequence( |
+ ['lineardom'], |
+ [ |
+ { 'command': 'forward', |
+ 'text': 'Label for nonexistent control', |
+ 'annotation': '' |
+ }, |
+ { 'command': 'forward', |
+ 'text': 'Label with no associated control', |
+ 'annotation': '' |
+ }, |
+ { 'command': 'forward', |
+ 'text': 'After', |
+ 'annotation': '' |
+ }, |
+ { 'command': 'backward', |
+ 'text': 'Label with no associated control', |
+ 'annotation': '' |
+ }, |
+ { 'command': 'backward', |
+ 'text': 'Label for nonexistent control', |
+ 'annotation': '' |
+ }, |
+ { 'command': 'backward', |
+ 'text': 'Before', |
+ 'annotation': '' |
+ } |
+ ]); |
+}); |
+ |
+ |
+/** |
+ * Test scrollbar value readout. |
+ */ |
+TEST_F('CvoxNavigationManagerUnitTest', 'Scrollbar', function() { |
+ this.loadHtml( |
+ '<div>' + |
+ '<p id="before">Before</p>' + |
+ '<div id="progress1" role="progressbar" class="range"' + |
+ 'aria-valuemin="0" aria-valuenow="1" aria-valuemax="5">' + |
+ '<div>[== ]</div>' + |
+ '</div>' + |
+ '<p id="after">After</p>' + |
+ '</div>'); |
+ this.checkNavSequence( |
+ ['lineardom', 'smart'], |
+ [ |
+ { 'command': 'forward', |
+ 'text': '', |
+ 'annotation': 'Progress bar 20%' |
+ } |
+ ]); |
+}); |
+ |
+ |
+/** |
+ * Test ARIA listbox where the whole box gets focus. |
+ */ |
+TEST_F('CvoxNavigationManagerUnitTest', 'AriaListboxActiveDescendant', function() { |
+ this.loadHtml( |
+ '<div>' + |
+ '<p id="before">Before</p>' + |
+ '<select size="3">' + |
+ '<option selected>Red</option>' + |
+ '<option>Yellow</option>' + |
+ '<option>Green</option>' + |
+ '</select>' + |
+ '<p id="middle1">Middle 1</p>' + |
+ '<div id="listbox" role="listbox" tabindex="0" aria-activedescendant="red">' + |
+ '<div id="red" aria-selected="true" role="option">Red</div>' + |
+ '<div id="yellow" role="option">Yellow</div>' + |
+ '<div id="green" role="option">Green</div>' + |
+ '</div>' + |
+ '<p id="middle2">Middle 2</p>' + |
+ '<div id="listbox2" role="listbox" tabindex="0"' + |
+ 'aria-activedescendant="red2">' + |
+ '<div id="red2" aria-selected="true" role="option"' + |
+ 'aria-posinset="10" aria-setsize="30">Red</div>' + |
+ '<div id="yellow2" role="option"' + |
+ 'aria-posinset="20" aria-setsize="30">Yellow</div>' + |
+ '<div id="green2" role="option"' + |
+ 'aria-posinset="30" aria-setsize="30">Green</div>' + |
+ '</div>' + |
+ '<p id="after">After</p>' + |
+ '</div>'); |
+ this.checkNavSequence( |
+ ['lineardom', 'smart'], |
+ [ |
+ { 'command': 'forward', |
+ 'userValue': 'Red', |
+ 'annotation': 'Combo box 1 of 3' |
+ }, |
+ { 'command': 'forward', |
+ 'text': 'Middle 1', |
+ 'annotation': '' |
+ }, |
+ { 'command': 'forward', |
+ 'userValue': 'Red', |
+ 'annotation': 'List box Selected 1 of 3' |
+ }, |
+ { 'command': 'forward', |
+ 'text': 'Middle 2', |
+ 'annotation': '' |
+ }, |
+ { 'command': 'forward', |
+ 'userValue': 'Red', |
+ 'annotation': 'List box Selected 10 of 30' |
+ }, |
+ { 'command': 'forward', |
+ 'text': 'After', |
+ 'annotation': '' |
+ } |
+ ]); |
+}); |
+ |
+ |
+/** |
+ * Test ARIA listbox where each option gets focus. |
+ */ |
+TEST_F('CvoxNavigationManagerUnitTest', 'AriaListboxOption', function() { |
+ this.loadHtml( |
+ '<div>' + |
+ '<p id="before">Before</p>' + |
+ '<div id="listbox" role="listbox">' + |
+ '<div id="red" tabindex="0" aria-selected="true" role="option">Red</div>' + |
+ '<div id="yellow" tabindex="-1" role="option">Yellow</div>' + |
+ '<div id="green" tabindex="-1" role="option">Green</div>' + |
+ '</div>' + |
+ '<p id="before">Middle</p>' + |
+ '<div id="listbox2" role="listbox">' + |
+ '<div id="red2" tabindex="0" aria-selected="true" role="option"' + |
+ 'aria-posinset="2" aria-setsize="6">Red</div>' + |
+ '<div id="yellow2" tabindex="-1" role="option"' + |
+ 'aria-posinset="4" aria-setsize="6">Yellow</div>' + |
+ '<div id="green2" tabindex="-1" role="option"' + |
+ 'aria-posinset="6" aria-setsize="6">Green</div>' + |
+ '</div>' + |
+ '<p id="after">After</p>' + |
+ '</div>'); |
+ this.checkNavSequence( |
+ ['lineardom', 'smart'], |
+ [ |
+ { 'command': 'forward', |
+ 'context': 'List box', |
+ 'text': 'Red', |
+ 'annotation': 'Selected 1 of 3' |
+ }, |
+ { 'command': 'forward', |
+ 'text': 'Yellow', |
+ 'annotation': '2 of 3' |
+ }, |
+ { 'command': 'forward', |
+ 'text': 'Green', |
+ 'annotation': '3 of 3' |
+ }, |
+ { 'command': 'forward', |
+ 'text': 'Middle', |
+ 'annotation': '' |
+ }, |
+ { 'command': 'forward', |
+ 'context': 'List box', |
+ 'text': 'Red', |
+ 'annotation': 'Selected 2 of 6' |
+ }, |
+ { 'command': 'forward', |
+ 'text': 'Yellow', |
+ 'annotation': '4 of 6' |
+ }, |
+ { 'command': 'forward', |
+ 'text': 'Green', |
+ 'annotation': '6 of 6' |
+ }, |
+ { 'command': 'forward', |
+ 'text': 'After', |
+ 'annotation': '' |
+ }, |
+ { 'command': 'backward', |
+ 'context': 'List box', |
+ 'text': 'Green', |
+ 'annotation': '6 of 6' |
+ }, |
+ { 'command': 'backward', |
+ 'text': 'Yellow', |
+ 'annotation': '4 of 6' |
+ }, |
+ { 'command': 'backward', |
+ 'text': 'Red', |
+ 'annotation': 'Selected 2 of 6' |
+ }, |
+ { 'command': 'backward', |
+ 'text': 'Middle', |
+ 'annotation': '' |
+ }, |
+ { 'command': 'backward', |
+ 'context': 'List box', |
+ 'text': 'Green', |
+ 'annotation': '3 of 3' |
+ }, |
+ { 'command': 'backward', |
+ 'text': 'Yellow', |
+ 'annotation': '2 of 3' |
+ }, |
+ { 'command': 'backward', |
+ 'text': 'Red', |
+ 'annotation': 'Selected 1 of 3' |
+ } |
+ ]); |
+}); |
+ |
+ |
+/** |
+ * Test ARIA listbox where each option gets focus and the outer container has |
+ * tabindex="-1" |
+ */ |
+TEST_F('CvoxNavigationManagerUnitTest', 'AriaListboxOptionOuterFocus', function() { |
+ this.loadHtml( |
+ '<div>' + |
+ '<p id="before">Before</p>' + |
+ '<div id="listbox" role="listbox" tabindex="-1">' + |
+ '<div id="red" tabindex="0" aria-selected="true" role="option">Red</div>' + |
+ '<div id="yellow" tabindex="-1" role="option">Yellow</div>' + |
+ '<div id="green" tabindex="-1" role="option">Green</div>' + |
+ '</div>' + |
+ '<p id="after">After</p>' + |
+ '</div>'); |
+ this.checkNavSequence( |
+ ['smart'], |
+ [ |
+ { 'command': 'forward', |
+ 'context': 'List box', |
+ 'text': 'Red', |
+ 'annotation': 'Selected 1 of 3' |
+ }, |
+ { 'command': 'forward', |
+ 'text': 'Yellow', |
+ 'annotation': '2 of 3' |
+ }, |
+ { 'command': 'forward', |
+ 'text': 'Green', |
+ 'annotation': '3 of 3' |
+ }, |
+ { 'command': 'forward', |
+ 'text': 'After', |
+ 'annotation': '' |
+ } |
+ ]); |
+}); |
+ |
+ |
+ |
+/** |
+ * Test ARIA menus. |
+ */ |
+TEST_F('CvoxNavigationManagerUnitTest', 'AriaMenus', function() { |
+ this.loadHtml( |
+ '<div>' + |
+ '<p id="before">Before</p>' + |
+ '<div role="menubar">' + |
+ '<div role="menuitem">File</div>' + |
+ '<div role="menuitem">Edit</div>' + |
+ '</div>' + |
+ '<div role="menu">' + |
+ '<div role="menuitem">New</div>' + |
+ '<div role="menuitem">Open</div>' + |
+ '</div>' + |
+ '<p id="after">After</p>' + |
+ '</div>'); |
+ this.checkNavSequence( |
+ ['lineardom', 'smart'], |
+ [ |
+ { 'command': 'forward', |
+ 'context': 'Menu bar', |
+ 'text': 'File', |
+ 'annotation': 'Menu' |
+ }, |
+ { 'command': 'forward', |
+ 'text': 'Edit', |
+ 'annotation': 'Menu' |
+ }, |
+ { 'command': 'forward', |
+ 'text': 'New', |
+ 'annotation': 'Menu item 1 of 2' |
+ }, |
+ { 'command': 'forward', |
+ 'text': 'Open', |
+ 'annotation': 'Menu item 2 of 2' |
+ } |
+ ]); |
+}); |
+ |
+/** |
+ * Test left and right navigation at the group level. |
+ */ |
+TEST_F('CvoxNavigationManagerUnitTest', 'LeftRightGroupNavigation', function() { |
+ this.loadHtml( |
+ '<div>' + |
+ '<p id="before">Before</p>' + |
+ '<h1>Alphabetical Fruits</h1>' + |
+ 'Apple <a href=\'#\'>Banana</a> Cranberry <a href=\'#\'>Date</a> Eggplant' + |
+ '<p id="after">After</p>' + |
+ '</div>'); |
+ this.checkNavSequence( |
+ ['smart'], |
+ [ |
+ { 'command': 'forward', |
+ 'text': 'Alphabetical Fruits', |
+ 'annotation': 'Heading 1' |
+ }, |
+ { 'command': 'right', |
+ 'text': 'Apple' |
+ }, |
+ { 'command': 'right', |
+ 'text': 'Banana', |
+ 'annotation': 'Internal link' |
+ }, |
+ { 'command': 'left', |
+ 'text': 'Apple' |
+ }, |
+ { 'command': 'forward', |
+ 'text': 'Banana', |
+ 'annotation': 'Internal link' |
+ }, |
+ { 'command': 'right', |
+ 'text': 'Cranberry' |
+ }, |
+ { 'command': 'backward', |
+ 'text': 'Banana', |
+ 'annotation': 'Internal link' |
+ } |
+ ]); |
+}); |
+ |
+/** |
+ * Test left and right navigation at the sentence level. |
+ */ |
+TEST_F('CvoxNavigationManagerUnitTest', 'LeftRightSentenceNavigation', function() { |
+ this.loadHtml( |
+ '<div>' + |
+ '<p id="before">Before</p>' + |
+ '<h1>Alphabetical Fruits</h1>' + |
+ 'Apples are delicious. An apple a day keeps the doctor away.' + |
+ '<a href="#">Banana</a> Cranberries are delicious. ' + |
+ 'A cranberry a day keeps the doctor away.' + |
+ '<p id="after">After</p>' + |
+ '</div>'); |
+ |
+ // TODO(dtseng): This is here until we can remove or fix all tests relating to |
+ // sentences. This test framework holds state across runs, so only this |
+ // expression is needed. |
+ cvox.NavigationShifter.allowSentence = true; |
+ this.checkNavSequence( |
+ ['lineardom'], |
+ [ |
+ { 'command': 'forward', |
+ 'text': 'Alphabetical Fruits', |
+ 'annotation': 'Heading 1' |
+ }, |
+ { 'command': 'forward', |
+ 'text': 'Apples are delicious. An apple a day keeps the doctor away.' |
+ }, |
+ { 'command': 'right', |
+ 'text': 'An apple a day keeps the doctor away.' |
+ }, |
+ { 'command': 'right', |
+ 'text': 'Banana', |
+ 'annotation': 'Internal link' |
+ }, |
+ { 'command': 'right', |
+ 'text': 'Cranberries are delicious.' |
+ }, |
+ { 'command': 'right', |
+ 'text': 'A cranberry a day keeps the doctor away.' |
+ }, |
+ { 'command': 'left', |
+ 'text': 'Cranberries are delicious.' |
+ }, |
+ { 'command': 'left', |
+ 'text': 'Banana', |
+ 'annotation': 'Internal link' |
+ }, |
+ { 'command': 'forward', |
+ 'text': 'Cranberries are delicious. A cranberry a day keeps the doctor away.' |
+ } |
+ ]); |
+}); |
+ |
+/** |
+ * Test left and right navigation at the word level. |
+ */ |
+TEST_F('CvoxNavigationManagerUnitTest', 'LeftRightWordNavigation', function() { |
+ this.loadHtml( |
+ '<div>' + |
+ '<p id="before">Before</p>' + |
+ '<h1>Alphabetical Fruits</h1>' + |
+ 'Apples are delicious. An apple a day keeps the doctor away.' + |
+ '<h2><a href="#">Banana</a></h2> Cranberries are delicious. ' + |
+ 'A cranberry a day keeps the doctor away.' + |
+ '<p id="after">After</p>' + |
+ '</div>'); |
+ this.checkNavSequence( |
+ ['selection'], |
+ [ |
+ { 'command': 'forward', |
+ 'text': 'Alphabetical Fruits', |
+ 'annotation': 'Heading 1' |
+ }, |
+ { 'command': 'forward', |
+ 'text': 'Apples are delicious.' |
+ }, |
+ { 'command': 'nextGranularity' }, |
+ { 'command': 'right', |
+ 'text': 'are' |
+ }, |
+ { 'command': 'right', |
+ 'text': 'delicious.' |
+ }, |
+ { 'command': 'right', |
+ 'text': 'An' |
+ }, |
+ { 'command': 'right', |
+ 'text': 'apple' |
+ }, |
+ { 'command': 'right', |
+ 'text': 'a' |
+ }, |
+ { 'command': 'left', |
+ 'text': 'apple' |
+ }, |
+ { 'command': 'left', |
+ 'text': 'An' |
+ }, |
+ { 'command': 'forward', |
+ 'text': 'Banana' |
+ }, |
+ { 'command': 'right', |
+ 'text': 'Cranberries' |
+ }, |
+ { 'command': 'previousGranularity' }, |
+ { 'command': 'previousGranularity', |
+ 'text': 'Cranberries are delicious. A cranberry a day keeps the doctor away.' |
+ } |
+ ]); |
+}); |
+ |
+/** |
+ * Test left and right navigation at the character level. |
+ */ |
+TEST_F('CvoxNavigationManagerUnitTest', 'LeftRightCharacterNavigation', function() { |
+ this.loadHtml( |
+ '<div>' + |
+ '<p id="before">Before</p>' + |
+ '<h1>Alphabetical Fruits</h1>' + |
+ 'Apples are delicious. An apple a day keeps the doctor away.' + |
+ '<a href="#">Banana</a> Cranberries are delicious. ' + |
+ 'A cranberry a day keeps ' + |
+ 'the doctor away.' + |
+ '<p id="after">After</p>' + |
+ '</div>'); |
+ this.checkNavSequence( |
+ ['selection'], |
+ [ |
+ { 'command': 'forward', |
+ 'text': 'Alphabetical Fruits', |
+ 'annotation': 'Heading 1' |
+ }, |
+ { 'command': 'forward', |
+ 'text': 'Apples are delicious.' |
+ }, |
+ { 'command': 'nextGranularity' }, |
+ { 'command': 'nextGranularity', |
+ 'text': 'Apples' |
+ }, |
+ { 'command': 'right', |
+ 'text': 'p' |
+ }, |
+ { 'command': 'right', |
+ 'text': 'p' |
+ }, |
+ { 'command': 'right', |
+ 'text': 'l' |
+ }, |
+ { 'command': 'right', |
+ 'text': 'e' |
+ }, |
+ { 'command': 'right', |
+ 'text': 's' |
+ }, |
+ { 'command': 'right', |
+ 'text': ' ' |
+ }, |
+ { 'command': 'right', |
+ 'text': 'a' |
+ }, |
+ { 'command': 'right', |
+ 'text': 'r' |
+ }, |
+ { 'command': 'right', |
+ 'text': 'e' |
+ }, |
+ { 'command': 'left', |
+ 'text': 'r' |
+ }, |
+ { 'command': 'left', |
+ 'text': 'a' |
+ }, |
+ { 'command': 'forward', |
+ 'text': 'delicious.' |
+ }, |
+ { 'command': 'right', |
+ 'text': 'e' |
+ }, |
+ { 'command': 'nextGranularity' }, |
+ { 'command': 'previousGranularity', |
+ 'text': 'elicious.' |
+ } |
+ ]); |
+}); |
+ |
+/** |
+ * Test whether fullyDescribe works |
+ */ |
+TEST_F('CvoxNavigationManagerUnitTest', 'FullyDescribe', function() { |
+ this.loadHtml( |
+ '<div>' + |
+ '<p id="before">Before</p>' + |
+ '<ul>' + |
+ '<li>A</li>' + |
+ '<ul>' + |
+ '<li>1</li>' + |
+ '<li>2</li>' + |
+ '</ul>' + |
+ '</ul>' + |
+ '<p id="after">After</p>' + |
+ '</div>'); |
+ this.checkNavSequence( |
+ ['lineardom'], |
+ [ |
+ { 'command': 'forward', |
+ 'context': 'List with 1 items', |
+ 'text': 'A', |
+ 'annotation': 'List item' |
+ }, |
+ { 'command': 'forward', |
+ 'context': 'List with 2 items', |
+ 'text': '1', |
+ 'annotation': 'List item' |
+ }, |
+ { 'command': 'forward', |
+ 'context': '', |
+ 'text': '2', |
+ 'annotation': 'List item' |
+ }, |
+ { 'command': 'fullyDescribe', |
+ 'context': '', |
+ 'text': '2', |
+ 'annotation': 'List item' |
+ } |
+ ]); |
+ this.waitForCalm(this.assertSpoken, |
+ 'List with 1 items A List item ' + |
+ 'List with 2 items 1 List item 2 List item ' + |
+ 'List with 1 items List with 2 items 2 List item'); |
+}); |
+ |
+ |
+/** |
+ * Test continuous reading mode. |
+ */ |
+TEST_F('CvoxNavigationManagerUnitTest', 'ContinuousReading', function() { |
+ this.loadHtml( |
+ '<div id="continuousTest">' + |
+ '<p id="before">Before</p>' + |
+ '<div>Some text.</div>' + |
+ '<div>First</div>' + |
+ '<div>Second</div>' + |
+ '<a href="#">Third</a>' + |
+ '<p id="after">After</p>' + |
+ '</div>'); |
+ cvox.ChromeVox.navigationManager.ignoreIframesNoMatterWhat(); |
+ this.waitForCalm(cvox.ChromeVoxTester.setStrategy, 'lineardom') |
+ .waitForCalm(cvox.ChromeVoxTester.syncToFirstNode) |
+ .waitForCalm(cvox.ChromeVoxTester.readFromHere) |
+ .waitForCalm(this.assertSpoken, |
+ 'Before Some text. First Second Third Internal link After') |
+ .waitForCalm(testDone); |
+}); |
+ |
+ |
+/** |
+ * Test HTML5 semantic elements |
+ */ |
+TEST_F('CvoxNavigationManagerUnitTest', 'SemanticElts', function() { |
+ this.loadHtml( |
+ '<p id="before">Before</p>' + |
+ '<article>' + |
+ '<header>' + |
+ '<time datetime="2009-10-22" pubdate="">October 22, 2009</time>' + |
+ '<h1>' + |
+ '<a href="#">Travel day</a>' + |
+ '</h1>' + |
+ '</header>' + |
+ '<p>Blah blah blah</p>' + |
+ '</article>' + |
+ '<div>' + |
+ '<p>October 17, 2009</p>' + |
+ '<h2>' + |
+ '<a href="#">I am going to Prague!</a>' + |
+ '</h2>' + |
+ '<p>More blah blah blah</p>' + |
+ '</div>' + |
+ '<p id="after">After</p>'); |
+ |
+ this.checkNavSequence( |
+ ['smart'], |
+ [ |
+ { 'command': 'forward', |
+ 'text': 'October 22, 2009', |
+ 'context': 'Article Header', |
+ 'annotation': 'Time' |
+ }, |
+ { |
+ 'text': 'Travel day', |
+ 'context': '', |
+ 'annotation': 'Internal link Heading 1' |
+ }, |
+ { |
+ 'text': 'Blah blah blah' |
+ }, |
+ { 'command': 'forward', |
+ 'text': 'October 17, 2009' |
+ }, |
+ { |
+ 'command': 'forward', |
+ 'text': 'I am going to Prague!', |
+ 'context': '', |
+ 'annotation': 'Internal link Heading 2' |
+ }, |
+ { |
+ 'command': 'forward', |
+ 'text': 'More blah blah blah' |
+ } |
+ ]); |
+}); |
+ |
+ |
+/** |
+ * Test aria-haspopup |
+ */ |
+TEST_F('CvoxNavigationManagerUnitTest', 'AriaHasPopup', function() { |
+ this.loadHtml( |
+ '<div>' + |
+ '<p id="before">Before</p>' + |
+ '<button>Alpha</button>' + |
+ '<button aria-haspopup="true">Bravo</button>' + |
+ '<div role="button" tabindex="0">Charlie</div>' + |
+ '<div role="button" aria-haspopup="true" tabindex="0">Delta</div>' + |
+ '<div role="menuitem" tabindex="0">Echo</div>' + |
+ '<div role="menuitem" aria-haspopup="true" tabindex="0">Foxtrot</div>' + |
+ '</div>'); |
+ this.checkNavSequence( |
+ ['lineardom', 'smart'], |
+ [ |
+ { 'command': 'forward', |
+ 'text': 'Alpha', |
+ 'annotation': 'Button' |
+ }, |
+ { 'command': 'forward', |
+ 'text': 'Bravo', |
+ 'annotation': 'Pop-up button' |
+ }, |
+ { 'command': 'forward', |
+ 'text': 'Charlie', |
+ 'annotation': 'Button' |
+ }, |
+ { 'command': 'forward', |
+ 'text': 'Delta', |
+ 'annotation': 'Pop-up button' |
+ }, |
+ { 'command': 'forward', |
+ 'text': 'Echo', |
+ 'annotation': 'Menu item' |
+ }, |
+ { 'command': 'forward', |
+ 'text': 'Foxtrot', |
+ 'annotation': 'Menu item with submenu' |
+ } |
+ ]); |
+}); |
+ |
+ |
+/** Test Aria Math Roles. */ |
+TEST_F('CvoxNavigationManagerUnitTest', 'AriaMathRoles', function() { |
+ this.loadHtml( |
+ '<p><div role="math"' + |
+ 'aria-label="a times x squared plus b times x plus c equals 0">' + |
+ '<math xmlns="http://www.w3.org/1998/Math/MathML">' + |
+ '<mrow><mrow><mrow><mi>a</mi><mo> ⁢ </mo>' + |
+ ' <msup id="foo"><mi>x</mi><mn>2</mn></msup>' + |
+ '</mrow><mo>+</mo><mrow><mi>b</mi><mo> ⁢ </mo>' + |
+ '<mi>x</mi></mrow><mo>+</mo><mi>c</mi></mrow><mo>=</mo><mn>0</mn></mrow>' + |
+ '</math></div></p>' + |
+ '<p><div role="math" aria-label="square root of n cube">' + |
+ '<math><msqrt><msup><mi>n</mi><mn>3</mn></msup></msqrt></math></div></p>' |
+ ); |
+ |
+ this.waitForCalm(this.userCommand, 'forward') |
+ .waitForCalm(this.assertSpoken, 'Math a times x squared plus b times x plus c equals 0') |
+ .waitForCalm(this.userCommand, 'forward') |
+ .waitForCalm(this.assertSpoken, 'Math square root of n cube') |
+ .waitForCalm(testDone); |
+}); |