diff options
Diffstat (limited to 'accessible/tests/mochitest/states.js')
-rw-r--r-- | accessible/tests/mochitest/states.js | 365 |
1 files changed, 365 insertions, 0 deletions
diff --git a/accessible/tests/mochitest/states.js b/accessible/tests/mochitest/states.js new file mode 100644 index 0000000000..c8ce9dd79d --- /dev/null +++ b/accessible/tests/mochitest/states.js @@ -0,0 +1,365 @@ +// ////////////////////////////////////////////////////////////////////////////// +// Helper functions for accessible states testing. +// +// requires: +// common.js +// role.js +// +// ////////////////////////////////////////////////////////////////////////////// +/* import-globals-from common.js */ +/* import-globals-from role.js */ + +// ////////////////////////////////////////////////////////////////////////////// +// State constants + +// const STATE_BUSY is defined in common.js +const STATE_ANIMATED = nsIAccessibleStates.STATE_ANIMATED; +const STATE_CHECKED = nsIAccessibleStates.STATE_CHECKED; +const STATE_CHECKABLE = nsIAccessibleStates.STATE_CHECKABLE; +const STATE_COLLAPSED = nsIAccessibleStates.STATE_COLLAPSED; +const STATE_DEFAULT = nsIAccessibleStates.STATE_DEFAULT; +const STATE_EXPANDED = nsIAccessibleStates.STATE_EXPANDED; +const STATE_EXTSELECTABLE = nsIAccessibleStates.STATE_EXTSELECTABLE; +const STATE_FLOATING = nsIAccessibleStates.STATE_FLOATING; +const STATE_FOCUSABLE = nsIAccessibleStates.STATE_FOCUSABLE; +const STATE_FOCUSED = nsIAccessibleStates.STATE_FOCUSED; +const STATE_HASPOPUP = nsIAccessibleStates.STATE_HASPOPUP; +const STATE_INVALID = nsIAccessibleStates.STATE_INVALID; +const STATE_INVISIBLE = nsIAccessibleStates.STATE_INVISIBLE; +const STATE_LINKED = nsIAccessibleStates.STATE_LINKED; +const STATE_MIXED = nsIAccessibleStates.STATE_MIXED; +const STATE_MULTISELECTABLE = nsIAccessibleStates.STATE_MULTISELECTABLE; +const STATE_OFFSCREEN = nsIAccessibleStates.STATE_OFFSCREEN; +const STATE_PRESSED = nsIAccessibleStates.STATE_PRESSED; +const STATE_PROTECTED = nsIAccessibleStates.STATE_PROTECTED; +const STATE_READONLY = nsIAccessibleStates.STATE_READONLY; +const STATE_REQUIRED = nsIAccessibleStates.STATE_REQUIRED; +const STATE_SELECTABLE = nsIAccessibleStates.STATE_SELECTABLE; +const STATE_SELECTED = nsIAccessibleStates.STATE_SELECTED; +const STATE_TRAVERSED = nsIAccessibleStates.STATE_TRAVERSED; +const STATE_UNAVAILABLE = nsIAccessibleStates.STATE_UNAVAILABLE; + +const EXT_STATE_ACTIVE = nsIAccessibleStates.EXT_STATE_ACTIVE; +const EXT_STATE_CURRENT = nsIAccessibleStates.EXT_STATE_CURRENT; +const EXT_STATE_DEFUNCT = nsIAccessibleStates.EXT_STATE_DEFUNCT; +const EXT_STATE_EDITABLE = nsIAccessibleStates.EXT_STATE_EDITABLE; +const EXT_STATE_ENABLED = nsIAccessibleStates.EXT_STATE_ENABLED; +const EXT_STATE_EXPANDABLE = nsIAccessibleStates.EXT_STATE_EXPANDABLE; +const EXT_STATE_HORIZONTAL = nsIAccessibleStates.EXT_STATE_HORIZONTAL; +const EXT_STATE_MODAL = nsIAccessibleStates.EXT_STATE_MODAL; +const EXT_STATE_MULTI_LINE = nsIAccessibleStates.EXT_STATE_MULTI_LINE; +const EXT_STATE_PINNED = nsIAccessibleStates.EXT_STATE_PINNED; +const EXT_STATE_SENSITIVE = nsIAccessibleStates.EXT_STATE_SENSITIVE; +const EXT_STATE_SINGLE_LINE = nsIAccessibleStates.EXT_STATE_SINGLE_LINE; +const EXT_STATE_STALE = nsIAccessibleStates.EXT_STATE_STALE; +const EXT_STATE_SUPPORTS_AUTOCOMPLETION = + nsIAccessibleStates.EXT_STATE_SUPPORTS_AUTOCOMPLETION; +const EXT_STATE_VERTICAL = nsIAccessibleStates.EXT_STATE_VERTICAL; +const EXT_STATE_SELECTABLE_TEXT = nsIAccessibleStates.EXT_STATE_SELECTABLE_TEXT; +const EXT_STATE_OPAQUE = nsIAccessibleStates.EXT_STATE_OPAQUE; + +const kOrdinalState = false; +const kExtraState = 1; + +// ////////////////////////////////////////////////////////////////////////////// +// Test functions + +/** + * Tests the states and extra states of the given accessible. + * Also tests for unwanted states and extra states. + * In addition, the function performs a few plausibility checks derived from the + * sstates and extra states passed in. + * + * @param aAccOrElmOrID The accessible, DOM element or ID to be tested. + * @param aState The state bits that are wanted. + * @param aExtraState The extra state bits that are wanted. + * @param aAbsentState State bits that are not wanted. + * @param aAbsentExtraState Extra state bits that are not wanted. + * @param aTestName The test name. + */ +function testStates( + aAccOrElmOrID, + aState, + aExtraState, + aAbsentState, + aAbsentExtraState, + aTestName +) { + var [state, extraState] = getStates(aAccOrElmOrID); + var role = getRole(aAccOrElmOrID); + var id = + prettyName(aAccOrElmOrID) + (aTestName ? " [" + aTestName + "]" : ""); + + // Primary test. + if (aState) { + isState(state & aState, aState, false, "wrong state bits for " + id + "!"); + } + + if (aExtraState) { + isState( + extraState & aExtraState, + aExtraState, + true, + "wrong extra state bits for " + id + "!" + ); + } + + if (aAbsentState) { + isState( + state & aAbsentState, + 0, + false, + "state bits should not be present in ID " + id + "!" + ); + } + + if (aAbsentExtraState) { + isState( + extraState & aAbsentExtraState, + 0, + true, + "extraState bits should not be present in ID " + id + "!" + ); + } + + // Additional test. + + // focused/focusable + if (state & STATE_FOCUSED) { + isState( + state & STATE_FOCUSABLE, + STATE_FOCUSABLE, + false, + "Focused " + id + " must be focusable!" + ); + } + + if (aAbsentState && aAbsentState & STATE_FOCUSABLE) { + isState( + state & STATE_FOCUSED, + 0, + false, + "Not focusable " + id + " must be not focused!" + ); + } + + // multiline/singleline + if (extraState & EXT_STATE_MULTI_LINE) { + isState( + extraState & EXT_STATE_SINGLE_LINE, + 0, + true, + "Multiline " + id + " cannot be singleline!" + ); + } + + if (extraState & EXT_STATE_SINGLE_LINE) { + isState( + extraState & EXT_STATE_MULTI_LINE, + 0, + true, + "Singleline " + id + " cannot be multiline!" + ); + } + + // expanded/collapsed/expandable + if (state & STATE_COLLAPSED || state & STATE_EXPANDED) { + isState( + extraState & EXT_STATE_EXPANDABLE, + EXT_STATE_EXPANDABLE, + true, + "Collapsed or expanded " + id + " must be expandable!" + ); + } + + if (state & STATE_COLLAPSED) { + isState( + state & STATE_EXPANDED, + 0, + false, + "Collapsed " + id + " cannot be expanded!" + ); + } + + if (state & STATE_EXPANDED) { + isState( + state & STATE_COLLAPSED, + 0, + false, + "Expanded " + id + " cannot be collapsed!" + ); + } + + if (aAbsentState && extraState & EXT_STATE_EXPANDABLE) { + if (aAbsentState & STATE_EXPANDED) { + isState( + state & STATE_COLLAPSED, + STATE_COLLAPSED, + false, + "Not expanded " + id + " must be collapsed!" + ); + } else if (aAbsentState & STATE_COLLAPSED) { + isState( + state & STATE_EXPANDED, + STATE_EXPANDED, + false, + "Not collapsed " + id + " must be expanded!" + ); + } + } + + // checked/mixed/checkable + if ( + state & STATE_CHECKED || + (state & STATE_MIXED && + role != ROLE_TOGGLE_BUTTON && + role != ROLE_PROGRESSBAR) + ) { + isState( + state & STATE_CHECKABLE, + STATE_CHECKABLE, + false, + "Checked or mixed element must be checkable!" + ); + } + + if (state & STATE_CHECKED) { + isState( + state & STATE_MIXED, + 0, + false, + "Checked element cannot be state mixed!" + ); + } + + if (state & STATE_MIXED) { + isState( + state & STATE_CHECKED, + 0, + false, + "Mixed element cannot be state checked!" + ); + } + + // selected/selectable + if (state & STATE_SELECTED && !(aAbsentState & STATE_SELECTABLE)) { + isState( + state & STATE_SELECTABLE, + STATE_SELECTABLE, + false, + "Selected element must be selectable!" + ); + } +} + +/** + * Tests an accessible and its sub tree for the passed in state bits. + * Used to make sure that states are propagated to descendants, for example the + * STATE_UNAVAILABLE from a container to its children. + * + * @param aAccOrElmOrID The accessible, DOM element or ID to be tested. + * @param aState The state bits that are wanted. + * @param aExtraState The extra state bits that are wanted. + * @param aAbsentState State bits that are not wanted. + */ +function testStatesInSubtree(aAccOrElmOrID, aState, aExtraState, aAbsentState) { + // test accessible and its subtree for propagated states. + var acc = getAccessible(aAccOrElmOrID); + if (!acc) { + return; + } + + if (getRole(acc) != ROLE_TEXT_LEAF) { + // Right now, text leafs don't get tested because the states are not being + // propagated. + testStates(acc, aState, aExtraState, aAbsentState); + } + + // Iterate over its children to see if the state got propagated. + var children = null; + try { + children = acc.children; + } catch (e) {} + ok(children, "Could not get children for " + aAccOrElmOrID + "!"); + + if (children) { + for (var i = 0; i < children.length; i++) { + var childAcc = children.queryElementAt(i, nsIAccessible); + testStatesInSubtree(childAcc, aState, aExtraState, aAbsentState); + } + } +} + +/** + * Fails if no defunct state on the accessible. + */ +function testIsDefunct(aAccessible, aTestName) { + var id = prettyName(aAccessible) + (aTestName ? " [" + aTestName + "]" : ""); + var [, /* state*/ extraState] = getStates(aAccessible); + isState( + extraState & EXT_STATE_DEFUNCT, + EXT_STATE_DEFUNCT, + true, + "no defuct state for " + id + "!" + ); +} + +function getStringStates(aAccOrElmOrID) { + var [state, extraState] = getStates(aAccOrElmOrID); + return statesToString(state, extraState); +} + +function getStates(aAccOrElmOrID) { + var acc = getAccessible(aAccOrElmOrID); + if (!acc) { + return [0, 0]; + } + + var state = {}, + extraState = {}; + acc.getState(state, extraState); + + return [state.value, extraState.value]; +} + +/** + * Return true if the accessible has given states. + */ +function hasState(aAccOrElmOrID, aState, aExtraState) { + var [state, exstate] = getStates(aAccOrElmOrID); + return ( + (aState ? state & aState : true) && + (aExtraState ? exstate & aExtraState : true) + ); +} + +// ////////////////////////////////////////////////////////////////////////////// +// Private implementation details + +/** + * Analogy of SimpleTest.is function used to compare states. + */ +function isState(aState1, aState2, aIsExtraStates, aMsg) { + if (aState1 == aState2) { + ok(true, aMsg); + return; + } + + var got = "0"; + if (aState1) { + got = statesToString( + aIsExtraStates ? 0 : aState1, + aIsExtraStates ? aState1 : 0 + ); + } + + var expected = "0"; + if (aState2) { + expected = statesToString( + aIsExtraStates ? 0 : aState2, + aIsExtraStates ? aState2 : 0 + ); + } + + ok(false, aMsg + "got '" + got + "', expected '" + expected + "'"); +} |