diff options
Diffstat (limited to 'testing/web-platform/mozilla/tests/dom/classList.html')
-rw-r--r-- | testing/web-platform/mozilla/tests/dom/classList.html | 526 |
1 files changed, 526 insertions, 0 deletions
diff --git a/testing/web-platform/mozilla/tests/dom/classList.html b/testing/web-platform/mozilla/tests/dom/classList.html new file mode 100644 index 0000000000..21d79f49e3 --- /dev/null +++ b/testing/web-platform/mozilla/tests/dom/classList.html @@ -0,0 +1,526 @@ +<!doctype html> +<meta charset=utf-8> +<title>Test for the classList element attribute</title> +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> +<div id="content"></div> +<script> +// This should be the same as dom/nodes/Element-classlist.html in the upstream +// tests! We have a couple of bits changed, which are marked with comments +// "LOCAL MODIFICATION". Do not change this without changing the upstream test +// as well! Merging upstream changes here occasionally might also be nice. + +const SVG_NS = "http://www.w3.org/2000/svg"; +const XHTML_NS = "http://www.w3.org/1999/xhtml" +const MATHML_NS = "http://www.w3.org/1998/Math/MathML"; + +// BEGIN LOCAL MODIFICATION: The spec does not have the onAttrModified event +// and does not want it, but we still support it. +var gMutationEvents = []; + +function onAttrModified(event) { + assert_equals(event.attrName, "class", "mutation on unexpected attribute"); + + gMutationEvents.push({ + attrChange: event.attrChange, + prevValue: event.prevValue, + newValue: event.newValue, + }); +} +// END LOCAL MODIFICATION + +function setClass(e, newVal) { + if (newVal === null) { + e.removeAttribute("class"); + } else { + e.setAttribute("class", newVal); + } +} + +function checkModification(e, funcName, args, expectedRes, before, after, + expectedException, desc) { + if (!Array.isArray(args)) { + args = [args]; + } + + test(function() { + var shouldThrow = typeof(expectedException) === "string"; + if (shouldThrow) { + // If an exception is thrown, the class attribute shouldn't change. + after = before; + } + setClass(e, before); + + // BEGIN LOCAL MODIFICATION + gMutationEvents = []; + e.addEventListener("DOMAttrModified", onAttrModified); + // END LOCAL MODIFICATION + var obs; + // If we have MutationObservers available, do some checks to make + // sure attribute sets are happening at sane times. + if (self.MutationObserver) { + obs = new MutationObserver(() => {}); + obs.observe(e, { attributes: true }); + } + if (shouldThrow) { + assert_throws_dom(expectedException, function() { + var list = e.classList; + var res = list[funcName].apply(list, args); + }); + } else { + var list = e.classList; + var res = list[funcName].apply(list, args); + } + if (obs) { + var mutationRecords = obs.takeRecords(); + obs.disconnect(); + if (shouldThrow) { + assert_equals(mutationRecords.length, 0, + "There should have been no mutation"); + } else if (funcName == "replace") { + assert_equals(mutationRecords.length == 1, + expectedRes, + "Should have a mutation exactly when replace() returns true"); + } else { + // For other functions, would need to check when exactly + // mutations are supposed to happen. + } + } + // BEGIN LOCAL MODIFICATION + e.removeEventListener("DOMAttrModified", onAttrModified); + // END LOCAL MODIFICATION + if (!shouldThrow) { + assert_equals(res, expectedRes, "wrong return value"); + } + + var expectedAfter = after; + + assert_equals(e.getAttribute("class"), expectedAfter, + "wrong class after modification"); + // BEGIN LOCAL MODIFICATION + var expectedMutation = before != after; + assert_equals(gMutationEvents.length, expectedMutation ? 1 : 0, + "unexpected mutation event count"); + if (expectedMutation && gMutationEvents.length) { + assert_equals(gMutationEvents[0].attrChange, + before == null ? MutationEvent.ADDITION + : MutationEvent.MODIFICATION, + "wrong type of attribute change"); + // If there wasn't any previous attribute, prevValue will return an empty + // string. + var expectedPrevValue = before === null ? "" : before; + assert_equals(gMutationEvents[0].prevValue, expectedPrevValue, + "wrong previous value"); + assert_equals(gMutationEvents[0].newValue, after, "wrong new value"); + } + // END LOCAL MODIFICATION + }, "classList." + funcName + "(" + args.map(format_value).join(", ") + + ") with attribute value " + format_value(before) + desc); +} + +function assignToClassListStrict(e) { + "use strict"; + e.classList = "foo"; + e.removeAttribute("class"); +} + +function assignToClassList(e) { + var expect = e.classList; + e.classList = "foo"; + assert_equals(e.classList, expect, + "classList should be unchanged after assignment"); + e.removeAttribute("class"); +} + +function testClassList(e, desc) { + + // assignment + + test(function() { + assignToClassListStrict(e); + assignToClassList(e); + }, "Assigning to classList" + desc); + + // supports + test(function() { + assert_throws_js(TypeError, function() { + e.classList.supports("a"); + }) + }, ".supports() must throw TypeError" + desc); + + // length attribute + + function checkLength(value, length) { + test(function() { + setClass(e, value); + assert_equals(e.classList.length, length); + }, "classList.length when " + + (value === null ? "removed" : "set to " + format_value(value)) + desc); + } + + checkLength(null, 0); + checkLength("", 0); + checkLength(" \t \f", 0); + checkLength("a", 1); + checkLength("a A", 2); + checkLength("\r\na\t\f", 1); + checkLength("a a", 1); + checkLength("a a a a a a", 1); + checkLength("a a b b", 2); + checkLength("a A B b", 4); + checkLength("a b c c b a a b c c", 3); + checkLength(" a a b", 2); + checkLength("a\tb\nc\fd\re f", 6); + + // [Stringifies] + + function checkStringifier(value, expected) { + test(function() { + setClass(e, value); + assert_equals(e.classList.toString(), expected); + }, "classList.toString() when " + + (value === null ? "removed" : "set to " + format_value(value)) + desc); + } + + checkStringifier(null, ""); + checkStringifier("foo", "foo"); + checkStringifier(" a a b", " a a b"); + + // item() method + + function checkItems(attributeValue, expectedValues) { + function checkItemFunction(index, expected) { + assert_equals(e.classList.item(index), expected, + "classList.item(" + index + ")"); + } + + function checkItemArray(index, expected) { + assert_equals(e.classList[index], expected, "classList[" + index + "]"); + } + + test(function() { + setClass(e, attributeValue); + + checkItemFunction(-1, null); + checkItemArray(-1, undefined); + + var i = 0; + while (i < expectedValues.length) { + checkItemFunction(i, expectedValues[i]); + checkItemArray(i, expectedValues[i]); + i++; + } + + checkItemFunction(i, null); + checkItemArray(i, undefined); + + checkItemFunction(0xffffffff, null); + checkItemArray(0xffffffff, undefined); + + checkItemFunction(0xfffffffe, null); + checkItemArray(0xfffffffe, undefined); + }, "classList.item() when set to " + format_value(attributeValue) + desc); + } + + checkItems(null, []); + checkItems("a", ["a"]); + checkItems("aa AA aa", ["aa", "AA"]); + checkItems("a b", ["a", "b"]); + checkItems(" a a b", ["a", "b"]); + checkItems("\t\n\f\r a\t\n\f\r b\t\n\f\r ", ["a", "b"]); + + // contains() method + + function checkContains(attributeValue, args, expectedRes) { + if (!Array.isArray(expectedRes)) { + expectedRes = Array(args.length).fill(expectedRes); + } + setClass(e, attributeValue); + for (var i = 0; i < args.length; i++) { + test(function() { + assert_equals(e.classList.contains(args[i]), expectedRes[i], + "classList.contains(\"" + args[i] + "\")"); + }, "classList.contains(" + format_value(args[i]) + ") when set to " + + format_value(attributeValue) + desc); + } + } + + checkContains(null, ["a", "", " "], false); + checkContains("", ["a"], false); + + checkContains("a", ["a"], true); + checkContains("a", ["aa", "b", "A", "a.", "a)",, "a'", 'a"', "a$", "a~", + "a?", "a\\"], false); + + // All "ASCII whitespace" per spec, before and after + checkContains("a", ["a\t", "\ta", "a\n", "\na", "a\f", "\fa", "a\r", "\ra", + "a ", " a"], false); + + checkContains("aa AA", ["aa", "AA", "aA"], [true, true, false]); + checkContains("a a a", ["a", "aa", "b"], [true, false, false]); + checkContains("a b c", ["a", "b"], true); + + checkContains("null undefined", [null, undefined], true); + checkContains("\t\n\f\r a\t\n\f\r b\t\n\f\r ", ["a", "b"], true); + + // add() method + + function checkAdd(before, argument, after, expectedException) { + checkModification(e, "add", argument, undefined, before, after, + expectedException, desc); + // Also check force toggle + // XXX https://github.com/whatwg/dom/issues/443 + //if (!Array.isArray(argument)) { + // checkModification(e, "toggle", [argument, true], true, before, after, + // expectedException); + //} + } + + checkAdd(null, "", null, "SyntaxError"); + checkAdd(null, ["a", ""], null, "SyntaxError"); + checkAdd(null, " ", null, "InvalidCharacterError"); + checkAdd(null, "\ta", null, "InvalidCharacterError"); + checkAdd(null, "a\t", null, "InvalidCharacterError"); + checkAdd(null, "\na", null, "InvalidCharacterError"); + checkAdd(null, "a\n", null, "InvalidCharacterError"); + checkAdd(null, "\fa", null, "InvalidCharacterError"); + checkAdd(null, "a\f", null, "InvalidCharacterError"); + checkAdd(null, "\ra", null, "InvalidCharacterError"); + checkAdd(null, "a\r", null, "InvalidCharacterError"); + checkAdd(null, " a", null, "InvalidCharacterError"); + checkAdd(null, "a ", null, "InvalidCharacterError"); + checkAdd(null, ["a", " "], null, "InvalidCharacterError"); + checkAdd(null, ["a", "aa "], null, "InvalidCharacterError"); + + checkAdd("a", "a", "a"); + checkAdd("aa", "AA", "aa AA"); + checkAdd("a b c", "a", "a b c"); + checkAdd("a a a b", "a", "a b"); + checkAdd(null, "a", "a"); + checkAdd("", "a", "a"); + checkAdd(" ", "a", "a"); + checkAdd(" \f", "a", "a"); + checkAdd("a", "b", "a b"); + checkAdd("a b c", "d", "a b c d"); + checkAdd("a b c ", "d", "a b c d"); + checkAdd(" a a b", "c", "a b c"); + checkAdd(" a a b", "a", "a b"); + checkAdd("\t\n\f\r a\t\n\f\r b\t\n\f\r ", "c", "a b c"); + + // multiple add + checkAdd("a b c ", ["d", "e"], "a b c d e"); + checkAdd("a b c ", ["a", "a"], "a b c"); + checkAdd("a b c ", ["d", "d"], "a b c d"); + checkAdd("a b c a ", [], "a b c"); + checkAdd(null, ["a", "b"], "a b"); + checkAdd("", ["a", "b"], "a b"); + + checkAdd(null, null, "null"); + checkAdd(null, undefined, "undefined"); + + // remove() method + + function checkRemove(before, argument, after, expectedException) { + checkModification(e, "remove", argument, undefined, before, after, + expectedException, desc); + // Also check force toggle + // XXX https://github.com/whatwg/dom/issues/443 + //if (!Array.isArray(argument)) { + // checkModification(e, "toggle", [argument, false], false, before, after, + // expectedException); + //} + } + + checkRemove(null, "", null, "SyntaxError"); + checkRemove(null, " ", null, "InvalidCharacterError"); + checkRemove("\ta", "\ta", "\ta", "InvalidCharacterError"); + checkRemove("a\t", "a\t", "a\t", "InvalidCharacterError"); + checkRemove("\na", "\na", "\na", "InvalidCharacterError"); + checkRemove("a\n", "a\n", "a\n", "InvalidCharacterError"); + checkRemove("\fa", "\fa", "\fa", "InvalidCharacterError"); + checkRemove("a\f", "a\f", "a\f", "InvalidCharacterError"); + checkRemove("\ra", "\ra", "\ra", "InvalidCharacterError"); + checkRemove("a\r", "a\r", "a\r", "InvalidCharacterError"); + checkRemove(" a", " a", " a", "InvalidCharacterError"); + checkRemove("a ", "a ", "a ", "InvalidCharacterError"); + checkRemove("aa ", "aa ", null, "InvalidCharacterError"); + + checkRemove(null, "a", null); + checkRemove("", "a", ""); + checkRemove("a b c", "d", "a b c"); + checkRemove("a b c", "A", "a b c"); + checkRemove(" a a a ", "a", ""); + checkRemove("a b", "a", "b"); + checkRemove("a b ", "a", "b"); + checkRemove("a a b", "a", "b"); + checkRemove("aa aa bb", "aa", "bb"); + checkRemove("a a b a a c a a", "a", "b c"); + + checkRemove("a b c", "b", "a c"); + checkRemove("aaa bbb ccc", "bbb", "aaa ccc"); + checkRemove(" a b c ", "b", "a c"); + checkRemove("a b b b c", "b", "a c"); + + checkRemove("a b c", "c", "a b"); + checkRemove(" a b c ", "c", "a b"); + checkRemove("a b c c c", "c", "a b"); + + checkRemove("a b a c a d a", "a", "b c d"); + checkRemove("AA BB aa CC AA dd aa", "AA", "BB aa CC dd"); + + checkRemove("\ra\na\ta\f", "a", ""); + checkRemove("\t\n\f\r a\t\n\f\r b\t\n\f\r ", "a", "b"); + + // multiple remove + checkRemove("a b c ", ["d", "e"], "a b c"); + checkRemove("a b c ", ["a", "b"], "c"); + checkRemove("a b c ", ["a", "c"], "b"); + checkRemove("a b c ", ["a", "a"], "b c"); + checkRemove("a b c ", ["d", "d"], "a b c"); + checkRemove("a b c ", [], "a b c"); + checkRemove(null, ["a", "b"], null); + checkRemove("", ["a", "b"], ""); + checkRemove("a a", [], "a"); + + checkRemove("null", null, ""); + checkRemove("undefined", undefined, ""); + + // toggle() method + + function checkToggle(before, argument, expectedRes, after, expectedException) { + checkModification(e, "toggle", argument, expectedRes, before, after, + expectedException, desc); + } + + checkToggle(null, "", null, null, "SyntaxError"); + checkToggle(null, "aa ", null, null, "InvalidCharacterError"); + + checkToggle(null, "a", true, "a"); + checkToggle("", "a", true, "a"); + checkToggle(" ", "a", true, "a"); + checkToggle(" \f", "a", true, "a"); + checkToggle("a", "b", true, "a b"); + checkToggle("a", "A", true, "a A"); + checkToggle("a b c", "d", true, "a b c d"); + checkToggle(" a a b", "d", true, "a b d"); + + checkToggle("a", "a", false, ""); + checkToggle(" a a a ", "a", false, ""); + checkToggle(" A A A ", "a", true, "A a"); + checkToggle(" a b c ", "b", false, "a c"); + checkToggle(" a b c b b", "b", false, "a c"); + checkToggle(" a b c ", "c", false, "a b"); + checkToggle(" a b c ", "a", false, "b c"); + checkToggle(" a a b", "b", false, "a"); + checkToggle("\t\n\f\r a\t\n\f\r b\t\n\f\r ", "a", false, "b"); + checkToggle("\t\n\f\r a\t\n\f\r b\t\n\f\r ", "c", true, "a b c"); + + checkToggle("null", null, false, ""); + checkToggle("", null, true, "null"); + checkToggle("undefined", undefined, false, ""); + checkToggle("", undefined, true, "undefined"); + + + // tests for the force argument handling + // XXX Remove these if https://github.com/whatwg/dom/issues/443 is fixed + + function checkForceToggle(before, argument, force, expectedRes, after, expectedException) { + checkModification(e, "toggle", [argument, force], expectedRes, before, + after, expectedException, desc); + } + + checkForceToggle("", "a", true, true, "a"); + checkForceToggle("a", "a", true, true, "a"); + checkForceToggle("a", "b", true, true, "a b"); + checkForceToggle("a b", "b", true, true, "a b"); + checkForceToggle("", "a", false, false, ""); + checkForceToggle("a", "a", false, false, ""); + checkForceToggle("a", "b", false, false, "a"); + checkForceToggle("a b", "b", false, false, "a"); + + + // replace() method + function checkReplace(before, token, newToken, expectedRes, after, expectedException) { + checkModification(e, "replace", [token, newToken], expectedRes, before, + after, expectedException, desc); + } + + checkReplace(null, "", "a", null, null, "SyntaxError"); + checkReplace(null, "", " ", null, null, "SyntaxError"); + checkReplace(null, " ", "a", null, null, "InvalidCharacterError"); + checkReplace(null, "\ta", "b", null, null, "InvalidCharacterError"); + checkReplace(null, "a\t", "b", null, null, "InvalidCharacterError"); + checkReplace(null, "\na", "b", null, null, "InvalidCharacterError"); + checkReplace(null, "a\n", "b", null, null, "InvalidCharacterError"); + checkReplace(null, "\fa", "b", null, null, "InvalidCharacterError"); + checkReplace(null, "a\f", "b", null, null, "InvalidCharacterError"); + checkReplace(null, "\ra", "b", null, null, "InvalidCharacterError"); + checkReplace(null, "a\r", "b", null, null, "InvalidCharacterError"); + checkReplace(null, " a", "b", null, null, "InvalidCharacterError"); + checkReplace(null, "a ", "b", null, null, "InvalidCharacterError"); + + checkReplace(null, "a", "", null, null, "SyntaxError"); + checkReplace(null, " ", "", null, null, "SyntaxError"); + checkReplace(null, "a", " ", null, null, "InvalidCharacterError"); + checkReplace(null, "b", "\ta", null, null, "InvalidCharacterError"); + checkReplace(null, "b", "a\t", null, null, "InvalidCharacterError"); + checkReplace(null, "b", "\na", null, null, "InvalidCharacterError"); + checkReplace(null, "b", "a\n", null, null, "InvalidCharacterError"); + checkReplace(null, "b", "\fa", null, null, "InvalidCharacterError"); + checkReplace(null, "b", "a\f", null, null, "InvalidCharacterError"); + checkReplace(null, "b", "\ra", null, null, "InvalidCharacterError"); + checkReplace(null, "b", "a\r", null, null, "InvalidCharacterError"); + checkReplace(null, "b", " a", null, null, "InvalidCharacterError"); + checkReplace(null, "b", "a ", null, null, "InvalidCharacterError"); + + checkReplace("a", "a", "a", true, "a"); + checkReplace("a", "a", "b", true, "b"); + checkReplace("a", "A", "b", false, "a"); + checkReplace("a b", "b", "A", true, "a A"); + checkReplace("a b", "c", "a", false, "a b"); + checkReplace("a b c", "d", "e", false, "a b c"); + // https://github.com/whatwg/dom/issues/443 + checkReplace("a a a b", "a", "a", true, "a b"); + checkReplace("a a a b", "c", "d", false, "a a a b"); + checkReplace(null, "a", "b", false, null); + checkReplace("", "a", "b", false, ""); + checkReplace(" ", "a", "b", false, " "); + checkReplace(" a \f", "a", "b", true, "b"); + checkReplace("a b c", "b", "d", true, "a d c"); + checkReplace("a b c", "c", "a", true, "a b"); + checkReplace("c b a", "c", "a", true, "a b"); + checkReplace("a b a", "a", "c", true, "c b"); + checkReplace("a b a", "b", "c", true, "a c"); + checkReplace(" a a b", "a", "c", true, "c b"); + checkReplace(" a a b", "b", "c", true, "a c"); + checkReplace("\t\n\f\r a\t\n\f\r b\t\n\f\r ", "a", "c", true, "c b"); + checkReplace("\t\n\f\r a\t\n\f\r b\t\n\f\r ", "b", "c", true, "a c"); + + checkReplace("a null", null, "b", true, "a b"); + checkReplace("a b", "a", null, true, "null b"); + checkReplace("a undefined", undefined, "b", true, "a b"); + checkReplace("a b", "a", undefined, true, "undefined b"); +} + +var content = document.getElementById("content"); + +var htmlNode = document.createElement("div"); +content.appendChild(htmlNode); +testClassList(htmlNode, " (HTML node)"); + +var xhtmlNode = document.createElementNS(XHTML_NS, "div"); +content.appendChild(xhtmlNode); +testClassList(xhtmlNode, " (XHTML node)"); + +var mathMLNode = document.createElementNS(MATHML_NS, "math"); +content.appendChild(mathMLNode); +testClassList(mathMLNode, " (MathML node)"); + +var xmlNode = document.createElementNS(null, "foo"); +content.appendChild(xmlNode); +testClassList(xmlNode, " (XML node with null namespace)"); + +var fooNode = document.createElementNS("http://example.org/foo", "foo"); +content.appendChild(fooNode); +testClassList(fooNode, " (foo node)"); +</script> |