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