merge JSTesting branch into trunk
[lhc/web/wiklou.git] / tests / qunit / suites / resources / jquery / jquery.tablesorter.test.js
1 ( function () {
2
3 var config = {
4 wgMonthNames: ['', 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
5 wgMonthNamesShort: ['', 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
6 wgDefaultDateFormat: 'dmy',
7 wgContentLanguage: 'en'
8 };
9
10 module( 'jquery.tablesorter', QUnit.newMwEnvironment( config ) );
11
12 test( '-- Initial check', function() {
13 expect(1);
14 ok( $.tablesorter, '$.tablesorter defined' );
15 });
16
17 /**
18 * Create an HTML table from an array of row arrays containing text strings.
19 * First row will be header row. No fancy rowspan/colspan stuff.
20 *
21 * @param {String[]} header
22 * @param {String[][]} data
23 * @return jQuery
24 */
25 var tableCreate = function( header, data ) {
26 var $table = $( '<table class="sortable"><thead></thead><tbody></tbody></table>' ),
27 $thead = $table.find( 'thead' ),
28 $tbody = $table.find( 'tbody' ),
29 $tr = $( '<tr>' );
30
31 $.each( header, function( i, str ) {
32 var $th = $( '<th>' );
33 $th.text( str ).appendTo( $tr );
34 });
35 $tr.appendTo( $thead );
36
37 for (var i = 0; i < data.length; i++) {
38 $tr = $( '<tr>' );
39 $.each( data[i], function( j, str ) {
40 var $td = $( '<td>' );
41 $td.text( str ).appendTo( $tr );
42 });
43 $tr.appendTo( $tbody );
44 }
45 return $table;
46 };
47
48 /**
49 * Extract text from table.
50 *
51 * @param {jQuery} $table
52 * @return String[][]
53 */
54 var tableExtract = function( $table ) {
55 var data = [];
56
57 $table.find( 'tbody' ).find( 'tr' ).each( function( i, tr ) {
58 var row = [];
59 $( tr ).find( 'td,th' ).each( function( i, td ) {
60 row.push( $( td ).text() );
61 });
62 data.push( row );
63 });
64 return data;
65 };
66
67 /**
68 * Run a table test by building a table with the given data,
69 * running some callback on it, then checking the results.
70 *
71 * @param {String} msg text to pass on to qunit for the comparison
72 * @param {String[]} header cols to make the table
73 * @param {String[][]} data rows/cols to make the table
74 * @param {String[][]} expected rows/cols to compare against at end
75 * @param {function($table)} callback something to do with the table before we compare
76 */
77 var tableTest = function( msg, header, data, expected, callback ) {
78 test( msg, function() {
79 expect(1);
80
81 var $table = tableCreate( header, data );
82 //$( 'body' ).append($table);
83
84 // Give caller a chance to set up sorting and manipulate the table.
85 callback( $table );
86
87 // Table sorting is done synchronously; if it ever needs to change back
88 // to asynchronous, we'll need a timeout or a callback here.
89 var extracted = tableExtract( $table );
90 deepEqual( extracted, expected, msg );
91 });
92 };
93
94 var reversed = function(arr) {
95 var arr2 = arr.slice(0);
96 arr2.reverse();
97 return arr2;
98 };
99
100 // Sample data set using planets named and their radius
101 var header = [ 'Planet' , 'Radius (km)'],
102 mercury = [ 'Mercury', '2439.7' ],
103 venus = [ 'Venus' , '6051.8' ],
104 earth = [ 'Earth' , '6371.0' ],
105 mars = [ 'Mars' , '3390.0' ],
106 jupiter = [ 'Jupiter', '69911' ],
107 saturn = [ 'Saturn' , '58232' ];
108
109 // Initial data set
110 var planets = [mercury, venus, earth, mars, jupiter, saturn];
111 var ascendingName = [earth, jupiter, mars, mercury, saturn, venus];
112 var ascendingRadius = [mercury, mars, venus, earth, saturn, jupiter];
113
114 tableTest(
115 'Basic planet table: ascending by name',
116 header,
117 planets,
118 ascendingName,
119 function( $table ) {
120 $table.tablesorter();
121 $table.find( '.headerSort:eq(0)' ).click();
122 }
123 );
124 tableTest(
125 'Basic planet table: ascending by name a second time',
126 header,
127 planets,
128 ascendingName,
129 function( $table ) {
130 $table.tablesorter();
131 $table.find( '.headerSort:eq(0)' ).click();
132 }
133 );
134 tableTest(
135 'Basic planet table: descending by name',
136 header,
137 planets,
138 reversed(ascendingName),
139 function( $table ) {
140 $table.tablesorter();
141 $table.find( '.headerSort:eq(0)' ).click().click();
142 }
143 );
144 tableTest(
145 'Basic planet table: ascending radius',
146 header,
147 planets,
148 ascendingRadius,
149 function( $table ) {
150 $table.tablesorter();
151 $table.find( '.headerSort:eq(1)' ).click();
152 }
153 );
154 tableTest(
155 'Basic planet table: descending radius',
156 header,
157 planets,
158 reversed(ascendingRadius),
159 function( $table ) {
160 $table.tablesorter();
161 $table.find( '.headerSort:eq(1)' ).click().click();
162 }
163 );
164
165
166 // Regression tests!
167 tableTest(
168 'Bug 28775: German-style (dmy) short numeric dates',
169 ['Date'],
170 [ // German-style dates are day-month-year
171 ['11.11.2011'],
172 ['01.11.2011'],
173 ['02.10.2011'],
174 ['03.08.2011'],
175 ['09.11.2011']
176 ],
177 [ // Sorted by ascending date
178 ['03.08.2011'],
179 ['02.10.2011'],
180 ['01.11.2011'],
181 ['09.11.2011'],
182 ['11.11.2011']
183 ],
184 function( $table ) {
185 mw.config.set( 'wgDefaultDateFormat', 'dmy' );
186 mw.config.set( 'wgContentLanguage', 'de' );
187
188 $table.tablesorter();
189 $table.find( '.headerSort:eq(0)' ).click();
190 }
191 );
192
193 tableTest(
194 'Bug 28775: American-style (mdy) short numeric dates',
195 ['Date'],
196 [ // American-style dates are month-day-year
197 ['11.11.2011'],
198 ['01.11.2011'],
199 ['02.10.2011'],
200 ['03.08.2011'],
201 ['09.11.2011']
202 ],
203 [ // Sorted by ascending date
204 ['01.11.2011'],
205 ['02.10.2011'],
206 ['03.08.2011'],
207 ['09.11.2011'],
208 ['11.11.2011']
209 ],
210 function( $table ) {
211 mw.config.set( 'wgDefaultDateFormat', 'mdy' );
212
213 $table.tablesorter();
214 $table.find( '.headerSort:eq(0)' ).click();
215 }
216 );
217
218 var ipv4 = [
219 // Some randomly generated fake IPs
220 ['45.238.27.109'],
221 ['44.172.9.22'],
222 ['247.240.82.209'],
223 ['204.204.132.158'],
224 ['170.38.91.162'],
225 ['197.219.164.9'],
226 ['45.68.154.72'],
227 ['182.195.149.80']
228 ];
229 var ipv4Sorted = [
230 // Sort order should go octet by octet
231 ['44.172.9.22'],
232 ['45.68.154.72'],
233 ['45.238.27.109'],
234 ['170.38.91.162'],
235 ['182.195.149.80'],
236 ['197.219.164.9'],
237 ['204.204.132.158'],
238 ['247.240.82.209']
239 ];
240
241 tableTest(
242 'Bug 17141: IPv4 address sorting',
243 ['IP'],
244 ipv4,
245 ipv4Sorted,
246 function( $table ) {
247 $table.tablesorter();
248 $table.find( '.headerSort:eq(0)' ).click();
249 }
250 );
251 tableTest(
252 'Bug 17141: IPv4 address sorting (reverse)',
253 ['IP'],
254 ipv4,
255 reversed(ipv4Sorted),
256 function( $table ) {
257 $table.tablesorter();
258 $table.find( '.headerSort:eq(0)' ).click().click();
259 }
260 );
261
262 var umlautWords = [
263 // Some words with Umlauts
264 ['Günther'],
265 ['Peter'],
266 ['Björn'],
267 ['Bjorn'],
268 ['Apfel'],
269 ['Äpfel'],
270 ['Strasse'],
271 ['Sträßschen']
272 ];
273
274 var umlautWordsSorted = [
275 // Some words with Umlauts
276 ['Äpfel'],
277 ['Apfel'],
278 ['Björn'],
279 ['Bjorn'],
280 ['Günther'],
281 ['Peter'],
282 ['Sträßschen'],
283 ['Strasse']
284 ];
285
286 tableTest(
287 'Accented Characters with custom collation',
288 ['Name'],
289 umlautWords,
290 umlautWordsSorted,
291 function( $table ) {
292 mw.config.set( 'tableSorterCollation', {
293 'ä': 'ae',
294 'ö': 'oe',
295 'ß': 'ss',
296 'ü':'ue'
297 } );
298
299 $table.tablesorter();
300 $table.find( '.headerSort:eq(0)' ).click();
301 }
302 );
303
304 var planetsRowspan = [["Earth","6051.8"], jupiter, ["Mars","6051.8"], mercury, saturn, venus];
305 var planetsRowspanII = [jupiter, mercury, saturn, ['Venus', '6371.0'], venus, ['Venus', '3390.0']];
306
307 tableTest(
308 'Basic planet table: same value for multiple rows via rowspan',
309 header,
310 planets,
311 planetsRowspan,
312 function( $table ) {
313 // Modify the table to have a multiuple-row-spanning cell:
314 // - Remove 2nd cell of 4th row, and, 2nd cell or 5th row.
315 $table.find( 'tr:eq(3) td:eq(1), tr:eq(4) td:eq(1)' ).remove();
316 // - Set rowspan for 2nd cell of 3rd row to 3.
317 // This covers the removed cell in the 4th and 5th row.
318 $table.find( 'tr:eq(2) td:eq(1)' ).prop( 'rowspan', '3' );
319
320 $table.tablesorter();
321 $table.find( '.headerSort:eq(0)' ).click();
322 }
323 );
324 tableTest(
325 'Basic planet table: Same value for multiple rows via rowspan II',
326 header,
327 planets,
328 planetsRowspanII,
329 function( $table ) {
330 // Modify the table to have a multiuple-row-spanning cell:
331 // - Remove 1st cell of 4th row, and, 1st cell or 5th row.
332 $table.find( 'tr:eq(3) td:eq(0), tr:eq(4) td:eq(0)' ).remove();
333 // - Set rowspan for 1st cell of 3rd row to 3.
334 // This covers the removed cell in the 4th and 5th row.
335 $table.find( 'tr:eq(2) td:eq(0)' ).prop( 'rowspan', '3' );
336
337 $table.tablesorter();
338 $table.find( '.headerSort:eq(0)' ).click();
339 }
340 );
341
342 var complexMDYDates = [
343 // Some words with Umlauts
344 ['January, 19 2010'],
345 ['April 21 1991'],
346 ['04 22 1991'],
347 ['5.12.1990'],
348 ['December 12 \'10']
349 ];
350
351 var complexMDYSorted = [
352 ["5.12.1990"],
353 ["April 21 1991"],
354 ["04 22 1991"],
355 ["January, 19 2010"],
356 ["December 12 '10"]
357 ];
358
359 tableTest(
360 'Complex date parsing I',
361 ['date'],
362 complexMDYDates,
363 complexMDYSorted,
364 function( $table ) {
365 mw.config.set( 'wgDefaultDateFormat', 'mdy' );
366
367 $table.tablesorter();
368 $table.find( '.headerSort:eq(0)' ).click();
369 }
370 );
371
372 var ascendingNameLegacy = ascendingName.slice(0);
373 ascendingNameLegacy[4] = ascendingNameLegacy[5];
374 ascendingNameLegacy.pop();
375
376 tableTest(
377 'Legacy compat with .sortbottom',
378 header,
379 planets,
380 ascendingNameLegacy,
381 function( $table ) {
382 $table.find( 'tr:last' ).addClass( 'sortbottom' );
383 $table.tablesorter();
384 $table.find( '.headerSort:eq(0)' ).click();
385 }
386 );
387
388 /** FIXME: the diff output is not very readeable. */
389 test( 'bug 32047 - caption must be before thead', function() {
390 var $table;
391 $table = $(
392 '<table class="sortable">' +
393 '<caption>CAPTION</caption>' +
394 '<tr><th>THEAD</th></tr>' +
395 '<tr><td>A</td></tr>' +
396 '<tr><td>B</td></tr>' +
397 '<tr class="sortbottom"><td>TFOOT</td></tr>' +
398 '</table>'
399 );
400 $table.tablesorter();
401
402 equals(
403 $table.children( ).get( 0 ).nodeName,
404 'CAPTION',
405 'First element after <thead> must be <caption> (bug 32047)'
406 );
407 });
408
409 test( 'data-sort-value attribute, when available, should override sorting position', function() {
410 var $table, data;
411
412 // Simple example, one without data-sort-value which should be sorted at it's text.
413 $table = $(
414 '<table class="sortable"><thead><tr><th>Data</th></tr></thead>' +
415 '<tbody>' +
416 '<tr><td>Cheetah</td></tr>' +
417 '<tr><td data-sort-value="Apple">Bird</td></tr>' +
418 '<tr><td data-sort-value="Bananna">Ferret</td></tr>' +
419 '<tr><td data-sort-value="Drupe">Elephant</td></tr>' +
420 '<tr><td data-sort-value="Cherry">Dolphin</td></tr>' +
421 '</tbody></table>'
422 );
423 $table.tablesorter().find( '.headerSort:eq(0)' ).click();
424
425 data = [];
426 $table.find( 'tbody > tr' ).each( function( i, tr ) {
427 $( tr ).find( 'td' ).each( function( i, td ) {
428 data.push( { data: $( td ).data( 'sort-value' ), text: $( td ).text() } );
429 });
430 });
431
432 deepEqual( data, [
433 {
434 "data": "Apple",
435 "text": "Bird"
436 }, {
437 "data": "Bananna",
438 "text": "Ferret"
439 }, {
440 "data": undefined,
441 "text": "Cheetah"
442 }, {
443 "data": "Cherry",
444 "text": "Dolphin"
445 }, {
446 "data": "Drupe",
447 "text": "Elephant"
448 }
449 ] );
450
451 // Another example
452 $table = $(
453 '<table class="sortable"><thead><tr><th>Data</th></tr></thead>' +
454 '<tbody>' +
455 '<tr><td>D</td></tr>' +
456 '<tr><td data-sort-value="E">A</td></tr>' +
457 '<tr><td>B</td></tr>' +
458 '<tr><td>G</td></tr>' +
459 '<tr><td data-sort-value="F">C</td></tr>' +
460 '</tbody></table>'
461 );
462 $table.tablesorter().find( '.headerSort:eq(0)' ).click();
463
464 data = [];
465 $table.find( 'tbody > tr' ).each( function( i, tr ) {
466 $( tr ).find( 'td' ).each( function( i, td ) {
467 data.push( { data: $( td ).data( 'sort-value' ), text: $( td ).text() } );
468 });
469 });
470
471 deepEqual( data, [
472 {
473 "data": undefined,
474 "text": "B"
475 }, {
476 "data": undefined,
477 "text": "D"
478 }, {
479 "data": "E",
480 "text": "A"
481 }, {
482 "data": "F",
483 "text": "C"
484 }, {
485 "data": undefined,
486 "text": "G"
487 }
488 ] );
489
490 });
491
492 var numbers = [
493 [ '12' ],
494 [ '7' ],
495 [ '13,000'],
496 [ '9' ],
497 [ '14' ],
498 [ '8.0' ]
499 ];
500 var numbersAsc = [
501 [ '7' ],
502 [ '8.0' ],
503 [ '9' ],
504 [ '12' ],
505 [ '14' ],
506 [ '13,000']
507 ];
508
509 tableTest( 'bug 8115: sort numbers with commas (ascending)',
510 ['Numbers'], numbers, numbersAsc,
511 function( $table ) {
512 $table.tablesorter();
513 $table.find( '.headerSort:eq(0)' ).click();
514 }
515 );
516
517 tableTest( 'bug 8115: sort numbers with commas (descending)',
518 ['Numbers'], numbers, reversed(numbersAsc),
519 function( $table ) {
520 $table.tablesorter();
521 $table.find( '.headerSort:eq(0)' ).click().click();
522 }
523 );
524 // TODO add numbers sorting tests for bug 8115 with a different language
525
526 test( 'bug 32888 - Tables inside a tableheader cell', function() {
527 expect(2);
528
529 var $table;
530 $table = $(
531 '<table class="sortable" id="32888">' +
532 '<tr><th>header<table id="32888-2">'+
533 '<tr><th>1</th><th>2</th></tr>' +
534 '</table></th></tr>' +
535 '<tr><td>A</td></tr>' +
536 '<tr><td>B</td></tr>' +
537 '</table>'
538 );
539 $table.tablesorter();
540
541 equals(
542 $table.find('> thead:eq(0) > tr > th.headerSort').length,
543 1,
544 'Child tables inside a headercell should not interfere with sortable headers (bug 32888)'
545 );
546 equals(
547 $('#32888-2').find('th.headerSort').length,
548 0,
549 'The headers of child tables inside a headercell should not be sortable themselves (bug 32888)'
550 );
551 });
552
553 })();