Merge "jquery.tablesorter: Reset unaffected columns' sort counts when sorting"
[lhc/web/wiklou.git] / tests / qunit / suites / resources / jquery / jquery.tablesorter.test.js
index b8d816e..4353464 100644 (file)
@@ -5,10 +5,12 @@
                wgMonthNames: ['', 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
                wgMonthNamesShort: ['', 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
                wgDefaultDateFormat: 'dmy',
+               wgSeparatorTransformTable: ['', ''],
+               wgDigitTransformTable: ['', ''],
                wgContentLanguage: 'en'
        };
 
-       QUnit.module( 'jquery.tablesorter', QUnit.newMwEnvironment({ config: config }) );
+       QUnit.module( 'jquery.tablesorter', QUnit.newMwEnvironment( { config: config } ) );
 
        /**
         * Create an HTML table from an array of row arrays containing text strings.
@@ -18,7 +20,7 @@
         * @param {String[][]} data
         * @return jQuery
         */
-       function tableCreate(  header, data ) {
+       function tableCreate( header, data ) {
                var i,
                        $table = $( '<table class="sortable"><thead></thead><tbody></tbody></table>' ),
                        $thead = $table.find( 'thead' ),
@@ -28,7 +30,7 @@
                $.each( header, function ( i, str ) {
                        var $th = $( '<th>' );
                        $th.text( str ).appendTo( $tr );
-               });
+               } );
                $tr.appendTo( $thead );
 
                for ( i = 0; i < data.length; i++ ) {
@@ -37,7 +39,7 @@
                        $.each( data[i], function ( j, str ) {
                                var $td = $( '<td>' );
                                $td.text( str ).appendTo( $tr );
-                       });
+                       } );
                        $tr.appendTo( $tbody );
                }
                return $table;
        function tableExtract( $table ) {
                var data = [];
 
-               $table.find( 'tbody' ).find( 'tr' ).each( function( i, tr ) {
+               $table.find( 'tbody' ).find( 'tr' ).each( function ( i, tr ) {
                        var row = [];
-                       $( tr ).find( 'td,th' ).each( function( i, td ) {
+                       $( tr ).find( 'td,th' ).each( function ( i, td ) {
                                row.push( $( td ).text() );
-                       });
+                       } );
                        data.push( row );
-               });
+               } );
                return data;
        }
 
                        // to asynchronous, we'll need a timeout or a callback here.
                        var extracted = tableExtract( $table );
                        assert.deepEqual( extracted, expected, msg );
-               });
+               } );
        }
 
