Index: chrome/browser/resources/chromeos/chromevox/chromevox/injected/event_watcher_test.unitjs |
diff --git a/chrome/browser/resources/chromeos/chromevox/chromevox/injected/event_watcher_test.unitjs b/chrome/browser/resources/chromeos/chromevox/chromevox/injected/event_watcher_test.unitjs |
new file mode 100644 |
index 0000000000000000000000000000000000000000..f9eac4dc678a22ace19154dd550448881421af8d |
--- /dev/null |
+++ b/chrome/browser/resources/chromeos/chromevox/chromevox/injected/event_watcher_test.unitjs |
@@ -0,0 +1,850 @@ |
+// 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 CvoxEventWatcherUnitTest() {} |
+ |
+CvoxEventWatcherUnitTest.prototype = { |
+ __proto__: ChromeVoxUnitTestBase.prototype, |
+ |
+ /** @override */ |
+ closureModuleDeps: [ |
+ 'cvox.ChromeVoxTester', |
+ 'cvox.SpokenListBuilder', |
+ ], |
+ |
+ /** @override */ |
+ setUp: function() { |
+ cvox.ChromeVoxTester.setUp(document); |
+ }, |
+ |
+ /** @override */ |
+ tearDown: function() { |
+ cvox.ChromeVoxTester.tearDown(document); |
+ }, |
+ |
+ /** |
+ * Create mock event object. |
+ * @param {Element} target The event target. |
+ * @param {number=} opt_keyCode The event key code (i.e. 13 for Enter). |
+ * @param {string=} opt_type The event type (i.e. 'keydown' or |
+ * 'focus'). |
+ * @param {number=} opt_timeStamp The event timeStamp. |
+ * @return {Event} The mock event. |
+ * @suppress {invalidCasts} |
+ */ |
+ createMockEvent: function(target, opt_keyCode, opt_type, opt_timeStamp) { |
+ var mockEvent = {}; |
+ mockEvent.target = target; |
+ if (opt_keyCode) { |
+ mockEvent.keyCode = opt_keyCode; |
+ } |
+ if (opt_type) { |
+ mockEvent.type = opt_type; |
+ } |
+ if (opt_timeStamp) { |
+ mockEvent.timeStamp = opt_timeStamp; |
+ } |
+ |
+ return /** @type {Event} */ (mockEvent); |
+ }, |
+ |
+ /** |
+ * Simulate typing a key into an text field by modifying a given field and |
+ * dispatching a keydown event to ChromeVoxEventWatcher. Allows modifying the |
+ * selection so arrow keypresses can be simulated. |
+ * @param {Element} textField The text field. |
+ * @param {string} newValue The new value for the text field. |
+ * @param {number} newSelStart The new selection start. |
+ * @param {number} newSelEnd The new selection end. |
+ * @param {number} keyCode The key code for the keydown event. |
+ * @return {Element} The modified text field. |
+ */ |
+ changeTextField: function( |
+ textField, newValue, newSelStart, newSelEnd, keyCode) { |
+ textField.value = newValue; |
+ textField.selectionStart = newSelStart; |
+ textField.selectionEnd = newSelEnd; |
+ |
+ cvox.ChromeVoxEventWatcher.keyDownEventWatcher( |
+ this.createMockEvent(textField, keyCode, 'keydown')); |
+ return textField; |
+ } |
+}; |
+ |
+TEST_F('CvoxEventWatcherUnitTest', 'ButtonFocusFeedback', function() { |
+ this.loadHtml('<div> <button id="alpha">Alpha</button> </div>'); |
+ this.setFocus('alpha'); |
+ this.waitForCalm(this.assertSpoken, 'Alpha Button'); |
+}); |
+ |
+/** |
+ * Test feedback when focusing links backwards (like shift-tabbing). |
+ */ |
+TEST_F('CvoxEventWatcherUnitTest', 'FocusLinksBackwards', function() { |
+ this.loadHtml('<div> <p>before</p>' + |
+ '<p><a href="#" id="l1">1</a></p>' + |
+ '<p><a href="#" id="l2">2</a></p>' + |
+ '<p><a href="#" id="l3">3</a></p>' + |
+ '</div>'); |
+ |
+ this.waitForCalm(this.setFocus, 'l1') |
+ .waitForCalm(this.setFocus, 'l2') |
+ .waitForCalm(this.setFocus, 'l3') |
+ .waitForCalm(this.setFocus, 'l2') |
+ .waitForCalm(this.setFocus, 'l1') |
+ .waitForCalm(this.assertSpoken, |
+ '1 Internal link 2 Internal link 3 Internal link ' + |
+ '2 Internal link 1 Internal link'); |
+}); |
+ |
+/** |
+ * Test feedback when an editable text field gets focus. |
+ */ |
+TEST_F('CvoxEventWatcherUnitTest', 'TextFocusFeedback', function() { |
+ this.loadHtml('<div>' + |
+ '<label for="mytext">Label</label>' + |
+ '<input id="mytext" value="Value" title="Title" />' + |
+ '</div>'); |
+ |
+ this.setFocus('mytext'); |
+ this.waitForCalm(this.assertSpoken, 'Label Value Edit text'); |
+}); |
+ |
+/** |
+ * Test feedback when a contenteditable field gets focus. |
+ */ |
+TEST_F('CvoxEventWatcherUnitTest', 'ContentEditableFocusFeedback', function() { |
+ this.loadHtml('<div>' + |
+ '<label for="mytext">Label</label>' + |
+ '<div id="mytext" contentEditable>This is editable</div>' + |
+ '</div>'); |
+ |
+ this.setFocus('mytext'); |
+ this.waitForCalm(this.assertSpoken, 'Label This is editable Edit text'); |
+}); |
+ |
+/** |
+ * Test feedback when an item in an dialog receives focus and then focus |
+ * leaves the dialog. |
+ */ |
+TEST_F('CvoxEventWatcherUnitTest', 'DialogFeedback', function() { |
+ this.loadHtml('<div>' + |
+ '<button id="show">Show</button>' + |
+ '<div aria-label="compose message" role="dialog">' + |
+ ' <button id="ok">OK</button>' + |
+ ' <button id="cancel">Cancel</button>' + |
+ '</div>' + |
+ '</div>'); |
+ |
+ // Enter the dialog by focusing an element inside it. |
+ this.setFocus('ok'); |
+ |
+ this.waitForCalm(this.assertSpoken, |
+ 'Entered dialog compose message Dialog OK Button'); |
+ |
+ // After we've entered a dialog, temporarily moving focus away shouldn't |
+ // have any effect if we move it right back. (Allow apps to trap focus.) |
+ this.waitForCalm(function() { |
+ this.setFocus('show') |
+ .setFocus('ok'); |
+ }); |
+ |
+ this.waitForCalm(this.assertSpoken, 'OK Button'); |
+ |
+ // Now move focus away and leave it there. |
+ this.waitForCalm(this.setFocus, 'show'); |
+ this.waitForCalm(this.assertSpoken, 'Exited dialog. Show Button'); |
+}); |
+ |
+/** |
+ * Test feedback when an item in an alert dialog receives focus. |
+ */ |
+TEST_F('CvoxEventWatcherUnitTest', 'AlertDialogFeedback', function() { |
+ this.loadHtml('<div>' + |
+ '<div role="alertdialog">' + |
+ ' <p>Are you sure you want to install Windows?</p>' + |
+ ' <button id="yes">Yes</button>' + |
+ ' <button id="no">No</button>' + |
+ '</div> </div>'); |
+ |
+ // Enter the dialog by focusing an element inside it. |
+ this.setFocus('no'); |
+ this.waitForCalm(this.assertSpoken, |
+ 'Entered dialog ' + |
+ 'Are you sure you want to install Windows? Yes Button No Button ' + |
+ 'No Button'); |
+}); |
+ |
+/** |
+ * Test feedback when focus moves to two different items in a alertdialog |
+ * quickly - make sure the notification that we entered the dialog |
+ * isn't interrupted. |
+ */ |
+TEST_F('CvoxEventWatcherUnitTest', 'DoubleFocusAlertDialogFeedback', function() { |
+ this.loadHtml('<div>' + |
+ '<div role="alertdialog">' + |
+ ' <p>Are these the droids you\'re looking for?</p>' + |
+ ' <button id="yes">Yes</button>' + |
+ ' <button id="no">No</button>' + |
+ '</div>' + |
+ '<button id="outside">Outside</button>' + |
+ '</div>'); |
+ |
+ // Enter the dialog by focusing an element inside it, but then the Jedi |
+ // mind trick quickly changes the default answer. |
+ this.setFocus('yes') |
+ .setFocus('no'); |
+ |
+ this.waitForCalm(this.assertSpokenList, |
+ this.spokenList() |
+ .categoryFlush('Entered dialog') |
+ .queue('Are these the droids you\'re looking for?') |
+ .queue('Yes') |
+ .queue('Button')); |
+ |
+ // Unfocus the dialog so we don't effect other tests. |
+ this.waitForCalm(this.setFocus, 'outside'); |
+ this.waitForCalm(this.assertSpoken, 'Exited dialog. Outside Button'); |
+}); |
+ |
+/** |
+ * Test recovery when a dialog box closes and the user sends a tab event. |
+ */ |
+TEST_F('CvoxEventWatcherUnitTest', 'CloseDialogTabRecovery', function() { |
+ this.loadHtml('<div id="container">' + |
+ '<p id="first">first node</p>' + |
+ '<button id="button">valid button before</button>' + |
+ '<p id="before">valid text before</p>' + |
+ '<p id="dialog">invalid after click</p>' + |
+ '<p id="last">valid text after</p>' + |
+ '</div>'); |
+ |
+ var first = $('first'); |
+ var dialog = $('dialog'); |
+ var displayNone = function() { |
+ dialog.style.display = 'none'; |
+ }; |
+ |
+ this.waitForCalm(cvox.ChromeVoxTester.syncToNode, first); |
+ this.waitForCalm(cvox.ChromeVoxTester.setStrategy, 'lineardom'); |
+ this.waitForCalm(this.userCommand, 'forward'); |
+ this.waitForCalm(this.assertSpoken, 'valid button before Button'); |
+ this.waitForCalm(this.userCommand, 'forward'); |
+ this.waitForCalm(this.assertSpoken, 'valid text before'); |
+ this.waitForCalm(this.userCommand, 'forward'); |
+ this.waitForCalm(this.assertSpoken, 'invalid after click'); |
+ |
+ // Invalidate the dialog box. |
+ this.waitForCalm(displayNone); |
+ this.waitForCalm(this.userCommand, 'forward'); |
+ this.waitForCalm(this.assertSpoken, 'valid text after'); |
+}); |
+ |
+/** |
+ * Test feedback when a list box with an active descendant receives focus. |
+ */ |
+TEST_F('CvoxEventWatcherUnitTest', 'ListBoxFeedback', function() { |
+ this.loadHtml('<div>' + |
+ '<p id="before">My listbox</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="after">After</p>' + |
+ '</div>'); |
+ |
+ // Focus the listbox. |
+ this.setFocus('listbox'); |
+ this.waitForCalm(this.assertSpoken, 'Red List box Selected 1 of 3') |
+ .waitForCalm(function() { |
+ // Set the activeDescendant and fire a keydown event. |
+ // TODO(dmazzoni): replace with a higher-level API that's |
+ // less brittle. |
+ var listbox = $('listbox'); |
+ listbox.setAttribute('aria-activeDescendant', 'yellow'); |
+ cvox.ChromeVoxEventWatcher.keyDownEventWatcher(/** @type {Event} */ ( |
+ { 'target': listbox, |
+ 'type': 'keydown' })); |
+ }) |
+ .waitForCalm(this.assertSpoken, 'Yellow 2 of 3'); |
+}); |
+ |
+/** |
+ * Test feedback when the items of a list box receive focus. |
+ */ |
+TEST_F('CvoxEventWatcherUnitTest', 'ListBoxOptionFeedback', function() { |
+ this.loadHtml('<div>' + |
+ '<p id="before">My listbox</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="after">After</p>' + |
+ '</div>'); |
+ |
+ // Focus the second item. |
+ this.setFocus('yellow'); |
+ |
+ this.waitForCalm(this.assertSpoken, 'List box Yellow 2 of 3') |
+ .waitForCalm(this.setFocus, 'red') |
+ .waitForCalm(this.assertSpoken, 'Red Selected 1 of 3'); |
+}); |
+ |
+/** |
+ * Test feedback when the list box is setting focus in response to arrow |
+ * (or some other) keypress and the user is also using ChromeVox navigation. |
+ */ |
+TEST_F('CvoxEventWatcherUnitTest', 'ListBoxOptionFeedbackWithFocus', function() { |
+ this.loadHtml('<div>' + |
+ '<p id="before">My listbox</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 id="blue" tabindex="-1" role="option">Blue</div>' + |
+ '</div>' + |
+ '<p id="after">After</p>' + |
+ '</div>'); |
+ |
+ // Simulate the user using ChromeVox navigation to move forward in the listbox |
+ this.waitForCalm(cvox.ChromeVoxTester.setStrategy, 'lineardom'); |
+ this.waitForCalm(cvox.ChromeVoxTester.syncToFirstNode); |
+ this.waitForCalm(this.userCommand, 'forward'); |
+ this.waitForCalm(this.assertSpoken, 'List box Red Selected 1 of 4'); |
+ |
+ // Simulate the listbox setting focus on items in the listbox in response to |
+ // keypresses |
+ this.waitForCalm(this.setFocus, 'yellow'); |
+ this.waitForCalm(this.assertSpoken, 'Yellow 2 of 4'); |
+ |
+ this.waitForCalm(this.setFocus, 'green'); |
+ this.waitForCalm(this.assertSpoken, 'Green 3 of 4'); |
+ |
+ // ChromeVox navigation again |
+ this.waitForCalm(this.userCommand, 'forward'); |
+ this.waitForCalm(this.assertSpoken, 'Blue 4 of 4'); |
+}); |
+ |
+/** |
+ * Test feedback when interacting with an editable text field. |
+ * The low-level details are tested in editable_text_test.js, this is |
+ * a higher-level test of how that code interacts with the event watcher. |
+ */ |
+TEST_F('CvoxEventWatcherUnitTest', 'EditableText', function() { |
+ cvox.ChromeVoxEditableTextBase.eventTypingEcho = false; |
+ this.loadHtml('<div>' + |
+ '<button id="before">Before</button>' + |
+ '<label for="input">Query</label>' + |
+ '<input id="input" value="abc">' + |
+ '<p>After</p>' + |
+ '</div>'); |
+ |
+ var before = $('before'); |
+ var input = $('input'); |
+ |
+ // Focus the button first. |
+ before.focus(); |
+ |
+ // Then focus the text field. |
+ input.focus(); |
+ input.setSelectionRange(3, 3); |
+ |
+ this.waitForCalm(this.changeTextField, input, 'abcd', 3, 3, 68) // 'd' |
+ .waitForCalm(this.changeTextField, input, 'abcde', 4, 4, 69) // 'e' |
+ .waitForCalm(this.assertSpokenList, |
+ this.spokenList() |
+ .categoryFlush('Query') |
+ .queue('abc') |
+ .queue('Edit text') |
+ .flush('d') |
+ .flush('e')); |
+}); |
+ |
+/** |
+ * Test feedback when interacting with an editable text field that drives |
+ * an listbox (to form an auto-complete combobox) but doesn't get updated. |
+ * The low-level details are tested in editable_text_test.js, this is |
+ * a higher-level test of how that code interacts with the event watcher. |
+ */ |
+TEST_F('CvoxEventWatcherUnitTest', 'EditableTextListbox', function() { |
+ this.loadHtml('<div>' + |
+ '<button id="before">Before</button>' + |
+ '<label for="input">Query</label>' + |
+ '<input id="input" value="" role="combobox" aria-autocomplete="list"' + |
+ ' aria-activedescendant>' + |
+ '<div role="listbox">' + |
+ ' <div id="option1" role="option">First pick</div>' + |
+ ' <div id="option2" role="option">Second pick</div>' + |
+ '</div>' + |
+ '<p>After</p>' + |
+ '</div>'); |
+ |
+ var before = $('before'); |
+ var input = $('input'); |
+ |
+ // Focus the text field. |
+ this.waitForCalm(this.setFocus, 'input') |
+ .waitForCalm(this.assertSpoken, 'Query Combo box Autocompletion list'); |
+ |
+ this.waitForCalm(function() { |
+ input.setAttribute('aria-activedescendant', 'option1'); |
+ this.changeTextField(input, '', 0, 0, 40); // 'down' |
+ }) |
+ .waitForCalm(this.assertSpoken, 'First pick 1 of 2'); |
+}); |
+ |
+/** |
+ * Test feedback when interacting with an editable text field that drives |
+ * an listbox (to form an auto-complete combobox) and *does* get updated. |
+ * The low-level details are tested in editable_text_test.js, this is |
+ * a higher-level test of how that code interacts with the event watcher. |
+ */ |
+TEST_F('CvoxEventWatcherUnitTest', 'EditableTextListboxUpdatingInput', function() { |
+ this.loadHtml('<div>' + |
+ '<button id="before">Before</button>' + |
+ '<label for="input">Query</label>' + |
+ '<input id="input" value="" role="combobox" aria-autocomplete="list"' + |
+ ' aria-activedescendant>' + |
+ '<div role="listbox">' + |
+ ' <div id="option1" role="option">First pick</div>' + |
+ ' <div id="option2" role="option">Second pick</div>' + |
+ '</div>' + |
+ '<p>After</p>' + |
+ '</div>'); |
+ |
+ var before = $('before'); |
+ var input = $('input'); |
+ |
+ // Focus the text field. |
+ this.waitForCalm(this.setFocus, 'input') |
+ .waitForCalm(this.assertSpoken, 'Query Combo box Autocompletion list'); |
+ |
+ this.waitForCalm(function() { |
+ input.setAttribute('aria-activedescendant', 'option1'); |
+ this.changeTextField(input, 'First pick', 9, 9, 40); // 'down' |
+ }) |
+ .waitForCalm(this.assertSpoken, 'First pick'); |
+}); |
+ |
+/** |
+ * Tests navigating through a multiline text area. |
+ */ |
+TEST_F('CvoxEventWatcherUnitTest', 'MultilineNavigation', function() { |
+ this.loadHtml('<div> <textarea id="area">' + |
+ 'one' + |
+ '\n\n' + |
+ 'two' + |
+ '\n\n' + |
+ 'three</textarea>' + |
+ '</div>'); |
+ |
+ var area = $('area'); |
+ |
+ function setAreaCursor(pos) { |
+ area.setSelectionRange(pos, pos); |
+ cvox.ChromeVoxEventWatcher.keyDownEventWatcher(/** @type {Event} */ ( |
+ { 'target': area, |
+ 'type': 'keydown' })); |
+ } |
+ |
+ area.focus(); |
+ this.waitForCalm(this.assertSpoken, 'one two three Text area') |
+ .waitForCalm(setAreaCursor, 0) |
+ // The cursor did not move, so don't say anything -- even though we |
+ // did press a key. |
+ .waitForCalm(this.assertSpoken, '') |
+ .waitForCalm(setAreaCursor, 5) // in front on the 'two' |
+ .waitForCalm(this.assertSpoken, 'two') |
+ .waitForCalm(setAreaCursor, 10) // in front of the 'three' |
+ .waitForCalm(this.assertSpoken, 'three') |
+ .waitForCalm(setAreaCursor, 0) // back to the first line |
+ .waitForCalm(this.assertSpoken, 'one') |
+ .waitForCalm(setAreaCursor, 4) // on the first new line |
+ .waitForCalm(this.assertSpoken, 'Blank') |
+ .waitForCalm(setAreaCursor, 5) |
+ .waitForCalm(this.assertSpoken, 'two') |
+ .waitForCalm(setAreaCursor, 9) |
+ .waitForCalm(this.assertSpoken, 'Blank') |
+ .waitForCalm(setAreaCursor, 10) |
+ .waitForCalm(this.assertSpoken, 'three'); |
+}); |
+ |
+TEST_F('CvoxEventWatcherUnitTest', 'ShouldWaitToProcess', function() { |
+ // The focus event just happened, wait. |
+ assertTrue( |
+ cvox.ChromeVoxEventWatcherUtil.shouldWaitToProcess(100, 100, 100)); |
+ // The focus event just happened, but the first event is old, don't wait. |
+ assertFalse( |
+ cvox.ChromeVoxEventWatcherUtil.shouldWaitToProcess(100, 0, 100)); |
+ // The focus event is old, don't wait. |
+ assertFalse( |
+ cvox.ChromeVoxEventWatcherUtil.shouldWaitToProcess(0, 0, 100)); |
+}); |
+ |
+/** |
+ * Test that no feedback is received for events that fire on elements |
+ * that are hidden (or the descendant of a hidden element). |
+ */ |
+TEST_F('CvoxEventWatcherUnitTest', 'AriaHiddenFeedback', function() { |
+ this.loadHtml('<div>' + |
+ '<div>' + |
+ ' <button id="button1">Button 1</button>' + |
+ ' <button id="button2" aria-hidden="true">Button 2</button>' + |
+ '</div>' + |
+ '<div aria-hidden="true">' + |
+ ' <h3>Random header</h3>' + |
+ ' <div>' + |
+ ' <button id="button3">Button 3</button>' + |
+ ' </div>' + |
+ ' <h3>Random header</h3>' + |
+ '</div>' + |
+ '<div>' + |
+ ' <button id="button4">Button 4</button>' + |
+ '</div>' + |
+ '</div>'); |
+ |
+ this.setFocus('button1') |
+ .waitForCalm(this.assertSpoken, 'Button 1 Button') |
+ .waitForCalm(this.setFocus, 'button2') |
+ .waitForCalm(this.assertSpoken, '') |
+ .waitForCalm(this.setFocus, 'button3') |
+ .waitForCalm(this.assertSpoken, '') |
+ .waitForCalm(this.setFocus, 'button4') |
+ .waitForCalm(this.assertSpoken, 'Button 4 Button'); |
+}); |
+ |
+/** |
+ * Test that key down events don't cause excessive value and state announcements |
+ * when arrowing around radiobuttons. |
+ */ |
+TEST_F('CvoxEventWatcherUnitTest', 'RadioButtonAnnouncements', function() { |
+ this.loadHtml( |
+ '<input id="radio1" type="radio" aria-label="green" tabindex=0>' + |
+ '<input id="radio2" type="radio" aria-label="blue" tabindex=0>'); |
+ function performKeyDown(dir) { |
+ var evt = document.createEvent('KeyboardEvent'); |
+ evt.initKeyboardEvent( |
+ 'keydown', true, true, window, dir, 0, false, false, false, false); |
+ |
+ document.activeElement.dispatchEvent(evt); |
+ }; |
+ |
+ var radio1 = $('radio1'); |
+ radio1.focus(); |
+ |
+ // TODO(dtseng): Repeated actual spoken text here; this is most certainly a |
+ // test framework bug. |
+ this.waitForCalm(this.assertSpoken, 'green Radio button unselected') |
+ .waitForCalm(performKeyDown, 'Right') // right arrow |
+ // Moves to next radiobutton. |
+ .waitForCalm(this.assertSpoken, |
+ 'blue Radio button selected blue Radio button selected') |
+ .waitForCalm(performKeyDown, 'Right') // right arrow |
+ // Arrowed beyond end. Should be quiet. |
+ .waitForCalm(this.assertSpoken, ''); |
+ |
+ this.waitForCalm(performKeyDown, 'Left') // left arrow |
+ // Moves back to first radio. |
+ .waitForCalm(this.assertSpoken, |
+ 'green Radio button selected green Radio button selected') |
+ .waitForCalm(performKeyDown, 'Left') // left arrow |
+ // Arrowed beyond beginning. Should be quiet. |
+ .waitForCalm(this.assertSpoken, ''); |
+}); |
+ |
+/** |
+ * Test time widget. |
+ */ |
+TEST_F('CvoxEventWatcherUnitTest', 'TimeWidget', function() { |
+ var chromeVer = -1; |
+ var userAgent = window.navigator.userAgent; |
+ var startIndex = userAgent.indexOf('Chrome/'); |
+ if (startIndex != -1) { |
+ userAgent = userAgent.substring(startIndex + 'Chrome/'.length); |
+ } |
+ var endIndex = userAgent.indexOf('.'); |
+ if (endIndex != -1) { |
+ userAgent = userAgent.substring(0, endIndex); |
+ } |
+ // This test will only work on Chrome 23 and higher. |
+ if (userAgent >= 23) { |
+ this.loadHtml( |
+ '<label for="timewidget">Set alarm for:</label>'); |
+ this.loadHtml( |
+ '<input id="timewidget" type="time" value="12:00">'); |
+ var performKeyDown = function(dir) { |
+ var evt = document.createEvent('KeyboardEvent'); |
+ evt.initKeyboardEvent( |
+ 'keydown', true, true, window, dir, 0, false, false, false, false); |
+ |
+ document.activeElement.dispatchEvent(evt); |
+ }; |
+ var performKeyUp = function(dir) { |
+ var evt = document.createEvent('KeyboardEvent'); |
+ evt.initKeyboardEvent( |
+ 'keyup', true, true, window, dir, 0, false, false, false, false); |
+ |
+ document.activeElement.dispatchEvent(evt); |
+ }; |
+ |
+ var timewidget = $('timewidget'); |
+ timewidget.focus(); |
+ |
+ this.waitForCalm(this.assertSpoken, |
+ 'Set alarm for: 12:00 Set alarm for: 12 hours 00 minutes PM'); |
+ |
+ this.waitForCalm(performKeyDown, 'Down') // down arrow |
+ .waitForCalm(performKeyUp, 'Down') // down arrow |
+ .waitForCalm(this.assertSpoken, |
+ '11 hours'); |
+ |
+ this.waitForCalm(performKeyDown, 'Down') // down arrow |
+ .waitForCalm(performKeyUp, 'Down') // down arrow |
+ .waitForCalm(this.assertSpoken, |
+ '10 hours'); |
+ |
+ this.waitForCalm(performKeyDown, 'Right') // right arrow |
+ .waitForCalm(performKeyUp, 'Right') // right arrow |
+ .waitForCalm(performKeyDown, 'Up') // right arrow |
+ .waitForCalm(performKeyUp, 'Up') // right arrow |
+ .waitForCalm(this.assertSpoken, |
+ '01 minutes'); |
+ |
+ this.waitForCalm(performKeyDown, 'Down') // down arrow |
+ .waitForCalm(performKeyUp, 'Down') // down arrow |
+ .waitForCalm(this.assertSpoken, |
+ '00 minutes'); |
+ |
+ this.waitForCalm(performKeyDown, 'Right') // right arrow |
+ .waitForCalm(performKeyUp, 'Right') // right arrow |
+ .waitForCalm(performKeyDown, 'Up') // right arrow |
+ .waitForCalm(performKeyUp, 'Up') // right arrow |
+ .waitForCalm(this.assertSpoken, |
+ 'AM'); |
+ |
+ this.waitForCalm(performKeyDown, 'Down') // down arrow |
+ .waitForCalm(performKeyUp, 'Down') // down arrow |
+ .waitForCalm(this.assertSpoken, |
+ 'PM'); |
+ } |
+}); |
+ |
+/** |
+ * Test date widget. |
+ */ |
+TEST_F('CvoxEventWatcherUnitTest', 'DateWidget', function() { |
+ var chromeVer = -1; |
+ var userAgent = window.navigator.userAgent; |
+ var startIndex = userAgent.indexOf('Chrome/'); |
+ if (startIndex != -1) { |
+ userAgent = userAgent.substring(startIndex + 'Chrome/'.length); |
+ } |
+ var endIndex = userAgent.indexOf('.'); |
+ if (endIndex != -1) { |
+ userAgent = userAgent.substring(0, endIndex); |
+ } |
+ // This test will only work on Chrome 25 and higher. |
+ if (userAgent >= 25) { |
+ this.loadHtml( |
+ '<label for="datewidget">Set birthdate:</label>'); |
+ this.loadHtml( |
+ '<input id="datewidget" type="date" value="1998-09-04"/>'); |
+ var performKeyDown = function(dir) { |
+ var evt = document.createEvent('KeyboardEvent'); |
+ evt.initKeyboardEvent( |
+ 'keydown', true, true, window, dir, 0, false, false, false, false); |
+ |
+ document.activeElement.dispatchEvent(evt); |
+ }; |
+ var performKeyUp = function(dir) { |
+ var evt = document.createEvent('KeyboardEvent'); |
+ evt.initKeyboardEvent( |
+ 'keyup', true, true, window, dir, 0, false, false, false, false); |
+ |
+ document.activeElement.dispatchEvent(evt); |
+ }; |
+ |
+ var datewidget = $('datewidget'); |
+ datewidget.focus(); |
+ |
+ this.waitForCalm(this.assertSpoken, |
+ 'Set birthdate: 1998-09-04 Date control Set birthdate: September 4 1998'); |
+ |
+ this.waitForCalm(performKeyDown, 'Down') // down arrow |
+ .waitForCalm(performKeyUp, 'Down') // down arrow |
+ .waitForCalm(this.assertSpoken, |
+ 'August'); |
+ |
+ this.waitForCalm(performKeyDown, 'Down') // down arrow |
+ .waitForCalm(performKeyUp, 'Down') // down arrow |
+ .waitForCalm(this.assertSpoken, |
+ 'July'); |
+ |
+ this.waitForCalm(performKeyDown, 'Right') // right arrow |
+ .waitForCalm(performKeyUp, 'Right') // right arrow |
+ .waitForCalm(performKeyDown, 'Up') // right arrow |
+ .waitForCalm(performKeyUp, 'Up') // right arrow |
+ .waitForCalm(this.assertSpoken, '5'); |
+ |
+ this.waitForCalm(performKeyDown, 'Down') // down arrow |
+ .waitForCalm(performKeyUp, 'Down') // down arrow |
+ .waitForCalm(this.assertSpoken, |
+ '4'); |
+ |
+ this.waitForCalm(performKeyDown, 'Right') // right arrow |
+ .waitForCalm(performKeyUp, 'Right') // right arrow |
+ .waitForCalm(performKeyDown, 'Up') // right arrow |
+ .waitForCalm(performKeyUp, 'Up') // right arrow |
+ .waitForCalm(this.assertSpoken, |
+ '1999'); |
+ |
+ this.waitForCalm(performKeyDown, 'Down') // down arrow |
+ .waitForCalm(performKeyUp, 'Down') // down arrow |
+ .waitForCalm(this.assertSpoken, |
+ '1998'); |
+ } |
+}); |
+ |
+/** |
+ * Test video widget. |
+ */ |
+TEST_F('CvoxEventWatcherUnitTest', 'VideoWidget', function() { |
+ this.loadHtml('<video id="chromevideo" poster="star.png" controls>' + |
+ '<source src="Chrome_ImF.mp4" ' + |
+ ' type=\'video/mp4; codecs="avc1.42E01E, mp4a.40.2"\' />' + |
+ '<source src="Chrome_ImF.webm" ' + |
+ ' type=\'video/webm; codecs="vp8, vorbis"\' />' + |
+ '<source src="Chrome_ImF.ogv" ' + |
+ ' type=\'video/ogg; codecs="theora, vorbis"\' />' + |
+ '</video>'); |
+ |
+ function performKeyDown(dir) { |
+ var evt = document.createEvent('KeyboardEvent'); |
+ evt.initKeyboardEvent( |
+ 'keydown', true, true, window, dir, 0, false, false, false, false); |
+ |
+ document.activeElement.dispatchEvent(evt); |
+ }; |
+ |
+ function performKeyUp(dir) { |
+ var evt = document.createEvent('KeyboardEvent'); |
+ evt.initKeyboardEvent( |
+ 'keyup', true, true, window, dir, 0, false, false, false, false); |
+ document.activeElement.dispatchEvent(evt); |
+ }; |
+ |
+ var self = this; |
+ var videowidget = $('chromevideo'); |
+ |
+ videowidget.onload = function() { |
+ videowidget.focus(); |
+ self.waitForCalm(performKeyDown, 'Enter') |
+ .waitForCalm(performKeyUp, 'Enter') |
+ .waitForCalm(self.assertEquals, videowidget.paused, false); |
+ |
+ self.waitForCalm(performKeyDown, 'Right') |
+ .waitForCalm(performKeyUp, 'Right') |
+ .waitForCalm(self.assertEquals, videowidget.currentTime, 0); |
+ |
+ self.waitForCalm(performKeyDown, 'Down') |
+ .waitForCalm(performKeyUp, 'Down') |
+ .waitForCalm(self.assertEquals, videowidget.volume, 0); |
+ }; |
+}); |
+ |
+/** |
+ * Test audio widget. |
+ */ |
+TEST_F('CvoxEventWatcherUnitTest', 'AudioWidget', function() { |
+ this.loadHtml( |
+ '<audio id="chromeaudio" controls>' + |
+ '<source src="http://www.html5rocks.com/en/tutorials/' + |
+ 'audio/quick/test.mp3" type="audio/mpeg" />' + |
+ '<source src="http://www.html5rocks.com/en/tutorials/' + |
+ 'audio/quick/test.ogg" type="audio/ogg" />'); |
+ |
+ function performKeyDown(dir) { |
+ var evt = document.createEvent('KeyboardEvent'); |
+ evt.initKeyboardEvent( |
+ 'keydown', true, true, window, dir, 0, false, false, false, false); |
+ |
+ document.activeElement.dispatchEvent(evt); |
+ }; |
+ |
+ function performKeyUp(dir) { |
+ var evt = document.createEvent('KeyboardEvent'); |
+ evt.initKeyboardEvent( |
+ 'keyup', true, true, window, dir, 0, false, false, false, false); |
+ document.activeElement.dispatchEvent(evt); |
+ }; |
+ |
+ var self = this; |
+ var audiowidget = $('chromeaudio'); |
+ |
+ audiowidget.onload = function() { |
+ audiowidget.focus(); |
+ self.waitForCalm(performKeyDown, 'Enter') |
+ .waitForCalm(performKeyUp, 'Enter') |
+ .waitForCalm(self.assertEquals, audiowidget.paused, false); |
+ |
+ self.waitForCalm(performKeyDown, 'Right') |
+ .waitForCalm(performKeyUp, 'Right') |
+ .waitForCalm(self.assertEquals, audiowidget.currentTime, 0); |
+ |
+ self.waitForCalm(performKeyDown, 'Down') |
+ .waitForCalm(performKeyUp, 'Down') |
+ .waitForCalm(self.assertEquals, audiowidget.volume, 0); |
+ }; |
+}); |
+ |
+/** |
+ * Test that ChromeVox speaks the correct state when a focused control |
+ * changes as the result of a key up, not just key down. |
+ */ |
+TEST_F('CvoxEventWatcherUnitTest', 'ToggleOnKeyUp', function() { |
+ this.loadHtml('<div>' + |
+ '<div tabIndex=0 id="pressable" role="button" aria-pressed="false">' + |
+ 'Toggle' + |
+ '</div>' + |
+ '</div>'); |
+ |
+ // Focus on the button. |
+ this.setFocus('pressable'); |
+ this.waitForCalm(this.assertSpoken, 'Toggle Button Not pressed'); |
+ |
+ function keyupSpace() { |
+ var evt = document.createEvent('KeyboardEvent'); |
+ evt.initKeyboardEvent( |
+ 'keyup', true, true, window, ' ', 0, false, false, false, false); |
+ document.activeElement.dispatchEvent(evt); |
+ } |
+ |
+ function keyupSpaceAndMarkPressed() { |
+ keyupSpace(); |
+ $('pressable').setAttribute('aria-pressed', 'true'); |
+ }; |
+ |
+ function keyupSpaceAndMarkNotPressed() { |
+ keyupSpace(); |
+ $('pressable').setAttribute('aria-pressed', 'false'); |
+ }; |
+ |
+ this.waitForCalm(keyupSpaceAndMarkPressed) |
+ .waitForCalm(this.assertSpoken, 'Pressed') |
+ .waitForCalm(keyupSpaceAndMarkNotPressed) |
+ .waitForCalm(this.assertSpoken, 'Not pressed'); |
+}); |