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