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