// Require selectors.js to be included before this. /* * Create and append special elements that cannot be created correctly with HTML markup alone. */ function setupSpecialElements(doc, parent) { // Setup null and undefined tests parent.appendChild(doc.createElement("null")); parent.appendChild(doc.createElement("undefined")); // Setup namespace tests var anyNS = doc.createElement("div"); var noNS = doc.createElement("div"); anyNS.id = "any-namespace"; noNS.id = "no-namespace"; var divs; div = [doc.createElement("div"), doc.createElementNS("http://www.w3.org/1999/xhtml", "div"), doc.createElementNS("", "div"), doc.createElementNS("http://www.example.org/ns", "div")]; div[0].id = "any-namespace-div1"; div[1].id = "any-namespace-div2"; div[2].setAttribute("id", "any-namespace-div3"); // Non-HTML elements can't use .id property div[3].setAttribute("id", "any-namespace-div4"); for (var i = 0; i < div.length; i++) { anyNS.appendChild(div[i]) } div = [doc.createElement("div"), doc.createElementNS("http://www.w3.org/1999/xhtml", "div"), doc.createElementNS("", "div"), doc.createElementNS("http://www.example.org/ns", "div")]; div[0].id = "no-namespace-div1"; div[1].id = "no-namespace-div2"; div[2].setAttribute("id", "no-namespace-div3"); // Non-HTML elements can't use .id property div[3].setAttribute("id", "no-namespace-div4"); for (i = 0; i < div.length; i++) { noNS.appendChild(div[i]) } parent.appendChild(anyNS); parent.appendChild(noNS); } /* * Check that the querySelector and querySelectorAll methods exist on the given Node */ function interfaceCheck(type, obj) { test(function() { var q = typeof obj.querySelector === "function"; assert_true(q, type + " supports querySelector."); }, type + " supports querySelector") test(function() { var qa = typeof obj.querySelectorAll === "function"; assert_true( qa, type + " supports querySelectorAll."); }, type + " supports querySelectorAll") test(function() { var list = obj.querySelectorAll("div"); if (obj.ownerDocument) { // The object is not a Document assert_true(list instanceof obj.ownerDocument.defaultView.NodeList, "The result should be an instance of a NodeList") } else { // The object is a Document assert_true(list instanceof obj.defaultView.NodeList, "The result should be an instance of a NodeList") } }, type + ".querySelectorAll returns NodeList instance") } /* * Verify that the NodeList returned by querySelectorAll is static and and that a new list is created after * each call. A static list should not be affected by subsequent changes to the DOM. */ function verifyStaticList(type, doc, root) { var pre, post, preLength; test(function() { pre = root.querySelectorAll("div"); preLength = pre.length; var div = doc.createElement("div"); (root.body || root).appendChild(div); assert_equals(pre.length, preLength, "The length of the NodeList should not change.") }, type + ": static NodeList") test(function() { post = root.querySelectorAll("div"), assert_equals(post.length, preLength + 1, "The length of the new NodeList should be 1 more than the previous list.") }, type + ": new NodeList") } /* * Verify handling of special values for the selector parameter, including stringification of * null and undefined, and the handling of the empty string. */ function runSpecialSelectorTests(type, root) { test(function() { // 1 assert_equals(root.querySelectorAll(null).length, 1, "This should find one element with the tag name 'NULL'."); }, type + ".querySelectorAll null") test(function() { // 2 assert_equals(root.querySelectorAll(undefined).length, 1, "This should find one element with the tag name 'UNDEFINED'."); }, type + ".querySelectorAll undefined") test(function() { // 3 assert_throws(TypeError(), function() { root.querySelectorAll(); }, "This should throw a TypeError.") }, type + ".querySelectorAll no parameter") test(function() { // 4 var elm = root.querySelector(null) assert_not_equals(elm, null, "This should find an element."); assert_equals(elm.tagName.toUpperCase(), "NULL", "The tag name should be 'NULL'.") }, type + ".querySelector null") test(function() { // 5 var elm = root.querySelector(undefined) assert_not_equals(elm, undefined, "This should find an element."); assert_equals(elm.tagName.toUpperCase(), "UNDEFINED", "The tag name should be 'UNDEFINED'.") }, type + ".querySelector undefined") test(function() { // 6 assert_throws(TypeError(), function() { root.querySelector(); }, "This should throw a TypeError.") }, type + ".querySelector no parameter") test(function() { // 7 result = root.querySelectorAll("*"); var i = 0; traverse(root, function(elem) { if (elem !== root) { assert_equals(elem, result[i], "The result in index " + i + " should be in tree order."); i++; } }) }, type + ".querySelectorAll tree order"); } /* * Execute queries with the specified valid selectors for both querySelector() and querySelectorAll() * Only run these tests when results are expected. Don't run for syntax error tests. */ function runValidSelectorTest(type, root, selectors, testType, docType) { var nodeType = ""; switch (root.nodeType) { case Node.DOCUMENT_NODE: nodeType = "document"; break; case Node.ELEMENT_NODE: nodeType = root.parentNode ? "element" : "detached"; break; case Node.DOCUMENT_FRAGMENT_NODE: nodeType = "fragment"; break; default: assert_unreached(); nodeType = "unknown"; // This should never happen. } for (var i = 0; i < selectors.length; i++) { var s = selectors[i]; var n = s["name"]; var q = s["selector"]; var e = s["expect"]; if ((!s["exclude"] || (s["exclude"].indexOf(nodeType) === -1 && s["exclude"].indexOf(docType) === -1)) && (s["testType"] & testType) ) { var foundall, found; test(function() { foundall = root.querySelectorAll(q); assert_not_equals(foundall, null, "The method should not return null.") assert_equals(foundall.length, e.length, "The method should return the expected number of matches.") for (var i = 0; i < e.length; i++) { assert_not_equals(foundall[i], null, "The item in index " + i + " should not be null.") assert_equals(foundall[i].getAttribute("id"), e[i], "The item in index " + i + " should have the expected ID."); assert_false(foundall[i].hasAttribute("data-clone"), "This should not be a cloned element."); } }, type + ".querySelectorAll: " + n + ": " + q); test(function() { found = root.querySelector(q); if (e.length > 0) { assert_not_equals(found, null, "The method should return a match.") assert_equals(found.getAttribute("id"), e[0], "The method should return the first match."); assert_equals(found, foundall[0], "The result should match the first item from querySelectorAll."); assert_false(found.hasAttribute("data-clone"), "This should not be annotated as a cloned element."); } else { assert_equals(found, null, "The method should not match anything."); } }, type + ".querySelector: " + n + ": " + q); } } } /* * Execute queries with the specified invalid selectors for both querySelector() and querySelectorAll() * Only run these tests when errors are expected. Don't run for valid selector tests. */ function runInvalidSelectorTest(type, root, selectors) { for (var i = 0; i < selectors.length; i++) { var s = selectors[i]; var n = s["name"]; var q = s["selector"]; test(function() { assert_throws("SyntaxError", function() { root.querySelector(q) }) }, type + ".querySelector: " + n + ": " + q); test(function() { assert_throws("SyntaxError", function() { root.querySelectorAll(q) }) }, type + ".querySelectorAll: " + n + ": " + q); } } function traverse(elem, fn) { if (elem.nodeType === elem.ELEMENT_NODE) { fn(elem); } elem = elem.firstChild; while (elem) { traverse(elem, fn); elem = elem.nextSibling; } } function getNodeType(node) { switch (node.nodeType) { case Node.DOCUMENT_NODE: return "document"; case Node.ELEMENT_NODE: return node.parentNode ? "element" : "detached"; case Node.DOCUMENT_FRAGMENT_NODE: return "fragment"; default: assert_unreached(); return "unknown"; // This should never happen. } }