-       function reversed(arr) {
+       /**
+        * Run a table test by building a table with the given HTML,
+        * running some callback on it, then checking the results.
+        *
+        * @param {String} msg text to pass on to qunit for the comparison
+        * @param {String} HTML to make the table
+        * @param {String[][]} expected rows/cols to compare against at end
+        * @param {function($table)} callback something to do with the table before we compare
+        */
+       function tableTestHTML( msg, html, expected, callback ) {
+               QUnit.test( msg, 1, function ( assert ) {
+                       var $table = $( html );
+
+                       // Give caller a chance to set up sorting and manipulate the table.
+                       if ( callback ) {
+                               callback( $table );
+                       } else {
+                               $table.tablesorter();
+                               $table.find( '#sortme' ).click();
+                       }
+
+                       // Table sorting is done synchronously; if it ever needs to change back
+                       // to asynchronous, we'll need a timeout or a callback here.
+                       var extracted = tableExtract( $table );
+                       assert.deepEqual( extracted, expected, msg );
+               } );
+       }
+
+       function reversed( arr ) {
                // Clone array
-               var arr2 = arr.slice(0);
+               var arr2 = arr.slice( 0 );
 
                arr2.reverse();
 
        }
 
        // Sample data set using planets named and their radius
-       var header  = [ 'Planet' , 'Radius (km)'],
+       var header = [ 'Planet' , 'Radius (km)'],
                mercury = [ 'Mercury', '2439.7' ],
-               venus   = [ 'Venus'  , '6051.8' ],
-               earth   = [ 'Earth'  , '6371.0' ],
-               mars    = [ 'Mars'   , '3390.0' ],
-               jupiter = [ 'Jupiter',  '69911' ],
-               saturn  = [ 'Saturn' ,  '58232' ];
+               venus = [ 'Venus'  , '6051.8' ],
+               earth = [ 'Earth'  , '6371.0' ],
+               mars = [ 'Mars'   , '3390.0' ],
+               jupiter = [ 'Jupiter', '69911' ],
+               saturn = [ 'Saturn' , '58232' ];
 
        // Initial data set
-       var planets         = [mercury, venus, earth, mars, jupiter, saturn];
-       var ascendingName   = [earth, jupiter, mars, mercury, saturn, venus];
+       var planets = [mercury, venus, earth, mars, jupiter, saturn];
+       var ascendingName = [earth, jupiter, mars, mercury, saturn, venus];
        var ascendingRadius = [mercury, mars, venus, earth, saturn, jupiter];
 
        tableTest(
                planets,
                ascendingName,
                function ( $table ) {
-                       $table.tablesorter( { sortList: [ { 0: 'asc' } ] } );
+                       $table.tablesorter( { sortList: [
+                               { 0: 'asc' }
+                       ] } );
                }
        );
        tableTest(
                'Basic planet table: sorting initially - descending by radius',
                header,
                planets,
-               reversed(ascendingRadius),
+               reversed( ascendingRadius ),
                function ( $table ) {
-                       $table.tablesorter( { sortList: [ { 1: 'desc' } ] } );
+                       $table.tablesorter( { sortList: [
+                               { 1: 'desc' }
+                       ] } );
                }
        );
        tableTest(
                        $table.find( '.headerSort:eq(0)' ).click();
                }
        );
+       tableTest(
+               'Basic planet table: ascending by name (multiple clicks)',
+               header,
+               planets,
+               ascendingName,
+               function ( $table ) {
+                       $table.tablesorter();
+                       $table.find( '.headerSort:eq(0)' ).click();
+                       $table.find( '.headerSort:eq(1)' ).click();
+                       $table.find( '.headerSort:eq(0)' ).click();
+               }
+       );
        tableTest(
                'Basic planet table: descending by name',
                header,
                planets,
-               reversed(ascendingName),
+               reversed( ascendingName ),
                function ( $table ) {
                        $table.tablesorter();
                        $table.find( '.headerSort:eq(0)' ).click().click();
                'Basic planet table: descending radius',
                header,
                planets,
-               reversed(ascendingRadius),
+               reversed( ascendingRadius ),
                function ( $table ) {
                        $table.tablesorter();
                        $table.find( '.headerSort:eq(1)' ).click().click();
                asc,
                function ( $table ) {
                        $table.tablesorter(
-                               { sortList: [ { 0: 'asc' }, { 1: 'asc' } ] }
+                               { sortList: [
+                                       { 0: 'asc' },
+                                       { 1: 'asc' }
+                               ] }
                        );
                }
        );
                function ( $table ) {
                        $table.tablesorter();
                        $table.data( 'tablesorter' ).sort(
-                               [ { 0: 'desc' }, { 1: 'asc' } ]
+                               [
+                                       { 0: 'desc' },
+                                       { 1: 'asc' }
+                               ]
                        );
                }
        );
                asc,
                function ( $table ) {
                        $table.tablesorter(
-                               { sortList: [ { 0: 'asc' }, { 1: 'asc' } ] }
+                               { sortList: [
+                                       { 0: 'asc' },
+                                       { 1: 'asc' }
+                               ] }
                        );
                        $table.data( 'tablesorter' ).sort(
-                               [ { 0: 'desc' }, { 1: 'asc' } ]
+                               [
+                                       { 0: 'desc' },
+                                       { 1: 'asc' }
+                               ]
                        );
                        $table.data( 'tablesorter' ).sort();
                }
        );
