зеркало из https://github.com/mozilla/gecko-dev.git
Bug 520709 - mochitest to ensure name/description are updated on a11y focus if they were changed on DOM focus, r=marcoz
This commit is contained in:
Родитель
e39a82fa4a
Коммит
5a8e76ad8d
|
@ -96,6 +96,7 @@ _TEST_FILES =\
|
|||
test_events_doc.html \
|
||||
test_events_draganddrop.html \
|
||||
test_events_flush.html \
|
||||
test_events_focus.html \
|
||||
test_events_focus.xul \
|
||||
test_events_mutation.html \
|
||||
test_events_tree.xul \
|
||||
|
|
|
@ -91,6 +91,7 @@ function unregisterA11yEventListener(aEventType, aEventHandler)
|
|||
listenA11yEvents(false);
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Event queue
|
||||
|
||||
|
@ -106,15 +107,6 @@ const INVOKER_ACTION_FAILED = 1;
|
|||
*/
|
||||
const DO_NOT_FINISH_TEST = 1;
|
||||
|
||||
/**
|
||||
* Common invoker checker (see eventSeq of eventQueue).
|
||||
*/
|
||||
function invokerChecker(aEventType, aTarget)
|
||||
{
|
||||
this.type = aEventType;
|
||||
this.target = aTarget;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates event queue for the given event type. The queue consists of invoker
|
||||
* objects, each of them generates the event of the event type. When queue is
|
||||
|
@ -539,6 +531,9 @@ function eventQueue(aEventType)
|
|||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Action sequence
|
||||
|
||||
/**
|
||||
* Deal with action sequence. Used when you need to execute couple of actions
|
||||
* each after other one.
|
||||
|
@ -586,6 +581,168 @@ function sequence()
|
|||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Event queue invokers
|
||||
|
||||
/**
|
||||
* Invokers defined below take a checker object implementing 'check' method
|
||||
* which will be called when proper event is handled. Invokers listen default
|
||||
* event type registered in event queue object.
|
||||
*
|
||||
* Note, you don't need to initialize 'target' and 'type' members of checker
|
||||
* object. The 'target' member will be initialized by invoker object and you are
|
||||
* free to use it in 'check' method.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Click invoker.
|
||||
*/
|
||||
function synthClick(aNodeOrID, aChecker)
|
||||
{
|
||||
this.__proto__ = new synthAction(aNodeOrID, aChecker);
|
||||
|
||||
this.invoke = function synthClick_invoke()
|
||||
{
|
||||
// Scroll the node into view, otherwise synth click may fail.
|
||||
this.DOMNode.scrollIntoView(true);
|
||||
|
||||
synthesizeMouse(this.DOMNode, 1, 1, {});
|
||||
}
|
||||
|
||||
this.getID = function synthFocus_getID()
|
||||
{
|
||||
return prettyName(aNodeOrID) + " click";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* General key press invoker.
|
||||
*/
|
||||
function synthKey(aNodeOrID, aKey, aArgs, aChecker)
|
||||
{
|
||||
this.__proto__ = new synthAction(aNodeOrID, aChecker);
|
||||
|
||||
this.invoke = function synthKey_invoke()
|
||||
{
|
||||
synthesizeKey(this.mKey, this.mArgs);
|
||||
}
|
||||
|
||||
this.getID = function synthFocus_getID()
|
||||
{
|
||||
return prettyName(aNodeOrID) + " '" + this.mKey + "' key";
|
||||
}
|
||||
|
||||
this.mKey = aKey;
|
||||
this.mArgs = aArgs ? aArgs : {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Tab key invoker.
|
||||
*/
|
||||
function synthTab(aNodeOrID, aChecker)
|
||||
{
|
||||
this.__proto__ = new synthKey(aNodeOrID, "VK_TAB", { shiftKey: false },
|
||||
aChecker);
|
||||
|
||||
this.getID = function synthTabTest_getID()
|
||||
{
|
||||
return prettyName(aNodeOrID) + " tab";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shift tab key invoker.
|
||||
*/
|
||||
function synthShiftTab(aNodeOrID, aChecker)
|
||||
{
|
||||
this.__proto__ = new synthKey(aNodeOrID, "VK_TAB", { shiftKey: true },
|
||||
aChecker);
|
||||
|
||||
this.getID = function synthTabTest_getID()
|
||||
{
|
||||
return prettyName(aNodeOrID) + " shift tab";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Down arrow key invoker.
|
||||
*/
|
||||
function synthDownKey(aNodeOrID, aChecker)
|
||||
{
|
||||
this.__proto__ = new synthKey(aNodeOrID, "VK_DOWN", null, aChecker);
|
||||
|
||||
this.getID = function synthDownKey_getID()
|
||||
{
|
||||
return prettyName(aNodeOrID) + " key down";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Right arrow key invoker.
|
||||
*/
|
||||
function synthRightKey(aNodeOrID, aChecker)
|
||||
{
|
||||
this.__proto__ = new synthKey(aNodeOrID, "VK_RIGHT", null, aChecker);
|
||||
|
||||
this.getID = function synthRightKey_getID()
|
||||
{
|
||||
return prettyName(aNodeOrID) + " key right";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Focus invoker.
|
||||
*/
|
||||
function synthFocus(aNodeOrID, aChecker)
|
||||
{
|
||||
this.__proto__ = new synthAction(aNodeOrID, aChecker);
|
||||
|
||||
this.invoke = function synthFocus_invoke()
|
||||
{
|
||||
this.DOMNode.focus();
|
||||
}
|
||||
|
||||
this.getID = function synthFocus_getID()
|
||||
{
|
||||
return prettyName(aNodeOrID) + " focus";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Select all invoker.
|
||||
*/
|
||||
function synthSelectAll(aNodeOrID, aChecker)
|
||||
{
|
||||
this.__proto__ = new synthAction(aNodeOrID, aChecker);
|
||||
|
||||
this.invoke = function synthSelectAll_invoke()
|
||||
{
|
||||
if (this.DOMNode instanceof Components.interfaces.nsIDOMHTMLInputElement)
|
||||
this.DOMNode.select();
|
||||
else
|
||||
window.getSelection().selectAllChildren(this.DOMNode);
|
||||
}
|
||||
|
||||
this.getID = function synthSelectAll_getID()
|
||||
{
|
||||
return aNodeOrID + " selectall";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Event queue checkers
|
||||
|
||||
/**
|
||||
* Common invoker checker (see eventSeq of eventQueue).
|
||||
*/
|
||||
function invokerChecker(aEventType, aTarget)
|
||||
{
|
||||
this.type = aEventType;
|
||||
this.target = aTarget;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Private implementation details.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -741,3 +898,22 @@ function sequenceItem(aProcessor, aEventType, aTarget, aItemID)
|
|||
|
||||
this.queue.push(invoker);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Event queue invokers
|
||||
|
||||
/**
|
||||
* Invoker base class for prepare an action.
|
||||
*/
|
||||
function synthAction(aNodeOrID, aChecker)
|
||||
{
|
||||
this.DOMNode = getNode(aNodeOrID);
|
||||
aChecker.target = this.DOMNode;
|
||||
|
||||
this.check = function synthAction_check(aEvent)
|
||||
{
|
||||
aChecker.check(aEvent);
|
||||
}
|
||||
|
||||
this.getID = function synthAction_getID() { return aNodeOrID + " action"; }
|
||||
}
|
||||
|
|
|
@ -20,135 +20,37 @@
|
|||
|
||||
<script type="application/javascript">
|
||||
/**
|
||||
* Invoker base class.
|
||||
* Generic checker.
|
||||
*/
|
||||
function synthAction(aNodeOrID, aCaretOffset)
|
||||
function checker(aCaretOffset)
|
||||
{
|
||||
this.DOMNode = getNode(aNodeOrID);
|
||||
|
||||
this.check = function synthAction_check(aEvent)
|
||||
this.check = function checker_check(aEvent)
|
||||
{
|
||||
is(aEvent.QueryInterface(nsIAccessibleCaretMoveEvent).caretOffset,
|
||||
this.caretOffset,
|
||||
"Wrong caret offset for " + aNodeOrID);
|
||||
aCaretOffset,
|
||||
"Wrong caret offset for " + prettyName(aEvent.target));
|
||||
}
|
||||
|
||||
this.getID = function synthAction_getID() { return aNodeOrID + " action"; }
|
||||
|
||||
this.caretOffset = aCaretOffset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Click invoker.
|
||||
* Click checker.
|
||||
*/
|
||||
function synthClick(aNodeOrID, aCaretOffset,
|
||||
aExtraNodeOrID, aExtraCaretOffset)
|
||||
function clickChecker(aCaretOffset, aExtraNodeOrID, aExtraCaretOffset)
|
||||
{
|
||||
this.__proto__ = new synthAction(aNodeOrID, aCaretOffset);
|
||||
this.__proto__ = new checker(aCaretOffset);
|
||||
|
||||
this.extraNode = getNode(aExtraNodeOrID);
|
||||
this.extraCaretOffset = aExtraCaretOffset;
|
||||
|
||||
this.invoke = function synthClick_invoke()
|
||||
{
|
||||
// Scroll the node into view, otherwise synth click may fail.
|
||||
this.DOMNode.scrollIntoView(true);
|
||||
|
||||
synthesizeMouse(this.DOMNode, 1, 1, {});
|
||||
}
|
||||
|
||||
this.check = function synthFocus_check(aEvent)
|
||||
this.check = function clickChecker_check(aEvent)
|
||||
{
|
||||
this.__proto__.check(aEvent);
|
||||
|
||||
if (this.extraNode) {
|
||||
var acc = getAccessible(this.extraNode, [nsIAccessibleText]);
|
||||
is(acc.caretOffset, this.extraCaretOffset,
|
||||
is(acc.caretOffset, aExtraCaretOffset,
|
||||
"Wrong caret offset for " + aExtraNodeOrID);
|
||||
}
|
||||
}
|
||||
|
||||
this.getID = function synthFocus_getID() { return aNodeOrID + " click"; }
|
||||
}
|
||||
|
||||
/**
|
||||
* Key press invokers.
|
||||
*/
|
||||
function synthKey(aNodeOrID, aCaretOffset, aKey, aArgs)
|
||||
{
|
||||
this.__proto__ = new synthAction(aNodeOrID, aCaretOffset);
|
||||
|
||||
this.invoke = function synthKey_invoke()
|
||||
{
|
||||
synthesizeKey(this.mKey, this.mArgs);
|
||||
}
|
||||
|
||||
this.mKey = aKey;
|
||||
this.mArgs = aArgs ? aArgs : {};
|
||||
}
|
||||
|
||||
function synthTabTest(aNodeOrID, aCaretOffset, aBackTab)
|
||||
{
|
||||
this.__proto__ = new synthKey(aNodeOrID, aCaretOffset,
|
||||
"VK_TAB", {shiftKey: aBackTab});
|
||||
|
||||
this.getID = function synthTabTest_getID() { return aNodeOrID + " tab"; }
|
||||
}
|
||||
|
||||
function synthDownKey(aNodeOrID, aCaretOffset)
|
||||
{
|
||||
this.__proto__ = new synthKey(aNodeOrID, aCaretOffset, "VK_DOWN");
|
||||
|
||||
this.getID = function synthDownKey_getID()
|
||||
{
|
||||
return aNodeOrID + " key down";
|
||||
}
|
||||
}
|
||||
|
||||
function synthRightKey(aNodeOrID, aCaretOffset)
|
||||
{
|
||||
this.__proto__ = new synthKey(aNodeOrID, aCaretOffset, "VK_RIGHT");
|
||||
|
||||
this.getID = function synthRightKey_getID()
|
||||
{
|
||||
return aNodeOrID + " key right";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Focus invoker.
|
||||
*/
|
||||
function synthFocus(aNodeOrID, aCaretOffset)
|
||||
{
|
||||
this.__proto__ = new synthAction(aNodeOrID, aCaretOffset);
|
||||
|
||||
this.invoke = function synthFocus_invoke()
|
||||
{
|
||||
this.DOMNode.focus();
|
||||
}
|
||||
|
||||
this.getID = function synthFocus_getID() { return aNodeOrID + " focus"; }
|
||||
}
|
||||
|
||||
/**
|
||||
* Select all invoker.
|
||||
*/
|
||||
function synthSelectAll(aNodeOrID, aCaretOffset)
|
||||
{
|
||||
this.__proto__ = new synthAction(aNodeOrID, aCaretOffset);
|
||||
|
||||
this.invoke = function synthSelectAll_invoke()
|
||||
{
|
||||
if (this.DOMNode instanceof Components.interfaces.nsIDOMHTMLInputElement)
|
||||
this.DOMNode.select();
|
||||
else
|
||||
window.getSelection().selectAllChildren(this.DOMNode);
|
||||
}
|
||||
|
||||
this.getID = function synthSelectAll_getID()
|
||||
{
|
||||
return aNodeOrID + " selectall";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -177,26 +79,27 @@
|
|||
gQueue = new eventQueue(nsIAccessibleEvent.EVENT_TEXT_CARET_MOVED);
|
||||
|
||||
var id = "textbox";
|
||||
gQueue.push(new synthFocus(id, 5));
|
||||
gQueue.push(new synthSelectAll(id, 5));
|
||||
gQueue.push(new synthClick(id, 0));
|
||||
gQueue.push(new synthRightKey(id, 1));
|
||||
gQueue.push(new synthFocus(id, new checker(5)));
|
||||
gQueue.push(new synthSelectAll(id, new checker(5)));
|
||||
gQueue.push(new synthClick(id, new checker(0)));
|
||||
gQueue.push(new synthRightKey(id, new checker(1)));
|
||||
|
||||
id = "textarea";
|
||||
gQueue.push(new synthClick(id, 0));
|
||||
gQueue.push(new synthRightKey(id, 1));
|
||||
gQueue.push(new synthDownKey(id, 12));
|
||||
gQueue.push(new synthClick(id, new checker(0)));
|
||||
gQueue.push(new synthRightKey(id, new checker(1)));
|
||||
gQueue.push(new synthDownKey(id, new checker(12)));
|
||||
|
||||
id = "p";
|
||||
gQueue.push(new synthClick(id, 0));
|
||||
gQueue.push(new synthRightKey(id, 1));
|
||||
gQueue.push(new synthDownKey(id, 6));
|
||||
gQueue.push(new synthClick(id, new checker(0)));
|
||||
gQueue.push(new synthRightKey(id, new checker(1)));
|
||||
gQueue.push(new synthDownKey(id, new checker(6)));
|
||||
|
||||
gQueue.push(new synthClick("p1_in_div", 0, "p2_in_div", -1));
|
||||
gQueue.push(new synthClick("p1_in_div",
|
||||
new clickChecker(0, "p2_in_div", -1)));
|
||||
|
||||
gQueue.push(new synthTabTest("p", 0, true));
|
||||
gQueue.push(new synthTabTest("textarea", 12, true));
|
||||
gQueue.push(new synthTabTest("p", 0, false));
|
||||
gQueue.push(new synthShiftTab("p", new checker(0)));
|
||||
gQueue.push(new synthShiftTab("textarea", new checker(12)));
|
||||
gQueue.push(new synthTab("p", new checker(0)));
|
||||
|
||||
gQueue.invoke(); // Will call SimpleTest.finish();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,121 @@
|
|||
<html>
|
||||
|
||||
<head>
|
||||
<title>Accessible focus testing</title>
|
||||
|
||||
<link rel="stylesheet" type="text/css"
|
||||
href="chrome://mochikit/content/tests/SimpleTest/test.css" />
|
||||
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/MochiKit/packed.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
|
||||
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/a11y/accessible/common.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/a11y/accessible/events.js"></script>
|
||||
|
||||
<script type="application/javascript">
|
||||
/**
|
||||
* Checker for invokers.
|
||||
*/
|
||||
function actionChecker(aDescription)
|
||||
{
|
||||
this.check = function actionChecker_check(aEvent)
|
||||
{
|
||||
var target = aEvent.accessible;
|
||||
is(target.description, aDescription,
|
||||
"Wrong description for " + prettyName(target));
|
||||
}
|
||||
}
|
||||
|
||||
var gFocusHandler = {
|
||||
handleEvent: function gFocusHandler_handleEvent(aEvent) {
|
||||
var elm = aEvent.target;
|
||||
if (elm.nodeType != nsIDOMNode.ELEMENT_NODE)
|
||||
return;
|
||||
|
||||
gTooltipElm.style.display = "block";
|
||||
|
||||
elm.setAttribute("aria-describedby", "tooltip");
|
||||
}
|
||||
};
|
||||
|
||||
var gBlurHandler = {
|
||||
handleEvent: function gBlurHandler_handleEvent(aEvent) {
|
||||
gTooltipElm.style.display = "none";
|
||||
|
||||
var elm = aEvent.target;
|
||||
if (elm.nodeType == nsIDOMNode.ELEMENT_NODE)
|
||||
elm.removeAttribute("aria-describedby");
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Do tests.
|
||||
*/
|
||||
|
||||
// gA11yEventDumpID = "eventdump"; // debug stuff
|
||||
|
||||
var gQueue = null;
|
||||
|
||||
var gButtonElm = null;
|
||||
var gTextboxElm = null;
|
||||
var gTooltipElm = null;
|
||||
|
||||
function doTests()
|
||||
{
|
||||
gButtonElm = getNode("button");
|
||||
gTextboxElm = getNode("textbox");
|
||||
gTooltipElm = getNode("tooltip");
|
||||
|
||||
gButtonElm.addEventListener("focus", gFocusHandler, false);
|
||||
gButtonElm.addEventListener("blur", gBlurHandler, false);
|
||||
gTextboxElm.addEventListener("focus", gFocusHandler, false);
|
||||
gTextboxElm.addEventListener("blur", gBlurHandler, false);
|
||||
|
||||
// The aria-describedby is changed on DOM focus. Accessible description
|
||||
// should be updated when a11y focus is fired.
|
||||
gQueue = new eventQueue(nsIAccessibleEvent.EVENT_FOCUS);
|
||||
gQueue.onFinish = function()
|
||||
{
|
||||
gButtonElm.removeEventListener("focus", gFocusHandler, false);
|
||||
gButtonElm.removeEventListener("blur", gBlurHandler, false);
|
||||
gTextboxElm.removeEventListener("focus", gFocusHandler, false);
|
||||
gTextboxElm.removeEventListener("blur", gBlurHandler, false);
|
||||
}
|
||||
|
||||
var checker = new actionChecker("It's a tooltip");
|
||||
gQueue.push(new synthFocus("button", checker));
|
||||
gQueue.push(new synthTab("textbox", checker));
|
||||
|
||||
gQueue.invoke(); // Will call SimpleTest.finish();
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addA11yLoadEvent(doTests);
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<a target="_blank"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=520709"
|
||||
title="mochitest to ensure name/description are updated on a11y focus if they were changed on DOM focus">
|
||||
Mozilla Bug 520709
|
||||
</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
|
||||
<div id="tooltip" style="display: none" aria-hidden="true">It's a tooltip</div>
|
||||
<button id="button">button</button>
|
||||
<input id="textbox">
|
||||
|
||||
<div id="eventdump"></div>
|
||||
</body>
|
||||
</html>
|
Загрузка…
Ссылка в новой задаче