Merge "[FileBackend] Added getScopedLocksForOps() function."
[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 // Simple example, one without data-sort-value which should be sorted at it's text.
412 $table = $(
413 '<table class="sortable"><thead><tr><th>Data</th></tr></thead>' +
414 '<tbody>' +
415 '<tr><td>Cheetah</td></tr>' +
416 '<tr><td data-sort-value="Apple">Bird</td></tr>' +
417 '<tr><td data-sort-value="Bananna">Ferret</td></tr>' +
418 '<tr><td data-sort-value="Drupe">Elephant</td></tr>' +
419 '<tr><td data-sort-value="Cherry">Dolphin</td></tr>' +
420 '</tbody></table>'
421 );
422 $table.tablesorter().find( '.headerSort:eq(0)' ).click();
423
424 data = [];
425 $table.find( 'tbody > tr' ).each( function( i, tr ) {
426 $( tr ).find( 'td' ).each( function( i, td ) {
427 data.push( { data: $( td ).data( 'sort-value' ), text: $( td ).text() } );
428 });
429 });
430
431 deepEqual( data, [
432 {
433 "data": "Apple",
434 "text": "Bird"
435 }, {
436 "data": "Bananna",
437 "text": "Ferret"
438 }, {
439 "data": undefined,
440 "text": "Cheetah"
441 }, {
442 "data": "Cherry",
443 "text": "Dolphin"
444 }, {
445 "data": "Drupe",
446 "text": "Elephant"
447 }
448 ] );
449
450 // Another example
451 $table = $(
452 '<table class="sortable"><thead><tr><th>Data</th></tr></thead>' +
453 '<tbody>' +
454 '<tr><td>D</td></tr>' +
455 '<tr><td data-sort-value="E">A</td></tr>' +
456 '<tr><td>B</td></tr>' +
457 '<tr><td>G</td></tr>' +
458 '<tr><td data-sort-value="F">C</td></tr>' +
459 '</tbody></table>'
460 );
461 $table.tablesorter().find( '.headerSort:eq(0)' ).click();
462
463 data = [];
464 $table.find( 'tbody > tr' ).each( function( i, tr ) {
465 $( tr ).find( 'td' ).each( function( i, td ) {
466 data.push( { data: $( td ).data( 'sort-value' ), text: $( td ).text() } );
467 });
468 });
469
470 deepEqual( data, [
471 {
472 "data": undefined,
473 "text": "B"
474 }, {
475 "data": undefined,
476 "text": "D"
477 }, {
478 "data": "E",
479 "text": "A"
480 }, {
481 "data": "F",
482 "text": "C"
483 }, {
484 "data": undefined,
485 "text": "G"
486 }
487 ] );
488
489 });
490
491 var numbers = [
492 [ '12' ],
493 [ '7' ],
494 [ '13,000'],
495 [ '9' ],
496 [ '14' ],
497 [ '8.0' ]
498 ];
499 var numbersAsc = [
500 [ '7' ],
501 [ '8.0' ],
502 [ '9' ],
503 [ '12' ],
504 [ '14' ],
505 [ '13,000']
506 ];
507
508 tableTest( 'bug 8115: sort numbers with commas (ascending)',
509 ['Numbers'], numbers, numbersAsc,
510 function( $table ) {
511 $table.tablesorter();
512 $table.find( '.headerSort:eq(0)' ).click();
513 }
514 );
515
516 tableTest( 'bug 8115: sort numbers with commas (descending)',
517 ['Numbers'], numbers, reversed(numbersAsc),
518 function( $table ) {
519 $table.tablesorter();
520 $table.find( '.headerSort:eq(0)' ).click().click();
521 }
522 );
523 // TODO add numbers sorting tests for bug 8115 with a different language
524
525 test( 'bug 32888 - Tables inside a tableheader cell', function() {
526 expect(2);
527
528 var $table;
529 $table = $(
530 '<table class="sortable" id="32888">' +
531 '<tr><th>header<table id="32888-2">'+
532 '<tr><th>1</th><th>2</th></tr>' +
533 '</table></th></tr>' +
534 '<tr><td>A</td></tr>' +
535 '<tr><td>B</td></tr>' +
536 '</table>'
537 );
538 $table.tablesorter();
539
540 equals(
541 $table.find('> thead:eq(0) > tr > th.headerSort').length,
542 1,
543 'Child tables inside a headercell should not interfere with sortable headers (bug 32888)'
544 );
545 equals(
546 $('#32888-2').find('th.headerSort').length,
547 0,
548 'The headers of child tables inside a headercell should not be sortable themselves (bug 32888)'
549 );
550 });
551
552 })( jQuery );