ParentNode-querySelector-All.js (9081B)
1 // Require selectors.js to be included before this. 2 3 /* 4 * Create and append special elements that cannot be created correctly with HTML markup alone. 5 */ 6 function setupSpecialElements(doc, parent) { 7 // Setup null and undefined tests 8 parent.appendChild(doc.createElement("null")); 9 parent.appendChild(doc.createElement("undefined")); 10 11 // Setup namespace tests 12 var anyNS = doc.createElement("div"); 13 var noNS = doc.createElement("div"); 14 anyNS.id = "any-namespace"; 15 noNS.id = "no-namespace"; 16 17 var divs; 18 div = [doc.createElement("div"), 19 doc.createElementNS("http://www.w3.org/1999/xhtml", "div"), 20 doc.createElementNS("", "div"), 21 doc.createElementNS("http://www.example.org/ns", "div")]; 22 23 div[0].id = "any-namespace-div1"; 24 div[1].id = "any-namespace-div2"; 25 div[2].setAttribute("id", "any-namespace-div3"); // Non-HTML elements can't use .id property 26 div[3].setAttribute("id", "any-namespace-div4"); 27 28 for (var i = 0; i < div.length; i++) { 29 anyNS.appendChild(div[i]) 30 } 31 32 div = [doc.createElement("div"), 33 doc.createElementNS("http://www.w3.org/1999/xhtml", "div"), 34 doc.createElementNS("", "div"), 35 doc.createElementNS("http://www.example.org/ns", "div")]; 36 37 div[0].id = "no-namespace-div1"; 38 div[1].id = "no-namespace-div2"; 39 div[2].setAttribute("id", "no-namespace-div3"); // Non-HTML elements can't use .id property 40 div[3].setAttribute("id", "no-namespace-div4"); 41 42 for (i = 0; i < div.length; i++) { 43 noNS.appendChild(div[i]) 44 } 45 46 parent.appendChild(anyNS); 47 parent.appendChild(noNS); 48 49 var span = doc.getElementById("attr-presence-i1"); 50 span.setAttributeNS("http://www.example.org/ns", "title", ""); 51 } 52 53 /* 54 * Check that the querySelector and querySelectorAll methods exist on the given Node 55 */ 56 function interfaceCheck(type, obj) { 57 test(function() { 58 var q = typeof obj.querySelector === "function"; 59 assert_true(q, type + " supports querySelector."); 60 }, type + " supports querySelector") 61 62 test(function() { 63 var qa = typeof obj.querySelectorAll === "function"; 64 assert_true( qa, type + " supports querySelectorAll."); 65 }, type + " supports querySelectorAll") 66 67 test(function() { 68 var list = obj.querySelectorAll("div"); 69 if (obj.ownerDocument) { // The object is not a Document 70 assert_true(list instanceof obj.ownerDocument.defaultView.NodeList, "The result should be an instance of a NodeList") 71 } else { // The object is a Document 72 assert_true(list instanceof obj.defaultView.NodeList, "The result should be an instance of a NodeList") 73 } 74 }, type + ".querySelectorAll returns NodeList instance") 75 } 76 77 /* 78 * Verify that the NodeList returned by querySelectorAll is static and and that a new list is created after 79 * each call. A static list should not be affected by subsequent changes to the DOM. 80 */ 81 function verifyStaticList(type, doc, root) { 82 var pre, post, preLength; 83 84 test(function() { 85 pre = root.querySelectorAll("div"); 86 preLength = pre.length; 87 88 var div = doc.createElement("div"); 89 (root.body || root).appendChild(div); 90 91 assert_equals(pre.length, preLength, "The length of the NodeList should not change.") 92 }, type + ": static NodeList") 93 94 test(function() { 95 post = root.querySelectorAll("div"), 96 assert_equals(post.length, preLength + 1, "The length of the new NodeList should be 1 more than the previous list.") 97 }, type + ": new NodeList") 98 } 99 100 /* 101 * Verify handling of special values for the selector parameter, including stringification of 102 * null and undefined, and the handling of the empty string. 103 */ 104 function runSpecialSelectorTests(type, root) { 105 let global = (root.ownerDocument || root).defaultView; 106 107 test(function() { // 1 108 assert_equals(root.querySelectorAll(null).length, 1, "This should find one element with the tag name 'NULL'."); 109 }, type + ".querySelectorAll null") 110 111 test(function() { // 2 112 assert_equals(root.querySelectorAll(undefined).length, 1, "This should find one element with the tag name 'UNDEFINED'."); 113 }, type + ".querySelectorAll undefined") 114 115 test(function() { // 3 116 assert_throws_js(global.TypeError, function() { 117 root.querySelectorAll(); 118 }, "This should throw a TypeError.") 119 }, type + ".querySelectorAll no parameter") 120 121 test(function() { // 4 122 var elm = root.querySelector(null) 123 assert_not_equals(elm, null, "This should find an element."); 124 assert_equals(elm.tagName.toUpperCase(), "NULL", "The tag name should be 'NULL'.") 125 }, type + ".querySelector null") 126 127 test(function() { // 5 128 var elm = root.querySelector(undefined) 129 assert_not_equals(elm, undefined, "This should find an element."); 130 assert_equals(elm.tagName.toUpperCase(), "UNDEFINED", "The tag name should be 'UNDEFINED'.") 131 }, type + ".querySelector undefined") 132 133 test(function() { // 6 134 assert_throws_js(global.TypeError, function() { 135 root.querySelector(); 136 }, "This should throw a TypeError.") 137 }, type + ".querySelector no parameter") 138 139 test(function() { // 7 140 result = root.querySelectorAll("*"); 141 var i = 0; 142 traverse(root, function(elem) { 143 if (elem !== root) { 144 assert_equals(elem, result[i], "The result in index " + i + " should be in tree order."); 145 i++; 146 } 147 }) 148 }, type + ".querySelectorAll tree order"); 149 } 150 151 /* 152 * Execute queries with the specified valid selectors for both querySelector() and querySelectorAll() 153 * Only run these tests when results are expected. Don't run for syntax error tests. 154 */ 155 function runValidSelectorTest(type, root, selectors, testType, docType) { 156 var nodeType = ""; 157 switch (root.nodeType) { 158 case Node.DOCUMENT_NODE: 159 nodeType = "document"; 160 break; 161 case Node.ELEMENT_NODE: 162 nodeType = root.parentNode ? "element" : "detached"; 163 break; 164 case Node.DOCUMENT_FRAGMENT_NODE: 165 nodeType = "fragment"; 166 break; 167 default: 168 assert_unreached(); 169 nodeType = "unknown"; // This should never happen. 170 } 171 172 for (var i = 0; i < selectors.length; i++) { 173 var s = selectors[i]; 174 var n = s["name"]; 175 var q = s["selector"]; 176 var e = s["expect"]; 177 178 if ((!s["exclude"] || (s["exclude"].indexOf(nodeType) === -1 && s["exclude"].indexOf(docType) === -1)) 179 && (s["testType"] & testType) ) { 180 var foundall, found; 181 182 test(function() { 183 foundall = root.querySelectorAll(q); 184 assert_not_equals(foundall, null, "The method should not return null.") 185 assert_equals(foundall.length, e.length, "The method should return the expected number of matches.") 186 187 for (var i = 0; i < e.length; i++) { 188 assert_not_equals(foundall[i], null, "The item in index " + i + " should not be null.") 189 assert_equals(foundall[i].getAttribute("id"), e[i], "The item in index " + i + " should have the expected ID."); 190 assert_false(foundall[i].hasAttribute("data-clone"), "This should not be a cloned element."); 191 } 192 }, type + ".querySelectorAll: " + n + ": " + q); 193 194 test(function() { 195 found = root.querySelector(q); 196 197 if (e.length > 0) { 198 assert_not_equals(found, null, "The method should return a match.") 199 assert_equals(found.getAttribute("id"), e[0], "The method should return the first match."); 200 assert_equals(found, foundall[0], "The result should match the first item from querySelectorAll."); 201 assert_false(found.hasAttribute("data-clone"), "This should not be annotated as a cloned element."); 202 } else { 203 assert_equals(found, null, "The method should not match anything."); 204 } 205 }, type + ".querySelector: " + n + ": " + q); 206 } 207 } 208 } 209 210 function windowFor(root) { 211 return root.defaultView || root.ownerDocument.defaultView; 212 } 213 214 /* 215 * Execute queries with the specified invalid selectors for both querySelector() and querySelectorAll() 216 * Only run these tests when errors are expected. Don't run for valid selector tests. 217 */ 218 function runInvalidSelectorTest(type, root, selectors) { 219 for (var i = 0; i < selectors.length; i++) { 220 var s = selectors[i]; 221 var n = s["name"]; 222 var q = s["selector"]; 223 224 test(function() { 225 assert_throws_dom("SyntaxError", windowFor(root).DOMException, function() { 226 root.querySelector(q) 227 }); 228 }, type + ".querySelector: " + n + ": " + q); 229 230 test(function() { 231 assert_throws_dom("SyntaxError", windowFor(root).DOMException, function() { 232 root.querySelectorAll(q) 233 }); 234 }, type + ".querySelectorAll: " + n + ": " + q); 235 } 236 } 237 238 function traverse(elem, fn) { 239 if (elem.nodeType === elem.ELEMENT_NODE) { 240 fn(elem); 241 } 242 elem = elem.firstChild; 243 while (elem) { 244 traverse(elem, fn); 245 elem = elem.nextSibling; 246 } 247 } 248 249 function getNodeType(node) { 250 switch (node.nodeType) { 251 case Node.DOCUMENT_NODE: 252 return "document"; 253 case Node.ELEMENT_NODE: 254 return node.parentNode ? "element" : "detached"; 255 case Node.DOCUMENT_FRAGMENT_NODE: 256 return "fragment"; 257 default: 258 assert_unreached(); 259 return "unknown"; // This should never happen. 260 } 261 }