+       tableTest(
+               'Sort via click event after having initialized the tablesorter with initial sorting',
+               header,
+               initial,
+               descasc,
+               function ( $table ) {
+                       $table.tablesorter(
+                               { sortList: [ { 0: 'asc' }, { 1: 'asc' } ] }
+                       );
+                       $table.find( '.headerSort:eq(0)' ).click();
+               }
+       );
+       tableTest(
+               'Multi-sort via click event after having initialized the tablesorter with initial sorting',
+               header,
+               initial,
+               asc,
+               function ( $table ) {
+                       $table.tablesorter(
+                               { sortList: [ { 0: 'desc' }, { 1: 'desc' } ] }
+                       );
+                       $table.find( '.headerSort:eq(0)' ).click();
+
+                       // Pretend to click while pressing the multi-sort key
+                       var event = $.Event( 'click' );
+                       event[$table.data( 'tablesorter' ).config.sortMultiSortKey] = true;
+                       $table.find( '.headerSort:eq(1)' ).trigger( event );
+               }
+       );
        QUnit.test( 'Reset sorting making table appear unsorted', 3, function ( assert ) {
                var $table = tableCreate( header, initial );
                $table.tablesorter(
-                       { sortList: [ { 0: 'desc' }, { 1: 'asc' } ] }
+                       { sortList: [
+                               { 0: 'desc' },
+                               { 1: 'asc' }
+                       ] }
                );
                $table.data( 'tablesorter' ).sort( [] );
 
                        $table.find( '.headerSort:eq(0)' ).click();
                }
        );
+       tableTest( 'Sorting with colspanned headers: sort spanned column twice',
+               header,
+               initial,
+               [ caa4, bbc2, abc3, aab5, aaa1 ],
+               function ( $table ) {
+                       // Make colspanned header for test
+                       $table.find( 'tr:eq(0) th:eq(1), tr:eq(0) th:eq(2)' ).remove();
+                       $table.find( 'tr:eq(0) th:eq(0)' ).prop( 'colspan', '3' );
+
+                       $table.tablesorter();
+                       $table.find( '.headerSort:eq(0)' ).click();
+                       $table.find( '.headerSort:eq(0)' ).click();
+               }
+       );
        tableTest( 'Sorting with colspanned headers: subsequent column',
                header,
                initial,
                        $table.find( '.headerSort:eq(1)' ).click();
                }
        );
+       tableTest( 'Sorting with colspanned headers: sort subsequent column twice',
+               header,
+               initial,
+               [ aab5, caa4, abc3, bbc2, aaa1 ],
+               function ( $table ) {
+                       // Make colspanned header for test
+                       $table.find( 'tr:eq(0) th:eq(1), tr:eq(0) th:eq(2)' ).remove();
+                       $table.find( 'tr:eq(0) th:eq(0)' ).prop( 'colspan', '3' );
+
+                       $table.tablesorter();
+                       $table.find( '.headerSort:eq(1)' ).click();
+                       $table.find( '.headerSort:eq(1)' ).click();
+               }
+       );
+
 
        // Regression tests!
        tableTest(
                'Bug 28775: German-style (dmy) short numeric dates',
                ['Date'],
-               [ // German-style dates are day-month-year
+               [
+                       // German-style dates are day-month-year
                        ['11.11.2011'],
                        ['01.11.2011'],
                        ['02.10.2011'],
                        ['03.08.2011'],
                        ['09.11.2011']
                ],
-               [ // Sorted by ascending date
+               [
+                       // Sorted by ascending date
                        ['03.08.2011'],
                        ['02.10.2011'],
                        ['01.11.2011'],
        tableTest(
                'Bug 28775: American-style (mdy) short numeric dates',
                ['Date'],
-               [ // American-style dates are month-day-year
+               [
+                       // American-style dates are month-day-year
                        ['11.11.2011'],
                        ['01.11.2011'],
                        ['02.10.2011'],
                        ['03.08.2011'],
                        ['09.11.2011']
                ],
-               [ // Sorted by ascending date
+               [
+                       // Sorted by ascending date
                        ['01.11.2011'],
                        ['02.10.2011'],
                        ['03.08.2011'],
                'Bug 17141: IPv4 address sorting (reverse)',
                ['IP'],
                ipv4,
-               reversed(ipv4Sorted),
+               reversed( ipv4Sorted ),
                function ( $table ) {
                        $table.tablesorter();
                        $table.find( '.headerSort:eq(0)' ).click().click();
                                'ä': 'ae',
                                'ö': 'oe',
                                'ß': 'ss',
-                               'ü':'ue'
+                               'ü': 'ue'
                        } );
 
                        $table.tablesorter();
                        3,
                        'Rowspan not exploded'
                );
-       });
+       } );
 
-       var planetsRowspan = [ [ 'Earth', '6051.8' ], jupiter, [ 'Mars', '6051.8' ], mercury, saturn, venus ];
+       var planetsRowspan = [
+               [ 'Earth', '6051.8' ],
+               jupiter,
+               [ 'Mars', '6051.8' ],
+               mercury,
+               saturn,
+               venus
+       ];
        var planetsRowspanII = [ jupiter, mercury, saturn, venus, [ 'Venus', '6371.0' ], [ 'Venus', '3390.0' ] ];
 
        tableTest(
                        //   This covers the removed cell in the 4th and 5th row.
                        $table.find( 'tr:eq(2) td:eq(1)' ).prop( 'rowspan', '3' );
 
-                       $table.tablesorter( { sortList: [ { 0: 'asc' } ] } );
+                       $table.tablesorter( { sortList: [
+                               { 0: 'asc' }
+                       ] } );
                }
        );
        tableTest(
                }
        );
 
