Follow-up I0b781c11 (2a55449): use User::getAutomaticGroups().
[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 QUnit.module( 'jquery.tablesorter', QUnit.newMwEnvironment({ config: config }) );
11
12 /**
13 * Create an HTML table from an array of row arrays containing text strings.
14 * First row will be header row. No fancy rowspan/colspan stuff.
15 *
16 * @param {String[]} header
17 * @param {String[][]} data
18 * @return jQuery
19 */
20 function tableCreate( header, data ) {
21 var i,
22 $table = $( '<table class="sortable"><thead></thead><tbody></tbody></table>' ),
23 $thead = $table.find( 'thead' ),
24 $tbody = $table.find( 'tbody' ),
25 $tr = $( '<tr>' );
26
27 $.each( header, function ( i, str ) {
28 var $th = $( '<th>' );
29 $th.text( str ).appendTo( $tr );
30 });
31 $tr.appendTo( $thead );
32
33 for ( i = 0; i < data.length; i++ ) {
34 $tr = $( '<tr>' );
35 $.each( data[i], function ( j, str ) {
36 var $td = $( '<td>' );
37 $td.text( str ).appendTo( $tr );
38 });
39 $tr.appendTo( $tbody );
40 }
41 return $table;
42 }
43
44 /**
45 * Extract text from table.
46 *
47 * @param {jQuery} $table
48 * @return String[][]
49 */
50 function tableExtract( $table ) {
51 var data = [];
52
53 $table.find( 'tbody' ).find( 'tr' ).each( function( i, tr ) {
54 var row = [];
55 $( tr ).find( 'td,th' ).each( function( i, td ) {
56 row.push( $( td ).text() );
57 });
58 data.push( row );
59 });
60 return data;
61 }
62
63 /**
64 * Run a table test by building a table with the given data,
65 * running some callback on it, then checking the results.
66 *
67 * @param {String} msg text to pass on to qunit for the comparison
68 * @param {String[]} header cols to make the table
69 * @param {String[][]} data rows/cols to make the table
70 * @param {String[][]} expected rows/cols to compare against at end
71 * @param {function($table)} callback something to do with the table before we compare
72 */
73 function tableTest( msg, header, data, expected, callback ) {
74 QUnit.test( msg, 1, function ( assert ) {
75 var $table = tableCreate( header, data );
76
77 // Give caller a chance to set up sorting and manipulate the table.
78 callback( $table );
79
80 // Table sorting is done synchronously; if it ever needs to change back
81 // to asynchronous, we'll need a timeout or a callback here.
82 var extracted = tableExtract( $table );
83 assert.deepEqual( extracted, expected, msg );
84 });
85 }
86
87 function reversed(arr) {
88 // Clone array
89 var arr2 = arr.slice(0);
90
91 arr2.reverse();
92
93 return arr2;
94 }
95
96 // Sample data set using planets named and their radius
97 var header = [ 'Planet' , 'Radius (km)'],
98 mercury = [ 'Mercury', '2439.7' ],
99 venus = [ 'Venus' , '6051.8' ],
100 earth = [ 'Earth' , '6371.0' ],
101 mars = [ 'Mars' , '3390.0' ],
102 jupiter = [ 'Jupiter', '69911' ],
103 saturn = [ 'Saturn' , '58232' ];
104
105 // Initial data set
106 var planets = [mercury, venus, earth, mars, jupiter, saturn];
107 var ascendingName = [earth, jupiter, mars, mercury, saturn, venus];
108 var ascendingRadius = [mercury, mars, venus, earth, saturn, jupiter];
109
110 tableTest(
111 'Basic planet table: ascending by name',
112 header,
113 planets,
114 ascendingName,
115 function ( $table ) {
116 $table.tablesorter();
117 $table.find( '.headerSort:eq(0)' ).click();
118 }
119 );
120 tableTest(
121 'Basic planet table: ascending by name a second time',
122 header,
123 planets,
124 ascendingName,
125 function ( $table ) {
126 $table.tablesorter();
127 $table.find( '.headerSort:eq(0)' ).click();
128 }
129 );
130 tableTest(
131 'Basic planet table: descending by name',
132 header,
133 planets,
134 reversed(ascendingName),
135 function ( $table ) {
136 $table.tablesorter();
137 $table.find( '.headerSort:eq(0)' ).click().click();
138 }
139 );
140 tableTest(
141 'Basic planet table: ascending radius',
142 header,
143 planets,
144 ascendingRadius,
145 function ( $table ) {
146 $table.tablesorter();
147 $table.find( '.headerSort:eq(1)' ).click();
148 }
149 );
150 tableTest(
151 'Basic planet table: descending radius',
152 header,
153 planets,
154 reversed(ascendingRadius),
155 function ( $table ) {
156 $table.tablesorter();
157 $table.find( '.headerSort:eq(1)' ).click().click();
158 }
159 );
160
161
162 // Regression tests!
163 tableTest(
164 'Bug 28775: German-style (dmy) short numeric dates',
165 ['Date'],
166 [ // German-style dates are day-month-year
167 ['11.11.2011'],
168 ['01.11.2011'],
169 ['02.10.2011'],
170 ['03.08.2011'],
171 ['09.11.2011']
172 ],
173 [ // Sorted by ascending date
174 ['03.08.2011'],
175 ['02.10.2011'],
176 ['01.11.2011'],
177 ['09.11.2011'],
178 ['11.11.2011']
179 ],
180 function ( $table ) {
181 mw.config.set( 'wgDefaultDateFormat', 'dmy' );
182 mw.config.set( 'wgContentLanguage', 'de' );
183
184 $table.tablesorter();
185 $table.find( '.headerSort:eq(0)' ).click();
186 }
187 );
188
189 tableTest(
190 'Bug 28775: American-style (mdy) short numeric dates',
191 ['Date'],
192 [ // American-style dates are month-day-year
193 ['11.11.2011'],
194 ['01.11.2011'],
195 ['02.10.2011'],
196 ['03.08.2011'],
197 ['09.11.2011']
198 ],
199 [ // Sorted by ascending date
200 ['01.11.2011'],
201 ['02.10.2011'],
202 ['03.08.2011'],
203 ['09.11.2011'],
204 ['11.11.2011']
205 ],
206 function ( $table ) {
207 mw.config.set( 'wgDefaultDateFormat', 'mdy' );
208
209 $table.tablesorter();
210 $table.find( '.headerSort:eq(0)' ).click();
211 }
212 );
213
214 var ipv4 = [
215 // Some randomly generated fake IPs
216 ['45.238.27.109'],
217 ['44.172.9.22'],
218 ['247.240.82.209'],
219 ['204.204.132.158'],
220 ['170.38.91.162'],
221 ['197.219.164.9'],
222 ['45.68.154.72'],
223 ['182.195.149.80']
224 ];
225 var ipv4Sorted = [
226 // Sort order should go octet by octet
227 ['44.172.9.22'],
228 ['45.68.154.72'],
229 ['45.238.27.109'],
230 ['170.38.91.162'],
231 ['182.195.149.80'],
232 ['197.219.164.9'],
233 ['204.204.132.158'],
234 ['247.240.82.209']
235 ];
236
237 tableTest(
238 'Bug 17141: IPv4 address sorting',
239 ['IP'],
240 ipv4,
241 ipv4Sorted,
242 function ( $table ) {
243 $table.tablesorter();
244 $table.find( '.headerSort:eq(0)' ).click();
245 }
246 );
247 tableTest(
248 'Bug 17141: IPv4 address sorting (reverse)',
249 ['IP'],
250 ipv4,
251 reversed(ipv4Sorted),
252 function ( $table ) {
253 $table.tablesorter();
254 $table.find( '.headerSort:eq(0)' ).click().click();
255 }
256 );
257
258 var umlautWords = [
259 // Some words with Umlauts
260 ['Günther'],
261 ['Peter'],
262 ['Björn'],
263 ['Bjorn'],
264 ['Apfel'],
265 ['Äpfel'],
266 ['Strasse'],
267 ['Sträßschen']
268 ];
269
270 var umlautWordsSorted = [
271 // Some words with Umlauts
272 ['Äpfel'],
273 ['Apfel'],
274 ['Björn'],
275 ['Bjorn'],
276 ['Günther'],
277 ['Peter'],
278 ['Sträßschen'],
279 ['Strasse']
280 ];
281
282 tableTest(
283 'Accented Characters with custom collation',
284 ['Name'],
285 umlautWords,
286 umlautWordsSorted,
287 function ( $table ) {
288 mw.config.set( 'tableSorterCollation', {
289 'ä': 'ae',
290 'ö': 'oe',
291 'ß': 'ss',
292 'ü':'ue'
293 } );
294
295 $table.tablesorter();
296 $table.find( '.headerSort:eq(0)' ).click();
297 }
298 );
299
300 var planetsRowspan = [["Earth","6051.8"], jupiter, ["Mars","6051.8"], mercury, saturn, venus];
301 var planetsRowspanII = [jupiter, mercury, saturn, venus, ['Venus', '6371.0'], ['Venus', '3390.0']];
302
303 tableTest(
304 'Basic planet table: same value for multiple rows via rowspan',
305 header,
306 planets,
307 planetsRowspan,
308 function ( $table ) {
309 // Modify the table to have a multiuple-row-spanning cell:
310 // - Remove 2nd cell of 4th row, and, 2nd cell or 5th row.
311 $table.find( 'tr:eq(3) td:eq(1), tr:eq(4) td:eq(1)' ).remove();
312 // - Set rowspan for 2nd cell of 3rd row to 3.
313 // This covers the removed cell in the 4th and 5th row.
314 $table.find( 'tr:eq(2) td:eq(1)' ).prop( 'rowspan', '3' );
315
316 $table.tablesorter();
317 $table.find( '.headerSort:eq(0)' ).click();
318 }
319 );
320 tableTest(
321 'Basic planet table: Same value for multiple rows via rowspan II',
322 header,
323 planets,
324 planetsRowspanII,
325 function ( $table ) {
326 // Modify the table to have a multiuple-row-spanning cell:
327 // - Remove 1st cell of 4th row, and, 1st cell or 5th row.
328 $table.find( 'tr:eq(3) td:eq(0), tr:eq(4) td:eq(0)' ).remove();
329 // - Set rowspan for 1st cell of 3rd row to 3.
330 // This covers the removed cell in the 4th and 5th row.
331 $table.find( 'tr:eq(2) td:eq(0)' ).prop( 'rowspan', '3' );
332
333 $table.tablesorter();
334 $table.find( '.headerSort:eq(0)' ).click();
335 }
336 );
337
338 var complexMDYDates = [
339 // Some words with Umlauts
340 ['January, 19 2010'],
341 ['April 21 1991'],
342 ['04 22 1991'],
343 ['5.12.1990'],
344 ['December 12 \'10']
345 ];
346
347 var complexMDYSorted = [
348 ["5.12.1990"],
349 ["April 21 1991"],
350 ["04 22 1991"],
351 ["January, 19 2010"],
352 ["December 12 '10"]
353 ];
354
355 tableTest(
356 'Complex date parsing I',
357 ['date'],
358 complexMDYDates,
359 complexMDYSorted,
360 function ( $table ) {
361 mw.config.set( 'wgDefaultDateFormat', 'mdy' );
362
363 $table.tablesorter();
364 $table.find( '.headerSort:eq(0)' ).click();
365 }
366 );
367
368 var ascendingNameLegacy = ascendingName.slice(0);
369 ascendingNameLegacy[4] = ascendingNameLegacy[5];
370 ascendingNameLegacy.pop();
371
372 tableTest(
373 'Legacy compat with .sortbottom',
374 header,
375 planets,
376 ascendingNameLegacy,
377 function( $table ) {
378 $table.find( 'tr:last' ).addClass( 'sortbottom' );
379 $table.tablesorter();
380 $table.find( '.headerSort:eq(0)' ).click();
381 }
382 );
383
384 /** FIXME: the diff output is not very readeable. */
385 QUnit.test( 'bug 32047 - caption must be before thead', function ( assert ) {
386 var $table;
387 $table = $(
388 '<table class="sortable">' +
389 '<caption>CAPTION</caption>' +
390 '<tr><th>THEAD</th></tr>' +
391 '<tr><td>A</td></tr>' +
392 '<tr><td>B</td></tr>' +
393 '<tr class="sortbottom"><td>TFOOT</td></tr>' +
394 '</table>'
395 );
396 $table.tablesorter();
397
398 assert.equal(
399 $table.children( ).get( 0 ).nodeName,
400 'CAPTION',
401 'First element after <thead> must be <caption> (bug 32047)'
402 );
403 });
404
405 QUnit.test( 'data-sort-value attribute, when available, should override sorting position', function ( assert ) {
406 var $table, data;
407
408 // Example 1: All cells except one cell without data-sort-value,
409 // which should be sorted at it's text content value.
410 $table = $(
411 '<table class="sortable"><thead><tr><th>Data</th></tr></thead>' +
412 '<tbody>' +
413 '<tr><td>Cheetah</td></tr>' +
414 '<tr><td data-sort-value="Apple">Bird</td></tr>' +
415 '<tr><td data-sort-value="Bananna">Ferret</td></tr>' +
416 '<tr><td data-sort-value="Drupe">Elephant</td></tr>' +
417 '<tr><td data-sort-value="Cherry">Dolphin</td></tr>' +
418 '</tbody></table>'
419 );
420 $table.tablesorter().find( '.headerSort:eq(0)' ).click();
421
422 data = [];
423 $table.find( 'tbody > tr' ).each( function( i, tr ) {
424 $( tr ).find( 'td' ).each( function( i, td ) {
425 data.push( {
426 data: $( td ).data( 'sortValue' ),
427 text: $( td ).text()
428 } );
429 });
430 });
431
432 assert.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 ], 'Order matches expected order (based on data-sort-value attribute values)' );
450
451 // Example 2
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( {
468 data: $( td ).data( 'sortValue' ),
469 text: $( td ).text()
470 } );
471 });
472 });
473
474 assert.deepEqual( data, [
475 {
476 data: undefined,
477 text: 'B'
478 }, {
479 data: undefined,
480 text: 'D'
481 }, {
482 data: 'E',
483 text: 'A'
484 }, {
485 data: 'F',
486 text: 'C'
487 }, {
488 data: undefined,
489 text: 'G'
490 }
491 ], 'Order matches expected order (based on data-sort-value attribute values)' );
492
493 // Example 3: Test that live changes are used from data-sort-value,
494 // even if they change after the tablesorter is constructed (bug 38152).
495 $table = $(
496 '<table class="sortable"><thead><tr><th>Data</th></tr></thead>' +
497 '<tbody>' +
498 '<tr><td>D</td></tr>' +
499 '<tr><td data-sort-value="1">A</td></tr>' +
500 '<tr><td>B</td></tr>' +
501 '<tr><td data-sort-value="2">G</td></tr>' +
502 '<tr><td>C</td></tr>' +
503 '</tbody></table>'
504 );
505 // initialize table sorter and sort once
506 $table
507 .tablesorter()
508 .find( '.headerSort:eq(0)' ).click();
509
510 // Change the sortValue data properties (bug 38152)
511 // - change data
512 $table.find( 'td:contains(A)' ).data( 'sortValue', 3 );
513 // - add data
514 $table.find( 'td:contains(B)' ).data( 'sortValue', 1 );
515 // - remove data, bring back attribute: 2
516 $table.find( 'td:contains(G)' ).removeData( 'sortValue' );
517
518 // Now sort again (twice, so it is back at Ascending)
519 $table.find( '.headerSort:eq(0)' ).click();
520 $table.find( '.headerSort:eq(0)' ).click();
521
522 data = [];
523 $table.find( 'tbody > tr' ).each( function( i, tr ) {
524 $( tr ).find( 'td' ).each( function( i, td ) {
525 data.push( {
526 data: $( td ).data( 'sortValue' ),
527 text: $( td ).text()
528 } );
529 });
530 });
531
532 assert.deepEqual( data, [
533 {
534 data: 1,
535 text: "B"
536 }, {
537 data: 2,
538 text: "G"
539 }, {
540 data: 3,
541 text: "A"
542 }, {
543 data: undefined,
544 text: "C"
545 }, {
546 data: undefined,
547 text: "D"
548 }
549 ], 'Order matches expected order, using the current sortValue in $.data()' );
550
551 });
552
553 var numbers = [
554 [ '12' ],
555 [ '7' ],
556 [ '13,000'],
557 [ '9' ],
558 [ '14' ],
559 [ '8.0' ]
560 ];
561 var numbersAsc = [
562 [ '7' ],
563 [ '8.0' ],
564 [ '9' ],
565 [ '12' ],
566 [ '14' ],
567 [ '13,000']
568 ];
569
570 tableTest( 'bug 8115: sort numbers with commas (ascending)',
571 ['Numbers'], numbers, numbersAsc,
572 function( $table ) {
573 $table.tablesorter();
574 $table.find( '.headerSort:eq(0)' ).click();
575 }
576 );
577
578 tableTest( 'bug 8115: sort numbers with commas (descending)',
579 ['Numbers'], numbers, reversed(numbersAsc),
580 function( $table ) {
581 $table.tablesorter();
582 $table.find( '.headerSort:eq(0)' ).click().click();
583 }
584 );
585 // TODO add numbers sorting tests for bug 8115 with a different language
586
587 QUnit.test( 'bug 32888 - Tables inside a tableheader cell', 2, function ( assert ) {
588 var $table;
589 $table = $(
590 '<table class="sortable" id="mw-bug-32888">' +
591 '<tr><th>header<table id="mw-bug-32888-2">'+
592 '<tr><th>1</th><th>2</th></tr>' +
593 '</table></th></tr>' +
594 '<tr><td>A</td></tr>' +
595 '<tr><td>B</td></tr>' +
596 '</table>'
597 );
598 $table.tablesorter();
599
600 assert.equal(
601 $table.find('> thead:eq(0) > tr > th.headerSort').length,
602 1,
603 'Child tables inside a headercell should not interfere with sortable headers (bug 32888)'
604 );
605 assert.equal(
606 $( '#mw-bug-32888-2' ).find('th.headerSort').length,
607 0,
608 'The headers of child tables inside a headercell should not be sortable themselves (bug 32888)'
609 );
610 });
611
612
613 var correctDateSorting1 = [
614 ['01 January 2010'],
615 ['05 February 2010'],
616 ['16 January 2010']
617 ];
618
619 var correctDateSortingSorted1 = [
620 ['01 January 2010'],
621 ['16 January 2010'],
622 ['05 February 2010']
623 ];
624
625 tableTest(
626 'Correct date sorting I',
627 ['date'],
628 correctDateSorting1,
629 correctDateSortingSorted1,
630 function ( $table ) {
631 mw.config.set( 'wgDefaultDateFormat', 'mdy' );
632
633 $table.tablesorter();
634 $table.find( '.headerSort:eq(0)' ).click();
635 }
636 );
637
638 var correctDateSorting2 = [
639 ['January 01 2010'],
640 ['February 05 2010'],
641 ['January 16 2010']
642 ];
643
644 var correctDateSortingSorted2 = [
645 ['January 01 2010'],
646 ['January 16 2010'],
647 ['February 05 2010']
648 ];
649
650 tableTest(
651 'Correct date sorting II',
652 ['date'],
653 correctDateSorting2,
654 correctDateSortingSorted2,
655 function ( $table ) {
656 mw.config.set( 'wgDefaultDateFormat', 'dmy' );
657
658 $table.tablesorter();
659 $table.find( '.headerSort:eq(0)' ).click();
660 }
661 );
662
663 }( jQuery ) );