<!doctypehtml>
<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.
// 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);
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 + "]");
}
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);
}
}
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");
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");
// 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");
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");
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.