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