mirror of
https://github.com/servo/servo
synced 2026-04-30 03:17:15 +02:00
408 lines
19 KiB
HTML
408 lines
19 KiB
HTML
<!doctype html>
|
|
<html>
|
|
<head class="test test">
|
|
<title class=" ">Element.classList in case-sensitive documents</title>
|
|
<link rel="help" href="https://dom.spec.whatwg.org/#concept-class">
|
|
<script type="text/javascript" src="/resources/testharness.js"></script>
|
|
<script type="text/javascript" src="/resources/testharnessreport.js"></script>
|
|
<style type="text/css">
|
|
.foo { font-style: italic; }
|
|
</style>
|
|
<script type="text/javascript">
|
|
var elem = document.getElementsByTagName('title')[0], secondelem = document.getElementsByTagName('head')[0];
|
|
test(function () {
|
|
assert_equals( typeof elem.classList, 'object', 'critical test; ignore any results after this' );
|
|
}, 'Element.classList must exist as an object');
|
|
test(function () {
|
|
assert_equals( typeof document.documentElement.classList, 'object' );
|
|
}, 'Element.classList must exist as an object even if the element has no class attribute');
|
|
test(function () {
|
|
assert_true( !!window.DOMTokenList );
|
|
}, 'DOMTokenList should be exposed for prototyping');
|
|
test(function () {
|
|
DOMTokenList.prototype.customProperty = true;
|
|
assert_true( elem.classList.customProperty );
|
|
}, 'prototyping DOMTokenList should work');
|
|
test(function () {
|
|
assert_true( elem.classList instanceof window.DOMTokenList );
|
|
assert_equals( elem.classList.constructor, window.DOMTokenList );
|
|
}, 'Element.classList must implement DOMTokenList');
|
|
test(function () {
|
|
assert_not_equals( getComputedStyle(elem,null).fontStyle, 'italic', 'critical test; required by the testsuite' );
|
|
}, 'CSS .foo selectors must not match elements without any class');
|
|
test(function () {
|
|
assert_equals( secondelem.classList.length, 1, 'duplicates in initial string should be removed per https://dom.spec.whatwg.org/#concept-class' );
|
|
assert_equals( secondelem.classList.item(0), 'test' );
|
|
assert_true( secondelem.classList.contains('test') );
|
|
}, 'classList must be correct for an element that has classes');
|
|
test(function () {
|
|
assert_equals( elem.classList.length, 0 );
|
|
}, 'classList.length must be 0 for an element that has no classes');
|
|
test(function () {
|
|
assert_false( elem.classList.contains('foo') );
|
|
}, 'classList must not contain an undefined class');
|
|
test(function () {
|
|
assert_equals( elem.classList.item(0), null );
|
|
}, 'classList.item() must return null for out-of-range index');
|
|
test(function () {
|
|
assert_equals( elem.classList.item(-1), null );
|
|
}, 'classList.item() must return null for negative index');
|
|
test(function () {
|
|
/* the normative part of the spec states that:
|
|
"unless tokens is empty, in which case there are no supported property indices"
|
|
...
|
|
"The term[...] supported property indices [is] used as defined in the WebIDL specification."
|
|
WebIDL creates actual OwnProperties and then [] just acts as a normal property lookup */
|
|
assert_equals( elem.classList[0], undefined );
|
|
}, 'classList[index] must be undefined for out-of-range index');
|
|
test(function () {
|
|
assert_equals( elem.classList[-1], undefined );
|
|
}, 'classList[index] must be undefined for negative index');
|
|
test(function () {
|
|
assert_equals( elem.className, ' ' );
|
|
}, 'className should contain initial markup whitespace');
|
|
test(function () {
|
|
assert_equals( elem.classList + '', ' ', 'implicit' );
|
|
assert_equals( elem.classList.toString(), ' ', 'explicit' );
|
|
}, 'classList should contain initial markup whitespace');
|
|
test(function () {
|
|
assert_false( elem.classList.contains('') );
|
|
}, '.contains(empty_string) must return false');
|
|
test(function () {
|
|
assert_throws( 'SYNTAX_ERR', function () { elem.classList.add(''); } );
|
|
}, '.add(empty_string) must throw a SYNTAX_ERR');
|
|
test(function () {
|
|
assert_throws( 'SYNTAX_ERR', function () { elem.classList.remove(''); } );
|
|
}, '.remove(empty_string) must throw a SYNTAX_ERR');
|
|
test(function () {
|
|
assert_throws( 'SYNTAX_ERR', function () { elem.classList.toggle(''); } );
|
|
}, '.toggle(empty_string) must throw a SYNTAX_ERR');
|
|
test(function () {
|
|
assert_throws( 'SYNTAX_ERR', function () { elem.classList.replace('', 'foo'); } );
|
|
assert_throws( 'SYNTAX_ERR', function () { elem.classList.replace('foo', ''); } );
|
|
assert_throws( 'SYNTAX_ERR', function () { elem.classList.replace('', 'foo bar'); } );
|
|
assert_throws( 'SYNTAX_ERR', function () { elem.classList.replace('foo bar', ''); } );
|
|
assert_throws( 'SYNTAX_ERR', function () { elem.classList.replace('', ''); } );
|
|
}, '.replace with empty_string must throw a SYNTAX_ERR');
|
|
test(function () {
|
|
assert_false( elem.classList.contains('a b') );
|
|
}, '.contains(string_with_spaces) must return false');
|
|
test(function () {
|
|
assert_throws( 'INVALID_CHARACTER_ERR', function () { elem.classList.add('a b'); } );
|
|
}, '.add(string_with_spaces) must throw an INVALID_CHARACTER_ERR');
|
|
test(function () {
|
|
assert_throws( 'INVALID_CHARACTER_ERR', function () { elem.classList.remove('a b'); } );
|
|
}, '.remove(string_with_spaces) must throw an INVALID_CHARACTER_ERR');
|
|
test(function () {
|
|
assert_throws( 'INVALID_CHARACTER_ERR', function () { elem.classList.toggle('a b'); } );
|
|
}, '.toggle(string_with_spaces) must throw an INVALID_CHARACTER_ERR');
|
|
test(function () {
|
|
assert_throws( 'INVALID_CHARACTER_ERR', function () { elem.classList.replace('z', 'a b'); } );
|
|
assert_throws( 'INVALID_CHARACTER_ERR', function () { elem.classList.replace('a b', 'z'); } );
|
|
assert_throws( 'INVALID_CHARACTER_ERR', function () { elem.classList.replace('a b', 'b c'); } );
|
|
}, '.replace with string_with_spaces must throw a INVALID_CHARACTER_ERR');
|
|
test(function () {
|
|
var foo = document.createElement('div');
|
|
foo.className = 'token1 token2 token3'
|
|
foo.classList.replace('token1', 'token3');
|
|
assert_equals( foo.classList.length, 2 );
|
|
assert_false( foo.classList.contains('token1') );
|
|
assert_true( foo.classList.contains('token2') );
|
|
assert_true( foo.classList.contains('token3') );
|
|
}, '.replace with an already existing token')
|
|
elem.className = 'foo';
|
|
test(function () {
|
|
assert_equals( getComputedStyle(elem,null).fontStyle, 'italic', 'critical test; required by the testsuite' );
|
|
}, 'computed style must update when setting .className');
|
|
test(function () {
|
|
assert_true( elem.classList.contains('foo') );
|
|
}, 'classList.contains must update when .className is changed');
|
|
test(function () {
|
|
assert_false( elem.classList.contains('FOO') );
|
|
}, 'classList.contains must be case sensitive');
|
|
test(function () {
|
|
assert_false( elem.classList.contains('foo.') );
|
|
assert_false( elem.classList.contains('foo)') );
|
|
assert_false( elem.classList.contains('foo\'') );
|
|
assert_false( elem.classList.contains('foo$') );
|
|
assert_false( elem.classList.contains('foo~') );
|
|
assert_false( elem.classList.contains('foo?') );
|
|
assert_false( elem.classList.contains('foo\\') );
|
|
}, 'classList.contains must not match when punctuation characters are added');
|
|
test(function () {
|
|
elem.classList.add('FOO');
|
|
assert_equals( getComputedStyle(elem,null).fontStyle, 'italic' );
|
|
}, 'classList.add must not cause the CSS selector to stop matching');
|
|
test(function () {
|
|
assert_true( elem.classList.contains('foo') );
|
|
}, 'classList.add must not remove existing classes');
|
|
test(function () {
|
|
assert_true( elem.classList.contains('FOO') );
|
|
}, 'classList.contains case sensitivity must match a case-specific string');
|
|
test(function () {
|
|
assert_equals( elem.classList.length, 2 );
|
|
}, 'classList.length must correctly reflect the number of tokens');
|
|
test(function () {
|
|
assert_equals( elem.classList.item(0), 'foo' );
|
|
}, 'classList.item(0) must return the first token');
|
|
test(function () {
|
|
assert_equals( elem.classList.item(1), 'FOO' );
|
|
}, 'classList.item must return case-sensitive strings and preserve token order');
|
|
test(function () {
|
|
assert_equals( elem.classList[0], 'foo' );
|
|
}, 'classList[0] must return the first token');
|
|
test(function () {
|
|
assert_equals( elem.classList[1], 'FOO' );
|
|
}, 'classList[index] must return case-sensitive strings and preserve token order');
|
|
test(function () {
|
|
/* the normative part of the spec states that:
|
|
"The object's supported property indices are the numbers in the range zero to the number of tokens in tokens minus one"
|
|
...
|
|
"The term[...] supported property indices [is] used as defined in the WebIDL specification."
|
|
WebIDL creates actual OwnProperties and then [] just acts as a normal property lookup */
|
|
assert_equals( elem.classList[2], undefined );
|
|
}, 'classList[index] must still be undefined for out-of-range index when earlier indexes exist');
|
|
test(function () {
|
|
assert_equals( elem.className, 'foo FOO' );
|
|
}, 'className must update correctly when items have been added through classList');
|
|
test(function () {
|
|
assert_equals( elem.classList + '', 'foo FOO', 'implicit' );
|
|
assert_equals( elem.classList.toString(), 'foo FOO', 'explicit' );
|
|
}, 'classList must stringify correctly when items have been added');
|
|
test(function () {
|
|
elem.classList.add('foo');
|
|
assert_equals( elem.classList.length, 2 );
|
|
assert_equals( elem.classList + '', 'foo FOO', 'implicit' );
|
|
assert_equals( elem.classList.toString(), 'foo FOO', 'explicit' );
|
|
}, 'classList.add should not add a token if it already exists');
|
|
test(function () {
|
|
elem.classList.remove('bar');
|
|
assert_equals( elem.classList.length, 2 );
|
|
assert_equals( elem.classList + '', 'foo FOO', 'implicit' );
|
|
assert_equals( elem.classList.toString(), 'foo FOO', 'explicit' );
|
|
}, 'classList.remove removes arguments passed, if they are present.');
|
|
test(function () {
|
|
elem.classList.remove('foo');
|
|
assert_equals( elem.classList.length, 1 );
|
|
assert_equals( elem.classList + '', 'FOO', 'implicit' );
|
|
assert_equals( elem.classList.toString(), 'FOO', 'explicit' );
|
|
assert_false( elem.classList.contains('foo') );
|
|
assert_true( elem.classList.contains('FOO') );
|
|
}, 'classList.remove must remove existing tokens');
|
|
test(function () {
|
|
assert_not_equals( getComputedStyle(elem,null).fontStyle, 'italic' );
|
|
}, 'classList.remove must not break case-sensitive CSS selector matching');
|
|
test(function () {
|
|
secondelem.classList.remove('test');
|
|
assert_equals( secondelem.classList.length, 0 );
|
|
assert_false( secondelem.classList.contains('test') );
|
|
}, 'classList.remove must remove duplicated tokens');
|
|
test(function () {
|
|
secondelem.className = 'token1 token2 token3';
|
|
secondelem.classList.remove('token2');
|
|
assert_equals( secondelem.classList + '', 'token1 token3', 'implicit' );
|
|
assert_equals( secondelem.classList.toString(), 'token1 token3', 'explicit' );
|
|
}, 'classList.remove must collapse whitespace around removed tokens');
|
|
test(function () {
|
|
secondelem.className = ' token1 token2 ';
|
|
secondelem.classList.remove('token2');
|
|
assert_equals( secondelem.classList + '', 'token1', 'implicit' );
|
|
assert_equals( secondelem.classList.toString(), 'token1', 'explicit' );
|
|
}, 'classList.remove must collapse whitespaces around each token');
|
|
test(function () {
|
|
secondelem.className = ' token1 token2 token1 ';
|
|
secondelem.classList.remove('token2');
|
|
assert_equals( secondelem.classList + '', 'token1', 'implicit' );
|
|
assert_equals( secondelem.classList.toString(), 'token1', 'explicit' );
|
|
}, 'classList.remove must collapse whitespaces around each token and remove duplicates');
|
|
test(function () {
|
|
secondelem.className = ' token1 token2 token1 ';
|
|
secondelem.classList.remove('token1');
|
|
assert_equals( secondelem.classList + '', 'token2', 'implicit' );
|
|
assert_equals( secondelem.classList.toString(), 'token2', 'explicit' );
|
|
}, 'classList.remove must collapse whitespace when removing duplicate tokens');
|
|
test(function () {
|
|
secondelem.className = ' token1 token1 ';
|
|
secondelem.classList.add('token1');
|
|
assert_equals( secondelem.classList + '', 'token1', 'implicit' );
|
|
assert_equals( secondelem.classList.toString(), 'token1', 'explicit' );
|
|
}, 'classList.add must collapse whitespaces and remove duplicates when adding a token that already exists');
|
|
test(function () {
|
|
assert_true(elem.classList.toggle('foo'));
|
|
assert_equals( elem.classList.length, 2 );
|
|
assert_true( elem.classList.contains('foo') );
|
|
assert_true( elem.classList.contains('FOO') );
|
|
}, 'classList.toggle must toggle tokens case-sensitively when adding');
|
|
test(function () {
|
|
assert_equals( getComputedStyle(elem,null).fontStyle, 'italic' );
|
|
}, 'classList.toggle must not break case-sensitive CSS selector matching');
|
|
test(function () {
|
|
assert_false(elem.classList.toggle('foo'));
|
|
}, 'classList.toggle must be able to remove tokens');
|
|
test(function () {
|
|
//will return true if the last test incorrectly removed both
|
|
assert_false(elem.classList.toggle('FOO'));
|
|
assert_false( elem.classList.contains('foo') );
|
|
assert_false( elem.classList.contains('FOO') );
|
|
}, 'classList.toggle must be case-sensitive when removing tokens');
|
|
test(function () {
|
|
secondelem.className = 'foo FOO'
|
|
secondelem.classList.replace('bar', 'baz');
|
|
assert_equals( secondelem.classList.length, 2 );
|
|
assert_equals( secondelem.classList + '', 'foo FOO', 'implicit' );
|
|
assert_equals( secondelem.classList.toString(), 'foo FOO', 'explicit' );
|
|
}, 'classList.replace replaces arguments passed, if they are present.');
|
|
test(function () {
|
|
secondelem.classList.replace('foo', 'bar');
|
|
assert_equals( secondelem.classList.length, 2 );
|
|
assert_equals( secondelem.classList + '', 'bar FOO', 'implicit' );
|
|
assert_equals( secondelem.classList.toString(), 'bar FOO', 'explicit' );
|
|
assert_false( secondelem.classList.contains('foo') );
|
|
assert_true( secondelem.classList.contains('bar') );
|
|
assert_true( secondelem.classList.contains('FOO') );
|
|
}, 'classList.replace must replace existing tokens');
|
|
test(function () {
|
|
assert_not_equals( getComputedStyle(secondelem,null).fontStyle, 'italic' );
|
|
}, 'classList.replace must not break case-sensitive CSS selector matching');
|
|
test(function () {
|
|
secondelem.className = 'token1 token2 token1'
|
|
secondelem.classList.replace('token1', 'token3');
|
|
assert_equals( secondelem.classList.length, 2 );
|
|
assert_false( secondelem.classList.contains('token1') );
|
|
assert_true( secondelem.classList.contains('token2') );
|
|
assert_true( secondelem.classList.contains('token3') );
|
|
}, 'classList.replace must replace duplicated tokens');
|
|
test(function () {
|
|
secondelem.className = 'token1 token2 token3';
|
|
secondelem.classList.replace('token2', 'token4');
|
|
assert_equals( secondelem.classList + '', 'token1 token4 token3', 'implicit' );
|
|
assert_equals( secondelem.classList.toString(), 'token1 token4 token3', 'explicit' );
|
|
}, 'classList.replace must collapse whitespace around replaced tokens');
|
|
test(function () {
|
|
secondelem.className = ' token1 token2 ';
|
|
secondelem.classList.replace('token2', 'token3');
|
|
assert_equals( secondelem.classList.length, 2 );
|
|
assert_equals( secondelem.classList + '', 'token1 token3', 'implicit' );
|
|
assert_equals( secondelem.classList.toString(), 'token1 token3', 'explicit' );
|
|
}, 'classList.replace must collapse whitespaces around each token');
|
|
test(function () {
|
|
secondelem.className = ' token1 token2 token1 ';
|
|
secondelem.classList.replace('token2', 'token3');
|
|
assert_equals( secondelem.classList + '', 'token1 token3', 'implicit' );
|
|
assert_equals( secondelem.classList.toString(), 'token1 token3', 'explicit' );
|
|
}, 'classList.replace must collapse whitespaces around each token and remove duplicates');
|
|
test(function () {
|
|
secondelem.className = ' token1 token2 token1 ';
|
|
secondelem.classList.replace('token1', 'token3');
|
|
assert_equals( secondelem.classList + '', 'token3 token2', 'implicit' );
|
|
assert_equals( secondelem.classList.toString(), 'token3 token2', 'explicit' );
|
|
}, 'classList.replace must collapse whitespace when replacing duplicate tokens');
|
|
test(function () {
|
|
assert_not_equals( getComputedStyle(elem,null).fontStyle, 'italic' );
|
|
}, 'CSS class selectors must stop matching when all classes have been removed');
|
|
test(function () {
|
|
assert_equals( elem.className, '' );
|
|
}, 'className must be empty when all classes have been removed');
|
|
test(function () {
|
|
assert_equals( elem.classList + '', '', 'implicit' );
|
|
assert_equals( elem.classList.toString(), '', 'explicit' );
|
|
}, 'classList must stringify to an empty string when all classes have been removed');
|
|
test(function () {
|
|
assert_equals( elem.classList.item(0), null );
|
|
}, 'classList.item(0) must return null when all classes have been removed');
|
|
test(function () {
|
|
/* the normative part of the spec states that:
|
|
"unless the length is zero, in which case there are no supported property indices"
|
|
...
|
|
"The term[...] supported property indices [is] used as defined in the WebIDL specification."
|
|
WebIDL creates actual OwnProperties and then [] just acts as a normal property lookup */
|
|
assert_equals( elem.classList[0], undefined );
|
|
}, 'classList[0] must be undefined when all classes have been removed');
|
|
test(function () {
|
|
var foo = document.createElement('div');
|
|
foo.classList.add();
|
|
assert_true( foo.hasAttribute('class') );
|
|
assert_equals( foo.classList + '', '', 'implicit' );
|
|
assert_equals( foo.classList.toString(), '', 'explicit' );
|
|
}, 'Invoking add or remove should set the class attribute');
|
|
// The ordered set parser must skip ASCII whitespace (U+0009, U+000A, U+000C, U+000D, and U+0020.)
|
|
test(function () {
|
|
var foo = document.createElement('div');
|
|
foo.className = 'a ';
|
|
foo.classList.add('b');
|
|
assert_equals(foo.className,'a b');
|
|
}, 'classList.add should treat " " as a space');
|
|
test(function () {
|
|
var foo = document.createElement('div');
|
|
foo.className = 'a\t';
|
|
foo.classList.add('b');
|
|
assert_equals(foo.className,'a b');
|
|
}, 'classList.add should treat \\t as a space');
|
|
test(function () {
|
|
var foo = document.createElement('div');
|
|
foo.className = 'a\r';
|
|
foo.classList.add('b');
|
|
assert_equals(foo.className,'a b');
|
|
}, 'classList.add should treat \\r as a space');
|
|
test(function () {
|
|
var foo = document.createElement('div');
|
|
foo.className = 'a\n';
|
|
foo.classList.add('b');
|
|
assert_equals(foo.className,'a b');
|
|
}, 'classList.add should treat \\n as a space');
|
|
test(function () {
|
|
var foo = document.createElement('div');
|
|
foo.className = 'a\f';
|
|
foo.classList.add('b');
|
|
assert_equals(foo.className,'a b');
|
|
}, 'classList.add should treat \\f as a space');
|
|
test(function () {
|
|
//WebIDL and ECMAScript 5 - a readonly property has a getter but not a setter
|
|
//ES5 makes [[Put]] fail but not throw
|
|
var failed = false;
|
|
secondelem.className = 'token1';
|
|
try {
|
|
secondelem.classList.length = 0;
|
|
} catch(e) {
|
|
failed = e;
|
|
}
|
|
assert_equals(secondelem.classList.length,1);
|
|
assert_false(failed,'an error was thrown');
|
|
}, 'classList.length must be read-only');
|
|
test(function () {
|
|
var realList = secondelem.classList;
|
|
secondelem.classList = 'foo bar';
|
|
assert_equals(secondelem.classList,realList);
|
|
assert_equals(secondelem.classList.length,2);
|
|
assert_equals(secondelem.classList[0],'foo');
|
|
assert_equals(secondelem.classList[1],'bar');
|
|
}, 'classList must have [PutForwards=value]');
|
|
test(function () {
|
|
var foo = document.createElement('div');
|
|
foo.className = 'a';
|
|
foo.classList.replace('token1', 'token2');
|
|
|
|
assert_equals(foo.className, 'a');
|
|
|
|
foo.classList.replace('a', 'b');
|
|
assert_equals(foo.className, 'b');
|
|
|
|
assert_throws('SYNTAX_ERR', function () { foo.classList.replace('t with space', '') });
|
|
assert_throws('INVALID_CHARACTER_ERR', function () { foo.classList.replace('t with space', 'foo') });
|
|
assert_throws('SYNTAX_ERR', function () { foo.classList.replace('', 'foo') });
|
|
}, 'classList.replace should work');
|
|
|
|
test(function() {
|
|
var foo = document.createElement('div');
|
|
assert_throws(new TypeError(),
|
|
function() { foo.classList.supports('hello') });
|
|
}, 'classList.supports should throw');
|
|
</script>
|
|
</head>
|
|
<body>
|
|
|
|
<div id="log"></div>
|
|
|
|
</body>
|
|
</html>
|