QUnit: fix swapped expected/actual fields
[lhc/web/wiklou.git] / tests / qunit / suites / resources / jquery / jquery.tablesorter.test.js
1 ( function ( $, mw ) {
2 var header,
3
4 // Data set "simple"
5 a1 = [ 'A', '1' ],
6 a2 = [ 'A', '2' ],
7 a3 = [ 'A', '3' ],
8 b1 = [ 'B', '1' ],
9 b2 = [ 'B', '2' ],
10 b3 = [ 'B', '3' ],
11 simple = [a2, b3, a1, a3, b2, b1],
12 simpleAsc = [a1, a2, a3, b1, b2, b3],
13 simpleDescasc = [b1, b2, b3, a1, a2, a3],
14
15 // Data set "colspan"
16 aaa1 = [ 'A', 'A', 'A', '1' ],
17 aab5 = [ 'A', 'A', 'B', '5' ],
18 abc3 = [ 'A', 'B', 'C', '3' ],
19 bbc2 = [ 'B', 'B', 'C', '2' ],
20 caa4 = [ 'C', 'A', 'A', '4' ],
21 colspanInitial = [ aab5, aaa1, abc3, bbc2, caa4 ],
22
23 // Data set "planets"
24 mercury = [ 'Mercury', '2439.7' ],
25 venus = [ 'Venus', '6051.8' ],
26 earth = [ 'Earth', '6371.0' ],
27 mars = [ 'Mars', '3390.0' ],
28 jupiter = [ 'Jupiter', '69911' ],
29 saturn = [ 'Saturn', '58232' ],
30 planets = [mercury, venus, earth, mars, jupiter, saturn],
31 planetsAscName = [earth, jupiter, mars, mercury, saturn, venus],
32 planetsAscRadius = [mercury, mars, venus, earth, saturn, jupiter],
33 planetsRowspan,
34 planetsRowspanII,
35 planetsAscNameLegacy,
36
37 // Data set "ipv4"
38 ipv4 = [
39 // Some randomly generated fake IPs
40 ['45.238.27.109'],
41 ['44.172.9.22'],
42 ['247.240.82.209'],
43 ['204.204.132.158'],
44 ['170.38.91.162'],
45 ['197.219.164.9'],
46 ['45.68.154.72'],
47 ['182.195.149.80']
48 ],
49 ipv4Sorted = [
50 // Sort order should go octet by octet
51 ['44.172.9.22'],
52 ['45.68.154.72'],
53 ['45.238.27.109'],
54 ['170.38.91.162'],
55 ['182.195.149.80'],
56 ['197.219.164.9'],
57 ['204.204.132.158'],
58 ['247.240.82.209']
59 ],
60
61 // Data set "umlaut"
62 umlautWords = [
63 ['Günther'],
64 ['Peter'],
65 ['Björn'],
66 ['Bjorn'],
67 ['Apfel'],
68 ['Äpfel'],
69 ['Strasse'],
70 ['Sträßschen']
71 ],
72 umlautWordsSorted = [
73 ['Äpfel'],
74 ['Apfel'],
75 ['Björn'],
76 ['Bjorn'],
77 ['Günther'],
78 ['Peter'],
79 ['Sträßschen'],
80 ['Strasse']
81 ],
82
83 complexMDYDates = [
84 ['January, 19 2010'],
85 ['April 21 1991'],
86 ['04 22 1991'],
87 ['5.12.1990'],
88 ['December 12 \'10']
89 ],
90 complexMDYSorted = [
91 ['5.12.1990'],
92 ['April 21 1991'],
93 ['04 22 1991'],
94 ['January, 19 2010'],
95 ['December 12 \'10']
96 ],
97
98 currencyUnsorted = [
99 ['1.02 $'],
100 ['$ 3.00'],
101 ['€ 2,99'],
102 ['$ 1.00'],
103 ['$3.50'],
104 ['$ 1.50'],
105 ['€ 0.99']
106 ],
107 currencySorted = [
108 ['€ 0.99'],
109 ['$ 1.00'],
110 ['1.02 $'],
111 ['$ 1.50'],
112 ['$ 3.00'],
113 ['$3.50'],
114 // Comma's sort after dots
115 // Not intentional but test to detect changes
116 ['€ 2,99']
117 ],
118
119 numbers = [
120 [ '12' ],
121 [ '7' ],
122 [ '13,000'],
123 [ '9' ],
124 [ '14' ],
125 [ '8.0' ]
126 ],
127 numbersAsc = [
128 [ '7' ],
129 [ '8.0' ],
130 [ '9' ],
131 [ '12' ],
132 [ '14' ],
133 [ '13,000']
134 ],
135
136 correctDateSorting1 = [
137 ['01 January 2010'],
138 ['05 February 2010'],
139 ['16 January 2010']
140 ],
141 correctDateSortingSorted1 = [
142 ['01 January 2010'],
143 ['16 January 2010'],
144 ['05 February 2010']
145 ],
146
147 correctDateSorting2 = [
148 ['January 01 2010'],
149 ['February 05 2010'],
150 ['January 16 2010']
151 ],
152 correctDateSortingSorted2 = [
153 ['January 01 2010'],
154 ['January 16 2010'],
155 ['February 05 2010']
156 ];
157
158 QUnit.module( 'jquery.tablesorter', QUnit.newMwEnvironment( {
159 config: {
160 wgMonthNames: ['', 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
161 wgMonthNamesShort: ['', 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
162 wgDefaultDateFormat: 'dmy',
163 wgSeparatorTransformTable: ['', ''],
164 wgDigitTransformTable: ['', ''],
165 wgContentLanguage: 'en'
166 }
167 } ) );
168
169 /**
170 * Create an HTML table from an array of row arrays containing text strings.
171 * First row will be header row. No fancy rowspan/colspan stuff.
172 *
173 * @param {String[]} header
174 * @param {String[][]} data
175 * @return jQuery
176 */
177 function tableCreate( header, data ) {
178 var i,
179 $table = $( '<table class="sortable"><thead></thead><tbody></tbody></table>' ),
180 $thead = $table.find( 'thead' ),
181 $tbody = $table.find( 'tbody' ),
182 $tr = $( '<tr>' );
183
184 $.each( header, function ( i, str ) {
185 var $th = $( '<th>' );
186 $th.text( str ).appendTo( $tr );
187 } );
188 $tr.appendTo( $thead );
189
190 for ( i = 0; i < data.length; i++ ) {
191 /*jshint loopfunc: true */
192 $tr = $( '<tr>' );
193 $.each( data[i], function ( j, str ) {
194 var $td = $( '<td>' );
195 $td.text( str ).appendTo( $tr );
196 } );
197 $tr.appendTo( $tbody );
198 }
199 return $table;
200 }
201
202 /**
203 * Extract text from table.
204 *
205 * @param {jQuery} $table
206 * @return String[][]
207 */
208 function tableExtract( $table ) {
209 var data = [];
210
211 $table.find( 'tbody' ).find( 'tr' ).each( function ( i, tr ) {
212 var row = [];
213 $( tr ).find( 'td,th' ).each( function ( i, td ) {
214 row.push( $( td ).text() );
215 } );
216 data.push( row );
217 } );
218 return data;
219 }
220
221 /**
222 * Run a table test by building a table with the given data,
223 * running some callback on it, then checking the results.
224 *
225 * @param {String} msg text to pass on to qunit for the comparison
226 * @param {String[]} header cols to make the table
227 * @param {String[][]} data rows/cols to make the table
228 * @param {String[][]} expected rows/cols to compare against at end
229 * @param {function($table)} callback something to do with the table before we compare
230 */
231 function tableTest( msg, header, data, expected, callback ) {
232 QUnit.test( msg, 1, function ( assert ) {
233 var extracted,
234 $table = tableCreate( header, data );
235
236 // Give caller a chance to set up sorting and manipulate the table.
237 callback( $table );
238
239 // Table sorting is done synchronously; if it ever needs to change back
240 // to asynchronous, we'll need a timeout or a callback here.
241 extracted = tableExtract( $table );
242 assert.deepEqual( extracted, expected, msg );
243 } );
244 }
245
246 /**
247 * Run a table test by building a table with the given HTML,
248 * running some callback on it, then checking the results.
249 *
250 * @param {String} msg text to pass on to qunit for the comparison
251 * @param {String} HTML to make the table
252 * @param {String[][]} expected rows/cols to compare against at end
253 * @param {function($table)} callback something to do with the table before we compare
254 */
255 function tableTestHTML( msg, html, expected, callback ) {
256 QUnit.test( msg, 1, function ( assert ) {
257 var extracted,
258 $table = $( html );
259
260 // Give caller a chance to set up sorting and manipulate the table.
261 if ( callback ) {
262 callback( $table );
263 } else {
264 $table.tablesorter();
265 $table.find( '#sortme' ).click();
266 }
267
268 // Table sorting is done synchronously; if it ever needs to change back
269 // to asynchronous, we'll need a timeout or a callback here.
270 extracted = tableExtract( $table );
271 assert.deepEqual( extracted, expected, msg );
272 } );
273 }
274
275 function reversed( arr ) {
276 // Clone array
277 var arr2 = arr.slice( 0 );
278
279 arr2.reverse();
280
281 return arr2;
282 }
283
284 // Sample data set using planets named and their radius
285 header = [ 'Planet', 'Radius (km)'];
286
287 tableTest(
288 'Basic planet table: sorting initially - ascending by name',
289 header,
290 planets,
291 planetsAscName,
292 function ( $table ) {
293 $table.tablesorter( { sortList: [
294 { 0: 'asc' }
295 ] } );
296 }
297 );
298 tableTest(
299 'Basic planet table: sorting initially - descending by radius',
300 header,
301 planets,
302 reversed( planetsAscRadius ),
303 function ( $table ) {
304 $table.tablesorter( { sortList: [
305 { 1: 'desc' }
306 ] } );
307 }
308 );
309 tableTest(
310 'Basic planet table: ascending by name',
311 header,
312 planets,
313 planetsAscName,
314 function ( $table ) {
315 $table.tablesorter();
316 $table.find( '.headerSort:eq(0)' ).click();
317 }
318 );
319 tableTest(
320 'Basic planet table: ascending by name a second time',
321 header,
322 planets,
323 planetsAscName,
324 function ( $table ) {
325 $table.tablesorter();
326 $table.find( '.headerSort:eq(0)' ).click();
327 }
328 );
329 tableTest(
330 'Basic planet table: ascending by name (multiple clicks)',
331 header,
332 planets,
333 planetsAscName,
334 function ( $table ) {
335 $table.tablesorter();
336 $table.find( '.headerSort:eq(0)' ).click();
337 $table.find( '.headerSort:eq(1)' ).click();
338 $table.find( '.headerSort:eq(0)' ).click();
339 }
340 );
341 tableTest(
342 'Basic planet table: descending by name',
343 header,
344 planets,
345 reversed( planetsAscName ),
346 function ( $table ) {
347 $table.tablesorter();
348 $table.find( '.headerSort:eq(0)' ).click().click();
349 }
350 );
351 tableTest(
352 'Basic planet table: ascending radius',
353 header,
354 planets,
355 planetsAscRadius,
356 function ( $table ) {
357 $table.tablesorter();
358 $table.find( '.headerSort:eq(1)' ).click();
359 }
360 );
361 tableTest(
362 'Basic planet table: descending radius',
363 header,
364 planets,
365 reversed( planetsAscRadius ),
366 function ( $table ) {
367 $table.tablesorter();
368 $table.find( '.headerSort:eq(1)' ).click().click();
369 }
370 );
371
372 header = [ 'column1', 'column2' ];
373
374 tableTest(
375 'Sorting multiple columns by passing sort list',
376 header,
377 simple,
378 simpleAsc,
379 function ( $table ) {
380 $table.tablesorter(
381 { sortList: [
382 { 0: 'asc' },
383 { 1: 'asc' }
384 ] }
385 );
386 }
387 );
388 tableTest(
389 'Sorting multiple columns by programmatically triggering sort()',
390 header,
391 simple,
392 simpleDescasc,
393 function ( $table ) {
394 $table.tablesorter();
395 $table.data( 'tablesorter' ).sort(
396 [
397 { 0: 'desc' },
398 { 1: 'asc' }
399 ]
400 );
401 }
402 );
403 tableTest(
404 'Reset to initial sorting by triggering sort() without any parameters',
405 header,
406 simple,
407 simpleAsc,
408 function ( $table ) {
409 $table.tablesorter(
410 { sortList: [
411 { 0: 'asc' },
412 { 1: 'asc' }
413 ] }
414 );
415 $table.data( 'tablesorter' ).sort(
416 [
417 { 0: 'desc' },
418 { 1: 'asc' }
419 ]
420 );
421 $table.data( 'tablesorter' ).sort();
422 }
423 );
424 tableTest(
425 'Sort via click event after having initialized the tablesorter with initial sorting',
426 header,
427 simple,
428 simpleDescasc,
429 function ( $table ) {
430 $table.tablesorter(
431 { sortList: [ { 0: 'asc' }, { 1: 'asc' } ] }
432 );
433 $table.find( '.headerSort:eq(0)' ).click();
434 }
435 );
436 tableTest(
437 'Multi-sort via click event after having initialized the tablesorter with initial sorting',
438 header,
439 simple,
440 simpleAsc,
441 function ( $table ) {
442 $table.tablesorter(
443 { sortList: [ { 0: 'desc' }, { 1: 'desc' } ] }
444 );
445 $table.find( '.headerSort:eq(0)' ).click();
446
447 // Pretend to click while pressing the multi-sort key
448 var event = $.Event( 'click' );
449 event[$table.data( 'tablesorter' ).config.sortMultiSortKey] = true;
450 $table.find( '.headerSort:eq(1)' ).trigger( event );
451 }
452 );
453 QUnit.test( 'Reset sorting making table appear unsorted', 3, function ( assert ) {
454 var $table = tableCreate( header, simple );
455 $table.tablesorter(
456 { sortList: [
457 { 0: 'desc' },
458 { 1: 'asc' }
459 ] }
460 );
461 $table.data( 'tablesorter' ).sort( [] );
462
463 assert.equal(
464 $table.find( 'th.headerSortUp' ).length + $table.find( 'th.headerSortDown' ).length,
465 0,
466 'No sort specific sort classes addign to header cells'
467 );
468
469 assert.equal(
470 $table.find( 'th' ).first().attr( 'title' ),
471 mw.msg( 'sort-ascending' ),
472 'First header cell has default title'
473 );
474
475 assert.equal(
476 $table.find( 'th' ).first().attr( 'title' ),
477 $table.find( 'th' ).last().attr( 'title' ),
478 'Both header cells\' titles match'
479 );
480 } );
481
482 // Sorting with colspans
483 header = [ 'column1a', 'column1b', 'column1c', 'column2' ];
484
485 tableTest( 'Sorting with colspanned headers: spanned column',
486 header,
487 colspanInitial,
488 [ aaa1, aab5, abc3, bbc2, caa4 ],
489 function ( $table ) {
490 // Make colspanned header for test
491 $table.find( 'tr:eq(0) th:eq(1), tr:eq(0) th:eq(2)' ).remove();
492 $table.find( 'tr:eq(0) th:eq(0)' ).attr( 'colspan', '3' );
493
494 $table.tablesorter();
495 $table.find( '.headerSort:eq(0)' ).click();
496 }
497 );
498 tableTest( 'Sorting with colspanned headers: sort spanned column twice',
499 header,
500 colspanInitial,
501 [ caa4, bbc2, abc3, aab5, aaa1 ],
502 function ( $table ) {
503 // Make colspanned header for test
504 $table.find( 'tr:eq(0) th:eq(1), tr:eq(0) th:eq(2)' ).remove();
505 $table.find( 'tr:eq(0) th:eq(0)' ).attr( 'colspan', '3' );
506
507 $table.tablesorter();
508 $table.find( '.headerSort:eq(0)' ).click();
509 $table.find( '.headerSort:eq(0)' ).click();
510 }
511 );
512 tableTest( 'Sorting with colspanned headers: subsequent column',
513 header,
514 colspanInitial,
515 [ aaa1, bbc2, abc3, caa4, aab5 ],
516 function ( $table ) {
517 // Make colspanned header for test
518 $table.find( 'tr:eq(0) th:eq(1), tr:eq(0) th:eq(2)' ).remove();
519 $table.find( 'tr:eq(0) th:eq(0)' ).attr( 'colspan', '3' );
520
521 $table.tablesorter();
522 $table.find( '.headerSort:eq(1)' ).click();
523 }
524 );
525 tableTest( 'Sorting with colspanned headers: sort subsequent column twice',
526 header,
527 colspanInitial,
528 [ aab5, caa4, abc3, bbc2, aaa1 ],
529 function ( $table ) {
530 // Make colspanned header for test
531 $table.find( 'tr:eq(0) th:eq(1), tr:eq(0) th:eq(2)' ).remove();
532 $table.find( 'tr:eq(0) th:eq(0)' ).attr( 'colspan', '3' );
533
534 $table.tablesorter();
535 $table.find( '.headerSort:eq(1)' ).click();
536 $table.find( '.headerSort:eq(1)' ).click();
537 }
538 );
539
540 tableTest(
541 'Basic planet table: one unsortable column',
542 header,
543 planets,
544 planets,
545 function ( $table ) {
546 $table.find( 'tr:eq(0) > th:eq(0)' ).addClass( 'unsortable' );
547
548 $table.tablesorter();
549 $table.find( 'tr:eq(0) > th:eq(0)' ).click();
550 }
551 );
552
553 // Regression tests!
554 tableTest(
555 'Bug 28775: German-style (dmy) short numeric dates',
556 ['Date'],
557 [
558 // German-style dates are day-month-year
559 ['11.11.2011'],
560 ['01.11.2011'],
561 ['02.10.2011'],
562 ['03.08.2011'],
563 ['09.11.2011']
564 ],
565 [
566 // Sorted by ascending date
567 ['03.08.2011'],
568 ['02.10.2011'],
569 ['01.11.2011'],
570 ['09.11.2011'],
571 ['11.11.2011']
572 ],
573 function ( $table ) {
574 mw.config.set( 'wgDefaultDateFormat', 'dmy' );
575 mw.config.set( 'wgContentLanguage', 'de' );
576
577 $table.tablesorter();
578 $table.find( '.headerSort:eq(0)' ).click();
579 }
580 );
581
582 tableTest(
583 'Bug 28775: American-style (mdy) short numeric dates',
584 ['Date'],
585 [
586 // American-style dates are month-day-year
587 ['11.11.2011'],
588 ['01.11.2011'],
589 ['02.10.2011'],
590 ['03.08.2011'],
591 ['09.11.2011']
592 ],
593 [
594 // Sorted by ascending date
595 ['01.11.2011'],
596 ['02.10.2011'],
597 ['03.08.2011'],
598 ['09.11.2011'],
599 ['11.11.2011']
600 ],
601 function ( $table ) {
602 mw.config.set( 'wgDefaultDateFormat', 'mdy' );
603
604 $table.tablesorter();
605 $table.find( '.headerSort:eq(0)' ).click();
606 }
607 );
608
609 tableTest(
610 'Bug 17141: IPv4 address sorting',
611 ['IP'],
612 ipv4,
613 ipv4Sorted,
614 function ( $table ) {
615 $table.tablesorter();
616 $table.find( '.headerSort:eq(0)' ).click();
617 }
618 );
619 tableTest(
620 'Bug 17141: IPv4 address sorting (reverse)',
621 ['IP'],
622 ipv4,
623 reversed( ipv4Sorted ),
624 function ( $table ) {
625 $table.tablesorter();
626 $table.find( '.headerSort:eq(0)' ).click().click();
627 }
628 );
629
630 tableTest(
631 'Accented Characters with custom collation',
632 ['Name'],
633 umlautWords,
634 umlautWordsSorted,
635 function ( $table ) {
636 mw.config.set( 'tableSorterCollation', {
637 'ä': 'ae',
638 'ö': 'oe',
639 'ß': 'ss',
640 'ü': 'ue'
641 } );
642
643 $table.tablesorter();
644 $table.find( '.headerSort:eq(0)' ).click();
645 }
646 );
647
648 QUnit.test( 'Rowspan not exploded on init', 1, function ( assert ) {
649 var $table = tableCreate( header, planets );
650
651 // Modify the table to have a multiple-row-spanning cell:
652 // - Remove 2nd cell of 4th row, and, 2nd cell or 5th row.
653 $table.find( 'tr:eq(3) td:eq(1), tr:eq(4) td:eq(1)' ).remove();
654 // - Set rowspan for 2nd cell of 3rd row to 3.
655 // This covers the removed cell in the 4th and 5th row.
656 $table.find( 'tr:eq(2) td:eq(1)' ).attr( 'rowspan', '3' );
657
658 $table.tablesorter();
659
660 assert.equal(
661 $table.find( 'tr:eq(2) td:eq(1)' ).prop( 'rowSpan' ),
662 3,
663 'Rowspan not exploded'
664 );
665 } );
666
667 planetsRowspan = [
668 [ 'Earth', '6051.8' ],
669 jupiter,
670 [ 'Mars', '6051.8' ],
671 mercury,
672 saturn,
673 venus
674 ];
675 planetsRowspanII = [ jupiter, mercury, saturn, venus, [ 'Venus', '6371.0' ], [ 'Venus', '3390.0' ] ];
676
677 tableTest(
678 'Basic planet table: same value for multiple rows via rowspan',
679 header,
680 planets,
681 planetsRowspan,
682 function ( $table ) {
683 // Modify the table to have a multiple-row-spanning cell:
684 // - Remove 2nd cell of 4th row, and, 2nd cell or 5th row.
685 $table.find( 'tr:eq(3) td:eq(1), tr:eq(4) td:eq(1)' ).remove();
686 // - Set rowspan for 2nd cell of 3rd row to 3.
687 // This covers the removed cell in the 4th and 5th row.
688 $table.find( 'tr:eq(2) td:eq(1)' ).attr( 'rowspan', '3' );
689
690 $table.tablesorter();
691 $table.find( '.headerSort:eq(0)' ).click();
692 }
693 );
694 tableTest(
695 'Basic planet table: same value for multiple rows via rowspan (sorting initially)',
696 header,
697 planets,
698 planetsRowspan,
699 function ( $table ) {
700 // Modify the table to have a multiple-row-spanning cell:
701 // - Remove 2nd cell of 4th row, and, 2nd cell or 5th row.
702 $table.find( 'tr:eq(3) td:eq(1), tr:eq(4) td:eq(1)' ).remove();
703 // - Set rowspan for 2nd cell of 3rd row to 3.
704 // This covers the removed cell in the 4th and 5th row.
705 $table.find( 'tr:eq(2) td:eq(1)' ).attr( 'rowspan', '3' );
706
707 $table.tablesorter( { sortList: [
708 { 0: 'asc' }
709 ] } );
710 }
711 );
712 tableTest(
713 'Basic planet table: Same value for multiple rows via rowspan II',
714 header,
715 planets,
716 planetsRowspanII,
717 function ( $table ) {
718 // Modify the table to have a multiple-row-spanning cell:
719 // - Remove 1st cell of 4th row, and, 1st cell or 5th row.
720 $table.find( 'tr:eq(3) td:eq(0), tr:eq(4) td:eq(0)' ).remove();
721 // - Set rowspan for 1st cell of 3rd row to 3.
722 // This covers the removed cell in the 4th and 5th row.
723 $table.find( 'tr:eq(2) td:eq(0)' ).attr( 'rowspan', '3' );
724
725 $table.tablesorter();
726 $table.find( '.headerSort:eq(0)' ).click();
727 }
728 );
729
730 tableTest(
731 'Complex date parsing I',
732 ['date'],
733 complexMDYDates,
734 complexMDYSorted,
735 function ( $table ) {
736 mw.config.set( 'wgDefaultDateFormat', 'mdy' );
737
738 $table.tablesorter();
739 $table.find( '.headerSort:eq(0)' ).click();
740 }
741 );
742
743 tableTest(
744 'Currency parsing I',
745 ['currency'],
746 currencyUnsorted,
747 currencySorted,
748 function ( $table ) {
749 $table.tablesorter();
750 $table.find( '.headerSort:eq(0)' ).click();
751 }
752 );
753
754 planetsAscNameLegacy = planetsAscName.slice( 0 );
755 planetsAscNameLegacy[4] = planetsAscNameLegacy[5];
756 planetsAscNameLegacy.pop();
757
758 tableTest(
759 'Legacy compat with .sortbottom',
760 header,
761 planets,
762 planetsAscNameLegacy,
763 function ( $table ) {
764 $table.find( 'tr:last' ).addClass( 'sortbottom' );
765 $table.tablesorter();
766 $table.find( '.headerSort:eq(0)' ).click();
767 }
768 );
769
770 QUnit.test( 'Test detection routine', 1, function ( assert ) {
771 var $table;
772 $table = $(
773 '<table class="sortable">' +
774 '<caption>CAPTION</caption>' +
775 '<tr><th>THEAD</th></tr>' +
776 '<tr><td>1</td></tr>' +
777 '<tr class="sortbottom"><td>text</td></tr>' +
778 '</table>'
779 );
780 $table.tablesorter();
781 $table.find( '.headerSort:eq(0)' ).click();
782
783 assert.equal(
784 $table.data( 'tablesorter' ).config.parsers[0].id,
785 'number',
786 'Correctly detected column content skipping sortbottom'
787 );
788 } );
789
790 /** FIXME: the diff output is not very readeable. */
791 QUnit.test( 'bug 32047 - caption must be before thead', 1, function ( assert ) {
792 var $table;
793 $table = $(
794 '<table class="sortable">' +
795 '<caption>CAPTION</caption>' +
796 '<tr><th>THEAD</th></tr>' +
797 '<tr><td>A</td></tr>' +
798 '<tr><td>B</td></tr>' +
799 '<tr class="sortbottom"><td>TFOOT</td></tr>' +
800 '</table>'
801 );
802 $table.tablesorter();
803
804 assert.equal(
805 $table.children().get( 0 ).nodeName,
806 'CAPTION',
807 'First element after <thead> must be <caption> (bug 32047)'
808 );
809 } );
810
811 QUnit.test( 'data-sort-value attribute, when available, should override sorting position', 3, function ( assert ) {
812 var $table, data;
813
814 // Example 1: All cells except one cell without data-sort-value,
815 // which should be sorted at it's text content value.
816 $table = $(
817 '<table class="sortable"><thead><tr><th>Data</th></tr></thead>' +
818 '<tbody>' +
819 '<tr><td>Cheetah</td></tr>' +
820 '<tr><td data-sort-value="Apple">Bird</td></tr>' +
821 '<tr><td data-sort-value="Bananna">Ferret</td></tr>' +
822 '<tr><td data-sort-value="Drupe">Elephant</td></tr>' +
823 '<tr><td data-sort-value="Cherry">Dolphin</td></tr>' +
824 '</tbody></table>'
825 );
826 $table.tablesorter().find( '.headerSort:eq(0)' ).click();
827
828 data = [];
829 $table.find( 'tbody > tr' ).each( function ( i, tr ) {
830 $( tr ).find( 'td' ).each( function ( i, td ) {
831 data.push( {
832 data: $( td ).data( 'sortValue' ),
833 text: $( td ).text()
834 } );
835 } );
836 } );
837
838 assert.deepEqual( data, [
839 {
840 data: 'Apple',
841 text: 'Bird'
842 },
843 {
844 data: 'Bananna',
845 text: 'Ferret'
846 },
847 {
848 data: undefined,
849 text: 'Cheetah'
850 },
851 {
852 data: 'Cherry',
853 text: 'Dolphin'
854 },
855 {
856 data: 'Drupe',
857 text: 'Elephant'
858 }
859 ], 'Order matches expected order (based on data-sort-value attribute values)' );
860
861 // Example 2
862 $table = $(
863 '<table class="sortable"><thead><tr><th>Data</th></tr></thead>' +
864 '<tbody>' +
865 '<tr><td>D</td></tr>' +
866 '<tr><td data-sort-value="E">A</td></tr>' +
867 '<tr><td>B</td></tr>' +
868 '<tr><td>G</td></tr>' +
869 '<tr><td data-sort-value="F">C</td></tr>' +
870 '</tbody></table>'
871 );
872 $table.tablesorter().find( '.headerSort:eq(0)' ).click();
873
874 data = [];
875 $table.find( 'tbody > tr' ).each( function ( i, tr ) {
876 $( tr ).find( 'td' ).each( function ( i, td ) {
877 data.push( {
878 data: $( td ).data( 'sortValue' ),
879 text: $( td ).text()
880 } );
881 } );
882 } );
883
884 assert.deepEqual( data, [
885 {
886 data: undefined,
887 text: 'B'
888 },
889 {
890 data: undefined,
891 text: 'D'
892 },
893 {
894 data: 'E',
895 text: 'A'
896 },
897 {
898 data: 'F',
899 text: 'C'
900 },
901 {
902 data: undefined,
903 text: 'G'
904 }
905 ], 'Order matches expected order (based on data-sort-value attribute values)' );
906
907 // Example 3: Test that live changes are used from data-sort-value,
908 // even if they change after the tablesorter is constructed (bug 38152).
909 $table = $(
910 '<table class="sortable"><thead><tr><th>Data</th></tr></thead>' +
911 '<tbody>' +
912 '<tr><td>D</td></tr>' +
913 '<tr><td data-sort-value="1">A</td></tr>' +
914 '<tr><td>B</td></tr>' +
915 '<tr><td data-sort-value="2">G</td></tr>' +
916 '<tr><td>C</td></tr>' +
917 '</tbody></table>'
918 );
919 // initialize table sorter and sort once
920 $table
921 .tablesorter()
922 .find( '.headerSort:eq(0)' ).click();
923
924 // Change the sortValue data properties (bug 38152)
925 // - change data
926 $table.find( 'td:contains(A)' ).data( 'sortValue', 3 );
927 // - add data
928 $table.find( 'td:contains(B)' ).data( 'sortValue', 1 );
929 // - remove data, bring back attribute: 2
930 $table.find( 'td:contains(G)' ).removeData( 'sortValue' );
931
932 // Now sort again (twice, so it is back at Ascending)
933 $table.find( '.headerSort:eq(0)' ).click();
934 $table.find( '.headerSort:eq(0)' ).click();
935
936 data = [];
937 $table.find( 'tbody > tr' ).each( function ( i, tr ) {
938 $( tr ).find( 'td' ).each( function ( i, td ) {
939 data.push( {
940 data: $( td ).data( 'sortValue' ),
941 text: $( td ).text()
942 } );
943 } );
944 } );
945
946 assert.deepEqual( data, [
947 {
948 data: 1,
949 text: 'B'
950 },
951 {
952 data: 2,
953 text: 'G'
954 },
955 {
956 data: 3,
957 text: 'A'
958 },
959 {
960 data: undefined,
961 text: 'C'
962 },
963 {
964 data: undefined,
965 text: 'D'
966 }
967 ], 'Order matches expected order, using the current sortValue in $.data()' );
968
969 } );
970
971 tableTest( 'bug 8115: sort numbers with commas (ascending)',
972 ['Numbers'], numbers, numbersAsc,
973 function ( $table ) {
974 $table.tablesorter();
975 $table.find( '.headerSort:eq(0)' ).click();
976 }
977 );
978
979 tableTest( 'bug 8115: sort numbers with commas (descending)',
980 ['Numbers'], numbers, reversed( numbersAsc ),
981 function ( $table ) {
982 $table.tablesorter();
983 $table.find( '.headerSort:eq(0)' ).click().click();
984 }
985 );
986 // TODO add numbers sorting tests for bug 8115 with a different language
987
988 QUnit.test( 'bug 32888 - Tables inside a tableheader cell', 2, function ( assert ) {
989 var $table;
990 $table = $(
991 '<table class="sortable" id="mw-bug-32888">' +
992 '<tr><th>header<table id="mw-bug-32888-2">' +
993 '<tr><th>1</th><th>2</th></tr>' +
994 '</table></th></tr>' +
995 '<tr><td>A</td></tr>' +
996 '<tr><td>B</td></tr>' +
997 '</table>'
998 );
999 $table.tablesorter();
1000
1001 assert.equal(
1002 $table.find( '> thead:eq(0) > tr > th.headerSort' ).length,
1003 1,
1004 'Child tables inside a headercell should not interfere with sortable headers (bug 32888)'
1005 );
1006 assert.equal(
1007 $( '#mw-bug-32888-2' ).find( 'th.headerSort' ).length,
1008 0,
1009 'The headers of child tables inside a headercell should not be sortable themselves (bug 32888)'
1010 );
1011 } );
1012
1013 tableTest(
1014 'Correct date sorting I',
1015 ['date'],
1016 correctDateSorting1,
1017 correctDateSortingSorted1,
1018 function ( $table ) {
1019 mw.config.set( 'wgDefaultDateFormat', 'mdy' );
1020
1021 $table.tablesorter();
1022 $table.find( '.headerSort:eq(0)' ).click();
1023 }
1024 );
1025
1026 tableTest(
1027 'Correct date sorting II',
1028 ['date'],
1029 correctDateSorting2,
1030 correctDateSortingSorted2,
1031 function ( $table ) {
1032 mw.config.set( 'wgDefaultDateFormat', 'dmy' );
1033
1034 $table.tablesorter();
1035 $table.find( '.headerSort:eq(0)' ).click();
1036 }
1037 );
1038
1039 QUnit.test( 'Sorting images using alt text', 1, function ( assert ) {
1040 var $table = $(
1041 '<table class="sortable">' +
1042 '<tr><th>THEAD</th></tr>' +
1043 '<tr><td><img alt="2"/></td></tr>' +
1044 '<tr><td>1</td></tr>' +
1045 '</table>'
1046 );
1047 $table.tablesorter().find( '.headerSort:eq(0)' ).click();
1048
1049 assert.equal(
1050 $table.find( 'td' ).first().text(),
1051 '1',
1052 'Applied correct sorting order'
1053 );
1054 } );
1055
1056 QUnit.test( 'Sorting images using alt text (complex)', 1, function ( assert ) {
1057 var $table = $(
1058 '<table class="sortable">' +
1059 '<tr><th>THEAD</th></tr>' +
1060 '<tr><td><img alt="D" />A</td></tr>' +
1061 '<tr><td>CC</td></tr>' +
1062 '<tr><td><a><img alt="A" /></a>F</tr>' +
1063 '<tr><td><img alt="A" /><strong>E</strong></tr>' +
1064 '<tr><td><strong><img alt="A" />D</strong></tr>' +
1065 '<tr><td><img alt="A" />C</tr>' +
1066 '</table>'
1067 );
1068 $table.tablesorter().find( '.headerSort:eq(0)' ).click();
1069
1070 assert.equal(
1071 $table.find( 'td' ).text(),
1072 'CDEFCCA',
1073 'Applied correct sorting order'
1074 );
1075 } );
1076
1077 QUnit.test( 'Sorting images using alt text (with format autodetection)', 1, function ( assert ) {
1078 var $table = $(
1079 '<table class="sortable">' +
1080 '<tr><th>THEAD</th></tr>' +
1081 '<tr><td><img alt="1" />7</td></tr>' +
1082 '<tr><td>1<img alt="6" /></td></tr>' +
1083 '<tr><td>5</td></tr>' +
1084 '<tr><td>4</td></tr>' +
1085 '</table>'
1086 );
1087 $table.tablesorter().find( '.headerSort:eq(0)' ).click();
1088
1089 assert.equal(
1090 $table.find( 'td' ).text(),
1091 '4517',
1092 'Applied correct sorting order'
1093 );
1094 } );
1095
1096 QUnit.test( 'bug 38911 - The row with the largest amount of columns should receive the sort indicators', 3, function ( assert ) {
1097 var $table = $(
1098 '<table class="sortable">' +
1099 '<thead>' +
1100 '<tr><th rowspan="2" id="A1">A1</th><th colspan="2">B2a</th></tr>' +
1101 '<tr><th id="B2b">B2b</th><th id="C2b">C2b</th></tr>' +
1102 '</thead>' +
1103 '<tr><td>A</td><td>Aa</td><td>Ab</td></tr>' +
1104 '<tr><td>B</td><td>Ba</td><td>Bb</td></tr>' +
1105 '</table>'
1106 );
1107 $table.tablesorter();
1108
1109 assert.equal(
1110 $table.find( '#A1' ).attr( 'class' ),
1111 'headerSort',
1112 'The first column of the first row should be sortable'
1113 );
1114 assert.equal(
1115 $table.find( '#B2b' ).attr( 'class' ),
1116 'headerSort',
1117 'The th element of the 2nd row of the 2nd column should be sortable'
1118 );
1119 assert.equal(
1120 $table.find( '#C2b' ).attr( 'class' ),
1121 'headerSort',
1122 'The th element of the 2nd row of the 3rd column should be sortable'
1123 );
1124 } );
1125
1126 QUnit.test( 'rowspans in table headers should prefer the last row when rows are equal in length', 2, function ( assert ) {
1127 var $table = $(
1128 '<table class="sortable">' +
1129 '<thead>' +
1130 '<tr><th rowspan="2" id="A1">A1</th><th>B2a</th></tr>' +
1131 '<tr><th id="B2b">B2b</th></tr>' +
1132 '</thead>' +
1133 '<tr><td>A</td><td>Aa</td></tr>' +
1134 '<tr><td>B</td><td>Ba</td></tr>' +
1135 '</table>'
1136 );
1137 $table.tablesorter();
1138
1139 assert.equal(
1140 $table.find( '#A1' ).attr( 'class' ),
1141 'headerSort',
1142 'The first column of the first row should be sortable'
1143 );
1144 assert.equal(
1145 $table.find( '#B2b' ).attr( 'class' ),
1146 'headerSort',
1147 'The th element of the 2nd row of the 2nd column should be sortable'
1148 );
1149 } );
1150
1151 QUnit.test( 'holes in the table headers should not throw JS errors', 2, function ( assert ) {
1152 var $table = $(
1153 '<table class="sortable">' +
1154 '<thead>' +
1155 '<tr><th id="A1">A1</th><th>B1</th><th id="C1" rowspan="2">C1</th></tr>' +
1156 '<tr><th id="A2">A2</th></tr>' +
1157 '</thead>' +
1158 '<tr><td>A</td><td>Aa</td><td>Aaa</td></tr>' +
1159 '<tr><td>B</td><td>Ba</td><td>Bbb</td></tr>' +
1160 '</table>'
1161 );
1162 $table.tablesorter();
1163 assert.equal( $table.find( '#A2' ).prop( 'headerIndex' ),
1164 0,
1165 'A2 should not be a sort header'
1166 );
1167 assert.equal( $table.find( '#C1' ).prop( 'headerIndex' ),
1168 1,
1169 'C1 should be a sort header, but will sort the wrong column'
1170 );
1171 } );
1172
1173 // bug 41889 - exploding rowspans in more complex cases
1174 tableTestHTML(
1175 'Rowspan exploding with row headers',
1176 '<table class="sortable">' +
1177 '<thead><tr><th id="sortme">n</th><th>foo</th><th>bar</th><th>baz</th></tr></thead>' +
1178 '<tbody>' +
1179 '<tr><td>1</td><th rowspan="2">foo</th><td rowspan="2">bar</td><td>baz</td></tr>' +
1180 '<tr><td>2</td><td>baz</td></tr>' +
1181 '</tbody></table>',
1182 [
1183 [ '1', 'foo', 'bar', 'baz' ],
1184 [ '2', 'foo', 'bar', 'baz' ]
1185 ]
1186 );
1187
1188 // bug 53211 - exploding rowspans in more complex cases
1189 QUnit.test(
1190 'Rowspan exploding with row headers and colspans', 1, function ( assert ) {
1191 var $table = $( '<table class="sortable">' +
1192 '<thead><tr><th rowspan="2">n</th><th colspan="2">foo</th><th rowspan="2">baz</th></tr>' +
1193 '<tr><th>foo</th><th>bar</th></tr></thead>' +
1194 '<tbody>' +
1195 '<tr><td>1</td><td>foo</td><td>bar</td><td>baz</td></tr>' +
1196 '<tr><td>2</td><td>foo</td><td>bar</td><td>baz</td></tr>' +
1197 '</tbody></table>' );
1198
1199 $table.tablesorter();
1200 assert.equal( $table.find( 'tr:eq(1) th:eq(1)').prop('headerIndex'),
1201 2,
1202 'Incorrect index of sort header' );
1203 }
1204 );
1205
1206 tableTestHTML(
1207 'Rowspan exploding with colspanned cells',
1208 '<table class="sortable">' +
1209 '<thead><tr><th id="sortme">n</th><th>foo</th><th>bar</th><th>baz</th></tr></thead>' +
1210 '<tbody>' +
1211 '<tr><td>1</td><td>foo</td><td>bar</td><td rowspan="2">baz</td></tr>' +
1212 '<tr><td>2</td><td colspan="2">foobar</td></tr>' +
1213 '</tbody></table>',
1214 [
1215 [ '1', 'foo', 'bar', 'baz' ],
1216 [ '2', 'foobar', 'baz' ]
1217 ]
1218 );
1219
1220 tableTestHTML(
1221 'Rowspan exploding with colspanned cells (2)',
1222 '<table class="sortable">' +
1223 '<thead><tr><th id="sortme">n</th><th>foo</th><th>bar</th><th>baz</th><th>quux</th></tr></thead>' +
1224 '<tbody>' +
1225 '<tr><td>1</td><td>foo</td><td>bar</td><td rowspan="2">baz</td><td>quux</td></tr>' +
1226 '<tr><td>2</td><td colspan="2">foobar</td><td>quux</td></tr>' +
1227 '</tbody></table>',
1228 [
1229 [ '1', 'foo', 'bar', 'baz', 'quux' ],
1230 [ '2', 'foobar', 'baz', 'quux' ]
1231 ]
1232 );
1233
1234 tableTestHTML(
1235 'Rowspan exploding with rightmost rows spanning most',
1236 '<table class="sortable">' +
1237 '<thead><tr><th id="sortme">n</th><th>foo</th><th>bar</th></tr></thead>' +
1238 '<tbody>' +
1239 '<tr><td>1</td><td rowspan="2">foo</td><td rowspan="4">bar</td></tr>' +
1240 '<tr><td>2</td></tr>' +
1241 '<tr><td>3</td><td rowspan="2">foo</td></tr>' +
1242 '<tr><td>4</td></tr>' +
1243 '</tbody></table>',
1244 [
1245 [ '1', 'foo', 'bar' ],
1246 [ '2', 'foo', 'bar' ],
1247 [ '3', 'foo', 'bar' ],
1248 [ '4', 'foo', 'bar' ]
1249 ]
1250 );
1251
1252 tableTestHTML(
1253 'Rowspan exploding with rightmost rows spanning most (2)',
1254 '<table class="sortable">' +
1255 '<thead><tr><th id="sortme">n</th><th>foo</th><th>bar</th><th>baz</th></tr></thead>' +
1256 '<tbody>' +
1257 '<tr><td>1</td><td rowspan="2">foo</td><td rowspan="4">bar</td><td>baz</td></tr>' +
1258 '<tr><td>2</td><td>baz</td></tr>' +
1259 '<tr><td>3</td><td rowspan="2">foo</td><td>baz</td></tr>' +
1260 '<tr><td>4</td><td>baz</td></tr>' +
1261 '</tbody></table>',
1262 [
1263 [ '1', 'foo', 'bar', 'baz' ],
1264 [ '2', 'foo', 'bar', 'baz' ],
1265 [ '3', 'foo', 'bar', 'baz' ],
1266 [ '4', 'foo', 'bar', 'baz' ]
1267 ]
1268 );
1269
1270 tableTestHTML(
1271 'Rowspan exploding with row-and-colspanned cells',
1272 '<table class="sortable">' +
1273 '<thead><tr><th id="sortme">n</th><th>foo1</th><th>foo2</th><th>bar</th><th>baz</th></tr></thead>' +
1274 '<tbody>' +
1275 '<tr><td>1</td><td rowspan="2">foo1</td><td rowspan="2">foo2</td><td rowspan="4">bar</td><td>baz</td></tr>' +
1276 '<tr><td>2</td><td>baz</td></tr>' +
1277 '<tr><td>3</td><td colspan="2" rowspan="2">foo</td><td>baz</td></tr>' +
1278 '<tr><td>4</td><td>baz</td></tr>' +
1279 '</tbody></table>',
1280 [
1281 [ '1', 'foo1', 'foo2', 'bar', 'baz' ],
1282 [ '2', 'foo1', 'foo2', 'bar', 'baz' ],
1283 [ '3', 'foo', 'bar', 'baz' ],
1284 [ '4', 'foo', 'bar', 'baz' ]
1285 ]
1286 );
1287
1288 tableTestHTML(
1289 'Rowspan exploding with uneven rowspan layout',
1290 '<table class="sortable">' +
1291 '<thead><tr><th id="sortme">n</th><th>foo1</th><th>foo2</th><th>foo3</th><th>bar</th><th>baz</th></tr></thead>' +
1292 '<tbody>' +
1293 '<tr><td>1</td><td rowspan="2">foo1</td><td rowspan="2">foo2</td><td rowspan="2">foo3</td><td>bar</td><td>baz</td></tr>' +
1294 '<tr><td>2</td><td rowspan="3">bar</td><td>baz</td></tr>' +
1295 '<tr><td>3</td><td rowspan="2">foo1</td><td rowspan="2">foo2</td><td rowspan="2">foo3</td><td>baz</td></tr>' +
1296 '<tr><td>4</td><td>baz</td></tr>' +
1297 '</tbody></table>',
1298 [
1299 [ '1', 'foo1', 'foo2', 'foo3', 'bar', 'baz' ],
1300 [ '2', 'foo1', 'foo2', 'foo3', 'bar', 'baz' ],
1301 [ '3', 'foo1', 'foo2', 'foo3', 'bar', 'baz' ],
1302 [ '4', 'foo1', 'foo2', 'foo3', 'bar', 'baz' ]
1303 ]
1304 );
1305
1306 }( jQuery, mediaWiki ) );