|
1 //////////////////////////////////////////////////////////////////////////////// |
|
2 // Name tests described by "markuprules.xml" file. |
|
3 |
|
4 var gNameRulesFileURL = "markuprules.xml"; |
|
5 |
|
6 var gRuleDoc = null; |
|
7 |
|
8 // Debuggin stuff. |
|
9 var gDumpToConsole = false; |
|
10 |
|
11 /** |
|
12 * Start name tests. Run through markup elements and test names for test |
|
13 * element (see namerules.xml for details). |
|
14 */ |
|
15 function testNames() |
|
16 { |
|
17 //enableLogging("tree,stack"); // debugging |
|
18 |
|
19 var request = new XMLHttpRequest(); |
|
20 request.open("get", gNameRulesFileURL, false); |
|
21 request.send(); |
|
22 |
|
23 gRuleDoc = request.responseXML; |
|
24 |
|
25 var markupElms = evaluateXPath(gRuleDoc, "//rules/rulesample/markup"); |
|
26 gTestIterator.iterateMarkups(markupElms); |
|
27 } |
|
28 |
|
29 //////////////////////////////////////////////////////////////////////////////// |
|
30 // Private section. |
|
31 |
|
32 /** |
|
33 * Helper class to interate through name tests. |
|
34 */ |
|
35 var gTestIterator = |
|
36 { |
|
37 iterateMarkups: function gTestIterator_iterateMarkups(aMarkupElms) |
|
38 { |
|
39 this.markupElms = aMarkupElms; |
|
40 |
|
41 this.iterateNext(); |
|
42 }, |
|
43 |
|
44 iterateRules: function gTestIterator_iterateRules(aElm, aContainer, |
|
45 aRuleSetElm, aRuleElms, |
|
46 aTestID) |
|
47 { |
|
48 this.ruleSetElm = aRuleSetElm; |
|
49 this.ruleElms = aRuleElms; |
|
50 this.elm = aElm; |
|
51 this.container = aContainer; |
|
52 this.testID = aTestID; |
|
53 |
|
54 this.iterateNext(); |
|
55 }, |
|
56 |
|
57 iterateNext: function gTestIterator_iterateNext() |
|
58 { |
|
59 if (this.markupIdx == -1) { |
|
60 this.markupIdx++; |
|
61 testNamesForMarkup(this.markupElms[this.markupIdx]); |
|
62 return; |
|
63 } |
|
64 |
|
65 this.ruleIdx++; |
|
66 if (this.ruleIdx == this.ruleElms.length) { |
|
67 // When test is finished then name is empty and no explict-name. |
|
68 var defaultName = this.ruleSetElm.hasAttribute("defaultName") ? |
|
69 this.ruleSetElm.getAttribute("defaultName") : null; |
|
70 testName(this.elm, defaultName, |
|
71 "Default name test (" + gTestIterator.testID + "). "); |
|
72 testAbsentAttrs(this.elm, {"explicit-name" : "true"}); |
|
73 |
|
74 this.markupIdx++; |
|
75 if (this.markupIdx == this.markupElms.length) { |
|
76 //disableLogging("tree"); // debugging |
|
77 SimpleTest.finish(); |
|
78 return; |
|
79 } |
|
80 |
|
81 this.ruleIdx = -1; |
|
82 |
|
83 if (gDumpToConsole) { |
|
84 dump("\nPend next markup processing. Wait for reorder event on " + |
|
85 prettyName(document) + "'\n"); |
|
86 } |
|
87 waitForEvent(EVENT_REORDER, document, testNamesForMarkup, |
|
88 null, this.markupElms[this.markupIdx]); |
|
89 |
|
90 document.body.removeChild(this.container); |
|
91 return; |
|
92 } |
|
93 |
|
94 testNameForRule(this.elm, this.ruleElms[this.ruleIdx]); |
|
95 }, |
|
96 |
|
97 markupElms: null, |
|
98 markupIdx: -1, |
|
99 rulesetElm: null, |
|
100 ruleElms: null, |
|
101 ruleIdx: -1, |
|
102 elm: null, |
|
103 container: null, |
|
104 testID: "" |
|
105 }; |
|
106 |
|
107 /** |
|
108 * Process every 'markup' element and test names for it. Used by testNames |
|
109 * function. |
|
110 */ |
|
111 function testNamesForMarkup(aMarkupElm) |
|
112 { |
|
113 if (gDumpToConsole) |
|
114 dump("\nProcessing markup '" + aMarkupElm.getAttribute("id") + "'\n"); |
|
115 |
|
116 var div = document.createElement("div"); |
|
117 div.setAttribute("id", "test"); |
|
118 |
|
119 var child = aMarkupElm.firstChild; |
|
120 while (child) { |
|
121 var newChild = document.importNode(child, true); |
|
122 div.appendChild(newChild); |
|
123 child = child.nextSibling; |
|
124 } |
|
125 |
|
126 if (gDumpToConsole) { |
|
127 dump("\nProcessing markup. Wait for reorder event on " + |
|
128 prettyName(document) + "'\n"); |
|
129 } |
|
130 waitForEvent(EVENT_REORDER, document, testNamesForMarkupRules, |
|
131 null, aMarkupElm, div); |
|
132 |
|
133 document.body.appendChild(div); |
|
134 } |
|
135 |
|
136 function testNamesForMarkupRules(aMarkupElm, aContainer) |
|
137 { |
|
138 var testID = aMarkupElm.getAttribute("id"); |
|
139 if (gDumpToConsole) |
|
140 dump("\nProcessing markup rules '" + testID + "'\n"); |
|
141 |
|
142 var serializer = new XMLSerializer(); |
|
143 |
|
144 var expr = "//html/body/div[@id='test']/" + aMarkupElm.getAttribute("ref"); |
|
145 var elm = evaluateXPath(document, expr, htmlDocResolver)[0]; |
|
146 |
|
147 var ruleId = aMarkupElm.getAttribute("ruleset"); |
|
148 var ruleElm = gRuleDoc.querySelector("[id='" + ruleId + "']"); |
|
149 var ruleElms = getRuleElmsByRulesetId(ruleId); |
|
150 |
|
151 var processMarkupRules = |
|
152 gTestIterator.iterateRules.bind(gTestIterator, elm, aContainer, |
|
153 ruleElm, ruleElms, testID); |
|
154 |
|
155 // Images may be recreated after we append them into subtree. We need to wait |
|
156 // in this case. If we are on profiling enabled build then stack tracing |
|
157 // works and thus let's log instead. Note, that works if you enabled logging |
|
158 // (refer to testNames() function). |
|
159 if (isAccessible(elm) || isLogged("stack")) |
|
160 processMarkupRules(); |
|
161 else |
|
162 waitForEvent(EVENT_SHOW, elm, processMarkupRules); |
|
163 } |
|
164 |
|
165 /** |
|
166 * Test name for current rule and current 'markup' element. Used by |
|
167 * testNamesForMarkup function. |
|
168 */ |
|
169 function testNameForRule(aElm, aRuleElm) |
|
170 { |
|
171 if (aRuleElm.hasAttribute("attr")) { |
|
172 if (gDumpToConsole) { |
|
173 dump("\nProcessing rule { attr: " + aRuleElm.getAttribute("attr") +" }\n"); |
|
174 } |
|
175 |
|
176 testNameForAttrRule(aElm, aRuleElm); |
|
177 |
|
178 } else if (aRuleElm.hasAttribute("elm")) { |
|
179 if (gDumpToConsole) { |
|
180 dump("\nProcessing rule { elm: " + aRuleElm.getAttribute("elm") + |
|
181 ", elmattr: " + aRuleElm.getAttribute("elmattr") +" }\n"); |
|
182 } |
|
183 |
|
184 testNameForElmRule(aElm, aRuleElm); |
|
185 |
|
186 } else if (aRuleElm.getAttribute("fromsubtree") == "true") { |
|
187 if (gDumpToConsole) { |
|
188 dump("\nProcessing rule { fromsubtree: " + |
|
189 aRuleElm.getAttribute("fromsubtree") +" }\n"); |
|
190 } |
|
191 |
|
192 testNameForSubtreeRule(aElm, aRuleElm); |
|
193 } |
|
194 } |
|
195 |
|
196 function testNameForAttrRule(aElm, aRule) |
|
197 { |
|
198 var name = ""; |
|
199 |
|
200 var attr = aRule.getAttribute("attr"); |
|
201 var attrValue = aElm.getAttribute(attr); |
|
202 |
|
203 var type = aRule.getAttribute("type"); |
|
204 if (type == "string") { |
|
205 name = attrValue; |
|
206 |
|
207 } else if (type == "ref" && attrValue) { |
|
208 var ids = attrValue.split(/\s+/); |
|
209 for (var idx = 0; idx < ids.length; idx++) { |
|
210 var labelElm = getNode(ids[idx]); |
|
211 if (name != "") |
|
212 name += " "; |
|
213 |
|
214 name += labelElm.getAttribute("textequiv"); |
|
215 } |
|
216 } |
|
217 |
|
218 var msg = "Attribute '" + attr + "' test (" + gTestIterator.testID + "). "; |
|
219 testName(aElm, name, msg); |
|
220 |
|
221 if (aRule.getAttribute("explict-name") != "false") |
|
222 testAttrs(aElm, {"explicit-name" : "true"}, true); |
|
223 else |
|
224 testAbsentAttrs(aElm, {"explicit-name" : "true"}); |
|
225 |
|
226 // If @recreated attribute is used then this attribute change recreates an |
|
227 // accessible. Wait for reorder event in this case or otherwise proceed next |
|
228 // test immediately. |
|
229 if (aRule.hasAttribute("recreated")) { |
|
230 waitForEvent(EVENT_REORDER, aElm.parentNode, |
|
231 gTestIterator.iterateNext, gTestIterator); |
|
232 aElm.removeAttribute(attr); |
|
233 |
|
234 } else if (aRule.hasAttribute("textchanged")) { |
|
235 waitForEvent(EVENT_TEXT_INSERTED, aElm, |
|
236 gTestIterator.iterateNext, gTestIterator); |
|
237 aElm.removeAttribute(attr); |
|
238 |
|
239 } else if (aRule.hasAttribute("contentchanged")) { |
|
240 waitForEvent(EVENT_REORDER, aElm, |
|
241 gTestIterator.iterateNext, gTestIterator); |
|
242 aElm.removeAttribute(attr); |
|
243 |
|
244 } else { |
|
245 aElm.removeAttribute(attr); |
|
246 gTestIterator.iterateNext(); |
|
247 } |
|
248 } |
|
249 |
|
250 function testNameForElmRule(aElm, aRule) |
|
251 { |
|
252 var labelElm; |
|
253 |
|
254 var tagname = aRule.getAttribute("elm"); |
|
255 var attrname = aRule.getAttribute("elmattr"); |
|
256 if (attrname) { |
|
257 var filter = { |
|
258 acceptNode: function filter_acceptNode(aNode) |
|
259 { |
|
260 if (aNode.localName == this.mLocalName && |
|
261 aNode.getAttribute(this.mAttrName) == this.mAttrValue) |
|
262 return NodeFilter.FILTER_ACCEPT; |
|
263 |
|
264 return NodeFilter.FILTER_SKIP; |
|
265 }, |
|
266 |
|
267 mLocalName: tagname, |
|
268 mAttrName: attrname, |
|
269 mAttrValue: aElm.getAttribute("id") |
|
270 }; |
|
271 |
|
272 var treeWalker = document.createTreeWalker(document.body, |
|
273 NodeFilter.SHOW_ELEMENT, |
|
274 filter); |
|
275 labelElm = treeWalker.nextNode(); |
|
276 |
|
277 } else { |
|
278 // if attrname is empty then look for the element in subtree. |
|
279 labelElm = aElm.getElementsByTagName(tagname)[0]; |
|
280 if (!labelElm) |
|
281 labelElm = aElm.getElementsByTagName("html:" + tagname)[0]; |
|
282 } |
|
283 |
|
284 if (!labelElm) { |
|
285 ok(false, msg + " Failed to find '" + tagname + "' element."); |
|
286 gTestIterator.iterateNext(); |
|
287 return; |
|
288 } |
|
289 |
|
290 var msg = "Element '" + tagname + "' test (" + gTestIterator.testID + ")."; |
|
291 testName(aElm, labelElm.getAttribute("textequiv"), msg); |
|
292 testAttrs(aElm, {"explicit-name" : "true"}, true); |
|
293 |
|
294 var parentNode = labelElm.parentNode; |
|
295 |
|
296 if (gDumpToConsole) { |
|
297 dump("\nProcessed elm rule. Wait for reorder event on " + |
|
298 prettyName(parentNode) + "\n"); |
|
299 } |
|
300 waitForEvent(EVENT_REORDER, parentNode, |
|
301 gTestIterator.iterateNext, gTestIterator); |
|
302 |
|
303 parentNode.removeChild(labelElm); |
|
304 } |
|
305 |
|
306 function testNameForSubtreeRule(aElm, aRule) |
|
307 { |
|
308 var msg = "From subtree test (" + gTestIterator.testID + ")."; |
|
309 testName(aElm, aElm.getAttribute("textequiv"), msg); |
|
310 testAbsentAttrs(aElm, {"explicit-name" : "true"}); |
|
311 |
|
312 if (gDumpToConsole) { |
|
313 dump("\nProcessed from subtree rule. Wait for reorder event on " + |
|
314 prettyName(aElm) + "\n"); |
|
315 } |
|
316 waitForEvent(EVENT_REORDER, aElm, gTestIterator.iterateNext, gTestIterator); |
|
317 |
|
318 while (aElm.firstChild) |
|
319 aElm.removeChild(aElm.firstChild); |
|
320 } |
|
321 |
|
322 /** |
|
323 * Return array of 'rule' elements. Used in conjunction with |
|
324 * getRuleElmsFromRulesetElm() function. |
|
325 */ |
|
326 function getRuleElmsByRulesetId(aRulesetId) |
|
327 { |
|
328 var expr = "//rules/ruledfn/ruleset[@id='" + aRulesetId + "']"; |
|
329 var rulesetElm = evaluateXPath(gRuleDoc, expr); |
|
330 return getRuleElmsFromRulesetElm(rulesetElm[0]); |
|
331 } |
|
332 |
|
333 function getRuleElmsFromRulesetElm(aRulesetElm) |
|
334 { |
|
335 var rulesetId = aRulesetElm.getAttribute("ref"); |
|
336 if (rulesetId) |
|
337 return getRuleElmsByRulesetId(rulesetId); |
|
338 |
|
339 var ruleElms = []; |
|
340 |
|
341 var child = aRulesetElm.firstChild; |
|
342 while (child) { |
|
343 if (child.localName == "ruleset") |
|
344 ruleElms = ruleElms.concat(getRuleElmsFromRulesetElm(child)); |
|
345 if (child.localName == "rule") |
|
346 ruleElms.push(child); |
|
347 |
|
348 child = child.nextSibling; |
|
349 } |
|
350 |
|
351 return ruleElms; |
|
352 } |
|
353 |
|
354 /** |
|
355 * Helper method to evaluate xpath expression. |
|
356 */ |
|
357 function evaluateXPath(aNode, aExpr, aResolver) |
|
358 { |
|
359 var xpe = new XPathEvaluator(); |
|
360 |
|
361 var resolver = aResolver; |
|
362 if (!resolver) { |
|
363 var node = aNode.ownerDocument == null ? |
|
364 aNode.documentElement : aNode.ownerDocument.documentElement; |
|
365 resolver = xpe.createNSResolver(node); |
|
366 } |
|
367 |
|
368 var result = xpe.evaluate(aExpr, aNode, resolver, 0, null); |
|
369 var found = []; |
|
370 var res; |
|
371 while (res = result.iterateNext()) |
|
372 found.push(res); |
|
373 |
|
374 return found; |
|
375 } |
|
376 |
|
377 function htmlDocResolver(aPrefix) { |
|
378 var ns = { |
|
379 'html' : 'http://www.w3.org/1999/xhtml' |
|
380 }; |
|
381 return ns[aPrefix] || null; |
|
382 } |