-       var ascendingNameLegacy = ascendingName.slice(0);
+       var ascendingNameLegacy = ascendingName.slice( 0 );
        ascendingNameLegacy[4] = ascendingNameLegacy[5];
        ascendingNameLegacy.pop();
 
                header,
                planets,
                ascendingNameLegacy,
-               function( $table ) {
+               function ( $table ) {
                        $table.find( 'tr:last' ).addClass( 'sortbottom' );
                        $table.tablesorter();
                        $table.find( '.headerSort:eq(0)' ).click();
                }
        );
 
-       QUnit.test( 'Test detection routine', function ( assert ) {
+       QUnit.test( 'Test detection routine', 1, function ( assert ) {
                var $table;
                $table = $(
                        '<table class="sortable">' +
-                       '<caption>CAPTION</caption>' +
-                       '<tr><th>THEAD</th></tr>' +
-                       '<tr><td>1</td></tr>' +
-                       '<tr class="sortbottom"><td>text</td></tr>' +
-                       '</table>'
+                               '<caption>CAPTION</caption>' +
+                               '<tr><th>THEAD</th></tr>' +
+                               '<tr><td>1</td></tr>' +
+                               '<tr class="sortbottom"><td>text</td></tr>' +
+                               '</table>'
                );
                $table.tablesorter();
                $table.find( '.headerSort:eq(0)' ).click();
        } );
 
        /** FIXME: the diff output is not very readeable. */
-       QUnit.test( 'bug 32047 - caption must be before thead', function ( assert ) {
+       QUnit.test( 'bug 32047 - caption must be before thead', 1, function ( assert ) {
                var $table;
                $table = $(
                        '<table class="sortable">' +
-                       '<caption>CAPTION</caption>' +
-                       '<tr><th>THEAD</th></tr>' +
-                       '<tr><td>A</td></tr>' +
-                       '<tr><td>B</td></tr>' +
-                       '<tr class="sortbottom"><td>TFOOT</td></tr>' +
-                       '</table>'
-                       );
+                               '<caption>CAPTION</caption>' +
+                               '<tr><th>THEAD</th></tr>' +
+                               '<tr><td>A</td></tr>' +
+                               '<tr><td>B</td></tr>' +
+                               '<tr class="sortbottom"><td>TFOOT</td></tr>' +
+                               '</table>'
+               );
                $table.tablesorter();
 
                assert.equal(
-                       $table.children( ).get( 0 ).nodeName,
+                       $table.children().get( 0 ).nodeName,
                        'CAPTION',
                        'First element after <thead> must be <caption> (bug 32047)'
                );
-       });
+       } );
 
-       QUnit.test( 'data-sort-value attribute, when available, should override sorting position', function ( assert ) {
+       QUnit.test( 'data-sort-value attribute, when available, should override sorting position', 3, function ( assert ) {
                var $table, data;
 
                // Example 1: All cells except one cell without data-sort-value,
                                '<tr><td data-sort-value="Bananna">Ferret</td></tr>' +
                                '<tr><td data-sort-value="Drupe">Elephant</td></tr>' +
                                '<tr><td data-sort-value="Cherry">Dolphin</td></tr>' +
-                       '</tbody></table>'
+                               '</tbody></table>'
                );
                $table.tablesorter().find( '.headerSort:eq(0)' ).click();
 
                data = [];
-               $table.find( 'tbody > tr' ).each( function( i, tr ) {
-                       $( tr ).find( 'td' ).each( function( i, td ) {
+               $table.find( 'tbody > tr' ).each( function ( i, tr ) {
+                       $( tr ).find( 'td' ).each( function ( i, td ) {
                                data.push( {
                                        data: $( td ).data( 'sortValue' ),
                                        text: $( td ).text()
                                } );
-                       });
-               });
+                       } );
+               } );
 
                assert.deepEqual( data, [
                        {
                                data: 'Apple',
                                text: 'Bird'
-                       }, {
+                       },
+                       {
                                data: 'Bananna',
                                text: 'Ferret'
-                       }, {
+                       },
+                       {
                                data: undefined,
                                text: 'Cheetah'
-                       }, {
+                       },
+                       {
                                data: 'Cherry',
                                text: 'Dolphin'
-                       }, {
+                       },
+                       {
                                data: 'Drupe',
                                text: 'Elephant'
                        }
                                '<tr><td>B</td></tr>' +
                                '<tr><td>G</td></tr>' +
                                '<tr><td data-sort-value="F">C</td></tr>' +
-                       '</tbody></table>'
+                               '</tbody></table>'
                );
                $table.tablesorter().find( '.headerSort:eq(0)' ).click();
 
                                        data: $( td ).data( 'sortValue' ),
                                        text: $( td ).text()
                                } );
-                       });
-               });
+                       } );
+               } );
 
                assert.deepEqual( data, [
                        {
                                data: undefined,
                                text: 'B'
-                       }, {
+                       },
+                       {
                                data: undefined,
                                text: 'D'
-                       }, {
+                       },
+                       {
                                data: 'E',
                                text: 'A'
-                       }, {
+                       },
+                       {
                                data: 'F',
                                text: 'C'
-                       }, {
+                       },
+                       {
                                data: undefined,
                                text: 'G'
                        }
                                '<tr><td>B</td></tr>' +
                                '<tr><td data-sort-value="2">G</td></tr>' +
                                '<tr><td>C</td></tr>' +
-                       '</tbody></table>'
+                               '</tbody></table>'
                );
                // initialize table sorter and sort once
                $table
                $table.find( '.headerSort:eq(0)' ).click();
 
                data = [];
-               $table.find( 'tbody > tr' ).each( function( i, tr ) {
-                       $( tr ).find( 'td' ).each( function( i, td ) {
+               $table.find( 'tbody > tr' ).each( function ( i, tr ) {
+                       $( tr ).find( 'td' ).each( function ( i, td ) {
                                data.push( {
                                        data: $( td ).data( 'sortValue' ),
                                        text: $( td ).text()
                                } );
-                       });
-               });
+                       } );
+               } );
 
                assert.deepEqual( data, [
                        {
                                data: 1,
                                text: 'B'
-                       }, {
+                       },
+                       {
                                data: 2,
                                text: 'G'
-                       }, {
+                       },
+                       {
                                data: 3,
                                text: 'A'
-                       }, {
+                       },
+                       {
                                data: undefined,
                                text: 'C'
-                       }, {
+                       },
+                       {
                                data: undefined,
                                text: 'D'
                        }
                ], 'Order matches expected order, using the current sortValue in $.data()' );
 
-       });
+       } );
 
        var numbers = [
                [ '12'    ],
 
        tableTest( 'bug 8115: sort numbers with commas (ascending)',
                ['Numbers'], numbers, numbersAsc,
-               function( $table ) {
+               function ( $table ) {
                        $table.tablesorter();
                        $table.find( '.headerSort:eq(0)' ).click();
                }
        );
 
        tableTest( 'bug 8115: sort numbers with commas (descending)',
-               ['Numbers'], numbers, reversed(numbersAsc),
-               function( $table ) {
+               ['Numbers'], numbers, reversed( numbersAsc ),
+               function ( $table ) {
                        $table.tablesorter();
                        $table.find( '.headerSort:eq(0)' ).click().click();
                }
                var $table;
                $table = $(
                        '<table class="sortable" id="mw-bug-32888">' +
-                       '<tr><th>header<table id="mw-bug-32888-2">'+
+                               '<tr><th>header<table id="mw-bug-32888-2">' +
                                '<tr><th>1</th><th>2</th></tr>' +
-                       '</table></th></tr>' +
-                       '<tr><td>A</td></tr>' +
-                       '<tr><td>B</td></tr>' +
-                       '</table>'
-                       );
+                               '</table></th></tr>' +
+                               '<tr><td>A</td></tr>' +
+                               '<tr><td>B</td></tr>' +
+                               '</table>'
+               );
                $table.tablesorter();
 
                assert.equal(
-                       $table.find('> thead:eq(0) > tr > th.headerSort').length,
+                       $table.find( '> thead:eq(0) > tr > th.headerSort' ).length,
                        1,
                        'Child tables inside a headercell should not interfere with sortable headers (bug 32888)'
                );
                assert.equal(
-                       $( '#mw-bug-32888-2' ).find('th.headerSort').length,
+                       $( '#mw-bug-32888-2' ).find( 'th.headerSort' ).length,
                        0,
                        'The headers of child tables inside a headercell should not be sortable themselves (bug 32888)'
                );
-       });
+       } );
 
 
        var correctDateSorting1 = [
                }
        );
 
