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