diff options
Diffstat (limited to 'accessible/tests/mochitest/attributes.js')
-rw-r--r-- | accessible/tests/mochitest/attributes.js | 490 |
1 files changed, 490 insertions, 0 deletions
diff --git a/accessible/tests/mochitest/attributes.js b/accessible/tests/mochitest/attributes.js new file mode 100644 index 0000000000..d41a56f959 --- /dev/null +++ b/accessible/tests/mochitest/attributes.js @@ -0,0 +1,490 @@ +/* import-globals-from common.js */ + +// ////////////////////////////////////////////////////////////////////////////// +// Object attributes. + +/** + * Test object attributes. + * + * @param aAccOrElmOrID [in] the accessible identifier + * @param aAttrs [in] the map of expected object attributes + * (name/value pairs) + * @param aSkipUnexpectedAttrs [in] points this function doesn't fail if + * unexpected attribute is encountered + */ +function testAttrs(aAccOrElmOrID, aAttrs, aSkipUnexpectedAttrs) { + testAttrsInternal(aAccOrElmOrID, aAttrs, aSkipUnexpectedAttrs); +} + +/** + * Test object attributes that must not be present. + * + * @param aAccOrElmOrID [in] the accessible identifier + * @param aAbsentAttrs [in] map of attributes that should not be + * present (name/value pairs) + */ +function testAbsentAttrs(aAccOrElmOrID, aAbsentAttrs) { + testAttrsInternal(aAccOrElmOrID, {}, true, aAbsentAttrs); +} + +/** + * Test object attributes that aren't right, but should be (todo) + * + * @param aAccOrElmOrID [in] the accessible identifier + * @param aKey [in] attribute name + * @param aExpectedValue [in] expected attribute value + */ +function todoAttr(aAccOrElmOrID, aKey, aExpectedValue) { + var accessible = getAccessible(aAccOrElmOrID); + if (!accessible) { + return; + } + + var attrs = null; + try { + attrs = accessible.attributes; + } catch (e) {} + + todo_is(attrs.getStringProperty(aKey), aExpectedValue, "attributes match"); +} + +/** + * Test CSS based object attributes. + */ +function testCSSAttrs(aID) { + var node = document.getElementById(aID); + var computedStyle = document.defaultView.getComputedStyle(node); + + var attrs = { + display: computedStyle.display, + "text-align": computedStyle.textAlign, + "text-indent": computedStyle.textIndent, + "margin-left": computedStyle.marginLeft, + "margin-right": computedStyle.marginRight, + "margin-top": computedStyle.marginTop, + "margin-bottom": computedStyle.marginBottom, + }; + testAttrs(aID, attrs, true); +} + +/** + * Test the accessible that it doesn't have CSS-based object attributes. + */ +function testAbsentCSSAttrs(aID) { + var attrs = { + display: "", + "text-align": "", + "text-indent": "", + "margin-left": "", + "margin-right": "", + "margin-top": "", + "margin-bottom": "", + }; + testAbsentAttrs(aID, attrs); +} + +/** + * Test group object attributes (posinset, setsize and level) and + * nsIAccessible::groupPosition() method. + * + * @param aAccOrElmOrID [in] the ID, DOM node or accessible + * @param aPosInSet [in] the value of 'posinset' attribute + * @param aSetSize [in] the value of 'setsize' attribute + * @param aLevel [in, optional] the value of 'level' attribute + */ +function testGroupAttrs(aAccOrElmOrID, aPosInSet, aSetSize, aLevel) { + var acc = getAccessible(aAccOrElmOrID); + var levelObj = {}, + posInSetObj = {}, + setSizeObj = {}; + acc.groupPosition(levelObj, setSizeObj, posInSetObj); + + if (aPosInSet && aSetSize) { + is( + posInSetObj.value, + aPosInSet, + "Wrong group position (posinset) for " + prettyName(aAccOrElmOrID) + ); + is( + setSizeObj.value, + aSetSize, + "Wrong size of the group (setsize) for " + prettyName(aAccOrElmOrID) + ); + + let attrs = { + posinset: String(aPosInSet), + setsize: String(aSetSize), + }; + testAttrs(aAccOrElmOrID, attrs, true); + } + + if (aLevel) { + is( + levelObj.value, + aLevel, + "Wrong group level for " + prettyName(aAccOrElmOrID) + ); + + let attrs = { level: String(aLevel) }; + testAttrs(aAccOrElmOrID, attrs, true); + } +} + +function testGroupParentAttrs(aAccOrElmOrID, aChildItemCount, aIsHierarchical) { + testAttrs( + aAccOrElmOrID, + { "child-item-count": String(aChildItemCount) }, + true + ); + + if (aIsHierarchical) { + testAttrs(aAccOrElmOrID, { hierarchical: "true" }, true); + } else { + testAbsentAttrs(aAccOrElmOrID, { hierarchical: "true" }); + } +} + +// ////////////////////////////////////////////////////////////////////////////// +// Text attributes. + +/** + * Test text attributes. + * + * @param aID [in] the ID of DOM element having text + * accessible + * @param aOffset [in] the offset inside text accessible to fetch + * text attributes + * @param aAttrs [in] the map of expected text attributes + * (name/value pairs) exposed at the offset + * @param aDefAttrs [in] the map of expected text attributes + * (name/value pairs) exposed on hyper text + * accessible + * @param aStartOffset [in] expected start offset where text attributes + * are applied + * @param aEndOffset [in] expected end offset where text attribute + * are applied + * @param aSkipUnexpectedAttrs [in] points the function doesn't fail if + * unexpected attribute is encountered + */ +function testTextAttrs( + aID, + aOffset, + aAttrs, + aDefAttrs, + aStartOffset, + aEndOffset, + aSkipUnexpectedAttrs +) { + var accessible = getAccessible(aID, [nsIAccessibleText]); + if (!accessible) { + return; + } + + var startOffset = { value: -1 }; + var endOffset = { value: -1 }; + + // do not include attributes exposed on hyper text accessible + var attrs = getTextAttributes( + aID, + accessible, + false, + aOffset, + startOffset, + endOffset + ); + + if (!attrs) { + return; + } + + var errorMsg = " for " + aID + " at offset " + aOffset; + + is(startOffset.value, aStartOffset, "Wrong start offset" + errorMsg); + is(endOffset.value, aEndOffset, "Wrong end offset" + errorMsg); + + compareAttrs(errorMsg, attrs, aAttrs, aSkipUnexpectedAttrs); + + // include attributes exposed on hyper text accessible + var expectedAttrs = {}; + for (let name in aAttrs) { + expectedAttrs[name] = aAttrs[name]; + } + + for (let name in aDefAttrs) { + if (!(name in expectedAttrs)) { + expectedAttrs[name] = aDefAttrs[name]; + } + } + + attrs = getTextAttributes( + aID, + accessible, + true, + aOffset, + startOffset, + endOffset + ); + + if (!attrs) { + return; + } + + compareAttrs(errorMsg, attrs, expectedAttrs, aSkipUnexpectedAttrs); +} + +/** + * Test default text attributes. + * + * @param aID [in] the ID of DOM element having text + * accessible + * @param aDefAttrs [in] the map of expected text attributes + * (name/value pairs) + * @param aSkipUnexpectedAttrs [in] points the function doesn't fail if + * unexpected attribute is encountered + */ +function testDefaultTextAttrs(aID, aDefAttrs, aSkipUnexpectedAttrs) { + var accessible = getAccessible(aID, [nsIAccessibleText]); + if (!accessible) { + return; + } + + var defAttrs = null; + try { + defAttrs = accessible.defaultTextAttributes; + } catch (e) {} + + if (!defAttrs) { + ok(false, "Can't get default text attributes for " + aID); + return; + } + + var errorMsg = ". Getting default text attributes for " + aID; + compareAttrs(errorMsg, defAttrs, aDefAttrs, aSkipUnexpectedAttrs); +} + +/** + * Test text attributes for wrong offset. + */ +function testTextAttrsWrongOffset(aID, aOffset) { + var res = false; + try { + var s = {}, + e = {}; + // Bug 1602031 + // eslint-disable-next-line no-undef + var acc = getAccessible(ID, [nsIAccessibleText]); + acc.getTextAttributes(false, 157, s, e); + } catch (ex) { + res = true; + } + + ok( + res, + "text attributes are calculated successfully at wrong offset " + + aOffset + + " for " + + prettyName(aID) + ); +} + +const kNormalFontWeight = function equalsToNormal(aWeight) { + return aWeight <= 400; +}; + +const kBoldFontWeight = function equalsToBold(aWeight) { + return aWeight > 400; +}; + +// The pt font size of the input element can vary by Linux distro. +const kInputFontSize = WIN + ? "10pt" + : MAC + ? "8pt" + : function() { + return true; + }; + +const kAbsentFontFamily = function(aFontFamily) { + return aFontFamily != "sans-serif"; +}; +const kInputFontFamily = function(aFontFamily) { + return aFontFamily != "sans-serif"; +}; + +const kMonospaceFontFamily = function(aFontFamily) { + return aFontFamily != "monospace"; +}; +const kSansSerifFontFamily = function(aFontFamily) { + return aFontFamily != "sans-serif"; +}; +const kSerifFontFamily = function(aFontFamily) { + return aFontFamily != "serif"; +}; + +const kCursiveFontFamily = LINUX ? "DejaVu Serif" : "Comic Sans MS"; + +/** + * Return used font from the given computed style. + */ +function fontFamily(aComputedStyle) { + var name = aComputedStyle.fontFamily; + switch (name) { + case "monospace": + return kMonospaceFontFamily; + case "sans-serif": + return kSansSerifFontFamily; + case "serif": + return kSerifFontFamily; + default: + return name; + } +} + +/** + * Build an object of default text attributes expected for the given accessible. + * + * @param aID [in] identifier of accessible + * @param aFontSize [in] font size + * @param aFontWeight [in, optional] kBoldFontWeight or kNormalFontWeight, + * default value is kNormalFontWeight + */ +function buildDefaultTextAttrs(aID, aFontSize, aFontWeight, aFontFamily) { + var elm = getNode(aID); + var computedStyle = document.defaultView.getComputedStyle(elm); + var bgColor = + computedStyle.backgroundColor == "rgba(0, 0, 0, 0)" + ? "rgb(255, 255, 255)" + : computedStyle.backgroundColor; + + var defAttrs = { + "font-style": computedStyle.fontStyle, + "font-size": aFontSize, + "background-color": bgColor, + "font-weight": aFontWeight ? aFontWeight : kNormalFontWeight, + color: computedStyle.color, + "font-family": aFontFamily ? aFontFamily : fontFamily(computedStyle), + "text-position": computedStyle.verticalAlign, + }; + + return defAttrs; +} + +// ////////////////////////////////////////////////////////////////////////////// +// Private. + +function getTextAttributes( + aID, + aAccessible, + aIncludeDefAttrs, + aOffset, + aStartOffset, + aEndOffset +) { + // This function expects the passed in accessible to already be queried for + // nsIAccessibleText. + var attrs = null; + try { + attrs = aAccessible.getTextAttributes( + aIncludeDefAttrs, + aOffset, + aStartOffset, + aEndOffset + ); + } catch (e) {} + + if (attrs) { + return attrs; + } + + ok(false, "Can't get text attributes for " + aID); + return null; +} + +function testAttrsInternal( + aAccOrElmOrID, + aAttrs, + aSkipUnexpectedAttrs, + aAbsentAttrs +) { + var accessible = getAccessible(aAccOrElmOrID); + if (!accessible) { + return; + } + + var attrs = null; + try { + attrs = accessible.attributes; + } catch (e) {} + + if (!attrs) { + ok(false, "Can't get object attributes for " + prettyName(aAccOrElmOrID)); + return; + } + + var errorMsg = " for " + prettyName(aAccOrElmOrID); + compareAttrs(errorMsg, attrs, aAttrs, aSkipUnexpectedAttrs, aAbsentAttrs); +} + +function compareAttrs( + aErrorMsg, + aAttrs, + aExpectedAttrs, + aSkipUnexpectedAttrs, + aAbsentAttrs +) { + // Check if all obtained attributes are expected and have expected value. + for (let prop of aAttrs.enumerate()) { + if (!(prop.key in aExpectedAttrs)) { + if (!aSkipUnexpectedAttrs) { + ok( + false, + "Unexpected attribute '" + + prop.key + + "' having '" + + prop.value + + "'" + + aErrorMsg + ); + } + } else { + var msg = "Attribute '" + prop.key + "' has wrong value" + aErrorMsg; + var expectedValue = aExpectedAttrs[prop.key]; + + if (typeof expectedValue == "function") { + ok(expectedValue(prop.value), msg); + } else { + is(prop.value, expectedValue, msg); + } + } + } + + // Check if all expected attributes are presented. + for (let name in aExpectedAttrs) { + var value = ""; + try { + value = aAttrs.getStringProperty(name); + } catch (e) {} + + if (!value) { + ok(false, "There is no expected attribute '" + name + "' " + aErrorMsg); + } + } + + // Check if all unexpected attributes are absent. + if (aAbsentAttrs) { + for (var name in aAbsentAttrs) { + var wasFound = false; + + for (let prop of aAttrs.enumerate()) { + if (prop.key == name) { + wasFound = true; + } + } + } + + ok( + !wasFound, + "There is an unexpected attribute '" + name + "' " + aErrorMsg + ); + } +} |