mshtml: Implement nextElementSibling for Elements.
[wine.git] / dlls / mshtml / tests / dom.js
blobe164c9502ff0e8b78b7d11770a683a85096d1132
1 /*
2  * Copyright 2017 Jacek Caban for CodeWeavers
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  */
19 var tests = [];
21 sync_test("input_selection", function() {
22     var input = document.createElement("input");
23     input.type = "text";
24     input.value = "test";
25     document.body.appendChild(input);
27     function test_range(start, end) {
28         ok(input.selectionStart === start, "input.selectionStart = " + input.selectionStart + " expected " + start);
29         ok(input.selectionEnd === end, "input.selectionEnd = " + input.selectionEnd + " expected " + end);
30     }
32     test_range(0, 0);
34     input.selectionStart = 2;
35     test_range(2, 2);
37     input.selectionStart = -1;
38     test_range(0, 2);
40     input.selectionStart = 10;
41     test_range(4, 4);
43     input.selectionEnd = 2;
44     test_range(2, 2);
46     input.selectionEnd = -1;
47     test_range(0, 0);
49     input.selectionEnd = 10;
50     test_range(0, 4);
52     input.setSelectionRange(2, 3);
53     test_range(2, 3);
55     input.setSelectionRange(-1, 10);
56     test_range(0, 4);
58     input.setSelectionRange(3, 3);
59     test_range(3, 3);
60 });
62 sync_test("textContent", function() {
63     var text = document.createTextNode("test");
64     ok(text.textContent === "test", "text.textContent = " + text.textContent);
66     var div = document.createElement("div");
67     document.body.appendChild(div);
68     div.innerHTML = "abc<script>/* */</script><div>text</div>";
69     ok(div.textContent === "abc/* */text", "div.textContent = " + div.textContent);
71     div.textContent = "test";
72     ok(div.textContent === "test", "div.textContent = " + div.textContent);
73     ok(div.childNodes.length === 1, "div.childNodes.length = " + div.childNodes.length);
74     ok(div.firstChild.textContent === "test", "div.firstChild.textContent = " + div.firstChild.textContent);
76     div.textContent = "";
77     ok(div.textContent === "", "div.textContent = " + div.textContent);
78     ok(div.childNodes.length === 0, "div.childNodes.length = " + div.childNodes.length);
80     div.textContent = null;
81     ok(div.textContent === "", "div.textContent = " + div.textContent);
82     div.textContent = 11;
83     ok(div.textContent === "11", "div.textContent = " + div.textContent);
84     div.textContent = 10.5;
85     ok(div.textContent === "10.5", "div.textContent = " + div.textContent);
87     ok(document.textContent === null, "document.textContent = " + document.textContent);
88 });
90 sync_test("ElementTraversal", function() {
91     var div = document.createElement("div");
92     div.innerHTML = "abc<b>bold</b><script>/* */</script><div>text</div>def";
93     ok(div.firstElementChild.outerHTML === "<b>bold</b>",
94             "div.firstElementChild.outerHTML = " + div.firstElementChild.outerHTML);
95     ok(div.lastElementChild.outerHTML === "<div>text</div>",
96             "div.lastElementChild.outerHTML = " + div.lastElementChild.outerHTML);
97     ok(div.firstElementChild.nextElementSibling.outerHTML === "<script>/* */</script>",
98             "div.firstElementChild.nextElementSibling.outerHTML = " + div.firstElementChild.nextElementSibling.outerHTML);
99     ok(div.lastElementChild.nextElementSibling === null,
100             "div.lastElementChild.nextElementSibling = " + div.lastElementChild.nextElementSibling);
102     div.innerHTML = "abc";
103     ok(div.firstElementChild === null, "div.firstElementChild = " + div.firstElementChild);
104     ok(div.lastElementChild === null, "div.lastElementChild = " + div.lastElementChild);
106     ok(!("firstElementChild" in document), "firstElementChild found in document");
107     ok(!("nextElementSibling" in document), "nextElementSibling found in document");
110 sync_test("head", function() {
111     var h = document.head;
112     ok(h.tagName === "HEAD", "h.tagName = " + h.tagName);
113     ok(h === document.getElementsByTagName("head")[0], "unexpected head element");
116 async_test("iframe", function() {
117     document.body.innerHTML = '<iframe src="runscript.html?frame.js"></iframe>'
118     var iframe = document.body.firstChild;
120     iframe.onload = guard(function() {
121         var r = iframe.contentWindow.global_object.get_global_value();
122         ok(r === "global value", "get_global_value() returned " + r);
124         var f = iframe.contentWindow.global_object.get_global_value;
125         ok(f() === "global value", "f() returned " + f());
127         next_test();
128     });
131 async_test("iframe_location", function() {
132     document.body.innerHTML = '<iframe src="emptyfile"></iframe>'
133     var iframe = document.body.firstChild;
135     iframe.onload = function() {
136         ok(iframe.contentWindow.location.pathname === "/emptyfile",
137            "path = " + iframe.contentWindow.location.pathname);
138         iframe.onload = function () {
139             ok(iframe.contentWindow.location.pathname === "/empty/file",
140                "path = " + iframe.contentWindow.location.pathname);
141             next_test();
142         }
143         iframe.src = "empty/file";
144     }
147 sync_test("anchor", function() {
148     var anchor_tests = [
149         { href: "http://www.winehq.org:123/about",
150           protocol: "http:", host: "www.winehq.org:123", path: "/about" },
151         { href: "https://www.winehq.org:123/about",
152           protocol: "https:", host: "www.winehq.org:123", path: "/about" },
153         { href: "about:blank",
154           protocol: "about:", host: "", path: "/blank", todo_pathname: 1 },
155         { href: "unknown:path",
156           protocol: "unknown:", host: "", path: "path" },
157         { href: "ftp:path",
158           protocol: "ftp:", host: "", path: "path" },
159         { href: "mailto:path",
160           protocol: "mailto:", host: "", path: "path" },
161         { href: "ws:path",
162           protocol: "ws:", host: "", path: "path" },
163         { href: "file:path",
164           protocol: "file:", host: "", path: "/path", todo_pathname: 1 },
165         { href: "file:///c:/dir/file.html",
166           protocol: "file:", host: "", path: "/c:/dir/file.html" },
167         { href: "http://www.winehq.org/about",
168           protocol: "http:", host: "www.winehq.org", path: "/about" },
169         { href: "https://www.winehq.org/about",
170           protocol: "https:", host: "www.winehq.org", path: "/about" },
171     ];
173     for(var i in anchor_tests) {
174         var t = anchor_tests[i];
175         document.body.innerHTML = '<a href="' + t.href + '">';
176         var anchor = document.body.firstChild;
177         ok(anchor.protocol === t.protocol, "anchor(" + t.href + ").protocol = " + anchor.protocol);
178         ok(anchor.host === t.host, "anchor(" + t.href + ").host = " + anchor.host);
179         todo_wine_if("todo_pathname" in t).
180         ok(anchor.pathname === t.path, "anchor(" + t.href + ").pathname = " + anchor.pathname);
181     }
184 sync_test("getElementsByClassName", function() {
185     var elems;
187     document.body.innerHTML = '<div class="class1">'
188         + '<div class="class1"></div>'
189         + '<a id="class1" class="class2"></a>'
190         + '</div>'
191         + '<script class="class1"></script>';
193     elems = document.getElementsByClassName("class1");
194     ok(elems.length === 3, "elems.length = " + elems.length);
195     ok(elems[0].tagName === "DIV", "elems[0].tagName = " + elems[0].tagName);
196     ok(elems[1].tagName === "DIV", "elems[1].tagName = " + elems[1].tagName);
197     ok(elems[2].tagName === "SCRIPT", "elems[2].tagName = " + elems[2].tagName);
199     elems = document.getElementsByClassName("class2");
200     ok(elems.length === 1, "elems.length = " + elems.length);
201     ok(elems[0].tagName === "A", "elems[0].tagName = " + elems[0].tagName);
203     elems = document.getElementsByClassName("classnotfound");
204     ok(elems.length == 0, "elems.length = " + elems.length);
207 sync_test("createElementNS", function() {
208     var svg_ns = "http://www.w3.org/2000/svg";
209     var elem;
211     elem = document.createElementNS(null, "test");
212     ok(elem.tagName === "test", "elem.tagName = " + elem.tagName);
213     ok(elem.namespaceURI === null, "elem.namespaceURI = " + elem.namespaceURI);
215     elem = document.createElementNS(svg_ns, "test");
216     ok(elem.tagName === "test", "elem.tagName = " + elem.tagName);
217     ok(elem.namespaceURI === svg_ns, "elem.namespaceURI = " + elem.namespaceURI);
219     elem = document.createElementNS(svg_ns, "svg");
220     ok(elem.tagName === "svg", "elem.tagName = " + elem.tagName);
221     ok(elem.namespaceURI === svg_ns, "elem.namespaceURI = " + elem.namespaceURI);
223     elem = document.createElementNS("test", "svg");
224     ok(elem.tagName === "svg", "elem.tagName = " + elem.tagName);
225     ok(elem.namespaceURI === "test", "elem.namespaceURI = " + elem.namespaceURI);
228 sync_test("query_selector", function() {
229     document.body.innerHTML = '<div class="class1">'
230         + '<div class="class1"></div>'
231         + '<a id="class1" class="class2"></a>'
232         + '</div>'
233         + '<script class="class1"></script>';
235     var e = document.querySelector("nomatch");
236     ok(e === null, "e = " + e);
237     e = document.body.querySelector("nomatch");
238     ok(e === null, "e = " + e);
240     e = document.querySelector(".class1");
241     ok(e.tagName === "DIV", "e.tagName = " + e.tagName);
242     e = document.body.querySelector(".class1");
243     ok(e.tagName === "DIV", "e.tagName = " + e.tagName);
244     ok(e.msMatchesSelector(".class1") === true, "msMatchesSelector returned " + e.msMatchesSelector(".class1"));
245     ok(e.msMatchesSelector(".class2") === false, "msMatchesSelector returned " + e.msMatchesSelector(".class2"));
247     e = document.querySelector("a");
248     ok(e.tagName === "A", "e.tagName = " + e.tagName);
249     e = document.body.querySelector("a");
250     ok(e.tagName === "A", "e.tagName = " + e.tagName);
253 sync_test("compare_position", function() {
254     document.body.innerHTML = '<div><div></div><div></div></div>';
256     var parent = document.body.firstChild;
257     var child1 = parent.firstChild;
258     var child2 = child1.nextSibling;
259     var elem = document.createElement("div");
261     function compare_position(node1, node2, expected_result, ignore_mask) {
262         var cmp = node1.compareDocumentPosition(node2);
263         ok((cmp & ~ignore_mask) == expected_result,
264            "compareDocumentPosition returned " + cmp + " expected " + expected_result);
265     }
267     compare_position(child1, child2, 4);
268     compare_position(child2, child1, 2);
269     compare_position(parent, child1, 0x14);
270     compare_position(parent, child2, 0x14);
271     compare_position(parent, elem, 0x21, 6);
272     compare_position(elem, parent, 0x21, 6);
275 sync_test("rects", function() {
276     document.body.innerHTML = '<div>test</div>';
277     var elem = document.body.firstChild;
278     var rects = elem.getClientRects();
279     var rect = elem.getBoundingClientRect();
281     ok(rects.length === 1, "rect.length = " + rects.length);
282     ok(rects[0].top === rect.top, "rects[0].top = " + rects[0].top + " rect.top = " + rect.top);
283     ok(rects[0].bottom === rect.bottom, "rects[0].bottom = " + rects[0].bottom + " rect.bottom = " + rect.bottom);
285     elem = document.createElement("style");
286     rects = elem.getClientRects();
287     ok(rects.length === 0, "rect.length = " + rects.length);
290 sync_test("document_owner", function() {
291     var node;
293     ok(document.ownerDocument === null, "ownerDocument = " + document.ownerDocument);
294     ok(document.body.ownerDocument === document,
295        "body.ownerDocument = " + document.body.ownerDocument);
296     ok(document.documentElement.ownerDocument === document,
297        "documentElement.ownerDocument = " + document.documentElement.ownerDocument);
299     node = document.createElement("test");
300     ok(node.ownerDocument === document, "element.ownerDocument = " + node.ownerDocument);
302     node = document.createDocumentFragment();
303     ok(node.ownerDocument === document, "fragment.ownerDocument = " + node.ownerDocument);
305     node = document.createTextNode("test");
306     ok(node.ownerDocument === document, "text.ownerDocument = " + node.ownerDocument);
309 sync_test("style_properties", function() {
310     document.body.innerHTML = '<div>test</div><svg></svg>';
311     var elem = document.body.firstChild;
312     var style = elem.style;
313     var current_style = elem.currentStyle;
314     var computed_style = window.getComputedStyle(elem);
315     var val;
317     style.cssFloat = "left";
318     ok(style.cssFloat === "left", "cssFloat = " + style.cssFloat);
319     ok(style.getPropertyValue("float") === "left",
320        'style.getPropertyValue("float") = ' + style.getPropertyValue("float"));
321     ok(style.getPropertyValue("cssFloat") === "",
322        'style.getPropertyValue("cssFloat") = ' + style.getPropertyValue("cssFloat"));
324     val = style.removeProperty("float");
325     ok(val === "left", "removeProperty() returned " + val);
326     ok(style.cssFloat === "", "cssFloat = " + style.cssFloat);
328     style.cssFloat = "left";
329     val = style.removeProperty("FloaT");
330     ok(val === "left", "removeProperty() returned " + val);
331     ok(style.cssFloat === "", "cssFloat = " + style.cssFloat);
333     style.cssFloat = "left";
334     val = style.removeProperty("cssFloat");
335     ok(val === "", "removeProperty() returned " + val);
336     ok(style.cssFloat === "left", "cssFloat = " + style.cssFloat);
337     ok(style["float"] === "left", "float = " + style["float"]);
339     style.testVal = "test";
340     val = style.removeProperty("testVal");
341     ok(val === "", "removeProperty() returned " + val);
342     ok(style.testVal === "test", "testVal = " + style.testVal);
344     style["z-index"] = 1;
345     ok(style.zIndex === 1, "zIndex = " + style.zIndex);
346     ok(style["z-index"] === 1, "z-index = " + style["z-index"]);
347     ok(style.getPropertyValue("z-index") === "1",
348        'style.getPropertyValue("x-index") = ' + style.getPropertyValue("z-index"));
349     ok(style.getPropertyValue("zIndex") === "",
350        'style.getPropertyValue("xIndex") = ' + style.getPropertyValue("zIndex"));
352     style.setProperty("border-width", "5px");
353     ok(style.borderWidth === "5px", "style.borderWidth = " + style.borderWidth);
355     try {
356         style.setProperty("border-width", 6);
357         ok(style.borderWidth === "5px", "style.borderWidth = " + style.borderWidth);
358     }catch(e) {
359         win_skip("skipping setProperty tests on too old IE version");
360         return;
361     }
363     style.setProperty("border-width", "7px", "test");
364     ok(style.borderWidth === "5px", "style.borderWidth = " + style.borderWidth);
366     style.setProperty("border-width", "6px", "");
367     ok(style.borderWidth === "6px", "style.borderWidth = " + style.borderWidth);
369     style.setProperty("border-width", "7px", "important");
370     ok(style.borderWidth === "7px", "style.borderWidth = " + style.borderWidth);
372     style.setProperty("border-width", "8px", undefined);
373     ok(style.borderWidth === "7px", "style.borderWidth = " + style.borderWidth);
375     style.clip = "rect(1px 1px 10px 10px)";
376     ok(style.clip === "rect(1px, 1px, 10px, 10px)", "style.clip = " + style.clip);
377     ok(current_style.clip === "rect(1px, 1px, 10px, 10px)",
378        "current_style.clip = " + current_style.clip);
379     ok(computed_style.clip === "rect(1px, 1px, 10px, 10px)",
380        "computed_style.clip = " + current_style.clip);
382     style.zIndex = 2;
383     ok(current_style.zIndex === 2, "current_style.zIndex = " + current_style.zIndex);
384     ok(computed_style.zIndex === 2, "computed_style.zIndex = " + computed_style.zIndex);
386     try {
387         current_style.zIndex = 1;
388         ok(false, "expected exception");
389     }catch(e) {}
391     try {
392         computed_style.zIndex = 1;
393         ok(false, "expected exception");
394     }catch(e) {}
396     elem = elem.nextSibling;
397     computed_style = window.getComputedStyle(elem);
399     elem.style.zIndex = 4;
400     ok(computed_style.zIndex === 4, "computed_style.zIndex = " + computed_style.zIndex);
402     window.getComputedStyle(elem, null);
405 sync_test("stylesheets", function() {
406     document.body.innerHTML = '<style>.div { margin-right: 1px; }</style>';
407     var elem = document.body.firstChild;
409     ok(document.styleSheets.length === 1, "document.styleSheets.length = " + document.styleSheets.length);
411     var stylesheet = document.styleSheets.item(0);
412     ok(stylesheet.rules.length === 1, "stylesheet.rules.length = " + stylesheet.rules.length);
413     ok(typeof(stylesheet.rules.item(0)) === "object",
414        "typeof(stylesheet.rules.item(0)) = " + typeof(stylesheet.rules.item(0)));
416     stylesheet = document.styleSheets[0];
417     ok(stylesheet.rules.length === 1, "document.styleSheets[0].rules.length = " + stylesheet.rules.length);
419     try {
420         stylesheet.rules.item(1);
421         ok(false, "expected exception");
422     }catch(e) {}
424     ok(stylesheet.href === null, "stylesheet.href = " + stylesheet.href);
426     var id = stylesheet.insertRule(".input { margin-left: 1px; }", 0);
427     ok(id === 0, "id = " + id);
428     ok(document.styleSheets.length === 1, "document.styleSheets.length = " + document.styleSheets.length);
430     try {
431         stylesheet.insertRule(".input { margin-left: 1px; }", 3);
432         ok(false, "expected exception");
433     }catch(e) {}
435     id = stylesheet.addRule(".p", "margin-top: 2px");
436     ok(id === 2, "id = " + id);
437     ok(document.styleSheets.length === 1, "document.styleSheets.length = " + document.styleSheets.length);
438     ok(stylesheet.rules.length === 3, "stylesheet.rules.length = " + stylesheet.rules.length);
440     id = stylesheet.addRule(".pre", "border: none", -1);
441     ok(id === 3, "id = " + id);
442     ok(stylesheet.rules.length === 4, "stylesheet.rules.length = " + stylesheet.rules.length);
444     id = stylesheet.addRule(".h1", " ", 0);
445     ok(id === 0, "id = " + id);
446     ok(stylesheet.rules.length === 5, "stylesheet.rules.length = " + stylesheet.rules.length);
448     id = stylesheet.addRule(".h2", "color: black", 8);
449     ok(id === 5, "id = " + id);
450     ok(stylesheet.rules.length === 6, "stylesheet.rules.length = " + stylesheet.rules.length);
452     try {
453         stylesheet.addRule("", "border: none", 0);
454         ok(false, "expected exception");
455     }catch(e) {}
456     try {
457         stylesheet.addRule(".img", "", 0);
458         ok(false, "expected exception");
459     }catch(e) {}
462 sync_test("storage", function() {
463     ok(typeof(window.sessionStorage) === "object",
464        "typeof(window.sessionStorage) = " + typeof(window.sessionStorage));
465     ok(typeof(window.localStorage) === "object" || typeof(window.localStorage) === "unknown",
466        "typeof(window.localStorage) = " + typeof(window.localStorage));
468     var item = sessionStorage.getItem("nonexisting");
469     ok(item === null, "item = " + item);
472 async_test("animation", function() {
473     document.body.innerHTML =
474         "<style>" +
475         "  @keyframes testAnimation {0% { opacity: 0; } 100% { opacity: 1; }}" +
476         "  .testAnimation { animation-name: testAnimation; animation-duration: 0.01s; }" +
477         "</style>";
478     var div = document.createElement("div");
479     div.addEventListener("animationstart", function() {
480         div.addEventListener("animationend", next_test);
481     });
482     document.body.appendChild(div);
483     div.className = "testAnimation";
486 sync_test("navigator", function() {
487     ok(typeof(window.navigator) === "object",
488        "typeof(window.navigator) = " + typeof(window.navigator));
490     var v = window.navigator;
491     ok(v === window.navigator, "v != window.navigator");
492     v.testProp = true;
493     ok(window.navigator.testProp, "window.navigator.testProp = " + window.navigator.testProp);
496 sync_test("elem_props", function() {
497     var elem = document.body;
499     ok(elem.accessKey === "", "accessKey = " + elem.accessKey);
500     elem.accessKey = "q";
501     ok(elem.accessKey === "q", "accessKey = " + elem.accessKey + " expected q");
504 async_test("animation_frame", function() {
505     var id = requestAnimationFrame(function(x) { ok(false, "request was supposed to be cancelled"); });
506     id = cancelAnimationFrame(id);
507     ok(id === undefined, "cancelAnimationFrame returned " + id);
509     id = requestAnimationFrame(function(x) {
510         ok(this === window, "this != window");
511         ok(typeof(x) === "number", "x = " + x);
512         ok(arguments.length === 1, "arguments.length = " + arguments.length);
513         next_test();
514     });
515     cancelAnimationFrame(0);
516     clearInterval(id);
517     clearTimeout(id);
518     ok(typeof(id) === "number", "id = " + id);
519     ok(id !== 0, "id = 0");
522 sync_test("title", function() {
523     var elem = document.createElement("div");
524     ok(elem.title === "", "div.title = " + elem.title);
525     ok(elem.getAttribute("title") === null, "title attribute = " + elem.getAttribute("title"));
526     elem.title = "test";
527     ok(elem.title === "test", "div.title = " + elem.title);
528     ok(elem.getAttribute("title") === "test", "title attribute = " + elem.getAttribute("title"));
531 sync_test("disabled", function() {
532     var elem = document.createElement("div");
533     document.body.appendChild(elem);
534     ok(elem.disabled === false, "div.disabled = " + elem.disabled);
535     ok(elem.getAttribute("disabled") === null, "disabled attribute = " + elem.getAttribute("disabled") + " expected null");
537     elem.disabled = true;
538     ok(elem.disabled === true, "div.disabled = " + elem.disabled);
539     ok(elem.getAttribute("disabled") === "", "disabled attribute = " + elem.getAttribute("disabled") + " expected \"\"");
541     elem.disabled = false;
542     ok(elem.disabled === false, "div.disabled = " + elem.disabled);
543     ok(elem.getAttribute("disabled") === null, "disabled attribute = " + elem.getAttribute("disabled") + " expected null");
545     elem.setAttribute("disabled", "false");
546     ok(elem.disabled === true, "div.disabled = " + elem.disabled);
547     ok(elem.getAttribute("disabled") === "false", "disabled attribute = " + elem.getAttribute("disabled"));
549     elem.removeAttribute("disabled");
550     ok(elem.disabled === false, "div.disabled = " + elem.disabled);
551     ok(elem.getAttribute("disabled") === null, "disabled attribute = " + elem.getAttribute("disabled") + " expected null");
554 sync_test("hasAttribute", function() {
555     document.body.innerHTML = '<div attr="test"></div>';
556     var elem = document.body.firstChild, r;
558     r = elem.hasAttribute("attr");
559     ok(r === true, "hasAttribute(attr) returned " + r);
560     r = elem.hasAttribute("attr2");
561     ok(r === false, "hasAttribute(attr2) returned " + r);
563     elem.setAttribute("attr2", "abc");
564     r = elem.hasAttribute("attr2");
565     ok(r === true, "hasAttribute(attr2) returned " + r);
567     elem.removeAttribute("attr");
568     r = elem.hasAttribute("attr");
569     ok(r === false, "hasAttribute(attr) returned " + r);
572 sync_test("classList", function() {
573     var elem = document.createElement("div");
574     var classList = elem.classList;
576     classList.add("a");
577     ok(elem.className === "a", "Expected className 'a', got " + elem.className);
579     classList.add("b");
580     ok(elem.className === "a b", "Expected className 'a b', got " + elem.className);
582     classList.add("c");
583     ok(elem.className === "a b c", "Expected className 'a b c', got " + elem.className);
585     classList.add(4);
586     ok(elem.className === "a b c 4", "Expected className 'a b c 4', got " + elem.className);
588     classList.add("c");
589     ok(elem.className === "a b c 4", "(2) Expected className 'a b c 4', got " + elem.className);
590     ok(("" + classList) === "a b c 4", "Expected classList value 'a b c 4', got " + classList);
591     ok(classList.toString() === "a b c 4", "Expected classList toString 'a b c 4', got " + classList.toString());
593     var exception = false
595     try
596     {
597         classList.add();
598     }
599     catch(e)
600     {
601         exception = true;
602     }
603     ok(exception && elem.className === "a b c 4", "Expected exception, className 'a b c 4', got exception "
604             + exception + ", className" + elem.className);
606     exception = false
607     try
608     {
609         classList.add("");
610     }
611     catch(e)
612     {
613         exception = true;
614     }
615     ok(exception, "Expected exception for classList.add(\"\")");
617     exception = false
618     try
619     {
620         classList.add("e f");
621     }
622     catch(e)
623     {
624         exception = true;
625     }
626     ok(exception, "Expected exception for classList.add(\"e f\")");
628     classList.remove("e");
629     ok(elem.className === "a b c 4", "remove: expected className 'a b c 4', got " + elem.className);
631     exception = false
632     try
633     {
634         classList.remove("e f");
635     }
636     catch(e)
637     {
638         exception = true;
639     }
640     ok(exception, "remove: expected exception for classList.remove(\"e f\")");
642     exception = false
643     try
644     {
645         classList.remove("");
646     }
647     catch(e)
648     {
649         exception = true;
650     }
651     ok(exception, "remove: expected exception for classList.remove(\"\")");
653     classList.remove("d");
654     ok(elem.className === "a b c 4", "remove: expected className 'a b c 4', got " + elem.className);
656     classList.remove("c");
657     ok(elem.className === "a b 4", "remove: expected className 'a b 4', got " + elem.className);
659     classList.remove(4);
660     ok(elem.className === "a b", "remove: expected className 'a b', got " + elem.className);
662     classList.remove('a');
663     ok(elem.className === "b", "remove: expected className 'b', got " + elem.className);
665     classList.remove("a");
666     ok(elem.className === "b", "remove (2): expected className 'b', got " + elem.className);
668     classList.remove("b");
669     ok(elem.className === "", "remove: expected className '', got " + elem.className);
671     elem.className = "  testclass    foobar  ";
672     ok(("" + classList) === "  testclass    foobar  ", "Expected classList value '  testclass    foobar  ', got " + classList);
673     ok(classList.toString() === "  testclass    foobar  ", "Expected classList toString '  testclass    foobar  ', got " + classList.toString());