ec341027497f868a587f82bc6df42113e76d1e08
[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 ) );
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 var ipv4CIDR = [
240 // Some randomly generated fake IPs
241 ['45.238.27.109/36'],
242 ['170.38.91.162/36'],
243 ['247.240.82.209/36'],
244 ['204.204.132.158/24'],
245 ['170.38.91.162/24']
246 ];
247 var ipv4CIDRSorted = [
248 // Sort order should go octet by octet
249 ['45.238.27.109/36'],
250 ['170.38.91.162/24'],
251 ['170.38.91.162/36'],
252 ['204.204.132.158/24'],
253 ['247.240.82.209/36']
254 ];
255 var ipv4Mixed = [
256 // Some randomly generated fake IPs
257 ['45.238.27.109'],
258 ['170.38.91.162'],
259 ['247.240.82.209'],
260 ['204.204.132.158/24'],
261 ['170.38.91.162/24']
262 ];
263 var ipv4MixedSorted = [
264 // Sort order should go octet by octet
265 ['45.238.27.109'],
266 ['170.38.91.162'],
267 ['170.38.91.162/24'],
268 ['204.204.132.158/24'],
269 ['247.240.82.209']
270 ];
271
272 tableTest(
273 'Bug 17141: IPv4 address sorting',
274 ['IP'],
275 ipv4,
276 ipv4Sorted,
277 function( $table ) {
278 $table.tablesorter();
279 $table.find( '.headerSort:eq(0)' ).click();
280 }
281 );
282 tableTest(
283 'Bug 17141: IPv4 address sorting (reverse)',
284 ['IP'],
285 ipv4,
286 reversed(ipv4Sorted),
287 function( $table ) {
288 $table.tablesorter();
289 $table.find( '.headerSort:eq(0)' ).click().click();
290 }
291 );
292 tableTest(
293 'Bug 34475: IPv4/CIDR address sorting',
294 ['IP'],
295 ipv4CIDR,
296 ipv4CIDRSorted,
297 function( $table ) {
298 $table.tablesorter();
299 $table.find( '.headerSort:eq(0)' ).click();
300 }
301 );
302
303 tableTest(
304 'Bug 34475: Mixed IPv4 and IP/CIDR address sorting',
305 ['IP'],
306 ipv4Mixed,
307 ipv4MixedSorted,
308 function( $table ) {
309 $table.tablesorter();
310 $table.find( '.headerSort:eq(0)' ).click();
311 }
312 );
313
314 var umlautWords = [
315 // Some words with Umlauts
316 ['Günther'],
317 ['Peter'],
318 ['Björn'],
319 ['Bjorn'],
320 ['Apfel'],
321 ['Äpfel'],
322 ['Strasse'],
323 ['Sträßschen']
324 ];
325
326 var umlautWordsSorted = [
327 // Some words with Umlauts
328 ['Äpfel'],
329 ['Apfel'],
330 ['Björn'],
331 ['Bjorn'],
332 ['Günther'],
333 ['Peter'],
334 ['Sträßschen'],
335 ['Strasse']
336 ];
337
338 tableTest(
339 'Accented Characters with custom collation',
340 ['Name'],
341 umlautWords,
342 umlautWordsSorted,
343 function( $table ) {
344 mw.config.set( 'tableSorterCollation', {
345 'ä': 'ae',
346 'ö': 'oe',
347 'ß': 'ss',
348 'ü':'ue'
349 } );
350
351 $table.tablesorter();
352 $table.find( '.headerSort:eq(0)' ).click();
353 }
354 );
355
356 var planetsRowspan = [["Earth","6051.8"], jupiter, ["Mars","6051.8"], mercury, saturn, venus];
357 var planetsRowspanII = [jupiter, mercury, saturn, ['Venus', '6371.0'], venus, ['Venus', '3390.0']];
358
359 tableTest(
360 'Basic planet table: same value for multiple rows via rowspan',
361 header,
362 planets,
363 planetsRowspan,
364 function( $table ) {
365 // Modify the table to have a multiuple-row-spanning cell:
366 // - Remove 2nd cell of 4th row, and, 2nd cell or 5th row.
367 $table.find( 'tr:eq(3) td:eq(1), tr:eq(4) td:eq(1)' ).remove();
368 // - Set rowspan for 2nd cell of 3rd row to 3.
369 // This covers the removed cell in the 4th and 5th row.
370 $table.find( 'tr:eq(2) td:eq(1)' ).prop( 'rowspan', '3' );
371
372 $table.tablesorter();
373 $table.find( '.headerSort:eq(0)' ).click();
374 }
375 );
376 tableTest(
377 'Basic planet table: Same value for multiple rows via rowspan II',
378 header,
379 planets,
380 planetsRowspanII,
381 function( $table ) {
382 // Modify the table to have a multiuple-row-spanning cell:
383 // - Remove 1st cell of 4th row, and, 1st cell or 5th row.
384 $table.find( 'tr:eq(3) td:eq(0), tr:eq(4) td:eq(0)' ).remove();
385 // - Set rowspan for 1st cell of 3rd row to 3.
386 // This covers the removed cell in the 4th and 5th row.
387 $table.find( 'tr:eq(2) td:eq(0)' ).prop( 'rowspan', '3' );
388
389 $table.tablesorter();
390 $table.find( '.headerSort:eq(0)' ).click();
391 }
392 );
393
394 var complexMDYDates = [
395 // Some words with Umlauts
396 ['January, 19 2010'],
397 ['April 21 1991'],
398 ['04 22 1991'],
399 ['5.12.1990'],
400 ['December 12 \'10']
401 ];
402
403 var complexMDYSorted = [
404 ["5.12.1990"],
405 ["April 21 1991"],
406 ["04 22 1991"],
407 ["January, 19 2010"],
408 ["December 12 '10"]
409 ];
410
411 tableTest(
412 'Complex date parsing I',
413 ['date'],
414 complexMDYDates,
415 complexMDYSorted,
416 function( $table ) {
417 mw.config.set( 'wgDefaultDateFormat', 'mdy' );
418
419 $table.tablesorter();
420 $table.find( '.headerSort:eq(0)' ).click();
421 }
422 );
423
424 var ascendingNameLegacy = ascendingName.slice(0);
425 ascendingNameLegacy[4] = ascendingNameLegacy[5];
426 ascendingNameLegacy.pop();
427
428 tableTest(
429 'Legacy compat with .sortbottom',
430 header,
431 planets,
432 ascendingNameLegacy,
433 function( $table ) {
434 $table.find( 'tr:last' ).addClass( 'sortbottom' );
435 $table.tablesorter();
436 $table.find( '.headerSort:eq(0)' ).click();
437 }
438 );
439
440 /** FIXME: the diff output is not very readeable. */
441 test( 'bug 32047 - caption must be before thead', function() {
442 var $table;
443 $table = $(
444 '<table class="sortable">' +
445 '<caption>CAPTION</caption>' +
446 '<tr><th>THEAD</th></tr>' +
447 '<tr><td>A</td></tr>' +
448 '<tr><td>B</td></tr>' +
449 '<tr class="sortbottom"><td>TFOOT</td></tr>' +
450 '</table>'
451 );
452 $table.tablesorter();
453
454 equals(
455 $table.children( ).get( 0 ).nodeName,
456 'CAPTION',
457 'First element after <thead> must be <caption> (bug 32047)'
458 );
459 });
460
461 test( 'data-sort-value attribute, when available, should override sorting position', function() {
462 var $table, data;
463
464 // Simple example, one without data-sort-value which should be sorted at it's text.
465 $table = $(
466 '<table class="sortable"><thead><tr><th>Data</th></tr></thead>' +
467 '<tbody>' +
468 '<tr><td>Cheetah</td></tr>' +
469 '<tr><td data-sort-value="Apple">Bird</td></tr>' +
470 '<tr><td data-sort-value="Bananna">Ferret</td></tr>' +
471 '<tr><td data-sort-value="Drupe">Elephant</td></tr>' +
472 '<tr><td data-sort-value="Cherry">Dolphin</td></tr>' +
473 '</tbody></table>'
474 );
475 $table.tablesorter().find( '.headerSort:eq(0)' ).click();
476
477 data = [];
478 $table.find( 'tbody > tr' ).each( function( i, tr ) {
479 $( tr ).find( 'td' ).each( function( i, td ) {
480 data.push( { data: $( td ).data( 'sort-value' ), text: $( td ).text() } );
481 });
482 });
483
484 deepEqual( data, [
485 {
486 "data": "Apple",
487 "text": "Bird"
488 }, {
489 "data": "Bananna",
490 "text": "Ferret"
491 }, {
492 "data": undefined,
493 "text": "Cheetah"
494 }, {
495 "data": "Cherry",
496 "text": "Dolphin"
497 }, {
498 "data": "Drupe",
499 "text": "Elephant"
500 }
501 ] );
502
503 // Another example
504 $table = $(
505 '<table class="sortable"><thead><tr><th>Data</th></tr></thead>' +
506 '<tbody>' +
507 '<tr><td>D</td></tr>' +
508 '<tr><td data-sort-value="E">A</td></tr>' +
509 '<tr><td>B</td></tr>' +
510 '<tr><td>G</td></tr>' +
511 '<tr><td data-sort-value="F">C</td></tr>' +
512 '</tbody></table>'
513 );
514 $table.tablesorter().find( '.headerSort:eq(0)' ).click();
515
516 data = [];
517 $table.find( 'tbody > tr' ).each( function( i, tr ) {
518 $( tr ).find( 'td' ).each( function( i, td ) {
519 data.push( { data: $( td ).data( 'sort-value' ), text: $( td ).text() } );
520 });
521 });
522
523 deepEqual( data, [
524 {
525 "data": undefined,
526 "text": "B"
527 }, {
528 "data": undefined,
529 "text": "D"
530 }, {
531 "data": "E",
532 "text": "A"
533 }, {
534 "data": "F",
535 "text": "C"
536 }, {
537 "data": undefined,
538 "text": "G"
539 }
540 ] );
541
542 });
543
544 var numbers = [
545 [ '12' ],
546 [ '7' ],
547 [ '13,000'],
548 [ '9' ],
549 [ '14' ],
550 [ '8.0' ]
551 ];
552 var numbersAsc = [
553 [ '7' ],
554 [ '8.0' ],
555 [ '9' ],
556 [ '12' ],
557 [ '14' ],
558 [ '13,000']
559 ];
560
561 tableTest( 'bug 8115: sort numbers with commas (ascending)',
562 ['Numbers'], numbers, numbersAsc,
563 function( $table ) {
564 $table.tablesorter();
565 $table.find( '.headerSort:eq(0)' ).click();
566 }
567 );
568
569 tableTest( 'bug 8115: sort numbers with commas (descending)',
570 ['Numbers'], numbers, reversed(numbersAsc),
571 function( $table ) {
572 $table.tablesorter();
573 $table.find( '.headerSort:eq(0)' ).click().click();
574 }
575 );
576 // TODO add numbers sorting tests for bug 8115 with a different language
577
578 test( 'bug 32888 - Tables inside a tableheader cell', function() {
579 expect(2);
580
581 var $table;
582 $table = $(
583 '<table class="sortable" id="32888">' +
584 '<tr><th>header<table id="32888-2">'+
585 '<tr><th>1</th><th>2</th></tr>' +
586 '</table></th></tr>' +
587 '<tr><td>A</td></tr>' +
588 '<tr><td>B</td></tr>' +
589 '</table>'
590 );
591 $table.tablesorter();
592
593 equals(
594 $table.find('> thead:eq(0) > tr > th.headerSort').length,
595 1,
596 'Child tables inside a headercell should not interfere with sortable headers (bug 32888)'
597 );
598 equals(
599 $('#32888-2').find('th.headerSort').length,
600 0,
601 'The headers of child tables inside a headercell should not be sortable themselves (bug 32888)'
602 );
603 });
604
605 })( jQuery );