Skip to content

Commit

Permalink
Merge pull request #42 from termi1uc1/master
Browse files Browse the repository at this point in the history
add tests for DOMTokenList (eg classList)
  • Loading branch information
Raynos committed Jan 30, 2012
2 parents 651067e + 3e206b9 commit ab17dbf
Showing 1 changed file with 245 additions and 0 deletions.
245 changes: 245 additions & 0 deletions test/tests.js
Expand Up @@ -284,6 +284,19 @@ function createElement() {
p1.appendChild(div2);
return e;
}
function makeEl(eltype,props,contents) {
var elem = document.createElement(eltype);
for( var i in props ) {
//just in case the framework extends object...
if( props.hasOwnProperty(i) ) {
elem.setAttribute(i,props[i]);
}
}
if( contents ) {
elem.innerHTML = contents;
}
return elem;
}

suites["test Element"] = {
"test classList": function (t) {
Expand All @@ -309,6 +322,238 @@ suites["test Element"] = {
"contains does not show properly");
t.equal(clist.toggle("foo"), true, "toggle does not return true");
t.equal(clist.contains("foo"), true, "toggle did not add token");


t.equal( makeEl('div',{"class":'test test'}).classList.length, 2, 'duplicates in initial string should be preserved' );

t.equal( makeEl('div',{"class":' '}).classList.length, 0, 'classList.length must be 0 for an element that has no tokens');

t.equal( makeEl('div',{"class":' '}).classList.contains('foo'), false, 'classList must not contain an undefined class');

t.equal( makeEl('div',{"class":' '}).classList.item(0), null, 'classList.item() must return null for out-of-range index');

t.equal( makeEl('div',{"class":' '}).classList.item(-1), null, 'classList.item() must return null for negative index');

/* 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 */
t.equal( makeEl('div',{"class":' '}).classList[0], window.undefined, 'className[index] must be undefined for out-of-range index');

t.equal( makeEl('div',{"class":' '}).classList[-1], window.undefined, 'className[index] must be undefined for negative index');

t.equal( makeEl('div',{"class":' '}).classList.toString(), ' ', 'empty className should stringify to contain the attribute\'s whitespace');

createDOMException = function(errStr) {
var ex = Object.create(DOMException.prototype);
ex.code = ex[errStr];
ex.message = errStr +': DOM Exception ' + this.code;
return ex;
}

t.throws(function () { makeEl('div',{"class":' '}).classList.contains(''); }, DOMException, '.contains(empty_string) must throw a SYNTAX_ERR');

t.throws(function () { makeEl('div',{"class":' '}).classList.add(''); }, DOMException, '.add(empty_string) must throw a SYNTAX_ERR');

t.throws(function () { makeEl('div',{"class":' '}).classList.remove(''); }, DOMException, '.remove(empty_string) must throw a SYNTAX_ERR');

t.throws(function () { makeEl('div',{"class":' '}).classList.toggle(''); }, DOMException, '.toggle(empty_string) must throw a SYNTAX_ERR');

t.throws(function () { makeEl('div',{"class":' '}).classList.contains('a b'); }, DOMException, '.contains(string_with_spaces) must throw an INVALID_CHARACTER_ERR');

t.throws(function () { makeEl('div',{"class":' '}).classList.add('a b'); }, DOMException, '.add(string_with_spaces) must throw an INVALID_CHARACTER_ERR');

t.throws(function () { makeEl('div',{"class":' '}).classList.remove('a b'); }, DOMException, '.remove(string_with_spaces) must throw an INVALID_CHARACTER_ERR');

t.throws(function () { makeEl('div',{"class":' '}).classList.toggle('a b'); }, DOMException, '.toggle(string_with_spaces) must throw an INVALID_CHARACTER_ERR');

var testEl = makeEl('div',{"class":'foo'}),
r = testEl.classList.contains('foo') == true &&
(testEl.setAttribute('class','bar'),
testEl.classList.contains('bar') == true) &&
testEl.classList.contains('foo') == false;
t.equal(r, true, 'classList.contains must update when the underlying attribute is changed');

t.equal( makeEl('div',{"class":'foo'}).classList.contains('FOO'), false, 'classList.contains must be case sensitive');

var r = (makeEl('div',{"class":'foo'}).classList.contains('foo.') == false) &&
(makeEl('div',{"class":'foo'}).classList.contains('foo)') == false) &&
(makeEl('div',{"class":'foo'}).classList.contains('foo\'') == false) &&
(makeEl('div',{"class":'foo'}).classList.contains('foo$') == false) &&
(makeEl('div',{"class":'foo'}).classList.contains('foo~') == false) &&
(makeEl('div',{"class":'foo'}).classList.contains('foo?') == false) &&
(makeEl('div',{"class":'foo'}).classList.contains('foo\\') == false);
t.equal(r, true, 'classList.contains must not match when punctuation characters are added');

var elem = makeEl('div',{"class":'foo'});
elem.classList.add('FOO');
t.equal( elem.classList.contains('foo'), true, 'classList.add must not remove existing tokens');

t.equal( makeEl('div',{"class":'foo FOO'}).classList.contains('FOO'), true, 'classList.contains case sensitivity must match a case-specific string');

t.equal( makeEl('div',{"class":'foo FOO'}).classList.length, 2 , 'classList.length must correctly reflect the number of tokens');

t.equal( makeEl('div',{"class":'foo FOO'}).classList.item(0), 'foo' , 'classList.item(0) must return the first token');

var elem = makeEl('div',{"class":'foo'});
elem.classList.add('FOO');
t.equal( elem.classList.item(1), 'FOO' , 'classList.item must return case-sensitive strings and preserve token order');

t.equal( makeEl('div',{"class":'foo FOO'}).classList[0], 'foo' , 'classList[0] must return the first token');

t.equal( makeEl('div',{"class":'foo FOO'}).classList[1], 'FOO' , 'classList[index] must return case-sensitive strings and preserve token order');

/* 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 */
t.equal( makeEl('div',{"class":'foo FOO'}).classList[2], window.undefined , 'classList[index] must still be undefined for out-of-range index when earlier indexes exist');

var elem = makeEl('div',{});
elem.classList.add('foo');
elem.classList.add('FOO');
t.equal( elem.getAttribute('class'), 'foo FOO' , 'class attribute must update correctly when items have been added through classList');

var elem = makeEl('div',{});
elem.classList.add('foo');
elem.classList.add('FOO');
t.equal( elem.className + '', 'foo FOO' , 'className must stringify correctly when items have been added');

var elem = makeEl('div',{"class":'foo FOO'});
elem.classList.add('foo');
t.equal( elem.classList.length == 2 && elem.className + '' == 'foo FOO', true , 'classList.add must not make any changes if an existing token is added');

var elem = makeEl('div',{"class":'foo FOO'});
elem.classList.remove('bar');
t.equal( elem.classList.length == 2 && elem.className + '' == 'foo FOO', true , 'classList.remove must not make any changes if a non-existing token is removed');

var elem = makeEl('div',{"class":'foo FOO'});
elem.classList.remove('foo');
r = (elem.classList.length == 1) &&
(elem.classList.toString() == 'FOO') &&
(elem.classList.contains('foo') == false) &&
(elem.classList.contains('FOO') == true);
t.equal(r, true, 'classList.remove must remove existing tokens');

var elem = makeEl('div',{"class":'test test'});
elem.classList.remove('test');
r = (elem.classList.length == 0) &&
(elem.classList.contains('test') == false);
t.equal(r, true, 'classList.remove must remove duplicated tokens');

var elem = makeEl('div',{"class":'token1 token2 token3'});
elem.classList.remove('token2');
t.equal( elem.classList.toString(), 'token1 token3', 'classList.remove must collapse whitespace around removed tokens');

var elem = makeEl('div',{"class":' token1 token2 '});
elem.classList.remove('token2');
t.equal( elem.classList.toString(), ' token1', 'classList.remove must only remove whitespace around removed tokens');

var elem = makeEl('div',{"class":' token1 token2 token1 '});
elem.classList.remove('token2');
t.equal( elem.classList.toString(), ' token1 token1 ', 'classList.remove must collapse multiple whitespace around removed tokens');

var elem = makeEl('div',{"class":' token1 token2 token1 '});
elem.classList.remove('token1');
t.equal( elem.classList.toString(), 'token2', 'classList.remove must collapse whitespace when removing multiple tokens');

var elem = makeEl('div',{"class":' token1 token1 '});
elem.classList.add('token1');
t.equal( elem.classList.toString(), ' token1 token1 ', 'classList.add must not affect whitespace when the token already exists');

var elem = makeEl('div',{"class":'FOO'});
r = (elem.classList.toggle('foo') == true) &&
(elem.classList.length == 2) &&
(elem.classList.contains('foo') == true) &&
(elem.classList.contains('FOO') == true);
t.equal(r, true, 'classList.toggle must toggle tokens case-sensitively when adding');

var elem = makeEl('div',{"class":'foo FOO'});
r = (elem.classList.toggle('foo') == false) &&
(elem.classList.toggle('FOO') == false) &&
(elem.classList.contains('foo') == false) &&
(elem.classList.contains('FOO') == false);
t.equal(r, true, 'classList.toggle must be able to remove tokens case-sensitively');

var elem = makeEl('div',{"class":'foo FOO'});
elem.classList.toggle('foo');
elem.classList.toggle('FOO');
t.equal( elem.getAttribute('class'), '', 'className attribute must be empty when all classes have been removed');

var elem = makeEl('div',{"class":'foo FOO'});
elem.classList.toggle('foo');
elem.classList.toggle('FOO');
t.equal( elem.classList.toString(), '', 'className must stringify to an empty string when all classes have been removed');

var elem = makeEl('div',{"class":'foo FOO'});
elem.classList.toggle('foo');
elem.classList.toggle('FOO');
t.equal( elem.classList.item(0), null, 'classList.item(0) must return null when all classes have been removed');

/* 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 */
var elem = makeEl('div',{"class":'foo FOO'});
elem.classList.toggle('foo');
elem.classList.toggle('FOO');
t.equal( elem.className[0], window.undefined, 'className[0] must be undefined when all classes have been removed');
//if the last character of DOMTokenSting underlying character is not a space character, append U+0020", where "space character" is from " \t\r\n\f"

var elem = makeEl('div',{"class":'a '});
elem.classList.add('b');
t.equal(elem.classList.toString(),'a b', 'classList.add should treat " " as a space');

var elem = makeEl('div',{"class":'a\t'});
elem.classList.add('b');
t.equal(elem.classList.toString(),'a\tb', 'classList.add should treat \\t as a space');

var elem = makeEl('div',{"class":'a\r'});
elem.classList.add('b');
t.equal(elem.classList.toString(),'a\rb', 'classList.add should treat \\r as a space');

var elem = makeEl('div',{"class":'a\n'});
elem.classList.add('b');
t.equal(elem.classList.toString(),'a\nb', 'classList.add should treat \\n as a space');

var elem = makeEl('div',{"class":'a\f'});
elem.classList.add('b');
t.equal(elem.classList.toString(),'a\fb', 'classList.add should treat \\f as a space');

var elem = makeEl('div',{"class":'foo'});
elem.classList.remove('foo');
elem.removeAttribute('className');
t.equal( elem.classList.toggle('foo'), true, 'classList.toggle must work after removing the className attribute');

//WebIDL and ECMAScript 5 - a readonly property has a getter but not a setter
//ES5 makes [[Put]] fail but not throw
var failed = false;
var elem = makeEl('div',{"class":'token1'});
try {
elem.classList.length = 0;
} catch(e) {
failed = e;
}
r = elem.classList.length == 1 &&
failed == false;
t.equal(r, true, 'classList.length must be read-only');

var failed = false, elem = makeEl('div',{"class":'test'}), realList = elem.classList;
try {
elem.className = 'dummy';
} catch(e) {
failed = e;
}
r = elem.classList == realList &&
elem.classList.toString() == 'dummy' &&
failed == false;
t.equal(r, true, 'classList must be read-only');



t.done();
},
"test children": function (t) {
Expand Down

0 comments on commit ab17dbf

Please sign in to comment.