Merge "(bug 31313) Clarify watchlist preference messages"
[lhc/web/wiklou.git] / tests / qunit / suites / resources / jquery / jquery.tablesorter.test.js
1 ( function ( $ ) {
2
3 var config = {
4 wgMonthNames: ['', 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
5 wgMonthNamesShort: ['', 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
6 wgDefaultDateFormat: 'dmy',
7 wgContentLanguage: 'en'
8 };
9
10 module( 'jquery.tablesorter', QUnit.newMwEnvironment({ config: config }) );
11
12 test( '-- Initial check', function() {
13 expect(1);
14 ok( $.tablesorter, '$.tablesorter defined' );
15 });
16
17 /**
18 * Create an HTML table from an array of row arrays containing text strings.
19 * First row will be header row. No fancy rowspan/colspan stuff.
20 *
21 * @param {String[]} header
22 * @param {String[][]} data
23 * @return jQuery
24 */
25 var tableCreate = function( header, data ) {
26 var $table = $( '<table class="sortable"><thead></thead><tbody></tbody></table>' ),
27 $thead = $table.find( 'thead' ),
28 $tbody = $table.find( 'tbody' ),
29 $tr = $( '<tr>' );
30
31 $.each( header, function( i, str ) {
32 var $th = $( '<th>' );
33 $th.text( str ).appendTo( $tr );
34 });
35 $tr.appendTo( $thead );
36
37 for (var i = 0; i < data.length; i++) {
38 $tr = $( '<tr>' );
39 $.each( data[i], function( j, str ) {
40 var $td = $( '<td>' );
41 $td.text( str ).appendTo( $tr );
42 });
43 $tr.appendTo( $tbody );
44 }
45 return $table;
46 };
47
48 /**
49 * Extract text from table.
50 *
51 * @param {jQuery} $table
52 * @return String[][]
53 */
54 var tableExtract = function( $table ) {
55 var data = [];
56
57 $table.find( 'tbody' ).find( 'tr' ).each( function( i, tr ) {
58 var row = [];
59 $( tr ).find( 'td,th' ).each( function( i, td ) {
60 row.push( $( td ).text() );
61 });
62 data.push( row );
63 });
64 return data;
65 };
66
67 /**
68 * Run a table test by building a table with the given data,
69 * running some callback on it, then checking the results.
70 *
71 * @param {String} msg text to pass on to qunit for the comparison
72 * @param {String[]} header cols to make the table
73 * @param {String[][]} data rows/cols to make the table
74 * @param {String[][]} expected rows/cols to compare against at end
75 * @param {function($table)} callback something to do with the table before we compare
76 */
77 var tableTest = function( msg, header, data, expected, callback ) {
78 test( msg, function() {
79 expect(1);
80
81 var $table = tableCreate( header, data );
82
83 // Give caller a chance to set up sorting and manipulate the table.
84 callback( $table );
85
86 // Table sorting is done synchronously; if it ever needs to change back
87 // to asynchronous, we'll need a timeout or a callback here.
88 var extracted = tableExtract( $table );
89 deepEqual( extracted, expected, msg );
90 });
91 };
92
93 var reversed = function(arr) {
94 var arr2 = arr.slice(0);
95 arr2.reverse();
96 return arr2;
97 };
98
99 // Sample data set using planets named and their radius
100 var header = [ 'Planet' , 'Radius (km)'],
101 mercury = [ 'Mercury', '2439.7' ],
102 venus = [ 'Venus' , '6051.8' ],
103 earth = [ 'Earth' , '6371.0' ],
104 mars = [ 'Mars' , '3390.0' ],
105 jupiter = [ 'Jupiter', '69911' ],
106 saturn = [ 'Saturn' , '58232' ];
107
108 // Initial data set
109 var planets = [mercury, venus, earth, mars, jupiter, saturn];
110 var ascendingName = [earth, jupiter, mars, mercury, saturn, venus];
111 var ascendingRadius = [mercury, mars, venus, earth, saturn, jupiter];
112
113 tableTest(
114 'Basic planet table: ascending by name',
115 header,
116 planets,
117 ascendingName,
118 function( $table ) {
119 $table.tablesorter();
120 $table.find( '.headerSort:eq(0)' ).click();
121 }
122 );
123 tableTest(
124 'Basic planet table: ascending by name a second time',
125 header,
126 planets,
127 ascendingName,
128 function( $table ) {
129 $table.tablesorter();
130 $table.find( '.headerSort:eq(0)' ).click();
131 }
132 );
133 tableTest(
134 'Basic planet table: descending by name',
135 header,
136 planets,
137 reversed(ascendingName),
138 function( $table ) {
139 $table.tablesorter();
140 $table.find( '.headerSort:eq(0)' ).click().click();
141 }
142 );
143 tableTest(
144 'Basic planet table: ascending radius',
145 header,
146 planets,
147 ascendingRadius,
148 function( $table ) {
149 $table.tablesorter();
150 $table.find( '.headerSort:eq(1)' ).click();
151 }
152 );
153 tableTest(
154 'Basic planet table: descending radius',
155 header,
156 planets,
157 reversed(ascendingRadius),
158 function( $table ) {
159 $table.tablesorter();
160 $table.find( '.headerSort:eq(1)' ).click().click();
161 }
162 );
163
164
165 // Regression tests!
166 tableTest(
167 'Bug 28775: German-style (dmy) short numeric dates',
168 ['Date'],
169 [ // German-style dates are day-month-year
170 ['11.11.2011'],
171 ['01.11.2011'],
172 ['02.10.2011'],
173 ['03.08.2011'],
174 ['09.11.2011']
175 ],
176 [ // Sorted by ascending date
177 ['03.08.2011'],
178 ['02.10.2011'],
179 ['01.11.2011'],
180 ['09.11.2011'],
181 ['11.11.2011']
182 ],
183 function( $table ) {
184 mw.config.set( 'wgDefaultDateFormat', 'dmy' );
185 mw.config.set( 'wgContentLanguage', 'de' );
186
187 $table.tablesorter();
188 $table.find( '.headerSort:eq(0)' ).click();
189 }
190 );
191
192 tableTest(
193 'Bug 28775: American-style (mdy) short numeric dates',
194 ['Date'],
195 [ // American-style dates are month-day-year
196 ['11.11.2011'],
197 ['01.11.2011'],
198 ['02.10.2011'],
199 ['03.08.2011'],
200 ['09.11.2011']
201 ],
202 [ // Sorted by ascending date
203 ['01.11.2011'],
204 ['02.10.2011'],
205 ['03.08.2011'],
206 ['09.11.2011'],
207 ['11.11.2011']
208 ],
209 function( $table ) {
210 mw.config.set( 'wgDefaultDateFormat', 'mdy' );
211
212 $table.tablesorter();
213 $table.find( '.headerSort:eq(0)' ).click();
214 }
215 );
216
217 var ipv4 = [
218 // Some randomly generated fake IPs
219 ['45.238.27.109'],
220 ['44.172.9.22'],
221 ['247.240.82.209'],
222 ['204.204.132.158'],
223 ['170.38.91.162'],
224 ['197.219.164.9'],
225 ['45.68.154.72'],
226 ['182.195.149.80']
227 ];
228 var ipv4Sorted = [
229 // Sort order should go octet by octet
230 ['44.172.9.22'],
231 ['45.68.154.72'],
232 ['45.238.27.109'],
233 ['170.38.91.162'],
234 ['182.195.149.80'],
235 ['197.219.164.9'],
236 ['204.204.132.158'],
237 ['247.240.82.209']
238 ];
239
240 tableTest(
241 'Bug 17141: IPv4 address sorting',
242 ['IP'],
243 ipv4,
244 ipv4Sorted,
245 function( $table ) {
246 $table.tablesorter();
247 $table.find( '.headerSort:eq(0)' ).click();
248 }
249 );
250 tableTest(
251 'Bug 17141: IPv4 address sorting (reverse)',
252 ['IP'],
253 ipv4,
254 reversed(ipv4Sorted),
255 function( $table ) {
256 $table.tablesorter();
257 $table.find( '.headerSort:eq(0)' ).click().click();
258 }
259 );
260
261 var umlautWords = [
262 // Some words with Umlauts
263 ['Günther'],
264 ['Peter'],
265 ['Björn'],
266 ['Bjorn'],
267 ['Apfel'],
268 ['Äpfel'],
269 ['Strasse'],
270 ['Sträßschen']
271 ];
272
273 var umlautWordsSorted = [
274 // Some words with Umlauts
275 ['Äpfel'],
276 ['Apfel'],
277 ['Björn'],
278 ['Bjorn'],
279 ['Günther'],
280 ['Peter'],
281 ['Sträßschen'],
282 ['Strasse']
283 ];
284
285 tableTest(
286 'Accented Characters with custom collation',
287 ['Name'],
288 umlautWords,
289 umlautWordsSorted,
290 function( $table ) {
291 mw.config.set( 'tableSorterCollation', {
292 'ä': 'ae',
293 'ö': 'oe',
294 'ß': 'ss',
295 'ü':'ue'
296 } );
297
298 $table.tablesorter();
299 $table.find( '.headerSort:eq(0)' ).click();
300 }
301 );
302
303 var planetsRowspan = [["Earth","6051.8"], jupiter, ["Mars","6051.8"], mercury, saturn, venus];
304 var planetsRowspanII = [jupiter, mercury, saturn, ['Venus', '6371.0'], venus, ['Venus', '3390.0']];
305
306 tableTest(
307 'Basic planet table: same value for multiple rows via rowspan',
308 header,
309 planets,
310 planetsRowspan,
311 function( $table ) {
312 // Modify the table to have a multiuple-row-spanning cell:
313 // - Remove 2nd cell of 4th row, and, 2nd cell or 5th row.
314 $table.find( 'tr:eq(3) td:eq(1), tr:eq(4) td:eq(1)' ).remove();
315 // - Set rowspan for 2nd cell of 3rd row to 3.
316 // This covers the removed cell in the 4th and 5th row.
317 $table.find( 'tr:eq(2) td:eq(1)' ).prop( 'rowspan', '3' );
318
319 $table.tablesorter();
320 $table.find( '.headerSort:eq(0)' ).click();
321 }
322 );
323 tableTest(
324 'Basic planet table: Same value for multiple rows via rowspan II',
325 header,
326 planets,
327 planetsRowspanII,
328 function( $table ) {
329 // Modify the table to have a multiuple-row-spanning cell:
330 // - Remove 1st cell of 4th row, and, 1st cell or 5th row.
331 $table.find( 'tr:eq(3) td:eq(0), tr:eq(4) td:eq(0)' ).remove();
332 // - Set rowspan for 1st cell of 3rd row to 3.
333 // This covers the removed cell in the 4th and 5th row.
334 $table.find( 'tr:eq(2) td:eq(0)' ).prop( 'rowspan', '3' );
335
336 $table.tablesorter();
337 $table.find( '.headerSort:eq(0)' ).click();
338 }
339 );
340
341 var complexMDYDates = [
342 // Some words with Umlauts
343 ['January, 19 2010'],
344 ['April 21 1991'],
345 ['04 22 1991'],
346 ['5.12.1990'],
347 ['December 12 \'10']
348 ];
349
350 var complexMDYSorted = [
351 ["5.12.1990"],
352 ["April 21 1991"],
353 ["04 22 1991"],
354 ["January, 19 2010"],
355 ["December 12 '10"]
356 ];
357
358 tableTest(
359 'Complex date parsing I',
360 ['date'],
361 complexMDYDates,
362 complexMDYSorted,
363 function( $table ) {
364 mw.config.set( 'wgDefaultDateFormat', 'mdy' );
365
366 $table.tablesorter();
367 $table.find( '.headerSort:eq(0)' ).click();
368 }
369 );
370
371 var ascendingNameLegacy = ascendingName.slice(0);
372 ascendingNameLegacy[4] = ascendingNameLegacy[5];
373 ascendingNameLegacy.pop();
374
375 tableTest(
376 'Legacy compat with .sortbottom',
377 header,
378 planets,
379 ascendingNameLegacy,
380 function( $table ) {
381 $table.find( 'tr:last' ).addClass( 'sortbottom' );
382 $table.tablesorter();
383 $table.find( '.headerSort:eq(0)' ).click();
384 }
385 );
386
387 /** FIXME: the diff output is not very readeable. */
388 test( 'bug 32047 - caption must be before thead', function() {
389 var $table;
390 $table = $(
391 '<table class="sortable">' +
392 '<caption>CAPTION</caption>' +
393 '<tr><th>THEAD</th></tr>' +
394 '<tr><td>A</td></tr>' +
395 '<tr><td>B</td></tr>' +
396 '<tr class="sortbottom"><td>TFOOT</td></tr>' +
397 '</table>'
398 );
399 $table.tablesorter();
400
401 equals(
402 $table.children( ).get( 0 ).nodeName,
403 'CAPTION',
404 'First element after <thead> must be <caption> (bug 32047)'
405 );
406 });
407
408 test( 'data-sort-value attribute, when available, should override sorting position', function() {
409 var $table, data;
410
411 // Example 1: All cells except one cell without data-sort-value,
412 // which should be sorted at it's text content value.
413 $table = $(
414 '<table class="sortable"><thead><tr><th>Data</th></tr></thead>' +
415 '<tbody>' +
416 '<tr><td>Cheetah</td></tr>' +
417 '<tr><td data-sort-value="Apple">Bird</td></tr>' +
418 '<tr><td data-sort-value="Bananna">Ferret</td></tr>' +
419 '<tr><td data-sort-value="Drupe">Elephant</td></tr>' +
420 '<tr><td data-sort-value="Cherry">Dolphin</td></tr>' +
421 '</tbody></table>'
422 );
423 $table.tablesorter().find( '.headerSort:eq(0)' ).click();
424
425 data = [];
426 $table.find( 'tbody > tr' ).each( function( i, tr ) {
427 $( tr ).find( 'td' ).each( function( i, td ) {
428 data.push( {
429 data: $( td ).data( 'sortValue' ),
430 text: $( td ).text()
431 } );
432 });
433 });
434
435 deepEqual( data, [
436 {
437 data: 'Apple',
438 text: 'Bird'
439 }, {
440 data: 'Bananna',
441 text: 'Ferret'
442 }, {
443 data: undefined,
444 text: 'Cheetah'
445 }, {
446 data: 'Cherry',
447 text: 'Dolphin'
448 }, {
449 data: 'Drupe',
450 text: 'Elephant'
451 }
452 ], 'Order matches expected order (based on data-sort-value attribute values)' );
453
454 // Example 2
455 $table = $(
456 '<table class="sortable"><thead><tr><th>Data</th></tr></thead>' +
457 '<tbody>' +
458 '<tr><td>D</td></tr>' +
459 '<tr><td data-sort-value="E">A</td></tr>' +
460 '<tr><td>B</td></tr>' +
461 '<tr><td>G</td></tr>' +
462 '<tr><td data-sort-value="F">C</td></tr>' +
463 '</tbody></table>'
464 );
465 $table.tablesorter().find( '.headerSort:eq(0)' ).click();
466
467 data = [];
468 $table.find( 'tbody > tr' ).each( function( i, tr ) {
469 $( tr ).find( 'td' ).each( function( i, td ) {
470 data.push( {
471 data: $( td ).data( 'sortValue' ),
472 text: $( td ).text()
473 } );
474 });
475 });
476
477 deepEqual( data, [
478 {
479 data: undefined,
480 text: 'B'
481 }, {
482 data: undefined,
483 text: 'D'
484 }, {
485 data: 'E',
486 text: 'A'
487 }, {
488 data: 'F',
489 text: 'C'
490 }, {
491 data: undefined,
492 text: 'G'
493 }
494 ], 'Order matches expected order (based on data-sort-value attribute values)' );
495
496 // Example 3: Test that live changes are used from data-sort-value,
497 // even if they change after the tablesorter is constructed (bug 38152).
498 $table = $(
499 '<table class="sortable"><thead><tr><th>Data</th></tr></thead>' +
500 '<tbody>' +
501 '<tr><td>D</td></tr>' +
502 '<tr><td data-sort-value="1">A</td></tr>' +
503 '<tr><td>B</td></tr>' +
504 '<tr><td data-sort-value="2">G</td></tr>' +
505 '<tr><td>C</td></tr>' +
506 '</tbody></table>'
507 );
508 // initialize table sorter and sort once
509 $table
510 .tablesorter()
511 .find( '.headerSort:eq(0)' ).click();
512
513 // Change the sortValue data properties (bug 38152)
514 // - change data
515 $table.find( 'td:contains(A)' ).data( 'sortValue', 3 );
516 // - add data
517 $table.find( 'td:contains(B)' ).data( 'sortValue', 1 );
518 // - remove data, bring back attribute: 2
519 $table.find( 'td:contains(G)' ).removeData( 'sortValue' );
520
521 // Now sort again (twice, so it is back at Ascending)
522 $table.find( '.headerSort:eq(0)' ).click();
523 $table.find( '.headerSort:eq(0)' ).click();
524
525 data = [];
526 $table.find( 'tbody > tr' ).each( function( i, tr ) {
527 $( tr ).find( 'td' ).each( function( i, td ) {
528 data.push( {
529 data: $( td ).data( 'sortValue' ),
530 text: $( td ).text()
531 } );
532 });
533 });
534
535 deepEqual( data, [
536 {
537 data: 1,
538 text: "B"
539 }, {
540 data: 2,
541 text: "G"
542 }, {
543 data: 3,
544 text: "A"
545 }, {
546 data: undefined,
547 text: "C"
548 }, {
549 data: undefined,
550 text: "D"
551 }
552 ], 'Order matches expected order, using the current sortValue in $.data()' );
553
554 });
555
556 var numbers = [
557 [ '12' ],
558 [ '7' ],
559 [ '13,000'],
560 [ '9' ],
561 [ '14' ],
562 [ '8.0' ]
563 ];
564 var numbersAsc = [
565 [ '7' ],
566 [ '8.0' ],
567 [ '9' ],
568 [ '12' ],
569 [ '14' ],
570 [ '13,000']
571 ];
572
573 tableTest( 'bug 8115: sort numbers with commas (ascending)',
574 ['Numbers'], numbers, numbersAsc,
575 function( $table ) {
576 $table.tablesorter();
577 $table.find( '.headerSort:eq(0)' ).click();
578 }
579 );
580
581 tableTest( 'bug 8115: sort numbers with commas (descending)',
582 ['Numbers'], numbers, reversed(numbersAsc),
583 function( $table ) {
584 $table.tablesorter();
585 $table.find( '.headerSort:eq(0)' ).click().click();
586 }
587 );
588 // TODO add numbers sorting tests for bug 8115 with a different language
589
590 test( 'bug 32888 - Tables inside a tableheader cell', function() {
591 expect(2);
592
593 var $table;
594 $table = $(
595 '<table class="sortable" id="mw-bug-32888">' +
596 '<tr><th>header<table id="mw-bug-32888-2">'+
597 '<tr><th>1</th><th>2</th></tr>' +
598 '</table></th></tr>' +
599 '<tr><td>A</td></tr>' +
600 '<tr><td>B</td></tr>' +
601 '</table>'
602 );
603 $table.tablesorter();
604
605 equals(
606 $table.find('> thead:eq(0) > tr > th.headerSort').length,
607 1,
608 'Child tables inside a headercell should not interfere with sortable headers (bug 32888)'
609 );
610 equals(
611 $( '#mw-bug-32888-2' ).find('th.headerSort').length,
612 0,
613 'The headers of child tables inside a headercell should not be sortable themselves (bug 32888)'
614 );
615 });
616
617
618 var correctDateSorting1 = [
619 ['01 January 2010'],
620 ['05 February 2010'],
621 ['16 January 2010'],
622 ];
623
624 var correctDateSortingSorted1 = [
625 ['01 January 2010'],
626 ['16 January 2010'],
627 ['05 February 2010'],
628 ];
629
630 tableTest(
631 'Correct date sorting I',
632 ['date'],
633 correctDateSorting1,
634 correctDateSortingSorted1,
635 function ( $table ) {
636 mw.config.set( 'wgDefaultDateFormat', 'mdy' );
637
638 $table.tablesorter();
639 $table.find( '.headerSort:eq(0)' ).click();
640 }
641 );
642
643 var correctDateSorting2 = [
644 ['January 01 2010'],
645 ['February 05 2010'],
646 ['January 16 2010'],
647 ];
648
649 var correctDateSortingSorted2 = [
650 ['January 01 2010'],
651 ['January 16 2010'],
652 ['February 05 2010'],
653 ];
654
655 tableTest(
656 'Correct date sorting II',
657 ['date'],
658 correctDateSorting2,
659 correctDateSortingSorted2,
660 function ( $table ) {
661 mw.config.set( 'wgDefaultDateFormat', 'dmy' );
662
663 $table.tablesorter();
664 $table.find( '.headerSort:eq(0)' ).click();
665 }
666 );
667
668 })( jQuery );