OLD | NEW |
(Empty) | |
| 1 /// Test for the Selectors API ported from |
| 2 /// <https://github.com/w3c/web-platform-tests/tree/master/selectors-api> |
| 3 /// |
| 4 /// Note: tried to make minimal changes possible here. Hence some oddities such |
| 5 /// as [test] arguments having a different order, long lines, etc. |
| 6 /// |
| 7 /// As usual with ports: being faithful to the original style is more important |
| 8 /// than other style goals, as it reduces friction to integrating changes |
| 9 /// from upstream. |
| 10 library html.test.selectors.level1_lib; |
| 11 |
| 12 import 'package:html/dom.dart'; |
| 13 import 'package:unittest/unittest.dart' as unittest; |
| 14 |
| 15 Document doc; |
| 16 |
| 17 /* |
| 18 * Create and append special elements that cannot be created correctly with HTML
markup alone. |
| 19 */ |
| 20 setupSpecialElements(parent) { |
| 21 // Setup null and undefined tests |
| 22 parent.append(doc.createElement("null")); |
| 23 parent.append(doc.createElement("undefined")); |
| 24 |
| 25 // Setup namespace tests |
| 26 var anyNS = doc.createElement("div"); |
| 27 var noNS = doc.createElement("div"); |
| 28 anyNS.id = "any-namespace"; |
| 29 noNS.id = "no-namespace"; |
| 30 |
| 31 var div; |
| 32 div = [ |
| 33 doc.createElement("div"), |
| 34 doc.createElementNS("http://www.w3.org/1999/xhtml", "div"), |
| 35 doc.createElementNS("", "div"), |
| 36 doc.createElementNS("http://www.example.org/ns", "div") |
| 37 ]; |
| 38 |
| 39 div[0].id = "any-namespace-div1"; |
| 40 div[1].id = "any-namespace-div2"; |
| 41 div[2].attributes["id"] = |
| 42 "any-namespace-div3"; // Non-HTML elements can't use .id property |
| 43 div[3].attributes["id"] = "any-namespace-div4"; |
| 44 |
| 45 for (var i = 0; i < div.length; i++) { |
| 46 anyNS.append(div[i]); |
| 47 } |
| 48 |
| 49 div = [ |
| 50 doc.createElement("div"), |
| 51 doc.createElementNS("http://www.w3.org/1999/xhtml", "div"), |
| 52 doc.createElementNS("", "div"), |
| 53 doc.createElementNS("http://www.example.org/ns", "div") |
| 54 ]; |
| 55 |
| 56 div[0].id = "no-namespace-div1"; |
| 57 div[1].id = "no-namespace-div2"; |
| 58 div[2].attributes["id"] = |
| 59 "no-namespace-div3"; // Non-HTML elements can't use .id property |
| 60 div[3].attributes["id"] = "no-namespace-div4"; |
| 61 |
| 62 for (var i = 0; i < div.length; i++) { |
| 63 noNS.append(div[i]); |
| 64 } |
| 65 |
| 66 parent.append(anyNS); |
| 67 parent.append(noNS); |
| 68 } |
| 69 |
| 70 /* |
| 71 * Check that the querySelector and querySelectorAll methods exist on the given
Node |
| 72 */ |
| 73 interfaceCheck(type, obj) { |
| 74 test(() { |
| 75 var q = obj.querySelector is Function; |
| 76 assert_true(q, type + " supports querySelector."); |
| 77 }, type + " supports querySelector"); |
| 78 |
| 79 test(() { |
| 80 var qa = obj.querySelectorAll is Function; |
| 81 assert_true(qa, type + " supports querySelectorAll."); |
| 82 }, type + " supports querySelectorAll"); |
| 83 |
| 84 test(() { |
| 85 var list = obj.querySelectorAll("div"); |
| 86 // TODO(jmesserly): testing List<Element> for now. It should return an |
| 87 // ElementList which has extra properties. Needed for dart:html compat. |
| 88 assert_true(list is List<Element>, |
| 89 "The result should be an instance of a NodeList"); |
| 90 }, type + ".querySelectorAll returns NodeList instance"); |
| 91 } |
| 92 |
| 93 /* |
| 94 * Verify that the NodeList returned by querySelectorAll is static and and that
a new list is created after |
| 95 * each call. A static list should not be affected by subsequent changes to the
DOM. |
| 96 */ |
| 97 verifyStaticList(type, root) { |
| 98 var pre, post, preLength; |
| 99 |
| 100 test(() { |
| 101 pre = root.querySelectorAll("div"); |
| 102 preLength = pre.length; |
| 103 |
| 104 var div = doc.createElement("div"); |
| 105 (root is Document ? root.body : root).append(div); |
| 106 |
| 107 assert_equals( |
| 108 pre.length, preLength, "The length of the NodeList should not change."); |
| 109 }, type + ": static NodeList"); |
| 110 |
| 111 test(() { |
| 112 post = root.querySelectorAll("div"); |
| 113 assert_equals(post.length, preLength + 1, |
| 114 "The length of the new NodeList should be 1 more than the previous list.
"); |
| 115 }, type + ": new NodeList"); |
| 116 } |
| 117 |
| 118 /* |
| 119 * Verify handling of special values for the selector parameter, including strin
gification of |
| 120 * null and undefined, and the handling of the empty string. |
| 121 */ |
| 122 runSpecialSelectorTests(type, root) { |
| 123 // Dart note: changed these tests because we don't have auto conversion to |
| 124 // String like JavaScript does. |
| 125 test(() { |
| 126 // 1 |
| 127 assert_equals(root.querySelectorAll('null').length, 1, |
| 128 "This should find one element with the tag name 'NULL'."); |
| 129 }, type + ".querySelectorAll null"); |
| 130 |
| 131 test(() { |
| 132 // 2 |
| 133 assert_equals(root.querySelectorAll('undefined').length, 1, |
| 134 "This should find one element with the tag name 'UNDEFINED'."); |
| 135 }, type + ".querySelectorAll undefined"); |
| 136 |
| 137 test(() { |
| 138 // 3 |
| 139 assert_throws((e) => e is NoSuchMethodError, () { |
| 140 root.querySelectorAll(); |
| 141 }, "This should throw a TypeError."); |
| 142 }, type + ".querySelectorAll no parameter"); |
| 143 |
| 144 test(() { |
| 145 // 4 |
| 146 var elm = root.querySelector('null'); |
| 147 assert_not_equals(elm, null, "This should find an element."); |
| 148 // TODO(jmesserly): change "localName" back to "tagName" once implemented. |
| 149 assert_equals( |
| 150 elm.localName.toUpperCase(), "NULL", "The tag name should be 'NULL'."); |
| 151 }, type + ".querySelector null"); |
| 152 |
| 153 test(() { |
| 154 // 5 |
| 155 var elm = root.querySelector('undefined'); |
| 156 assert_not_equals(elm, 'undefined', "This should find an element."); |
| 157 // TODO(jmesserly): change "localName" back to "tagName" once implemented. |
| 158 assert_equals(elm.localName.toUpperCase(), |
| 159 "UNDEFINED", "The tag name should be 'UNDEFINED'."); |
| 160 }, type + ".querySelector undefined"); |
| 161 |
| 162 test(() { |
| 163 // 6 |
| 164 assert_throws((e) => e is NoSuchMethodError, () { |
| 165 root.querySelector(); |
| 166 }, "This should throw a TypeError."); |
| 167 }, type + ".querySelector no parameter"); |
| 168 |
| 169 test(() { |
| 170 // 7 |
| 171 var result = root.querySelectorAll("*"); |
| 172 var i = 0; |
| 173 traverse(root, (elem) { |
| 174 if (!identical(elem, root)) { |
| 175 assert_equals( |
| 176 elem, result[i], "The result in index $i should be in tree order."); |
| 177 i++; |
| 178 } |
| 179 }); |
| 180 }, type + ".querySelectorAll tree order"); |
| 181 } |
| 182 |
| 183 /* |
| 184 * Execute queries with the specified valid selectors for both querySelector() a
nd querySelectorAll() |
| 185 * Only run these tests when results are expected. Don't run for syntax error te
sts. |
| 186 */ |
| 187 runValidSelectorTest(type, root, selectors, testType, docType) { |
| 188 var nodeType = ""; |
| 189 switch (root.nodeType) { |
| 190 case Node.DOCUMENT_NODE: |
| 191 nodeType = "document"; |
| 192 break; |
| 193 case Node.ELEMENT_NODE: |
| 194 nodeType = root.parentNode != null ? "element" : "detached"; |
| 195 break; |
| 196 case Node.DOCUMENT_FRAGMENT_NODE: |
| 197 nodeType = "fragment"; |
| 198 break; |
| 199 default: |
| 200 throw new StateError("Reached unreachable code path."); |
| 201 } |
| 202 |
| 203 for (var i = 0; i < selectors.length; i++) { |
| 204 var s = selectors[i]; |
| 205 var n = s["name"]; |
| 206 var q = s["selector"]; |
| 207 var e = s["expect"]; |
| 208 |
| 209 if ((s["exclude"] is! List || |
| 210 (s["exclude"].indexOf(nodeType) == -1 && |
| 211 s["exclude"].indexOf(docType) == -1)) && |
| 212 (s["testType"] & testType != 0)) { |
| 213 //console.log("Running tests " + nodeType + ": " + s["testType"] + "&" + t
estType + "=" + (s["testType"] & testType) + ": " + JSON.stringify(s)) |
| 214 var foundall, found; |
| 215 |
| 216 test(() { |
| 217 foundall = root.querySelectorAll(q); |
| 218 assert_not_equals(foundall, null, "The method should not return null."); |
| 219 assert_equals(foundall.length, e.length, |
| 220 "The method should return the expected number of matches."); |
| 221 |
| 222 for (var i = 0; i < e.length; i++) { |
| 223 assert_not_equals( |
| 224 foundall[i], null, "The item in index $i should not be null."); |
| 225 assert_equals(foundall[i].attributes["id"], e[i], |
| 226 "The item in index $i should have the expected ID."); |
| 227 assert_false(foundall[i].attributes.containsKey("data-clone"), |
| 228 "This should not be a cloned element."); |
| 229 } |
| 230 }, type + ".querySelectorAll: " + n + ": " + q); |
| 231 |
| 232 test(() { |
| 233 found = root.querySelector(q); |
| 234 |
| 235 if (e.length > 0) { |
| 236 assert_not_equals(found, null, "The method should return a match."); |
| 237 assert_equals(found.attributes["id"], e[0], |
| 238 "The method should return the first match."); |
| 239 assert_equals(found, foundall[0], |
| 240 "The result should match the first item from querySelectorAll."); |
| 241 assert_false(found.attributes.containsKey("data-clone"), |
| 242 "This should not be annotated as a cloned element."); |
| 243 } else { |
| 244 assert_equals(found, null, "The method should not match anything."); |
| 245 } |
| 246 }, type + ".querySelector: " + n + ": " + q); |
| 247 } else { |
| 248 //console.log("Excluding for " + nodeType + ": " + s["testType"] + "&" + t
estType + "=" + (s["testType"] & testType) + ": " + JSON.stringify(s)) |
| 249 } |
| 250 } |
| 251 } |
| 252 |
| 253 /* |
| 254 * Execute queries with the specified invalid selectors for both querySelector()
and querySelectorAll() |
| 255 * Only run these tests when errors are expected. Don't run for valid selector t
ests. |
| 256 */ |
| 257 runInvalidSelectorTest(type, root, selectors) { |
| 258 for (var i = 0; i < selectors.length; i++) { |
| 259 var s = selectors[i]; |
| 260 var n = s["name"]; |
| 261 var q = s["selector"]; |
| 262 |
| 263 // Dart note: FormatException seems a reasonable mapping of SyntaxError |
| 264 test(() { |
| 265 assert_throws((e) => e is FormatException, () { |
| 266 root.querySelector(q); |
| 267 }); |
| 268 }, type + ".querySelector: " + n + ": " + q); |
| 269 |
| 270 test(() { |
| 271 assert_throws((e) => e is FormatException, () { |
| 272 root.querySelectorAll(q); |
| 273 }); |
| 274 }, type + ".querySelectorAll: " + n + ": " + q); |
| 275 } |
| 276 } |
| 277 |
| 278 traverse(Node elem, fn) { |
| 279 if (elem.nodeType == Node.ELEMENT_NODE) { |
| 280 fn(elem); |
| 281 } |
| 282 |
| 283 // Dart note: changed this since our DOM API doesn't support nextNode yet. |
| 284 for (var node in elem.nodes) { |
| 285 traverse(node, fn); |
| 286 } |
| 287 } |
| 288 |
| 289 test(Function body, String name) => unittest.test(name, body); |
| 290 |
| 291 assert_true(value, String reason) => |
| 292 unittest.expect(value, true, reason: reason); |
| 293 |
| 294 assert_false(value, String reason) => |
| 295 unittest.expect(value, false, reason: reason); |
| 296 |
| 297 assert_equals(x, y, reason) => unittest.expect(x, y, reason: reason); |
| 298 |
| 299 assert_not_equals(x, y, reason) => |
| 300 unittest.expect(x, unittest.isNot(y), reason: reason); |
| 301 |
| 302 assert_throws(exception, body, [reason]) => |
| 303 unittest.expect(body, unittest.throwsA(exception), reason: reason); |
OLD | NEW |