Bug 1473690 - add selectable ingegration tests. r=eeejay, jchen

MozReview-Commit-ID: 9ka7CjOJcew
This commit is contained in:
Yura Zenevich 2018-07-18 15:58:01 -04:00
parent c35c35bb11
commit dd1eb27a85
9 changed files with 145 additions and 135 deletions

View File

@ -219,8 +219,9 @@ this.ContentControl.prototype = {
}
}
// Action invoked will be presented on checkable state change.
if (!Utils.getState(aAccessible).contains(States.CHECKABLE)) {
// Action invoked will be presented on checked/selected state change.
if (!Utils.getState(aAccessible).contains(States.CHECKABLE) &&
!Utils.getState(aAccessible).contains(States.SELECTABLE)) {
this._contentScope.get().sendAsyncMessage("AccessFu:Present",
Presentation.actionInvoked(aAccessible, "click"));
}

View File

@ -163,25 +163,12 @@ this.EventManager.prototype = {
}
case Events.STATE_CHANGE:
{
let event = aEvent.QueryInterface(Ci.nsIAccessibleStateChangeEvent);
let state = Utils.getState(event);
const event = aEvent.QueryInterface(Ci.nsIAccessibleStateChangeEvent);
const state = Utils.getState(event);
if (state.contains(States.CHECKED)) {
if (aEvent.accessible.role === Roles.SWITCH) {
this.present(
Presentation.
actionInvoked(aEvent.accessible,
event.isEnabled ? "on" : "off"));
} else {
this.present(
Presentation.
actionInvoked(aEvent.accessible,
event.isEnabled ? "check" : "uncheck"));
}
this.present(Presentation.checked(aEvent.accessible));
} else if (state.contains(States.SELECTED)) {
this.present(
Presentation.
actionInvoked(aEvent.accessible,
event.isEnabled ? "select" : "unselect"));
this.present(Presentation.selected(aEvent.accessible));
}
break;
}

View File

@ -770,8 +770,12 @@ var UtteranceGenerator = { // jshint ignore:line
}
},
/**
* Add localized state information to output data.
* Note: We do not expose checked and selected states, we let TalkBack do it for us
* there. This is because we expose the checked information on the node info itself.
*/
_addState: function _addState(aOutput, aState, aRoleStr) {
if (aState.contains(States.UNAVAILABLE)) {
aOutput.push({string: "stateUnavailable"});
}
@ -780,22 +784,6 @@ var UtteranceGenerator = { // jshint ignore:line
aOutput.push({string: "stateReadonly"});
}
// Don't utter this in Jelly Bean, we let TalkBack do it for us there.
// This is because we expose the checked information on the node itself.
// XXX: this means the checked state is always appended to the end,
// regardless of the utterance ordering preference.
if ((Utils.AndroidSdkVersion < 16 || Utils.MozBuildApp === "browser") &&
aState.contains(States.CHECKABLE)) {
let checked = aState.contains(States.CHECKED);
let statetr;
if (aRoleStr === "switch") {
statetr = checked ? "stateOn" : "stateOff";
} else {
statetr = checked ? "stateChecked" : "stateNotChecked";
}
aOutput.push({string: statetr});
}
if (aState.contains(States.PRESSED)) {
aOutput.push({string: "statePressed"});
}
@ -817,10 +805,6 @@ var UtteranceGenerator = { // jshint ignore:line
if (aState.contains(States.HASPOPUP)) {
aOutput.push({string: "stateHasPopup"});
}
if (aState.contains(States.SELECTED)) {
aOutput.push({string: "stateSelected"});
}
},
_getListUtterance:

View File

@ -98,25 +98,38 @@ class AndroidPresentor {
}
/**
* An object's action has been invoked.
* @param {nsIAccessible} aObject the object that has been invoked.
* @param {string} aActionName the name of the action.
* An object's check action has been invoked.
* Note: Checkable objects use TalkBack's text derived from the event state, so we don't
* populate the text here.
* @param {nsIAccessible} aAccessible the object that has been invoked.
*/
actionInvoked(aObject, aActionName) {
let state = Utils.getState(aObject);
// Checkable objects use TalkBack's text derived from the event state,
// so we don't populate the text here.
let text = null;
if (!state.contains(States.CHECKABLE)) {
text = Utils.localize(UtteranceGenerator.genForAction(aObject,
aActionName));
}
checked(aAccessible) {
return [{
eventType: AndroidEvents.VIEW_CLICKED,
text,
checked: state.contains(States.CHECKED)
checked: Utils.getState(aAccessible).contains(States.CHECKED)
}];
}
/**
* An object's select action has been invoked.
* @param {nsIAccessible} aAccessible the object that has been invoked.
*/
selected(aAccessible) {
return [{
eventType: AndroidEvents.VIEW_CLICKED,
selected: Utils.getState(aAccessible).contains(States.SELECTED)
}];
}
/**
* An object's action has been invoked.
* @param {nsIAccessible} aAccessible the object that has been invoked.
* @param {string} aActionName the name of the action.
*/
actionInvoked(aAccessible, aActionName) {
return [{
eventType: AndroidEvents.VIEW_CLICKED,
text: Utils.localize(UtteranceGenerator.genForAction(aAccessible, aActionName))
}];
}
@ -292,6 +305,7 @@ class AndroidPresentor {
checkable: state.contains(States.CHECKABLE),
checked: state.contains(States.CHECKED),
editable: state.contains(States.EDITABLE),
selected: state.contains(States.SELECTED)
};
if (EDIT_TEXT_ROLES.has(aContext.accessible.role)) {

View File

@ -368,6 +368,13 @@ class AccessFuContentTestRunner {
`Got ${JSON.stringify(aEvent.text)}, expected ${JSON.stringify(aExpected)}.`);
}
eventInfoMatches(aEvent, aExpected) {
for (let key in aExpected) {
is(aEvent[key], aExpected[key], `Event info matches for ${key}. ` +
`Got ${aEvent[key]}, expected ${aExpected[key]}.`);
}
}
androidScrollForward() {
this.sendMessage({
name: "AccessFu:AndroidScroll",

View File

@ -44,7 +44,8 @@
evt = await runner.moveNext("Simple",
AndroidEvents.VIEW_ACCESSIBILITY_FOCUSED);
runner.eventTextMatches(evt, ["many option", "not checked", "check button", "First item", "list", "1 item"]);
runner.eventInfoMatches(evt, { checked: false });
runner.eventTextMatches(evt, ["many option", "check button", "First item", "list", "1 item"]);
evt = await runner.activateCurrent(0,
AndroidEvents.VIEW_CLICKED);
@ -76,7 +77,8 @@
evt = await runner.moveNext("Simple",
AndroidEvents.VIEW_ACCESSIBILITY_FOCUSED);
runner.eventTextMatches(evt, ["Light", "off", "switch"]);
runner.eventInfoMatches(evt, { checked: false });
runner.eventTextMatches(evt, ["Light", "switch"]);
evt = await runner.activateCurrent(0,
AndroidEvents.VIEW_CLICKED);
@ -88,7 +90,8 @@
evt = await runner.movePrevious("Simple",
AndroidEvents.VIEW_ACCESSIBILITY_FOCUSED);
runner.eventTextMatches(evt, ["Light", "on", "switch"]);
runner.eventInfoMatches(evt, { checked: true });
runner.eventTextMatches(evt, ["Light", "switch"]);
evt = await runner.activateCurrent(0,
AndroidEvents.VIEW_CLICKED);
@ -124,7 +127,8 @@
evt = await runner.movePrevious("Simple",
AndroidEvents.VIEW_ACCESSIBILITY_FOCUSED);
runner.eventTextMatches(evt, ["many option", "checked", "check button"]);
runner.eventInfoMatches(evt, { checked: true });
runner.eventTextMatches(evt, ["many option", "check button"]);
evt = await runner.activateCurrent(0,
AndroidEvents.VIEW_CLICKED);
@ -176,7 +180,8 @@
evt = await runner.moveNext("FormElement",
AndroidEvents.VIEW_ACCESSIBILITY_FOCUSED);
runner.eventTextMatches(evt, ["such app", "many option", "not checked", "check button", "First item", "list", "1 item"]);
runner.eventInfoMatches(evt, { checked: false });
runner.eventTextMatches(evt, ["such app", "many option", "check button", "First item", "list", "1 item"]);
evt = await runner.moveNext("FormElement",
AndroidEvents.VIEW_ACCESSIBILITY_FOCUSED);
@ -188,7 +193,8 @@
evt = await runner.movePrevious("FormElement",
AndroidEvents.VIEW_ACCESSIBILITY_FOCUSED);
runner.eventTextMatches(evt, ["many option", "not checked", "check button", "First item", "list", "1 item"]);
runner.eventInfoMatches(evt, { checked: false });
runner.eventTextMatches(evt, ["many option", "check button", "First item", "list", "1 item"]);
evt = await runner.movePrevious("FormElement",
AndroidEvents.VIEW_ACCESSIBILITY_FOCUSED);
@ -210,7 +216,8 @@
runner.eventTextMatches(evt, ["such app", "wow", "heading level 1"]);
evt = await runner.moveNext("Simple",
AndroidEvents.VIEW_ACCESSIBILITY_FOCUSED);
runner.eventTextMatches(evt, ["many option", "not checked", "check button", "First item", "list", "1 item"]);
runner.eventInfoMatches(evt, { checked: false });
runner.eventTextMatches(evt, ["many option", "check button", "First item", "list", "1 item"]);
evt = await runner.moveFirst("Simple",
AndroidEvents.VIEW_ACCESSIBILITY_FOCUSED);
@ -411,7 +418,8 @@
evt = await runner.moveNext("Simple",
AndroidEvents.VIEW_ACCESSIBILITY_FOCUSED);
runner.eventTextMatches(evt, ["Light", "off", "switch"]);
runner.eventInfoMatches(evt, { checked: false });
runner.eventTextMatches(evt, ["Light", "switch"]);
evt = await runner.moveNext("Simple",
AndroidEvents.VIEW_ACCESSIBILITY_FOCUSED);

View File

@ -253,11 +253,10 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=753984
}, {
// Test selected tab
accOrElmOrID: "tab1",
expectedUtterance: [[{"string": "pagetablist"},
{"string": "stateSelected"}, {"string": "pagetab"},
expectedUtterance: [[{"string": "pagetablist"}, {"string": "pagetab"},
{"string": "objItemOfN", "args": [1, 2]}, "Account"], ["Account",
{"string": "stateSelected"}, {"string": "pagetab"},
{"string": "objItemOfN", "args": [1, 2]}, {"string": "pagetablist"}]
{"string": "pagetab"}, {"string": "objItemOfN", "args": [1, 2]},
{"string": "pagetablist"}]
],
expectedBraille: [[{"string": "pagetabAbbr"},
{"string": "objItemOfN", "args": [1, 2]}, "Account"], ["Account",
@ -277,27 +276,19 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=753984
}, {
// Landing on this label should mimic landing on the checkbox.
accOrElmOrID: "label1",
expectedUtterance: [[{"string": "stateNotChecked"},
{"string": "checkbutton"}, "Orange"], ["Orange",
{"string": "stateNotChecked"}, {"string": "checkbutton"}]],
expectedBraille: [[{"string": "stateUncheckedAbbr"}, "Orange"],
["Orange", {"string": "stateUncheckedAbbr"}]]
expectedUtterance: [[{"string": "checkbutton"}, "Orange"], ["Orange",
{"string": "checkbutton"}]],
expectedBraille: [["Orange"], ["Orange"]]
}, {
// Here we get a top-level view of the form.
accOrElmOrID: "form1",
expectedUtterance: [[{"string": "label"},
{"string": "stateNotChecked"}, {"string": "checkbutton"}, "Orange",
"Orange", {"string": "stateNotChecked"}, {"string": "checkbutton"},
"Blue", {"string": "label"}, "Blue"], ["Orange",
{"string": "stateNotChecked"}, {"string": "checkbutton"}, "Orange",
{"string": "label"}, "Blue", {"string": "stateNotChecked"},
expectedUtterance: [[{"string": "label"}, {"string": "checkbutton"}, "Orange",
"Orange", {"string": "checkbutton"}, "Blue", {"string": "label"}, "Blue"],
["Orange", {"string": "checkbutton"}, "Orange", {"string": "label"}, "Blue",
{"string": "checkbutton"}, "Blue", {"string": "label"}]],
expectedBraille: [[{"string": "labelAbbr"},
{"string": "stateUncheckedAbbr"}, "Orange", "Orange",
{"string": "stateUncheckedAbbr"}, "Blue", {"string": "labelAbbr"},
"Blue"], ["Orange", {"string": "stateUncheckedAbbr"}, "Orange",
{"string": "labelAbbr"}, "Blue", {"string": "stateUncheckedAbbr"},
"Blue", {"string": "labelAbbr"}]]
expectedBraille: [[{"string": "labelAbbr"}, "Orange", "Orange", "Blue",
{"string": "labelAbbr"}, "Blue"], ["Orange", "Orange",
{"string": "labelAbbr"}, "Blue", "Blue", {"string": "labelAbbr"}]]
}, {
// This is a non-nesting label.
accOrElmOrID: "label2",
@ -308,19 +299,15 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=753984
}, {
// This is a distinct control.
accOrElmOrID: "input2",
expectedUtterance: [[{"string": "stateNotChecked"},
{"string": "checkbutton"}, "Blue"], ["Blue",
{"string": "stateNotChecked"}, {"string": "checkbutton"}]],
expectedBraille: [[{"string": "stateUncheckedAbbr"}, "Blue"], ["Blue",
{"string": "stateUncheckedAbbr"}]]
expectedUtterance: [[ {"string": "checkbutton"}, "Blue"],
["Blue", {"string": "checkbutton"}]],
expectedBraille: [["Blue"], ["Blue"]]
}, {
// This is a nested control.
accOrElmOrID: "input1",
expectedUtterance: [[{"string": "stateNotChecked"},
{"string": "checkbutton"}, "Orange"], ["Orange",
{"string": "stateNotChecked"}, {"string": "checkbutton"}]],
expectedBraille: [[{"string": "stateUncheckedAbbr"}, "Orange"],
["Orange", {"string": "stateUncheckedAbbr"}]]
expectedUtterance: [[ {"string": "checkbutton"}, "Orange"], ["Orange",
{"string": "checkbutton"}]],
expectedBraille: [["Orange"], ["Orange"]]
}, {
// Landing on this label should mimic landing on the entry.
accOrElmOrID: "label3",
@ -350,30 +337,20 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=753984
["Secret Password", {"string": "passwordtextAbbr"}]]
}, {
accOrElmOrID: "input5",
expectedUtterance: [[{"string": "stateChecked"},
{"string": "checkbutton"}, "Boring label"], ["Boring label",
{"string": "stateChecked"}, {"string": "checkbutton"}]],
expectedBraille: [[{"string": "stateCheckedAbbr"}, "Boring label"],
["Boring label", {"string": "stateCheckedAbbr"}]]
expectedUtterance: [[{"string": "checkbutton"}, "Boring label"],
["Boring label", {"string": "checkbutton"}]],
expectedBraille: [["Boring label"], ["Boring label"]]
}, {
accOrElmOrID: "radio_unselected",
expectedUtterance: [[{"string": "stateNotChecked"},
{"string": "radiobutton"}, "any old radio button"],
["any old radio button", {"string": "stateNotChecked"},
{"string": "radiobutton"}]
expectedUtterance: [[{"string": "radiobutton"}, "any old radio button"],
["any old radio button", {"string": "radiobutton"}]
],
expectedBraille: [
[{"string": "stateUncheckedAbbr"}, "any old radio button"],
["any old radio button", {"string": "stateUncheckedAbbr"}]]
expectedBraille: [["any old radio button"], ["any old radio button"]]
}, {
accOrElmOrID: "radio_selected",
expectedUtterance: [[{"string": "stateChecked"},
{"string": "radiobutton"}, "a unique radio button"],
["a unique radio button", {"string": "stateChecked"},
{"string": "radiobutton"}]],
expectedBraille: [
[{"string": "stateCheckedAbbr"}, "a unique radio button"],
["a unique radio button", {"string": "stateCheckedAbbr"}]]
expectedUtterance: [[{"string": "radiobutton"}, "a unique radio button"],
["a unique radio button", {"string": "radiobutton"}]],
expectedBraille: [["a unique radio button"], ["a unique radio button"]]
}, {
accOrElmOrID: "togglebutton_notpressed",
expectedUtterance: [[{"string": "togglebutton"}, "I am not pressed"],
@ -437,8 +414,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=753984
}, {
accOrElmOrID: "gridcell3",
oldAccOrElmOrID: "grid",
expectedUtterance: [[{"string": "stateSelected"}, "5"],
["5", {"string": "stateSelected"}]],
expectedUtterance: [["5"], ["5"]],
expectedBraille: [["5"], ["5"]],
}, {
accOrElmOrID: "frequency",
@ -451,9 +427,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=753984
}, {
accOrElmOrID: "selected-combobox-option",
oldAccOrElmOrID: "frequency",
expectedUtterance: [[{"string": "stateSelected"},
{"string": "comboboxoption"}, "15 min"], ["15 min",
{"string": "stateSelected"}, {"string": "comboboxoption"}]],
expectedUtterance: [[{"string": "comboboxoption"}, "15 min"],
["15 min", {"string": "comboboxoption"}]],
expectedBraille: [[{"string": "comboboxoptionAbbr"}, "15 min"], [
"15 min", {"string": "comboboxoptionAbbr"}]]
}, {
@ -484,19 +459,14 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=753984
expectedBraille: [["Last sync: 30min ago"], ["Last sync: 30min ago"]]
}, {
accOrElmOrID: "switch-1",
expectedUtterance: [[{"string": "stateOn"}, {"string": "switch"},
"Simple switch"], ["Simple switch", {"string": "stateOn"},
{"string": "switch"}]],
expectedBraille: [[{"string": "stateCheckedAbbr"}, "Simple switch"],
["Simple switch", {"string": "stateCheckedAbbr"}]]
expectedUtterance: [[{"string": "switch"}, "Simple switch"],
["Simple switch", {"string": "switch"}]],
expectedBraille: [["Simple switch"], ["Simple switch"]]
}, {
accOrElmOrID: "switch-2",
expectedUtterance: [[{"string": "stateOff"},
{"string": "switch"}, "Another switch"], ["Another switch",
{"string": "stateOff"}, {"string": "switch"}]],
expectedBraille: [
[{"string": "stateUncheckedAbbr"}, "Another switch"],
["Another switch", {"string": "stateUncheckedAbbr"}]]
expectedUtterance: [[{"string": "switch"}, "Another switch"],
["Another switch", {"string": "switch"}]],
expectedBraille: [["Another switch"], ["Another switch"]]
}];
// Test all possible utterance order preference values.

View File

@ -197,14 +197,21 @@ class AccessibilityTest : BaseSessionTest() {
})
}
private fun waitUntilClick(checked: Boolean) {
private fun waitUntilClick(checked: Boolean? = null, selected: Boolean? = null) {
sessionRule.waitUntilCalled(object : EventDelegate {
@AssertCalled(count = 1)
override fun onClicked(event: AccessibilityEvent) {
assertThat("Checked state matches", event.isChecked, equalTo(checked))
var nodeId = getSourceId(event)
var node = provider.createAccessibilityNodeInfo(nodeId)
assertThat("Checkbox node is checked", node.isChecked, equalTo(checked))
if (checked != null) {
assertThat("Event's checked state matches", event.isChecked, equalTo(checked))
assertThat("Checkbox node has correct checked state", node.isChecked, equalTo(checked))
}
if (selected != null) {
assertThat("Selectable node has correct selected state", node.isSelected, equalTo(selected))
}
}
})
}
@ -393,9 +400,37 @@ class AccessibilityTest : BaseSessionTest() {
})
provider.performAction(nodeId, AccessibilityNodeInfo.ACTION_CLICK, null)
waitUntilClick(true);
waitUntilClick(checked = true)
provider.performAction(nodeId, AccessibilityNodeInfo.ACTION_CLICK, null)
waitUntilClick(false);
waitUntilClick(checked = false)
}
@Test fun testSelectable() {
var nodeId = View.NO_ID
sessionRule.session.loadString(
"""<ul style="list-style-type: none;" role="listbox">
<li id="li" role="option" onclick="this.setAttribute('aria-selected',
this.getAttribute('aria-selected') == 'true' ? 'false' : 'true')">1</li>
</ul>""","text/html")
sessionRule.waitForPageStop()
provider.performAction(nodeId, AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null)
sessionRule.waitUntilCalled(object : EventDelegate {
@AssertCalled(count = 1)
override fun onAccessibilityFocused(event: AccessibilityEvent) {
nodeId = getSourceId(event)
var node = provider.createAccessibilityNodeInfo(nodeId)
assertThat("Selectable node is clickable", node.isClickable, equalTo(true))
assertThat("Selectable node is not selected", node.isSelected, equalTo(false))
assertThat("Selectable node has correct role", node.text.toString(), equalTo("1 option list box"))
}
})
provider.performAction(nodeId, AccessibilityNodeInfo.ACTION_CLICK, null)
waitUntilClick(selected = true)
provider.performAction(nodeId, AccessibilityNodeInfo.ACTION_CLICK, null)
waitUntilClick(selected = false)
}
}

View File

@ -379,6 +379,7 @@ public class SessionAccessibility {
node.setPassword(message.getBoolean("password"));
node.setFocusable(message.getBoolean("focusable"));
node.setFocused(message.getBoolean("focused"));
node.setSelected(message.getBoolean("selected"));
node.setClassName(message.getString("className", "android.view.View"));
@ -435,6 +436,9 @@ public class SessionAccessibility {
if (message.containsKey("checked")) {
node.setChecked(message.getBoolean("checked"));
}
if (message.containsKey("selected")) {
node.setSelected(message.getBoolean("selected"));
}
}
private void sendAccessibilityEvent(final GeckoBundle message) {