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:
Alexander Surkov 2009-10-06 15:50:47 +08:00
parent e39a82fa4a
commit 5a8e76ad8d
4 changed files with 332 additions and 131 deletions

View File

@ -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 \

View File

@ -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"; }
}

View File

@ -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();
}

View File

@ -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>