-QUnit.test( 'Sorting images using alt text', function ( assert ) {
-       var $table = $(
+       QUnit.test( 'Sorting images using alt text', 1, function ( assert ) {
+               var $table = $(
+                       '<table class="sortable">' +
+                               '<tr><th>THEAD</th></tr>' +
+                               '<tr><td><img alt="2"/></td></tr>' +
+                               '<tr><td>1</td></tr>' +
+                               '</table>'
+               );
+               $table.tablesorter().find( '.headerSort:eq(0)' ).click();
+
+               assert.equal(
+                       $table.find( 'td' ).first().text(),
+                       '1',
+                       'Applied correct sorting order'
+               );
+       } );
+
+       QUnit.test( 'Sorting images using alt text (complex)', 1, function ( assert ) {
+               var $table = $(
+                       '<table class="sortable">' +
+                               '<tr><th>THEAD</th></tr>' +
+                               '<tr><td><img alt="D" />A</td></tr>' +
+                               '<tr><td>CC</td></tr>' +
+                               '<tr><td><a><img alt="A" /></a>F</tr>' +
+                               '<tr><td><img alt="A" /><strong>E</strong></tr>' +
+                               '<tr><td><strong><img alt="A" />D</strong></tr>' +
+                               '<tr><td><img alt="A" />C</tr>' +
+                               '</table>'
+               );
+               $table.tablesorter().find( '.headerSort:eq(0)' ).click();
+
+               assert.equal(
+                       $table.find( 'td' ).text(),
+                       'CDEFCCA',
+                       'Applied correct sorting order'
+               );
+       } );
+
+       QUnit.test( 'Sorting images using alt text (with format autodetection)', 1, function ( assert ) {
+               var $table = $(
+                       '<table class="sortable">' +
+                               '<tr><th>THEAD</th></tr>' +
+                               '<tr><td><img alt="1" />7</td></tr>' +
+                               '<tr><td>1<img alt="6" /></td></tr>' +
+                               '<tr><td>5</td></tr>' +
+                               '<tr><td>4</td></tr>' +
+                               '</table>'
+               );
+               $table.tablesorter().find( '.headerSort:eq(0)' ).click();
+
+               assert.equal(
+                       $table.find( 'td' ).text(),
+                       '4517',
+                       'Applied correct sorting order'
+               );
+       } );
+
+       // bug 41889 - exploding rowspans in more complex cases
+       tableTestHTML(
+               'Rowspan exploding with row headers',
+               '<table class="sortable">' +
+                       '<thead><tr><th id="sortme">n</th><th>foo</th><th>bar</th><th>baz</th></tr></thead>' +
+                       '<tbody>' +
+                       '<tr><td>1</td><th rowspan="2">foo</th><td rowspan="2">bar</td><td>baz</td></tr>' +
+                       '<tr><td>2</td><td>baz</td></tr>' +
+                       '</tbody></table>',
+               [
+                       [ '1', 'foo', 'bar', 'baz' ],
+                       [ '2', 'foo', 'bar', 'baz' ]
+               ]
+       );
+
+       tableTestHTML(
+               'Rowspan exploding with colspanned cells',
                '<table class="sortable">' +
-               '<tr><th>THEAD</th></tr>' +
-               '<tr><td><img alt="2"/></td></tr>' +
-               '<tr><td>1</td></tr>' +
-               '</table>'
+                       '<thead><tr><th id="sortme">n</th><th>foo</th><th>bar</th><th>baz</th></tr></thead>' +
+                       '<tbody>' +
+                       '<tr><td>1</td><td>foo</td><td>bar</td><td rowspan="2">baz</td></tr>' +
+                       '<tr><td>2</td><td colspan="2">foobar</td></tr>' +
+                       '</tbody></table>',
+               [
+                       [ '1', 'foo', 'bar', 'baz' ],
+                       [ '2', 'foobar', 'baz' ]
+               ]
        );
-       $table.tablesorter().find( '.headerSort:eq(0)' ).click();
 
-       assert.equal(
-               $table.find( 'td' ).first().text(),
-               '1',
-               'Applied correct sorting order'
+       tableTestHTML(
+               'Rowspan exploding with colspanned cells (2)',
+               '<table class="sortable">' +
+                       '<thead><tr><th id="sortme">n</th><th>foo</th><th>bar</th><th>baz</th><th>quux</th></tr></thead>' +
+                       '<tbody>' +
+                       '<tr><td>1</td><td>foo</td><td>bar</td><td rowspan="2">baz</td><td>quux</td></tr>' +
+                       '<tr><td>2</td><td colspan="2">foobar</td><td>quux</td></tr>' +
+                       '</tbody></table>',
+               [
+                       [ '1', 'foo', 'bar', 'baz', 'quux' ],
+                       [ '2', 'foobar', 'baz', 'quux' ]
+               ]
        );
-} );
 
-QUnit.test( 'Sorting images using alt text (complex)', function ( assert ) {
-       var $table = $(
+       tableTestHTML(
+               'Rowspan exploding with rightmost rows spanning most',
                '<table class="sortable">' +
-               '<tr><th>THEAD</th></tr>' +
-               '<tr><td><img alt="D" />A</td></tr>' +
-               '<tr><td>CC</td></tr>' +
-               '<tr><td><a><img alt="A" /></a>F</tr>' +
-               '<tr><td><img alt="A" /><strong>E</strong></tr>' +
-               '<tr><td><strong><img alt="A" />D</strong></tr>' +
-               '<tr><td><img alt="A" />C</tr>' +
-               '</table>'
-       );
-       $table.tablesorter().find( '.headerSort:eq(0)' ).click();
-
-       assert.equal(
-               $table.find( 'td' ).text(),
-               'CDEFCCA',
-               'Applied correct sorting order'
-       );
-} );
-
-QUnit.test( 'Sorting images using alt text (with format autodetection)', function ( assert ) {
-       var $table = $(
+                       '<thead><tr><th id="sortme">n</th><th>foo</th><th>bar</th></tr></thead>' +
+                       '<tbody>' +
+                       '<tr><td>1</td><td rowspan="2">foo</td><td rowspan="4">bar</td></tr>' +
+                       '<tr><td>2</td></tr>' +
+                       '<tr><td>3</td><td rowspan="2">foo</td></tr>' +
+                       '<tr><td>4</td></tr>' +
+                       '</tbody></table>',
+               [
+                       [ '1', 'foo', 'bar' ],
+                       [ '2', 'foo', 'bar' ],
+                       [ '3', 'foo', 'bar' ],
+                       [ '4', 'foo', 'bar' ]
+               ]
+       );
+
+       tableTestHTML(
+               'Rowspan exploding with rightmost rows spanning most (2)',
                '<table class="sortable">' +
-               '<tr><th>THEAD</th></tr>' +
-               '<tr><td><img alt="1" />7</td></tr>' +
-               '<tr><td>1<img alt="6" /></td></tr>' +
-               '<tr><td>5</td></tr>' +
-               '<tr><td>4</td></tr>' +
-               '</table>'
+                       '<thead><tr><th id="sortme">n</th><th>foo</th><th>bar</th><th>baz</th></tr></thead>' +
+                       '<tbody>' +
+                       '<tr><td>1</td><td rowspan="2">foo</td><td rowspan="4">bar</td><td>baz</td></tr>' +
+                       '<tr><td>2</td><td>baz</td></tr>' +
+                       '<tr><td>3</td><td rowspan="2">foo</td><td>baz</td></tr>' +
+                       '<tr><td>4</td><td>baz</td></tr>' +
+                       '</tbody></table>',
+               [
+                       [ '1', 'foo', 'bar', 'baz' ],
+                       [ '2', 'foo', 'bar', 'baz' ],
+                       [ '3', 'foo', 'bar', 'baz' ],
+                       [ '4', 'foo', 'bar', 'baz' ]
+               ]
        );
-       $table.tablesorter().find( '.headerSort:eq(0)' ).click();
 
-       assert.equal(
-               $table.find( 'td' ).text(),
-               '4517',
-               'Applied correct sorting order'
+       tableTestHTML(
+               'Rowspan exploding with row-and-colspanned cells',
+               '<table class="sortable">' +
+                       '<thead><tr><th id="sortme">n</th><th>foo1</th><th>foo2</th><th>bar</th><th>baz</th></tr></thead>' +
+                       '<tbody>' +
+                       '<tr><td>1</td><td rowspan="2">foo1</td><td rowspan="2">foo2</td><td rowspan="4">bar</td><td>baz</td></tr>' +
+                       '<tr><td>2</td><td>baz</td></tr>' +
+                       '<tr><td>3</td><td colspan="2" rowspan="2">foo</td><td>baz</td></tr>' +
+                       '<tr><td>4</td><td>baz</td></tr>' +
+                       '</tbody></table>',
+               [
+                       [ '1', 'foo1', 'foo2', 'bar', 'baz' ],
+                       [ '2', 'foo1', 'foo2', 'bar', 'baz' ],
+                       [ '3', 'foo', 'bar', 'baz' ],
+                       [ '4', 'foo', 'bar', 'baz' ]
+               ]
+       );
+
+       tableTestHTML(
+               'Rowspan exploding with uneven rowspan layout',
+               '<table class="sortable">' +
+                       '<thead><tr><th id="sortme">n</th><th>foo1</th><th>foo2</th><th>foo3</th><th>bar</th><th>baz</th></tr></thead>' +
+                       '<tbody>' +
+                       '<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>' +
+                       '<tr><td>2</td><td rowspan="3">bar</td><td>baz</td></tr>' +
+                       '<tr><td>3</td><td rowspan="2">foo1</td><td rowspan="2">foo2</td><td rowspan="2">foo3</td><td>baz</td></tr>' +
+                       '<tr><td>4</td><td>baz</td></tr>' +
+                       '</tbody></table>',
+               [
+                       [ '1', 'foo1', 'foo2', 'foo3', 'bar', 'baz' ],
+                       [ '2', 'foo1', 'foo2', 'foo3', 'bar', 'baz' ],
+                       [ '3', 'foo1', 'foo2', 'foo3', 'bar', 'baz' ],
+                       [ '4', 'foo1', 'foo2', 'foo3', 'bar', 'baz' ]
+               ]
        );
-} );
 
 }( jQuery, mediaWiki ) );