--- a/accessible/tests/browser/.eslintrc
+++ b/accessible/tests/browser/.eslintrc
@@ -6,20 +6,23 @@
"globals": {
// Content scripts have global 'content' object
"content": true,
// Defined in accessible/tests/mochitest/ common.js, name.js, states.js
"prettyName": true,
"statesToString": true,
"eventTypeToString": true,
+ "testAttrs": true,
+ "testAbsentAttrs": true,
"testName": true,
"testDescr": true,
"testStates": true,
"testRelation": true,
+ "testValue": true,
"testAccessibleTree": true,
"isAccessible": true,
"getAccessibleDOMNodeID": true,
// Defined for all accessibility browser tests.
"addAccessibleTask": true,
"BrowserTestUtils": true,
"ContentTask": true,
--- a/accessible/tests/browser/browser.ini
+++ b/accessible/tests/browser/browser.ini
@@ -9,21 +9,24 @@ support-files =
doc_treeupdate_removal.xhtml
doc_treeupdate_visibility.html
doc_treeupdate_whitespace.html
!/accessible/tests/mochitest/*.js
!/accessible/tests/mochitest/letters.gif
!/accessible/tests/mochitest/moz.png
# Caching tests
+[browser_caching_attributes.js]
[browser_caching_description.js]
[browser_caching_name.js]
skip-if = e10s
[browser_caching_relations.js]
[browser_caching_states.js]
+[browser_caching_value.js]
+skip-if = e10s # Bug 1276721: QueryInterface is not working with proxies.
# Events tests
[browser_events_caretmove.js]
[browser_events_hide.js]
[browser_events_show.js]
[browser_events_statechange.js]
[browser_events_textchange.js]
new file mode 100644
--- /dev/null
+++ b/accessible/tests/browser/browser_caching_attributes.js
@@ -0,0 +1,117 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+'use strict';
+
+/* global EVENT_FOCUS */
+
+loadScripts({ name: 'attributes.js', dir: MOCHITESTS_DIR });
+
+/**
+ * Default textbox accessible attributes.
+ */
+const defaultAttributes = {
+ 'margin-top': '0px',
+ 'margin-right': '0px',
+ 'margin-bottom': '0px',
+ 'margin-left': '0px',
+ 'text-align': 'start',
+ 'text-indent': '0px',
+ 'id': 'textbox',
+ 'tag': 'input',
+ 'display': 'inline'
+};
+
+/**
+ * Test data has the format of:
+ * {
+ * desc {String} description for better logging
+ * expected {Object} expected attributes for given accessibles
+ * unexpected {Object} unexpected attributes for given accessibles
+ *
+ * action {?Function*} an optional action that yields a change in
+ * attributes
+ * attrs {?Array} an optional list of attributes to update
+ * waitFor {?Number} an optional event to wait for
+ * }
+ */
+const attributesTests = [{
+ desc: 'Initiall accessible attributes',
+ expected: defaultAttributes,
+ unexpected: {
+ 'line-number': '1',
+ 'explicit-name': 'true',
+ 'container-live': 'polite',
+ 'live': 'polite'
+ }
+}, {
+ desc: '@line-number attribute is present when textbox is focused',
+ action: function*(browser) {
+ yield invokeFocus(browser, 'textbox');
+ },
+ waitFor: EVENT_FOCUS,
+ expected: Object.assign({}, defaultAttributes, { 'line-number': '1' }),
+ unexpected: {
+ 'explicit-name': 'true',
+ 'container-live': 'polite',
+ 'live': 'polite'
+ }
+}, {
+ desc: '@aria-live sets container-live and live attributes',
+ attrs: [{
+ attr: 'aria-live',
+ value: 'polite'
+ }],
+ expected: Object.assign({}, defaultAttributes, {
+ 'line-number': '1',
+ 'container-live': 'polite',
+ 'live': 'polite'
+ }),
+ unexpected: {
+ 'explicit-name': 'true'
+ }
+}, {
+ desc: '@title attribute sets explicit-name attribute to true',
+ attrs: [{
+ attr: 'title',
+ value: 'textbox'
+ }],
+ expected: Object.assign({}, defaultAttributes, {
+ 'line-number': '1',
+ 'explicit-name': 'true',
+ 'container-live': 'polite',
+ 'live': 'polite'
+ }),
+ unexpected: {}
+}];
+
+/**
+ * Test caching of accessible object attributes
+ */
+addAccessibleTask(`
+ <input id="textbox" value="hello">`,
+ function* (browser, accDoc) {
+ let textbox = findAccessibleChildByID(accDoc, 'textbox');
+ for (let { desc, action, attrs, expected, waitFor, unexpected } of attributesTests) {
+ info(desc);
+ let onUpdate;
+
+ if (waitFor) {
+ onUpdate = waitForEvent(waitFor, 'textbox');
+ }
+
+ if (action) {
+ yield action(browser);
+ } else if (attrs) {
+ for (let { attr, value } of attrs) {
+ yield invokeSetAttribute(browser, 'textbox', attr, value);
+ }
+ }
+
+ yield onUpdate;
+ testAttrs(textbox, expected);
+ testAbsentAttrs(textbox, unexpected);
+ }
+ }
+);
new file mode 100644
--- /dev/null
+++ b/accessible/tests/browser/browser_caching_value.js
@@ -0,0 +1,155 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+'use strict';
+
+/* global nsIAccessibleValue, EVENT_VALUE_CHANGE, EVENT_TEXT_VALUE_CHANGE */
+
+loadScripts({ name: 'value.js', dir: MOCHITESTS_DIR });
+
+/**
+ * Test data has the format of:
+ * {
+ * desc {String} description for better logging
+ * id {String} given accessible DOMNode ID
+ * expected {String} expected value for a given accessible
+ * action {?Function*} an optional action that yields a value change
+ * attrs {?Array} an optional list of attributes to update
+ * waitFor {?Number} an optional value change event to wait for
+ * }
+ */
+const valueTests = [{
+ desc: 'Initially value is set to 1st element of select',
+ id: 'select',
+ expected: '1st'
+}, {
+ desc: 'Value should update to 3rd when 3 is pressed',
+ id: 'select',
+ action: function*(browser) {
+ yield invokeFocus(browser, 'select');
+ yield BrowserTestUtils.synthesizeKey('3', {}, browser);
+ },
+ waitFor: EVENT_TEXT_VALUE_CHANGE,
+ expected: '3rd'
+}, {
+ desc: 'Initially value is set to @aria-valuenow for slider',
+ id: 'slider',
+ expected: ['5', 5, 0, 7, 0]
+}, {
+ desc: 'Value should change when @aria-valuenow is updated',
+ id: 'slider',
+ attrs: [{
+ attr: 'aria-valuenow',
+ value: '6'
+ }],
+ waitFor: EVENT_VALUE_CHANGE,
+ expected: ['6', 6, 0, 7, 0]
+}, {
+ desc: 'Value should change when @aria-valuetext is set',
+ id: 'slider',
+ attrs: [{
+ attr: 'aria-valuetext',
+ value: 'plain'
+ }],
+ waitFor: EVENT_TEXT_VALUE_CHANGE,
+ expected: ['plain', 6, 0, 7, 0]
+}, {
+ desc: 'Value should change when @aria-valuetext is updated',
+ id: 'slider',
+ attrs: [{
+ attr: 'aria-valuetext',
+ value: 'hey!'
+ }],
+ waitFor: EVENT_TEXT_VALUE_CHANGE,
+ expected: ['hey!', 6, 0, 7, 0]
+}, {
+ desc: 'Value should change to @aria-valuetext when @aria-valuenow is removed',
+ id: 'slider',
+ attrs: [{
+ attr: 'aria-valuenow'
+ }],
+ expected: ['hey!', 0, 0, 7, 0]
+}, {
+ desc: 'Initially value is not set for combobox',
+ id: 'combobox',
+ expected: ''
+}, {
+ desc: 'Value should change when @value attribute is updated',
+ id: 'combobox',
+ attrs: [{
+ attr: 'value',
+ value: 'hello'
+ }],
+ waitFor: EVENT_TEXT_VALUE_CHANGE,
+ expected: 'hello'
+}, {
+ desc: 'Initially value corresponds to @value attribute for progress',
+ id: 'progress',
+ expected: '22%'
+}, {
+ desc: 'Value should change when @value attribute is updated',
+ id: 'progress',
+ attrs: [{
+ attr: 'value',
+ value: '50'
+ }],
+ waitFor: EVENT_VALUE_CHANGE,
+ expected: '50%'
+}, {
+ desc: 'Initially value corresponds to @value attribute for range',
+ id: 'range',
+ expected: '6'
+}, {
+ desc: 'Value should change when slider is moved',
+ id: 'range',
+ action: function*(browser) {
+ yield invokeFocus(browser, 'range');
+ yield BrowserTestUtils.synthesizeKey('VK_LEFT', {}, browser);
+ },
+ waitFor: EVENT_VALUE_CHANGE,
+ expected: '5'
+}];
+
+/**
+ * Test caching of accessible object values
+ */
+addAccessibleTask(`
+ <div id="slider" role="slider" aria-valuenow="5"
+ aria-valuemin="0" aria-valuemax="7">slider</div>
+ <select id="select">
+ <option>1st</option>
+ <option>2nd</option>
+ <option>3rd</option>
+ </select>
+ <input id="combobox" role="combobox" aria-autocomplete="inline">
+ <progress id="progress" value="22" max="100"></progress>
+ <input type="range" id="range" min="0" max="10" value="6">`,
+ function* (browser, accDoc) {
+ for (let { desc, id, action, attrs, expected, waitFor } of valueTests) {
+ info(desc);
+ let acc = findAccessibleChildByID(accDoc, id);
+ let onUpdate;
+
+ if (waitFor) {
+ onUpdate = waitForEvent(waitFor, id);
+ }
+
+ if (action) {
+ yield action(browser);
+ } else if (attrs) {
+ for (let { attr, value } of attrs) {
+ yield invokeSetAttribute(browser, id, attr, value);
+ }
+ }
+
+ yield onUpdate;
+ if (Array.isArray(expected)) {
+ acc.QueryInterface(nsIAccessibleValue);
+ testValue(acc, ...expected);
+ } else {
+ is(acc.value, expected, `Correct value for ${prettyName(acc)}`);
+ }
+ }
+ }
+);
--- a/accessible/tests/browser/events.js
+++ b/accessible/tests/browser/events.js
@@ -5,28 +5,32 @@
'use strict';
/* global nsIAccessibleEvent, nsIAccessibleDocument,
nsIAccessibleStateChangeEvent, nsIAccessibleTextChangeEvent */
/* exported EVENT_REORDER, EVENT_SHOW, EVENT_TEXT_INSERTED, EVENT_TEXT_REMOVED,
EVENT_DOCUMENT_LOAD_COMPLETE, EVENT_HIDE, EVENT_TEXT_CARET_MOVED,
EVENT_DESCRIPTION_CHANGE, EVENT_NAME_CHANGE, EVENT_STATE_CHANGE,
+ EVENT_VALUE_CHANGE, EVENT_TEXT_VALUE_CHANGE, EVENT_FOCUS,
waitForEvent, waitForMultipleEvents */
const EVENT_DOCUMENT_LOAD_COMPLETE = nsIAccessibleEvent.EVENT_DOCUMENT_LOAD_COMPLETE;
const EVENT_HIDE = nsIAccessibleEvent.EVENT_HIDE;
const EVENT_REORDER = nsIAccessibleEvent.EVENT_REORDER;
const EVENT_SHOW = nsIAccessibleEvent.EVENT_SHOW;
const EVENT_STATE_CHANGE = nsIAccessibleEvent.EVENT_STATE_CHANGE;
const EVENT_TEXT_CARET_MOVED = nsIAccessibleEvent.EVENT_TEXT_CARET_MOVED;
const EVENT_TEXT_INSERTED = nsIAccessibleEvent.EVENT_TEXT_INSERTED;
const EVENT_TEXT_REMOVED = nsIAccessibleEvent.EVENT_TEXT_REMOVED;
const EVENT_DESCRIPTION_CHANGE = nsIAccessibleEvent.EVENT_DESCRIPTION_CHANGE;
const EVENT_NAME_CHANGE = nsIAccessibleEvent.EVENT_NAME_CHANGE;
+const EVENT_VALUE_CHANGE = nsIAccessibleEvent.EVENT_VALUE_CHANGE;
+const EVENT_TEXT_VALUE_CHANGE = nsIAccessibleEvent.EVENT_TEXT_VALUE_CHANGE;
+const EVENT_FOCUS = nsIAccessibleEvent.EVENT_FOCUS;
/**
* Describe an event in string format.
* @param {nsIAccessibleEvent} event event to strigify
*/
function eventToString(event) {
let type = eventTypeToString(event.eventType);
let info = `Event type: ${type}`;
--- a/accessible/tests/mochitest/common.js
+++ b/accessible/tests/mochitest/common.js
@@ -286,16 +286,19 @@ function getAccessible(aAccOrElmOrID, aI
if (!aInterfaces)
return acc;
if (!(aInterfaces instanceof Array))
aInterfaces = [ aInterfaces ];
for (var index = 0; index < aInterfaces.length; index++) {
+ if (acc instanceof aInterfaces[index]) {
+ continue;
+ }
try {
acc.QueryInterface(aInterfaces[index]);
} catch (e) {
if (!(aDoNotFailIf & DONOTFAIL_IF_NO_INTERFACE))
ok(false, "Can't query " + aInterfaces[index] + " for " + aAccOrElmOrID);
return null;
}