911d244cded40de663d9eda639151d5fd1647aef
[WebKit-https.git] / LayoutTests / fast / dom / HTMLElement / script-tests / class-list.js
1 description('Tests the classList attribute and its properties.');
2
3 var element;
4
5 function createElement(className)
6 {
7     element = document.createElement('p');
8     element.className = className;
9 }
10
11 debug('Tests from http://simon.html5.org/test/html/dom/reflecting/DOMTokenList/');
12
13 // http://simon.html5.org/test/html/dom/reflecting/DOMTokenList/setting/001.htm
14 // Firefox throws here but WebKit does not throw on setting readonly idl
15 // attributes.
16 createElement('x');
17 try {
18     element.classList = 'y';
19     shouldBeEqualToString('String(element.classList)', 'x');
20 } catch (ex) {
21     testPassed('Throwing on set is acceptable');
22 }
23
24 // http://simon.html5.org/test/html/dom/reflecting/DOMTokenList/getting/001.htm
25 createElement('');
26 shouldEvaluateTo('element.classList.length', 0);
27
28 // http://simon.html5.org/test/html/dom/reflecting/DOMTokenList/getting/002.htm
29 createElement('x');
30 shouldEvaluateTo('element.classList.length', 1);
31
32 // http://simon.html5.org/test/html/dom/reflecting/DOMTokenList/getting/003.htm
33 createElement('x x');
34 shouldEvaluateTo('element.classList.length', 2);
35
36 // http://simon.html5.org/test/html/dom/reflecting/DOMTokenList/getting/004.htm
37 createElement('x y');
38 shouldEvaluateTo('element.classList.length', 2);
39
40 // http://simon.html5.org/test/html/dom/reflecting/DOMTokenList/getting/005.htm
41 createElement('');
42 element.classList.add('x');
43 shouldBeEqualToString('element.className', 'x');
44
45 // http://simon.html5.org/test/html/dom/reflecting/DOMTokenList/getting/006.htm
46 createElement('x');
47 element.classList.add('x');
48 shouldBeEqualToString('element.className', 'x');
49
50 // http://simon.html5.org/test/html/dom/reflecting/DOMTokenList/getting/007.htm
51 createElement('x  x');
52 element.classList.add('x');
53 shouldBeEqualToString('element.className', 'x  x');
54
55 // http://simon.html5.org/test/html/dom/reflecting/DOMTokenList/getting/008.htm
56 createElement('y');
57 element.classList.add('x');
58 shouldBeEqualToString('element.className', 'y x');
59
60 // http://simon.html5.org/test/html/dom/reflecting/DOMTokenList/getting/009.htm
61 createElement('');
62 element.classList.remove('x');
63 shouldBeEqualToString('element.className', '');
64
65 // http://simon.html5.org/test/html/dom/reflecting/DOMTokenList/getting/010.htm
66 createElement('x');
67 element.classList.remove('x');
68 shouldBeEqualToString('element.className', '');
69
70 // http://simon.html5.org/test/html/dom/reflecting/DOMTokenList/getting/011.htm
71 createElement(' y x  y ');
72 element.classList.remove('x');
73 shouldBeEqualToString('element.className', ' y y ');
74
75 // http://simon.html5.org/test/html/dom/reflecting/DOMTokenList/getting/012.htm
76 createElement(' x y  x ');
77 element.classList.remove('x');
78 shouldBeEqualToString('element.className', 'y');
79
80
81 debug('Ensure that we can handle empty class name correctly');
82 element = document.createElement('span');
83 shouldBeTrue("element.classList.toggle('x')");
84 shouldBeEqualToString('element.className', 'x');
85 shouldBeFalse("element.classList.toggle('x')");
86 shouldBeEqualToString('element.className', '');
87
88 element = document.createElement('span');
89 shouldBeFalse('element.classList.contains(\'x\')');
90 shouldBeUndefined('element.classList[1]');
91 element.classList.remove('x');
92 element.classList.add('x')
93
94
95 debug('Test toggle with force argument')
96
97 createElement('');
98 shouldBeTrue("element.classList.toggle('x', true)");
99 shouldBeEqualToString('element.className', 'x');
100 shouldBeTrue("element.classList.toggle('x', true)");
101 shouldBeEqualToString('element.className', 'x');
102 shouldBeFalse("element.classList.toggle('x', false)");
103 shouldBeEqualToString('element.className', '');
104 shouldBeFalse("element.classList.toggle('x', false)");
105 shouldBeEqualToString('element.className', '');
106
107 shouldThrowDOMException(function() {
108     element.classList.toggle('', true);
109 }, DOMException.SYNTAX_ERR);
110
111 shouldThrowDOMException(function() {
112     element.classList.toggle('x y', false);
113 }, DOMException.INVALID_CHARACTER_ERR);
114
115
116 debug('Testing add in presence of trailing white spaces.');
117
118 createElement('x ');
119 element.classList.add('y');
120 shouldBeEqualToString('element.className', 'x y');
121
122 createElement('x\t');
123 element.classList.add('y');
124 shouldBeEqualToString('element.className', 'x\ty');
125
126 createElement(' ');
127 element.classList.add('y');
128 shouldBeEqualToString('element.className', ' y');
129
130
131 debug('Test invalid tokens');
132
133 // Testing exception due to invalid token
134
135 // shouldThrow from js-test-pre.js is not sufficient.
136 function shouldThrowDOMException(f, ec)
137 {
138     try {
139         f();
140         testFailed('Expected an exception');
141     } catch (ex) {
142         if (!(ex instanceof DOMException)) {
143             testFailed('Exception is not an instance of DOMException, found: ' +
144                        Object.toString.call(ex));
145             return;
146         }
147         if (ec !== ex.code) {
148             testFailed('Wrong exception code: ' + ex.code);
149             return;
150         }
151     }
152     var formattedFunction = String(f).replace(/^function.+\{\s*/m, '').
153         replace(/;?\s+\}/m, '');
154     testPassed(formattedFunction + ' threw expected DOMException with code ' + ec);
155 }
156
157 createElement('x');
158 shouldThrowDOMException(function() {
159     element.classList.contains('');
160 }, DOMException.SYNTAX_ERR);
161
162 createElement('x y');
163 shouldThrowDOMException(function() {
164     element.classList.contains('x y');
165 }, DOMException.INVALID_CHARACTER_ERR);
166
167 createElement('');
168 shouldThrowDOMException(function() {
169     element.classList.add('');
170 }, DOMException.SYNTAX_ERR);
171
172 createElement('');
173 shouldThrowDOMException(function() {
174     element.classList.add('x y');
175 }, DOMException.INVALID_CHARACTER_ERR);
176
177 createElement('');
178 shouldThrowDOMException(function() {
179     element.classList.remove('');
180 }, DOMException.SYNTAX_ERR);
181
182 createElement('');
183 shouldThrowDOMException(function() {
184     element.classList.remove('x y');
185 }, DOMException.INVALID_CHARACTER_ERR);
186
187
188
189 createElement('');
190 shouldThrowDOMException(function() {
191     element.classList.toggle('');
192 }, DOMException.SYNTAX_ERR);
193
194 createElement('x y');
195 shouldThrowDOMException(function() {
196     element.classList.toggle('x y');
197 }, DOMException.INVALID_CHARACTER_ERR);
198
199 createElement('');
200 shouldThrow("element.classList.toggle()");
201
202 debug('Indexing');
203
204 createElement('x');
205 shouldBeEqualToString('element.classList[0]', 'x');
206 shouldBeEqualToString('element.classList.item(0)', 'x');
207
208 createElement('x x');
209 shouldBeEqualToString('element.classList[1]', 'x');
210 shouldBeEqualToString('element.classList.item(1)', 'x');
211
212 createElement('x y');
213 shouldBeEqualToString('element.classList[1]', 'y');
214 shouldBeEqualToString('element.classList.item(1)', 'y');
215
216 createElement('');
217 shouldBeUndefined('element.classList[0]');
218 shouldBeNull('element.classList.item(0)');
219
220 createElement('x y z');
221 shouldBeUndefined('element.classList[4]');
222 shouldBeNull('element.classList.item(4)');
223 shouldBeUndefined('element.classList[-1]');  // Not a valid index so should not trigger item().
224 shouldBeNull('element.classList.item(-1)');
225 shouldThrow('element.classList.item()');
226
227 debug('Test case since DOMTokenList is case sensitive');
228
229 createElement('x');
230 shouldBeTrue('element.classList.contains(\'x\')');
231 shouldBeFalse('element.classList.contains(\'X\')');
232 shouldBeEqualToString('element.classList[0]', 'x');
233 shouldThrow('element.classList.contains()');
234
235 createElement('X');
236 shouldBeTrue('element.classList.contains(\'X\')');
237 shouldBeFalse('element.classList.contains(\'x\')');
238 shouldBeEqualToString('element.classList[0]', 'X');
239
240
241 debug('Testing whitespace');
242 // U+0020 SPACE, U+0009 CHARACTER TABULATION (tab), U+000A LINE FEED (LF),
243 // U+000C FORM FEED (FF), and U+000D CARRIAGE RETURN (CR)
244
245 createElement('x\u0020y');
246 shouldEvaluateTo('element.classList.length', 2);
247
248 createElement('x\u0009y');
249 shouldEvaluateTo('element.classList.length', 2);
250
251 createElement('x\u000Ay');
252 shouldEvaluateTo('element.classList.length', 2);
253
254 createElement('x\u000Cy');
255 shouldEvaluateTo('element.classList.length', 2);
256
257 createElement('x\u000Dy');
258 shouldEvaluateTo('element.classList.length', 2);
259
260
261 debug('DOMTokenList presence and type');
262
263
264 // Safari returns object
265 // Firefox returns object
266 // IE8 returns object
267 // Chrome returns function
268 // assertEquals('object', typeof DOMTokenList);
269 shouldBeTrue('\'undefined\' != typeof DOMTokenList');
270
271 shouldBeEqualToString('typeof DOMTokenList.prototype', 'object');
272
273 createElement('x');
274 shouldBeEqualToString('typeof element.classList', 'object');
275
276 shouldEvaluateTo('element.classList.constructor', 'DOMTokenList');
277
278 shouldBeTrue('element.classList === element.classList');
279
280 // Bug 93628
281 document.body.classList.add('FAIL');
282 shouldBeTrue('document.body.classList.contains("FAIL")');
283 document.body.classList.remove('FAIL');
284 shouldBeEqualToString('document.body.className', '');
285
286 // Variadic
287
288 debug('Variadic calls');
289
290 createElement('');
291 element.classList.add('a', 'b');
292 shouldBeEqualToString('element.className', 'a b');
293
294 element.classList.add('a', 'b', 'c');
295 shouldBeEqualToString('element.className', 'a b c');
296
297 element.classList.add(null, {toString: function() { return 'd' }}, undefined, 0, false);
298 shouldBeEqualToString('element.className', 'a b c null d undefined 0 false');
299
300 createElement('');
301 element.classList.add('a', 'b', 'a');
302 shouldBeEqualToString('element.className', 'a b');
303
304 createElement('');
305 shouldThrowDOMException(function() {
306     element.classList.add('a', 'b', '');
307 }, DOMException.SYNTAX_ERR);
308 shouldBeEqualToString('element.className', '');
309
310 shouldThrowDOMException(function() {
311     element.classList.add('a', 'b', 'c d');
312 }, DOMException.INVALID_CHARACTER_ERR);
313 shouldBeEqualToString('element.className', '');
314
315 shouldThrow('element.classList.add("a", {toString: function() { throw new Error("user error"); }}, "b")', '"Error: user error"');
316 shouldBeEqualToString('element.className', '');
317
318 createElement('');
319 shouldNotThrow('element.classList.add()');
320
321 createElement('');
322 var observer = new WebKitMutationObserver(function() {});
323 observer.observe(element, {attributes: true});
324 element.classList.add('a', 'c');
325 shouldBe('observer.takeRecords().length', '1');
326
327
328 createElement('a b c d  ');
329 element.classList.remove('a', 'c');
330 shouldBeEqualToString('element.className', 'b d  ');
331
332 element.classList.remove('b', 'b');
333 shouldBeEqualToString('element.className', 'd  ');
334
335 createElement('a b c null d undefined 0 false');
336 element.classList.remove(null, {toString: function() { return 'd' }}, undefined, 0, false);
337 shouldBeEqualToString('element.className', 'a b c');
338
339 createElement('a b');
340 shouldThrowDOMException(function() {
341     element.classList.remove('a', 'b', '');
342 }, DOMException.SYNTAX_ERR);
343 shouldBeEqualToString('element.className', 'a b');
344
345 shouldThrow('element.classList.remove("a", {toString: function() { throw new Error("user error"); }}, "b")', '"Error: user error"');
346 shouldBeEqualToString('element.className', 'a b');
347
348 shouldThrowDOMException(function() {
349     element.classList.remove('a', 'b', 'c d');
350 }, DOMException.INVALID_CHARACTER_ERR);
351 shouldBeEqualToString('element.className', 'a b');
352
353 shouldNotThrow('element.classList.remove()');
354
355 createElement('a b c');
356 observer = new WebKitMutationObserver(function() {});
357 observer.observe(element, {attributes: true});
358 element.classList.remove('a', 'c');
359 shouldBe('observer.takeRecords().length', '1');