Commit 2f442a2b authored by dtseng@chromium.org's avatar dtseng@chromium.org

Support basic reading of focus for both desktop and tabs trees.

This implements:
- basic spoken and braille feedback on tabs and desktop trees.
- gives us a conceptual outline of the three entry points to interact with ChromeVox next:
1. keyboard (right now only possible by DOM events delivered through the content script)
2. through events received from the tabs automation tree
3.  through events received from the desktop automation tree
- test added against production ChromeVox for status tray.

Review URL: https://codereview.chromium.org/337843005

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@278682 0039d316-1c4b-4281-b951-d872f2087c98
parent 783fdc02
// 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.
/**
* Base test fixture for ChromeVox end to end tests.
*
* These tests run against production ChromeVox inside of the extension's
* background page context.
* @constructor
*/
function ChromeVoxE2ETest() {}
ChromeVoxE2ETest.prototype = {
__proto__: testing.Test.prototype,
/**
* @override
* No UI in the background context.
*/
runAccessibilityChecks: false,
/** @override */
isAsync: true,
/** @override */
browsePreload: null,
/** @override */
testGenCppIncludes: function() {
GEN_BLOCK(function() {/*!
#include "ash/accessibility_delegate.h"
#include "base/bind.h"
#include "base/callback.h"
#include "chrome/browser/chromeos/accessibility/accessibility_manager.h"
#include "chrome/common/extensions/extension_constants.h"
*/});
},
/** @override */
testGenPreamble: function() {
GEN_BLOCK(function() {/*!
if (chromeos::AccessibilityManager::Get()->IsSpokenFeedbackEnabled()) {
chromeos::AccessibilityManager::Get()->EnableSpokenFeedback(false,
ash::A11Y_NOTIFICATION_NONE);
}
base::Closure load_cb =
base::Bind(&chromeos::AccessibilityManager::EnableSpokenFeedback,
base::Unretained(chromeos::AccessibilityManager::Get()),
true,
ash::A11Y_NOTIFICATION_NONE);
WaitForExtension(extension_misc::kChromeVoxExtensionId, load_cb);
*/});
}
};
/**
* Similar to |TEST_F|. Generates a test for the given |testFixture|,
* |testName|, and |testFunction|.
* Used this variant when an |isAsync| fixture wants to temporarily mix in an
* sync test.
* @param {string} testFixture Fixture name.
* @param {string} testName Test name.
* @param {function} testFunction The test impl.
*/
function SYNC_TEST_F(testFixture, testName, testFunction) {
var wrappedTestFunction = function() {
testFunction();
testDone([true, '']);
};
TEST_F(testFixture, testName, wrappedTestFunction);
}
......@@ -3,52 +3,91 @@
// found in the LICENSE file.
// Include test fixture.
GEN_INCLUDE(['../../../chromevox/testing/chromevox_unittest_base.js']);
GEN_INCLUDE(['../../../chromevox/testing/chromevox_e2e_test.js']);
/**
* Test fixture for cvox2.Background.
* @constructor
* @extends {ChromeVoxUnitTestBase}
* @extends {ChromeVoxE2ETest}
*/
function BackgroundTest() {}
BackgroundTest.prototype = {
__proto__: ChromeVoxUnitTestBase.prototype,
__proto__: ChromeVoxE2ETest.prototype,
/** @override */
browsePreload: null,
setUp: function() {
this.mockTts = new MockTts();
cvox.ChromeVox.tts = this.mockTts;
}
};
/** @override */
testGenCppIncludes: function() {
GEN_BLOCK(function() {/*!
#include "ash/accessibility_delegate.h"
#include "base/bind.h"
#include "base/callback.h"
#include "chrome/browser/chromeos/accessibility/accessibility_manager.h"
#include "chrome/common/extensions/extension_constants.h"
*/});
},
/**
* Mock tts class.
* @constructor
* @extends {cvox.TtsInterface}
*/
var MockTts = function() {
};
MockTts.prototype = {
/** Tracks all spoken text. @type {!Array.<string>} */
utterances: [],
/** @override */
testGenPreamble: function() {
GEN_BLOCK(function() {/*!
if (chromeos::AccessibilityManager::Get()->IsSpokenFeedbackEnabled()) {
chromeos::AccessibilityManager::Get()->EnableSpokenFeedback(false,
ash::A11Y_NOTIFICATION_NONE);
}
speak: function(textString, queueMode, properties) {
this.utterances.push(textString);
},
base::Closure load_cb =
base::Bind(&chromeos::AccessibilityManager::EnableSpokenFeedback,
base::Unretained(chromeos::AccessibilityManager::Get()),
true,
ash::A11Y_NOTIFICATION_NONE);
WaitForExtension(extension_misc::kChromeVoxExtensionId, load_cb);
*/});
/**
* Checks to see if a string was spoken.
* @param {string} textString The string to check.
* @return {boolean} True if the string was spoken (possibly as part of a
* larger utterance).
*/
checkIfTextWasSpoken: function(textString) {
return this.utterances.some(function(t) {
return t.indexOf(textString) != -1;
});
}
};
/** Tests ChromeVox classic is in this context. */
TEST_F('BackgroundTest', 'ClassicNamespaces', function() {
/** Tests that ChromeVox classic is in this context. */
SYNC_TEST_F('BackgroundTest', 'ClassicNamespaces', function() {
assertEquals('object', typeof(cvox));
assertEquals('function', typeof(cvox.ChromeVoxBackground));
});
/** Tests that ChromeVox next is in this context. */
SYNC_TEST_F('BackgroundTest', 'NextNamespaces', function() {
assertEquals('object', typeof(cvox2));
assertEquals('function', typeof(cvox2.Background));
});
/** Tests that ChromeVox reads the desktop tree. */
TEST_F('BackgroundTest', 'DesktopFocus', function() {
function findStatusTray(root) {
if (root.role == chrome.automation.RoleType.button &&
root.attributes.name == 'Status tray') {
return root;
}
for (var i = 0; i < root.children().length; i++) {
var found = findStatusTray(root.children()[i]);
if (found)
return found;
}
return null;
}
chrome.automation.getDesktop(function(root) {
var testButton = findStatusTray(root);
testButton.addEventListener(chrome.automation.EventType.focus,
function(e) {
var result =
cvox.ChromeVox.tts.checkIfTextWasSpoken('Status tray button');
testDone([result, '']);
},
true);
testButton.focus();
});
});
......@@ -20,7 +20,16 @@ cvox2.global.accessibility =
cvox2.Background = function() {
// Only needed with unmerged ChromeVox classic loaded before.
cvox2.global.accessibility.setAccessibilityEnabled(false);
chrome.automation.getDesktop(this.onGotDesktop.bind(this));
// Register listeners for ...
// Desktop.
chrome.automation.getDesktop(this.onGotTree.bind(this));
// Tabs.
chrome.tabs.onUpdated.addListener(this.onTabUpdated.bind(this));
// Keyboard events (currently Messages from content script).
chrome.extension.onConnect.addListener(this.onConnect.bind(this));
};
cvox2.Background.prototype = {
......@@ -32,54 +41,58 @@ cvox2.Background.prototype = {
PORT_ID: 'chromevox2',
/**
* Waits until a desktop automation tree becomes available.
* Thereafter, registers a simple exploration mode for the desktop tree.
* @param {AutomationTree} tree The desktop automation tree.
* Handles chrome.extension.onConnect.
* @param {Object} port The port.
*/
onGotDesktop: function(tree) {
if (!tree.root) {
window.setTimeout(this.onGotDesktop, 500);
onConnect: function(port) {
if (port.name != this.PORT_ID)
return;
}
chrome.extension.onConnect.addListener(function(port) {
if (port.name != this.PORT_ID)
return;
var cur = tree.root;
port.onMessage.addListener(function(message) {
switch (message.keydown) {
case 37:
cur = cur.previousSibling() || cur;
break;
case 38:
cur = cur.parent() || cur;
break;
case 39:
cur = cur.nextSibling() || cur;
break;
case 40:
cur = cur.firstChild() || cur;
break;
}
var index = 1;
if (cur.parent())
index = cur.parent().children().indexOf(cur) + 1;
var name = '';
if (cur.attributes && cur.attributes['ax_attr_name'])
name = cur.attributes['ax_attr_name'];
var utterance = index + ' ' + name + cur.role;
chrome.tts.speak(String(utterance), {lang: 'en-US'});
});
}.bind(this));
port.onMessage.addListener(this.onMessage.bind(this));
},
/**
* Dispatches messages to specific handlers.
* @param {Object} message The message.
*/
onMessage: function(message) {
if (message.keyDown)
this.onKeyDown(message);
},
/**
* Handles key down messages from the content script.
* @param {Object} message The key down message.
*/
onKeyDown: function(message) {
// TODO(dtseng): Implement.
},
/**
* Handles chrome.tabs.onUpdate.
* @param {number} tabId The tab id.
* @param {Object.<string, (string|boolean)>} changeInfo Information about
* the updated tab.
*/
onTabUpdated: function(tabId, changeInfo) {
chrome.automation.getTree(this.onGotTree.bind(this));
},
/**
* Handles all setup once a new automation tree appears.
* @param {AutomationTree} tree The new automation tree.
*/
onGotTree: function(root) {
// Register all automation event listeners.
tree.root.addEventListener('focus', this.onDesktopEvent.bind(this), true);
root.addEventListener(chrome.automation.EventType.focus,
this.onAutomationEvent.bind(this),
true);
},
/**
* A generic handler for all desktop automation events.
* @param {AutomationEvent} evt The event.
*/
onDesktopEvent: function(evt) {
onAutomationEvent: function(evt) {
var output = evt.target.attributes.name + ' ' + evt.target.role;
cvox.ChromeVox.tts.speak(output);
cvox.ChromeVox.braille.write(cvox.NavBraille.fromText(output));
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment