| // 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() { |
| ChromeVoxUnitTestBase.call(this); |
| } |
| |
| CvoxEventWatcherUnitTest.prototype = { |
| __proto__: ChromeVoxUnitTestBase.prototype, |
| |
| /** @override */ |
| isAsync: true, |
| |
| /** @override */ |
| closureModuleDeps: ChromeVoxUnitTestBase.prototype.closureModuleDeps.concat([ |
| 'cvox.ChromeVoxTester', |
| ]), |
| |
| /** @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') |
| .waitForCalm(function() { |
| this.setFocus('show') |
| .setFocus('ok'); |
| }) |
| .waitForCalm(this.assertSpoken, 'OK Button') |
| .waitForCalm(this.setFocus, 'show') |
| .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')) |
| .waitForCalm(this.setFocus, 'outside') |
| .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) |
| .waitForCalm(cvox.ChromeVoxTester.setStrategy, 'lineardom') |
| .waitForCalm(this.userCommand, 'forward') |
| .waitForCalm(this.assertSpoken, 'valid button before Button') |
| .waitForCalm(this.userCommand, 'forward') |
| .waitForCalm(this.assertSpoken, 'valid text before') |
| .waitForCalm(this.userCommand, 'forward') |
| .waitForCalm(this.assertSpoken, 'invalid after click') |
| .waitForCalm(displayNone) |
| .waitForCalm(this.userCommand, 'forward') |
| .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') |
| .waitForCalm(cvox.ChromeVoxTester.syncToFirstNode) |
| .waitForCalm(this.userCommand, 'forward') |
| .waitForCalm(this.assertSpoken, 'List box Red Selected 1 of 4') |
| .waitForCalm(this.setFocus, 'yellow') |
| .waitForCalm(this.assertSpoken, 'Yellow 2 of 4') |
| .waitForCalm(this.setFocus, 'green') |
| .waitForCalm(this.assertSpoken, 'Green 3 of 4') |
| .waitForCalm(this.userCommand, 'forward') |
| .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'); |
| }); |
| |
| SYNC_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', |
| 'DISABLED_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 = new KeyboardEvent("keydown", {key: dir}); |
| 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, 'ArrowRight') // right arrow |
| // Moves to next radiobutton. |
| .waitForCalm(this.assertSpoken, |
| 'blue Radio button selected blue Radio button selected') |
| .waitForCalm(performKeyDown, 'ArrowRight') // right arrow |
| // Arrowed beyond end. Should be quiet. |
| .waitForCalm(this.assertSpoken, ''); |
| |
| this.waitForCalm(performKeyDown, 'ArrowLeft') // left arrow |
| // Moves back to first radio. |
| .waitForCalm(this.assertSpoken, |
| 'green Radio button selected green Radio button selected') |
| .waitForCalm(performKeyDown, 'ArrowLeft') // left arrow |
| // Arrowed beyond beginning. Should be quiet. |
| .waitForCalm(this.assertSpoken, ''); |
| }); |
| |
| /** |
| * Test time widget. |
| * Disabled because test relies on removed behavior; see crbug.com/520519. |
| */ |
| TEST_F('CvoxEventWatcherUnitTest', 'DISABLED_TimeWidget', function() { |
| this.loadHtml( |
| '<label for="timewidget">Set alarm for:</label>' + |
| '<input id="timewidget" type="time" value="12:00">'); |
| var performKeyDown = function(dir) { |
| var evt = new KeyboardEvent("keydown", {key: dir}); |
| document.activeElement.dispatchEvent(evt); |
| }; |
| var performKeyUp = function(dir) { |
| var evt = new KeyboardEvent("keyup", {key: dir}); |
| 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, 'ArrowDown') // down arrow |
| .waitForCalm(performKeyUp, 'ArrowDown') // down arrow |
| .waitForCalm(this.assertSpoken, |
| '11 hours'); |
| |
| this.waitForCalm(performKeyDown, 'ArrowDown') // down arrow |
| .waitForCalm(performKeyUp, 'ArrowDown') // down arrow |
| .waitForCalm(this.assertSpoken, |
| '10 hours'); |
| |
| this.waitForCalm(performKeyDown, 'ArrowRight') // right arrow |
| .waitForCalm(performKeyUp, 'ArrowRight') // right arrow |
| .waitForCalm(performKeyDown, 'ArrowUp') // right arrow |
| .waitForCalm(performKeyUp, 'ArrowUp') // right arrow |
| .waitForCalm(this.assertSpoken, |
| '01 minutes'); |
| |
| this.waitForCalm(performKeyDown, 'ArrowDown') // down arrow |
| .waitForCalm(performKeyUp, 'ArrowDown') // down arrow |
| .waitForCalm(this.assertSpoken, |
| '00 minutes'); |
| |
| this.waitForCalm(performKeyDown, 'ArrowRight') // right arrow |
| .waitForCalm(performKeyUp, 'ArrowRight') // right arrow |
| .waitForCalm(performKeyDown, 'ArrowUp') // right arrow |
| .waitForCalm(performKeyUp, 'ArrowUp') // right arrow |
| .waitForCalm(this.assertSpoken, |
| 'AM'); |
| |
| this.waitForCalm(performKeyDown, 'ArrowDown') // down arrow |
| .waitForCalm(performKeyUp, 'ArrowDown') // down arrow |
| .waitForCalm(this.assertSpoken, |
| 'PM'); |
| }); |
| |
| /** |
| * Test date widget. |
| * Disabled because test relies on removed behavior; see crbug.com/520519. |
| */ |
| TEST_F('CvoxEventWatcherUnitTest', 'DISABLED_DateWidget', function() { |
| this.loadHtml( |
| '<label for="datewidget">Set birthdate:</label>' + |
| '<input id="datewidget" type="date" value="1998-09-04"/>'); |
| var performKeyDown = function(dir) { |
| var evt = new KeyboardEvent("keydown", {key: dir}); |
| document.activeElement.dispatchEvent(evt); |
| }; |
| var performKeyUp = function(dir) { |
| var evt = new KeyboardEvent("keyup", {key: dir}); |
| 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') |
| |
| .waitForCalm(performKeyDown, 'ArrowDown') // down arrow |
| .waitForCalm(performKeyUp, 'ArrowDown') // down arrow |
| .waitForCalm(this.assertSpoken, |
| 'August') |
| |
| .waitForCalm(performKeyDown, 'ArrowDown') // down arrow |
| .waitForCalm(performKeyUp, 'ArrowDown') // down arrow |
| .waitForCalm(this.assertSpoken, 'July') |
| |
| .waitForCalm(performKeyDown, 'ArrowRight') // right arrow |
| .waitForCalm(performKeyUp, 'ArrowRight') // right arrow |
| .waitForCalm(performKeyDown, 'ArrowUp') // right arrow |
| .waitForCalm(performKeyUp, 'ArrowUp') // right arrow |
| .waitForCalm(this.assertSpoken, '5') |
| |
| .waitForCalm(performKeyDown, 'ArrowDown') // down arrow |
| .waitForCalm(performKeyUp, 'ArrowDown') // down arrow |
| .waitForCalm(this.assertSpoken, '4') |
| |
| .waitForCalm(performKeyDown, 'ArrowRight') // right arrow |
| .waitForCalm(performKeyUp, 'ArrowRight') // right arrow |
| .waitForCalm(performKeyDown, 'ArrowUp') // right arrow |
| .waitForCalm(performKeyUp, 'ArrowUp') // right arrow |
| .waitForCalm(this.assertSpoken, |
| '1999') |
| |
| .waitForCalm(performKeyDown, 'ArrowDown') // down arrow |
| .waitForCalm(performKeyUp, 'ArrowDown') // down arrow |
| .waitForCalm(this.assertSpoken, |
| '1998'); |
| }); |
| |
| /** |
| * 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'); |
| }); |
| |
| /** |
| * Exiting dialog message should not interrupt a live region. |
| */ |
| TEST_F('CvoxEventWatcherUnitTest', 'DISABLED_ExitDialogWithLiveRegion', function() { |
| this.loadHtml( |
| '<div role="dialog" aria-label="MyAlert">' + |
| ' <h1>Heading</h1>' + |
| ' <button id="initial">Initial focus</button>' + |
| '</div>' + |
| '<div>' + |
| ' <button id="final">Final focus</button>' + |
| '</div>' + |
| '<div id="live" aria-live="polite"></div>'); |
| |
| // Focus on the button in the dialog. |
| this.setFocus('initial'); |
| this.waitForCalm(this.assertSpoken, |
| 'Entered dialog MyAlert Dialog Initial focus Button') |
| .waitForCalm(function() { |
| $('live').innerText = 'Live region changed'; |
| }) |
| .waitForCalm(function() { |
| this.setFocus('final'); |
| }) |
| .waitForCalm(function() { |
| var ulist = cvox.ChromeVoxTester.testTts().getSpeechQueueOutput(); |
| assertEquals('Live region changed', ulist[0]); |
| assertEquals('Exited dialog.', ulist[1]); |
| assertEquals('Final focus', ulist[2]); |
| assertEquals('Button', ulist[3]); |
| }); |
| }); |