michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: // Name tests described by "markuprules.xml" file. michael@0: michael@0: var gNameRulesFileURL = "markuprules.xml"; michael@0: michael@0: var gRuleDoc = null; michael@0: michael@0: // Debuggin stuff. michael@0: var gDumpToConsole = false; michael@0: michael@0: /** michael@0: * Start name tests. Run through markup elements and test names for test michael@0: * element (see namerules.xml for details). michael@0: */ michael@0: function testNames() michael@0: { michael@0: //enableLogging("tree,stack"); // debugging michael@0: michael@0: var request = new XMLHttpRequest(); michael@0: request.open("get", gNameRulesFileURL, false); michael@0: request.send(); michael@0: michael@0: gRuleDoc = request.responseXML; michael@0: michael@0: var markupElms = evaluateXPath(gRuleDoc, "//rules/rulesample/markup"); michael@0: gTestIterator.iterateMarkups(markupElms); michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: // Private section. michael@0: michael@0: /** michael@0: * Helper class to interate through name tests. michael@0: */ michael@0: var gTestIterator = michael@0: { michael@0: iterateMarkups: function gTestIterator_iterateMarkups(aMarkupElms) michael@0: { michael@0: this.markupElms = aMarkupElms; michael@0: michael@0: this.iterateNext(); michael@0: }, michael@0: michael@0: iterateRules: function gTestIterator_iterateRules(aElm, aContainer, michael@0: aRuleSetElm, aRuleElms, michael@0: aTestID) michael@0: { michael@0: this.ruleSetElm = aRuleSetElm; michael@0: this.ruleElms = aRuleElms; michael@0: this.elm = aElm; michael@0: this.container = aContainer; michael@0: this.testID = aTestID; michael@0: michael@0: this.iterateNext(); michael@0: }, michael@0: michael@0: iterateNext: function gTestIterator_iterateNext() michael@0: { michael@0: if (this.markupIdx == -1) { michael@0: this.markupIdx++; michael@0: testNamesForMarkup(this.markupElms[this.markupIdx]); michael@0: return; michael@0: } michael@0: michael@0: this.ruleIdx++; michael@0: if (this.ruleIdx == this.ruleElms.length) { michael@0: // When test is finished then name is empty and no explict-name. michael@0: var defaultName = this.ruleSetElm.hasAttribute("defaultName") ? michael@0: this.ruleSetElm.getAttribute("defaultName") : null; michael@0: testName(this.elm, defaultName, michael@0: "Default name test (" + gTestIterator.testID + "). "); michael@0: testAbsentAttrs(this.elm, {"explicit-name" : "true"}); michael@0: michael@0: this.markupIdx++; michael@0: if (this.markupIdx == this.markupElms.length) { michael@0: //disableLogging("tree"); // debugging michael@0: SimpleTest.finish(); michael@0: return; michael@0: } michael@0: michael@0: this.ruleIdx = -1; michael@0: michael@0: if (gDumpToConsole) { michael@0: dump("\nPend next markup processing. Wait for reorder event on " + michael@0: prettyName(document) + "'\n"); michael@0: } michael@0: waitForEvent(EVENT_REORDER, document, testNamesForMarkup, michael@0: null, this.markupElms[this.markupIdx]); michael@0: michael@0: document.body.removeChild(this.container); michael@0: return; michael@0: } michael@0: michael@0: testNameForRule(this.elm, this.ruleElms[this.ruleIdx]); michael@0: }, michael@0: michael@0: markupElms: null, michael@0: markupIdx: -1, michael@0: rulesetElm: null, michael@0: ruleElms: null, michael@0: ruleIdx: -1, michael@0: elm: null, michael@0: container: null, michael@0: testID: "" michael@0: }; michael@0: michael@0: /** michael@0: * Process every 'markup' element and test names for it. Used by testNames michael@0: * function. michael@0: */ michael@0: function testNamesForMarkup(aMarkupElm) michael@0: { michael@0: if (gDumpToConsole) michael@0: dump("\nProcessing markup '" + aMarkupElm.getAttribute("id") + "'\n"); michael@0: michael@0: var div = document.createElement("div"); michael@0: div.setAttribute("id", "test"); michael@0: michael@0: var child = aMarkupElm.firstChild; michael@0: while (child) { michael@0: var newChild = document.importNode(child, true); michael@0: div.appendChild(newChild); michael@0: child = child.nextSibling; michael@0: } michael@0: michael@0: if (gDumpToConsole) { michael@0: dump("\nProcessing markup. Wait for reorder event on " + michael@0: prettyName(document) + "'\n"); michael@0: } michael@0: waitForEvent(EVENT_REORDER, document, testNamesForMarkupRules, michael@0: null, aMarkupElm, div); michael@0: michael@0: document.body.appendChild(div); michael@0: } michael@0: michael@0: function testNamesForMarkupRules(aMarkupElm, aContainer) michael@0: { michael@0: var testID = aMarkupElm.getAttribute("id"); michael@0: if (gDumpToConsole) michael@0: dump("\nProcessing markup rules '" + testID + "'\n"); michael@0: michael@0: var serializer = new XMLSerializer(); michael@0: michael@0: var expr = "//html/body/div[@id='test']/" + aMarkupElm.getAttribute("ref"); michael@0: var elm = evaluateXPath(document, expr, htmlDocResolver)[0]; michael@0: michael@0: var ruleId = aMarkupElm.getAttribute("ruleset"); michael@0: var ruleElm = gRuleDoc.querySelector("[id='" + ruleId + "']"); michael@0: var ruleElms = getRuleElmsByRulesetId(ruleId); michael@0: michael@0: var processMarkupRules = michael@0: gTestIterator.iterateRules.bind(gTestIterator, elm, aContainer, michael@0: ruleElm, ruleElms, testID); michael@0: michael@0: // Images may be recreated after we append them into subtree. We need to wait michael@0: // in this case. If we are on profiling enabled build then stack tracing michael@0: // works and thus let's log instead. Note, that works if you enabled logging michael@0: // (refer to testNames() function). michael@0: if (isAccessible(elm) || isLogged("stack")) michael@0: processMarkupRules(); michael@0: else michael@0: waitForEvent(EVENT_SHOW, elm, processMarkupRules); michael@0: } michael@0: michael@0: /** michael@0: * Test name for current rule and current 'markup' element. Used by michael@0: * testNamesForMarkup function. michael@0: */ michael@0: function testNameForRule(aElm, aRuleElm) michael@0: { michael@0: if (aRuleElm.hasAttribute("attr")) { michael@0: if (gDumpToConsole) { michael@0: dump("\nProcessing rule { attr: " + aRuleElm.getAttribute("attr") +" }\n"); michael@0: } michael@0: michael@0: testNameForAttrRule(aElm, aRuleElm); michael@0: michael@0: } else if (aRuleElm.hasAttribute("elm")) { michael@0: if (gDumpToConsole) { michael@0: dump("\nProcessing rule { elm: " + aRuleElm.getAttribute("elm") + michael@0: ", elmattr: " + aRuleElm.getAttribute("elmattr") +" }\n"); michael@0: } michael@0: michael@0: testNameForElmRule(aElm, aRuleElm); michael@0: michael@0: } else if (aRuleElm.getAttribute("fromsubtree") == "true") { michael@0: if (gDumpToConsole) { michael@0: dump("\nProcessing rule { fromsubtree: " + michael@0: aRuleElm.getAttribute("fromsubtree") +" }\n"); michael@0: } michael@0: michael@0: testNameForSubtreeRule(aElm, aRuleElm); michael@0: } michael@0: } michael@0: michael@0: function testNameForAttrRule(aElm, aRule) michael@0: { michael@0: var name = ""; michael@0: michael@0: var attr = aRule.getAttribute("attr"); michael@0: var attrValue = aElm.getAttribute(attr); michael@0: michael@0: var type = aRule.getAttribute("type"); michael@0: if (type == "string") { michael@0: name = attrValue; michael@0: michael@0: } else if (type == "ref" && attrValue) { michael@0: var ids = attrValue.split(/\s+/); michael@0: for (var idx = 0; idx < ids.length; idx++) { michael@0: var labelElm = getNode(ids[idx]); michael@0: if (name != "") michael@0: name += " "; michael@0: michael@0: name += labelElm.getAttribute("textequiv"); michael@0: } michael@0: } michael@0: michael@0: var msg = "Attribute '" + attr + "' test (" + gTestIterator.testID + "). "; michael@0: testName(aElm, name, msg); michael@0: michael@0: if (aRule.getAttribute("explict-name") != "false") michael@0: testAttrs(aElm, {"explicit-name" : "true"}, true); michael@0: else michael@0: testAbsentAttrs(aElm, {"explicit-name" : "true"}); michael@0: michael@0: // If @recreated attribute is used then this attribute change recreates an michael@0: // accessible. Wait for reorder event in this case or otherwise proceed next michael@0: // test immediately. michael@0: if (aRule.hasAttribute("recreated")) { michael@0: waitForEvent(EVENT_REORDER, aElm.parentNode, michael@0: gTestIterator.iterateNext, gTestIterator); michael@0: aElm.removeAttribute(attr); michael@0: michael@0: } else if (aRule.hasAttribute("textchanged")) { michael@0: waitForEvent(EVENT_TEXT_INSERTED, aElm, michael@0: gTestIterator.iterateNext, gTestIterator); michael@0: aElm.removeAttribute(attr); michael@0: michael@0: } else if (aRule.hasAttribute("contentchanged")) { michael@0: waitForEvent(EVENT_REORDER, aElm, michael@0: gTestIterator.iterateNext, gTestIterator); michael@0: aElm.removeAttribute(attr); michael@0: michael@0: } else { michael@0: aElm.removeAttribute(attr); michael@0: gTestIterator.iterateNext(); michael@0: } michael@0: } michael@0: michael@0: function testNameForElmRule(aElm, aRule) michael@0: { michael@0: var labelElm; michael@0: michael@0: var tagname = aRule.getAttribute("elm"); michael@0: var attrname = aRule.getAttribute("elmattr"); michael@0: if (attrname) { michael@0: var filter = { michael@0: acceptNode: function filter_acceptNode(aNode) michael@0: { michael@0: if (aNode.localName == this.mLocalName && michael@0: aNode.getAttribute(this.mAttrName) == this.mAttrValue) michael@0: return NodeFilter.FILTER_ACCEPT; michael@0: michael@0: return NodeFilter.FILTER_SKIP; michael@0: }, michael@0: michael@0: mLocalName: tagname, michael@0: mAttrName: attrname, michael@0: mAttrValue: aElm.getAttribute("id") michael@0: }; michael@0: michael@0: var treeWalker = document.createTreeWalker(document.body, michael@0: NodeFilter.SHOW_ELEMENT, michael@0: filter); michael@0: labelElm = treeWalker.nextNode(); michael@0: michael@0: } else { michael@0: // if attrname is empty then look for the element in subtree. michael@0: labelElm = aElm.getElementsByTagName(tagname)[0]; michael@0: if (!labelElm) michael@0: labelElm = aElm.getElementsByTagName("html:" + tagname)[0]; michael@0: } michael@0: michael@0: if (!labelElm) { michael@0: ok(false, msg + " Failed to find '" + tagname + "' element."); michael@0: gTestIterator.iterateNext(); michael@0: return; michael@0: } michael@0: michael@0: var msg = "Element '" + tagname + "' test (" + gTestIterator.testID + ")."; michael@0: testName(aElm, labelElm.getAttribute("textequiv"), msg); michael@0: testAttrs(aElm, {"explicit-name" : "true"}, true); michael@0: michael@0: var parentNode = labelElm.parentNode; michael@0: michael@0: if (gDumpToConsole) { michael@0: dump("\nProcessed elm rule. Wait for reorder event on " + michael@0: prettyName(parentNode) + "\n"); michael@0: } michael@0: waitForEvent(EVENT_REORDER, parentNode, michael@0: gTestIterator.iterateNext, gTestIterator); michael@0: michael@0: parentNode.removeChild(labelElm); michael@0: } michael@0: michael@0: function testNameForSubtreeRule(aElm, aRule) michael@0: { michael@0: var msg = "From subtree test (" + gTestIterator.testID + ")."; michael@0: testName(aElm, aElm.getAttribute("textequiv"), msg); michael@0: testAbsentAttrs(aElm, {"explicit-name" : "true"}); michael@0: michael@0: if (gDumpToConsole) { michael@0: dump("\nProcessed from subtree rule. Wait for reorder event on " + michael@0: prettyName(aElm) + "\n"); michael@0: } michael@0: waitForEvent(EVENT_REORDER, aElm, gTestIterator.iterateNext, gTestIterator); michael@0: michael@0: while (aElm.firstChild) michael@0: aElm.removeChild(aElm.firstChild); michael@0: } michael@0: michael@0: /** michael@0: * Return array of 'rule' elements. Used in conjunction with michael@0: * getRuleElmsFromRulesetElm() function. michael@0: */ michael@0: function getRuleElmsByRulesetId(aRulesetId) michael@0: { michael@0: var expr = "//rules/ruledfn/ruleset[@id='" + aRulesetId + "']"; michael@0: var rulesetElm = evaluateXPath(gRuleDoc, expr); michael@0: return getRuleElmsFromRulesetElm(rulesetElm[0]); michael@0: } michael@0: michael@0: function getRuleElmsFromRulesetElm(aRulesetElm) michael@0: { michael@0: var rulesetId = aRulesetElm.getAttribute("ref"); michael@0: if (rulesetId) michael@0: return getRuleElmsByRulesetId(rulesetId); michael@0: michael@0: var ruleElms = []; michael@0: michael@0: var child = aRulesetElm.firstChild; michael@0: while (child) { michael@0: if (child.localName == "ruleset") michael@0: ruleElms = ruleElms.concat(getRuleElmsFromRulesetElm(child)); michael@0: if (child.localName == "rule") michael@0: ruleElms.push(child); michael@0: michael@0: child = child.nextSibling; michael@0: } michael@0: michael@0: return ruleElms; michael@0: } michael@0: michael@0: /** michael@0: * Helper method to evaluate xpath expression. michael@0: */ michael@0: function evaluateXPath(aNode, aExpr, aResolver) michael@0: { michael@0: var xpe = new XPathEvaluator(); michael@0: michael@0: var resolver = aResolver; michael@0: if (!resolver) { michael@0: var node = aNode.ownerDocument == null ? michael@0: aNode.documentElement : aNode.ownerDocument.documentElement; michael@0: resolver = xpe.createNSResolver(node); michael@0: } michael@0: michael@0: var result = xpe.evaluate(aExpr, aNode, resolver, 0, null); michael@0: var found = []; michael@0: var res; michael@0: while (res = result.iterateNext()) michael@0: found.push(res); michael@0: michael@0: return found; michael@0: } michael@0: michael@0: function htmlDocResolver(aPrefix) { michael@0: var ns = { michael@0: 'html' : 'http://www.w3.org/1999/xhtml' michael@0: }; michael@0: return ns[aPrefix] || null; michael@0: }