From a1789ddde42033f1b05cc4929491214ee6e79383 Mon Sep 17 00:00:00 2001 From: Pierre Schmitz Date: Thu, 17 Dec 2015 09:15:42 +0100 Subject: Update to MediaWiki 1.26.0 --- .../resources/jquery/jquery.accessKeyLabel.test.js | 40 +- .../resources/jquery/jquery.autoEllipsis.test.js | 4 +- .../suites/resources/jquery/jquery.color.test.js | 2 +- .../resources/jquery/jquery.colorUtil.test.js | 36 +- .../suites/resources/jquery/jquery.hidpi.test.js | 16 + .../resources/jquery/jquery.localize.test.js | 14 +- .../jquery/jquery.makeCollapsible.test.js | 18 + .../resources/jquery/jquery.mwExtension.test.js | 15 +- .../resources/jquery/jquery.placeholder.test.js | 172 ++++----- .../jquery/jquery.tablesorter.parsers.test.js | 220 +++++------ .../resources/jquery/jquery.tablesorter.test.js | 413 ++++++++++++--------- .../resources/jquery/jquery.textSelection.test.js | 33 +- .../mediawiki.api/mediawiki.ForeignApi.test.js | 39 ++ .../resources/mediawiki.api/mediawiki.api.test.js | 346 ++++++++++------- .../mediawiki.api/mediawiki.api.upload.test.js | 35 ++ .../mediawiki.api/mediawiki.api.watch.test.js | 6 +- .../resources/mediawiki/mediawiki.RegExp.test.js | 38 ++ .../resources/mediawiki/mediawiki.Title.test.js | 171 +++++---- .../resources/mediawiki/mediawiki.Uri.test.js | 10 +- .../resources/mediawiki/mediawiki.cldr.test.js | 6 +- .../resources/mediawiki/mediawiki.cookie.test.js | 4 +- .../mediawiki/mediawiki.errorLogger.test.js | 2 +- .../mediawiki/mediawiki.experiments.test.js | 63 ++++ .../mediawiki/mediawiki.jqueryMsg.test.js | 269 ++++++++++++-- .../resources/mediawiki/mediawiki.jscompat.test.js | 2 +- .../resources/mediawiki/mediawiki.language.test.js | 14 +- .../mediawiki.messagePoster.factory.test.js | 2 +- .../resources/mediawiki/mediawiki.storage.test.js | 36 ++ .../suites/resources/mediawiki/mediawiki.test.js | 283 +++++++++----- .../resources/mediawiki/mediawiki.toc.test.js | 2 +- .../resources/mediawiki/mediawiki.track.test.js | 18 + .../resources/mediawiki/mediawiki.util.test.js | 127 +++---- tests/qunit/suites/resources/startup.test.js | 4 +- 33 files changed, 1588 insertions(+), 872 deletions(-) create mode 100644 tests/qunit/suites/resources/mediawiki.api/mediawiki.ForeignApi.test.js create mode 100644 tests/qunit/suites/resources/mediawiki.api/mediawiki.api.upload.test.js create mode 100644 tests/qunit/suites/resources/mediawiki/mediawiki.RegExp.test.js create mode 100644 tests/qunit/suites/resources/mediawiki/mediawiki.experiments.test.js create mode 100644 tests/qunit/suites/resources/mediawiki/mediawiki.storage.test.js (limited to 'tests/qunit/suites') diff --git a/tests/qunit/suites/resources/jquery/jquery.accessKeyLabel.test.js b/tests/qunit/suites/resources/jquery/jquery.accessKeyLabel.test.js index 4484467d..cf34fc11 100644 --- a/tests/qunit/suites/resources/jquery/jquery.accessKeyLabel.test.js +++ b/tests/qunit/suites/resources/jquery/jquery.accessKeyLabel.test.js @@ -1,7 +1,7 @@ ( function ( $ ) { QUnit.module( 'jquery.accessKeyLabel', QUnit.newMwEnvironment( { messages: { - 'brackets': '[$1]', + brackets: '[$1]', 'word-separator': ' ' } } ) ); @@ -9,23 +9,23 @@ var getAccessKeyPrefixTestData = [ // ua string, platform string, expected prefix // Internet Explorer - ['Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)', 'Win32', 'alt-'], - ['Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)', 'Win32', 'alt-'], - ['Mozilla/5.0 (Windows NT 6.3; Win64; x64; Trident/7.0; rv:11.0) like Gecko', 'Win64', 'alt-'], + [ 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)', 'Win32', 'alt-' ], + [ 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)', 'Win32', 'alt-' ], + [ 'Mozilla/5.0 (Windows NT 6.3; Win64; x64; Trident/7.0; rv:11.0) like Gecko', 'Win64', 'alt-' ], // Firefox - ['Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1.19) Gecko/20110420 Firefox/3.5.19', 'MacIntel', 'ctrl-'], - ['Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.17) Gecko/20110422 Ubuntu/10.10 (maverick) Firefox/3.6.17', 'Linux i686', 'alt-shift-'], - ['Mozilla/5.0 (Windows NT 6.0; rv:2.0.1) Gecko/20100101 Firefox/4.0.1', 'Win32', 'alt-shift-'], + [ 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1.19) Gecko/20110420 Firefox/3.5.19', 'MacIntel', 'ctrl-' ], + [ 'Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.17) Gecko/20110422 Ubuntu/10.10 (maverick) Firefox/3.6.17', 'Linux i686', 'alt-shift-' ], + [ 'Mozilla/5.0 (Windows NT 6.0; rv:2.0.1) Gecko/20100101 Firefox/4.0.1', 'Win32', 'alt-shift-' ], // Safari / Konqueror - ['Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_7; nl-nl) AppleWebKit/531.22.7 (KHTML, like Gecko) Version/4.0.5 Safari/531.22.7', 'MacIntel', 'ctrl-alt-'], - ['Mozilla/5.0 (Windows; U; Windows NT 6.0; cs-CZ) AppleWebKit/533.21.1 (KHTML, like Gecko) Version/4.0.5 Safari/531.22.7', 'Win32', 'alt-'], - ['Mozilla/5.0 (X11; Linux i686) KHTML/4.9.1 (like Gecko) Konqueror/4.9', 'Linux i686', 'ctrl-'], + [ 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_7; nl-nl) AppleWebKit/531.22.7 (KHTML, like Gecko) Version/4.0.5 Safari/531.22.7', 'MacIntel', 'ctrl-alt-' ], + [ 'Mozilla/5.0 (Windows; U; Windows NT 6.0; cs-CZ) AppleWebKit/533.21.1 (KHTML, like Gecko) Version/4.0.5 Safari/531.22.7', 'Win32', 'alt-' ], + [ 'Mozilla/5.0 (X11; Linux i686) KHTML/4.9.1 (like Gecko) Konqueror/4.9', 'Linux i686', 'ctrl-' ], // Opera - ['Opera/9.80 (Windows NT 5.1)', 'Win32', 'shift-esc-'], - ['Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.52 Safari/537.36 OPR/15.0.1147.130', 'Win32', 'shift-esc-'], + [ 'Opera/9.80 (Windows NT 5.1)', 'Win32', 'shift-esc-' ], + [ 'Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.52 Safari/537.36 OPR/15.0.1147.130', 'Win32', 'shift-esc-' ], // Chrome - ['Mozilla/5.0 (Macintosh; Intel Mac OS X 10_5_8) AppleWebKit/534.30 (KHTML, like Gecko) Chrome/12.0.742.112 Safari/534.30', 'MacIntel', 'ctrl-option-'], - ['Mozilla/5.0 (X11; Linux i686) AppleWebKit/534.30 (KHTML, like Gecko) Chrome/12.0.742.68 Safari/534.30', 'Linux i686', 'alt-shift-'] + [ 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_5_8) AppleWebKit/534.30 (KHTML, like Gecko) Chrome/12.0.742.112 Safari/534.30', 'MacIntel', 'ctrl-option-' ], + [ 'Mozilla/5.0 (X11; Linux i686) AppleWebKit/534.30 (KHTML, like Gecko) Chrome/12.0.742.68 Safari/534.30', 'Linux i686', 'alt-shift-' ] ], // strings appended to title to make sure updateTooltipAccessKeys handles them correctly updateTooltipAccessKeysTestData = [ '', ' [a]', ' [test-a]', ' [alt-b]' ]; @@ -39,9 +39,9 @@ var i; for ( i = 0; i < getAccessKeyPrefixTestData.length; i++ ) { assert.equal( $.fn.updateTooltipAccessKeys.getAccessKeyPrefix( { - userAgent: getAccessKeyPrefixTestData[i][0], - platform: getAccessKeyPrefixTestData[i][1] - } ), getAccessKeyPrefixTestData[i][2], 'Correct prefix for ' + getAccessKeyPrefixTestData[i][0] ); + userAgent: getAccessKeyPrefixTestData[ i ][ 0 ], + platform: getAccessKeyPrefixTestData[ i ][ 1 ] + } ), getAccessKeyPrefixTestData[ i ][ 2 ], 'Correct prefix for ' + getAccessKeyPrefixTestData[ i ][ 0 ] ); } } ); @@ -52,13 +52,13 @@ // (no browser is known using such a short prefix, though) or "Alt+Umschalt+" in German Firefox. result = /^Title \[(.+)[aA]\]$/.exec( title ); assert.ok( result, 'title should match expected structure.' ); - assert.notEqual( result[1], 'test-', 'Prefix used for testing shouldn\'t be used in production.' ); + assert.notEqual( result[ 1 ], 'test-', 'Prefix used for testing shouldn\'t be used in production.' ); } ); QUnit.test( 'updateTooltipAccessKeys - no access key', updateTooltipAccessKeysTestData.length, function ( assert ) { var i, oldTitle, $input, newTitle; for ( i = 0; i < updateTooltipAccessKeysTestData.length; i++ ) { - oldTitle = 'Title' + updateTooltipAccessKeysTestData[i]; + oldTitle = 'Title' + updateTooltipAccessKeysTestData[ i ]; $input = $( makeInput( oldTitle ) ); $( '#qunit-fixture' ).append( $input ); newTitle = $input.updateTooltipAccessKeys().prop( 'title' ); @@ -70,7 +70,7 @@ $.fn.updateTooltipAccessKeys.setTestMode( true ); var i, oldTitle, $input, newTitle; for ( i = 0; i < updateTooltipAccessKeysTestData.length; i++ ) { - oldTitle = 'Title' + updateTooltipAccessKeysTestData[i]; + oldTitle = 'Title' + updateTooltipAccessKeysTestData[ i ]; $input = $( makeInput( oldTitle, 'a' ) ); $( '#qunit-fixture' ).append( $input ); newTitle = $input.updateTooltipAccessKeys().prop( 'title' ); diff --git a/tests/qunit/suites/resources/jquery/jquery.autoEllipsis.test.js b/tests/qunit/suites/resources/jquery/jquery.autoEllipsis.test.js index e8c51214..a1b2e5c3 100644 --- a/tests/qunit/suites/resources/jquery/jquery.autoEllipsis.test.js +++ b/tests/qunit/suites/resources/jquery/jquery.autoEllipsis.test.js @@ -11,7 +11,7 @@ function findDivergenceIndex( a, b ) { var i = 0; - while ( i < a.length && i < b.length && a[i] === b[i] ) { + while ( i < a.length && i < b.length && a[ i ] === b[ i ] ) { i++; } return i; @@ -41,7 +41,7 @@ // Add two characters using scary black magic spanText = $span.text(); d = findDivergenceIndex( origText, spanText ); - spanTextNew = spanText.slice( 0, d ) + origText[d] + origText[d] + '...'; + spanTextNew = spanText.slice( 0, d ) + origText[ d ] + origText[ d ] + '...'; assert.gt( spanTextNew.length, spanText.length, 'Verify that the new span-length is indeed greater' ); diff --git a/tests/qunit/suites/resources/jquery/jquery.color.test.js b/tests/qunit/suites/resources/jquery/jquery.color.test.js index c8e8ac70..9afd793e 100644 --- a/tests/qunit/suites/resources/jquery/jquery.color.test.js +++ b/tests/qunit/suites/resources/jquery/jquery.color.test.js @@ -10,7 +10,7 @@ $canvas.animate( { backgroundColor: '#000' }, 10 ).promise().then( function () { var endColors = $.colorUtil.getRGB( $canvas.css( 'background-color' ) ); - assert.deepEqual( endColors, [0, 0, 0], 'end state' ); + assert.deepEqual( endColors, [ 0, 0, 0 ], 'end state' ); } ); this.clock.tick( 20 ); diff --git a/tests/qunit/suites/resources/jquery/jquery.colorUtil.test.js b/tests/qunit/suites/resources/jquery/jquery.colorUtil.test.js index 39ae363c..00de895d 100644 --- a/tests/qunit/suites/resources/jquery/jquery.colorUtil.test.js +++ b/tests/qunit/suites/resources/jquery/jquery.colorUtil.test.js @@ -4,25 +4,25 @@ QUnit.test( 'getRGB', 18, function ( assert ) { assert.strictEqual( $.colorUtil.getRGB(), undefined, 'No arguments' ); assert.strictEqual( $.colorUtil.getRGB( '' ), undefined, 'Empty string' ); - assert.deepEqual( $.colorUtil.getRGB( [0, 100, 255] ), [0, 100, 255], 'Parse array of rgb values' ); - assert.deepEqual( $.colorUtil.getRGB( 'rgb(0,100,255)' ), [0, 100, 255], 'Parse simple rgb string' ); - assert.deepEqual( $.colorUtil.getRGB( 'rgb(0, 100, 255)' ), [0, 100, 255], 'Parse simple rgb string with spaces' ); - assert.deepEqual( $.colorUtil.getRGB( 'rgb(0%,20%,40%)' ), [0, 51, 102], 'Parse rgb string with percentages' ); - assert.deepEqual( $.colorUtil.getRGB( 'rgb(0%, 20%, 40%)' ), [0, 51, 102], 'Parse rgb string with percentages and spaces' ); - assert.deepEqual( $.colorUtil.getRGB( '#f2ddee' ), [242, 221, 238], 'Hex string: 6 char lowercase' ); - assert.deepEqual( $.colorUtil.getRGB( '#f2DDEE' ), [242, 221, 238], 'Hex string: 6 char uppercase' ); - assert.deepEqual( $.colorUtil.getRGB( '#f2DdEe' ), [242, 221, 238], 'Hex string: 6 char mixed' ); - assert.deepEqual( $.colorUtil.getRGB( '#eee' ), [238, 238, 238], 'Hex string: 3 char lowercase' ); - assert.deepEqual( $.colorUtil.getRGB( '#EEE' ), [238, 238, 238], 'Hex string: 3 char uppercase' ); - assert.deepEqual( $.colorUtil.getRGB( '#eEe' ), [238, 238, 238], 'Hex string: 3 char mixed' ); - assert.deepEqual( $.colorUtil.getRGB( 'rgba(0, 0, 0, 0)' ), [255, 255, 255], 'Zero rgba for Safari 3; Transparent (whitespace)' ); + assert.deepEqual( $.colorUtil.getRGB( [ 0, 100, 255 ] ), [ 0, 100, 255 ], 'Parse array of rgb values' ); + assert.deepEqual( $.colorUtil.getRGB( 'rgb(0,100,255)' ), [ 0, 100, 255 ], 'Parse simple rgb string' ); + assert.deepEqual( $.colorUtil.getRGB( 'rgb(0, 100, 255)' ), [ 0, 100, 255 ], 'Parse simple rgb string with spaces' ); + assert.deepEqual( $.colorUtil.getRGB( 'rgb(0%,20%,40%)' ), [ 0, 51, 102 ], 'Parse rgb string with percentages' ); + assert.deepEqual( $.colorUtil.getRGB( 'rgb(0%, 20%, 40%)' ), [ 0, 51, 102 ], 'Parse rgb string with percentages and spaces' ); + assert.deepEqual( $.colorUtil.getRGB( '#f2ddee' ), [ 242, 221, 238 ], 'Hex string: 6 char lowercase' ); + assert.deepEqual( $.colorUtil.getRGB( '#f2DDEE' ), [ 242, 221, 238 ], 'Hex string: 6 char uppercase' ); + assert.deepEqual( $.colorUtil.getRGB( '#f2DdEe' ), [ 242, 221, 238 ], 'Hex string: 6 char mixed' ); + assert.deepEqual( $.colorUtil.getRGB( '#eee' ), [ 238, 238, 238 ], 'Hex string: 3 char lowercase' ); + assert.deepEqual( $.colorUtil.getRGB( '#EEE' ), [ 238, 238, 238 ], 'Hex string: 3 char uppercase' ); + assert.deepEqual( $.colorUtil.getRGB( '#eEe' ), [ 238, 238, 238 ], 'Hex string: 3 char mixed' ); + assert.deepEqual( $.colorUtil.getRGB( 'rgba(0, 0, 0, 0)' ), [ 255, 255, 255 ], 'Zero rgba for Safari 3; Transparent (whitespace)' ); // Perhaps this is a bug in colorUtil, but it is the current behavior so, let's keep // track of it, so we will know in case it would ever change. assert.strictEqual( $.colorUtil.getRGB( 'rgba(0,0,0,0)' ), undefined, 'Zero rgba without whitespace' ); - assert.deepEqual( $.colorUtil.getRGB( 'lightGreen' ), [144, 238, 144], 'Color names (lightGreen)' ); - assert.deepEqual( $.colorUtil.getRGB( 'transparent' ), [255, 255, 255], 'Color names (transparent)' ); + assert.deepEqual( $.colorUtil.getRGB( 'lightGreen' ), [ 144, 238, 144 ], 'Color names (lightGreen)' ); + assert.deepEqual( $.colorUtil.getRGB( 'transparent' ), [ 255, 255, 255 ], 'Color names (transparent)' ); assert.strictEqual( $.colorUtil.getRGB( 'mediaWiki' ), undefined, 'Inexisting color name' ); } ); @@ -37,9 +37,9 @@ // Re-create the rgbToHsl return array items, limited to two decimals. hsl = $.colorUtil.rgbToHsl( 144, 238, 144 ); - ret = [ dualDecimals( hsl[0] ), dualDecimals( hsl[1] ), dualDecimals( hsl[2] ) ]; + ret = [ dualDecimals( hsl[ 0 ] ), dualDecimals( hsl[ 1 ] ), dualDecimals( hsl[ 2 ] ) ]; - assert.deepEqual( ret, [0.33, 0.73, 0.75], 'rgb(144, 238, 144): hsl(0.33, 0.73, 0.75)' ); + assert.deepEqual( ret, [ 0.33, 0.73, 0.75 ], 'rgb(144, 238, 144): hsl(0.33, 0.73, 0.75)' ); } ); QUnit.test( 'hslToRgb', 1, function ( assert ) { @@ -47,9 +47,9 @@ rgb = $.colorUtil.hslToRgb( 0.3, 0.7, 0.8 ); // Re-create the hslToRgb return array items, rounded to whole numbers. - ret = [ Math.round( rgb[0] ), Math.round( rgb[1] ), Math.round( rgb[2] ) ]; + ret = [ Math.round( rgb[ 0 ] ), Math.round( rgb[ 1 ] ), Math.round( rgb[ 2 ] ) ]; - assert.deepEqual( ret, [183, 240, 168], 'hsl(0.3, 0.7, 0.8): rgb(183, 240, 168)' ); + assert.deepEqual( ret, [ 183, 240, 168 ], 'hsl(0.3, 0.7, 0.8): rgb(183, 240, 168)' ); } ); QUnit.test( 'getColorBrightness', 2, function ( assert ) { diff --git a/tests/qunit/suites/resources/jquery/jquery.hidpi.test.js b/tests/qunit/suites/resources/jquery/jquery.hidpi.test.js index 906369ee..8c628765 100644 --- a/tests/qunit/suites/resources/jquery/jquery.hidpi.test.js +++ b/tests/qunit/suites/resources/jquery/jquery.hidpi.test.js @@ -6,6 +6,22 @@ assert.equal( typeof devicePixelRatio, 'number', '$.devicePixelRatio() returns a number' ); } ); + QUnit.test( 'bracketedDevicePixelRatio', 1, function ( assert ) { + var devicePixelRatio = $.devicePixelRatio(); + assert.equal( typeof devicePixelRatio, 'number', '$.bracketedDevicePixelRatio() returns a number' ); + } ); + + QUnit.test( 'bracketDevicePixelRatio', 8, function ( assert ) { + assert.equal( $.bracketDevicePixelRatio( 0.75 ), 1, '0.75 gives 1' ); + assert.equal( $.bracketDevicePixelRatio( 1 ), 1, '1 gives 1' ); + assert.equal( $.bracketDevicePixelRatio( 1.25 ), 1.5, '1.25 gives 1.5' ); + assert.equal( $.bracketDevicePixelRatio( 1.5 ), 1.5, '1.5 gives 1.5' ); + assert.equal( $.bracketDevicePixelRatio( 1.75 ), 2, '1.75 gives 2' ); + assert.equal( $.bracketDevicePixelRatio( 2 ), 2, '2 gives 2' ); + assert.equal( $.bracketDevicePixelRatio( 2.5 ), 2, '2.5 gives 2' ); + assert.equal( $.bracketDevicePixelRatio( 3 ), 2, '3 gives 2' ); + } ); + QUnit.test( 'matchSrcSet', 6, function ( assert ) { var srcset = 'onefive.png 1.5x, two.png 2x'; diff --git a/tests/qunit/suites/resources/jquery/jquery.localize.test.js b/tests/qunit/suites/resources/jquery/jquery.localize.test.js index 3ef27903..c503fc99 100644 --- a/tests/qunit/suites/resources/jquery/jquery.localize.test.js +++ b/tests/qunit/suites/resources/jquery/jquery.localize.test.js @@ -76,8 +76,8 @@ html = '
'; $lc = $( html ).localize( { keys: { - 'title': 'foo-' + x + '-title', - 'label': 'foo-' + x + '-label' + title: 'foo-' + x + '-title', + label: 'foo-' + x + '-label' } } ).find( 'span' ); @@ -88,7 +88,7 @@ html = '
'; $lc = $( html ).localize( { params: { - 'foo-welcome': [sitename, 'yesterday'] + 'foo-welcome': [ sitename, 'yesterday' ] } } ).find( 'span' ); @@ -100,12 +100,12 @@ $lc = $( html ).localize( { prefix: 'foo-', keys: { - 'title': x + '-title', - 'label': x + '-label' + title: x + '-title', + label: x + '-label' }, params: { - 'title': [sitename, '3 minutes ago'], - 'label': [sitename, '3 minutes ago'] + title: [ sitename, '3 minutes ago' ], + label: [ sitename, '3 minutes ago' ] } } ).find( 'span' ); diff --git a/tests/qunit/suites/resources/jquery/jquery.makeCollapsible.test.js b/tests/qunit/suites/resources/jquery/jquery.makeCollapsible.test.js index 80405819..c51e4093 100644 --- a/tests/qunit/suites/resources/jquery/jquery.makeCollapsible.test.js +++ b/tests/qunit/suites/resources/jquery/jquery.makeCollapsible.test.js @@ -336,4 +336,22 @@ this.clock.tick( 500 ); } ); + QUnit.test( 'cloned collapsibles can be made collapsible again', 2, function ( assert ) { + var test = this, + $collapsible = prepareCollapsible( + '
' + loremIpsum + '
' + ), + $clone = $collapsible.clone() // clone without data and events + .appendTo( '#qunit-fixture' ).makeCollapsible(), + $content = $clone.find( '.mw-collapsible-content' ); + + assert.assertTrue( $content.is( ':visible' ), 'content is visible' ); + + $clone.on( 'afterCollapse.mw-collapsible', function () { + assert.assertTrue( $content.is( ':hidden' ), 'after collapsing: content is hidden' ); + } ); + + $clone.find( '.mw-collapsible-toggle a' ).trigger( 'click' ); + test.clock.tick( 500 ); + } ); }( mediaWiki, jQuery ) ); diff --git a/tests/qunit/suites/resources/jquery/jquery.mwExtension.test.js b/tests/qunit/suites/resources/jquery/jquery.mwExtension.test.js index 795c2bbb..029edd55 100644 --- a/tests/qunit/suites/resources/jquery/jquery.mwExtension.test.js +++ b/tests/qunit/suites/resources/jquery/jquery.mwExtension.test.js @@ -1,5 +1,14 @@ ( function ( $ ) { - QUnit.module( 'jquery.mwExtension', QUnit.newMwEnvironment() ); + QUnit.module( 'jquery.mwExtension', QUnit.newMwEnvironment( { + // This entire module is deprecated. + // Surpress deprecation warnings in test output. + setup: function () { + this.suppressWarnings(); + }, + teardown: function () { + this.restoreWarnings(); + } + } ) ); QUnit.test( 'String functions', 7, function ( assert ) { assert.equal( $.trimLeft( ' foo bar ' ), 'foo bar ', 'trimLeft' ); @@ -43,9 +52,9 @@ } ); QUnit.test( 'Comparison functions', 5, function ( assert ) { - assert.ok( $.compareArray( [0, 'a', [], [2, 'b'] ], [0, 'a', [], [2, 'b'] ] ), + assert.ok( $.compareArray( [ 0, 'a', [], [ 2, 'b' ] ], [ 0, 'a', [], [ 2, 'b' ] ] ), 'compareArray: Two deep arrays that are excactly the same' ); - assert.ok( !$.compareArray( [1], [2] ), 'compareArray: Two different arrays (false)' ); + assert.ok( !$.compareArray( [ 1 ], [ 2 ] ), 'compareArray: Two different arrays (false)' ); assert.ok( $.compareObject( {}, {} ), 'compareObject: Two empty objects' ); assert.ok( $.compareObject( { foo: 1 }, { foo: 1 } ), 'compareObject: Two the same objects' ); diff --git a/tests/qunit/suites/resources/jquery/jquery.placeholder.test.js b/tests/qunit/suites/resources/jquery/jquery.placeholder.test.js index 78c185f1..5d0ddebb 100644 --- a/tests/qunit/suites/resources/jquery/jquery.placeholder.test.js +++ b/tests/qunit/suites/resources/jquery/jquery.placeholder.test.js @@ -1,13 +1,13 @@ -( function ($) { +( function ( $ ) { - QUnit.module('jquery.placeholder', QUnit.newMwEnvironment()); + QUnit.module( 'jquery.placeholder', QUnit.newMwEnvironment() ); - QUnit.test('caches results of feature tests', 2, function (assert) { - assert.strictEqual( typeof $.fn.placeholder.input, 'boolean', '$.fn.placeholder.input'); - assert.strictEqual( typeof $.fn.placeholder.textarea, 'boolean', '$.fn.placeholder.textarea'); - }); + QUnit.test( 'caches results of feature tests', 2, function ( assert ) { + assert.strictEqual( typeof $.fn.placeholder.input, 'boolean', '$.fn.placeholder.input' ); + assert.strictEqual( typeof $.fn.placeholder.textarea, 'boolean', '$.fn.placeholder.textarea' ); + } ); - if ($.fn.placeholder.input && $.fn.placeholder.textarea) { + if ( $.fn.placeholder.input && $.fn.placeholder.textarea ) { return; } @@ -20,126 +20,126 @@ '' + '' + '', - testElement = function ($el, assert) { + testElement = function ( $el, assert ) { - var el = $el[0], - placeholder = el.getAttribute('placeholder'); + var el = $el[ 0 ], + placeholder = el.getAttribute( 'placeholder' ); - assert.strictEqual($el.placeholder(), $el, 'should be chainable'); + assert.strictEqual( $el.placeholder(), $el, 'should be chainable' ); - assert.strictEqual(el.value, placeholder, 'should set `placeholder` text as `value`'); - assert.strictEqual($el.prop('value'), '', 'propHooks works properly'); - assert.strictEqual($el.val(), '', 'valHooks works properly'); - assert.ok($el.hasClass('placeholder'), 'should have `placeholder` class'); + assert.strictEqual( el.value, placeholder, 'should set `placeholder` text as `value`' ); + assert.strictEqual( $el.prop( 'value' ), '', 'propHooks works properly' ); + assert.strictEqual( $el.val(), '', 'valHooks works properly' ); + assert.ok( $el.hasClass( 'placeholder' ), 'should have `placeholder` class' ); // test on focus $el.focus(); - assert.strictEqual(el.value, '', '`value` should be the empty string on focus'); - assert.strictEqual($el.prop('value'), '', 'propHooks works properly'); - assert.strictEqual($el.val(), '', 'valHooks works properly'); - assert.ok(!$el.hasClass('placeholder'), 'should not have `placeholder` class on focus'); + assert.strictEqual( el.value, '', '`value` should be the empty string on focus' ); + assert.strictEqual( $el.prop( 'value' ), '', 'propHooks works properly' ); + assert.strictEqual( $el.val(), '', 'valHooks works properly' ); + assert.ok( !$el.hasClass( 'placeholder' ), 'should not have `placeholder` class on focus' ); // and unfocus (blur) again $el.blur(); - assert.strictEqual(el.value, placeholder, 'should set `placeholder` text as `value`'); - assert.strictEqual($el.prop('value'), '', 'propHooks works properly'); - assert.strictEqual($el.val(), '', 'valHooks works properly'); - assert.ok($el.hasClass('placeholder'), 'should have `placeholder` class'); + assert.strictEqual( el.value, placeholder, 'should set `placeholder` text as `value`' ); + assert.strictEqual( $el.prop( 'value' ), '', 'propHooks works properly' ); + assert.strictEqual( $el.val(), '', 'valHooks works properly' ); + assert.ok( $el.hasClass( 'placeholder' ), 'should have `placeholder` class' ); // change the value - $el.val('lorem ipsum'); - assert.strictEqual($el.prop('value'), 'lorem ipsum', '`$el.val(string)` should change the `value` property'); - assert.strictEqual(el.value, 'lorem ipsum', '`$el.val(string)` should change the `value` attribute'); - assert.ok(!$el.hasClass('placeholder'), '`$el.val(string)` should remove `placeholder` class'); + $el.val( 'lorem ipsum' ); + assert.strictEqual( $el.prop( 'value' ), 'lorem ipsum', '`$el.val(string)` should change the `value` property' ); + assert.strictEqual( el.value, 'lorem ipsum', '`$el.val(string)` should change the `value` attribute' ); + assert.ok( !$el.hasClass( 'placeholder' ), '`$el.val(string)` should remove `placeholder` class' ); // and clear it again - $el.val(''); - assert.strictEqual($el.prop('value'), '', '`$el.val("")` should change the `value` property'); - assert.strictEqual(el.value, placeholder, '`$el.val("")` should change the `value` attribute'); - assert.ok($el.hasClass('placeholder'), '`$el.val("")` should re-enable `placeholder` class'); + $el.val( '' ); + assert.strictEqual( $el.prop( 'value' ), '', '`$el.val("")` should change the `value` property' ); + assert.strictEqual( el.value, placeholder, '`$el.val("")` should change the `value` attribute' ); + assert.ok( $el.hasClass( 'placeholder' ), '`$el.val("")` should re-enable `placeholder` class' ); // make sure the placeholder property works as expected. - assert.strictEqual($el.prop('placeholder'), placeholder, '$el.prop(`placeholder`) should return the placeholder value'); - $el.placeholder('new placeholder'); - assert.strictEqual(el.getAttribute('placeholder'), 'new placeholder', '$el.placeholder() should set the placeholder value'); - assert.strictEqual(el.value, 'new placeholder', '$el.placeholder() should update the displayed placeholder value'); - $el.placeholder(placeholder); + assert.strictEqual( $el.prop( 'placeholder' ), placeholder, '$el.prop(`placeholder`) should return the placeholder value' ); + $el.placeholder( 'new placeholder' ); + assert.strictEqual( el.getAttribute( 'placeholder' ), 'new placeholder', '$el.placeholder() should set the placeholder value' ); + assert.strictEqual( el.value, 'new placeholder', '$el.placeholder() should update the displayed placeholder value' ); + $el.placeholder( placeholder ); }; - QUnit.test('emulates placeholder for ', 22, function (assert) { - $('
').html(html).appendTo($('#qunit-fixture')); - testElement($('#input-type-text'), assert); - }); + QUnit.test( 'emulates placeholder for ', 22, function ( assert ) { + $( '
' ).html( html ).appendTo( $( '#qunit-fixture' ) ); + testElement( $( '#input-type-text' ), assert ); + } ); - QUnit.test('emulates placeholder for ', 22, function (assert) { - $('
').html(html).appendTo($('#qunit-fixture')); - testElement($('#input-type-search'), assert); - }); + QUnit.test( 'emulates placeholder for ', 22, function ( assert ) { + $( '
' ).html( html ).appendTo( $( '#qunit-fixture' ) ); + testElement( $( '#input-type-search' ), assert ); + } ); - QUnit.test('emulates placeholder for ', 22, function (assert) { - $('
').html(html).appendTo($('#qunit-fixture')); - testElement($('#input-type-email'), assert); - }); + QUnit.test( 'emulates placeholder for ', 22, function ( assert ) { + $( '
' ).html( html ).appendTo( $( '#qunit-fixture' ) ); + testElement( $( '#input-type-email' ), assert ); + } ); - QUnit.test('emulates placeholder for ', 22, function (assert) { - $('
').html(html).appendTo($('#qunit-fixture')); - testElement($('#input-type-url'), assert); - }); + QUnit.test( 'emulates placeholder for ', 22, function ( assert ) { + $( '
' ).html( html ).appendTo( $( '#qunit-fixture' ) ); + testElement( $( '#input-type-url' ), assert ); + } ); - QUnit.test('emulates placeholder for ', 22, function (assert) { - $('
').html(html).appendTo($('#qunit-fixture')); - testElement($('#input-type-tel'), assert); - }); + QUnit.test( 'emulates placeholder for ', 22, function ( assert ) { + $( '
' ).html( html ).appendTo( $( '#qunit-fixture' ) ); + testElement( $( '#input-type-tel' ), assert ); + } ); - QUnit.test('emulates placeholder for ', 13, function (assert) { - $('
').html(html).appendTo($('#qunit-fixture')); + QUnit.test( 'emulates placeholder for ', 13, function ( assert ) { + $( '
' ).html( html ).appendTo( $( '#qunit-fixture' ) ); var selector = '#input-type-password', - $el = $(selector), - el = $el[0], - placeholder = el.getAttribute('placeholder'); + $el = $( selector ), + el = $el[ 0 ], + placeholder = el.getAttribute( 'placeholder' ); - assert.strictEqual($el.placeholder(), $el, 'should be chainable'); + assert.strictEqual( $el.placeholder(), $el, 'should be chainable' ); // Re-select the element, as it gets replaced by another one in some browsers - $el = $(selector); - el = $el[0]; + $el = $( selector ); + el = $el[ 0 ]; - assert.strictEqual(el.value, placeholder, 'should set `placeholder` text as `value`'); - assert.strictEqual($el.prop('value'), '', 'propHooks works properly'); - assert.strictEqual($el.val(), '', 'valHooks works properly'); - assert.ok($el.hasClass('placeholder'), 'should have `placeholder` class'); + assert.strictEqual( el.value, placeholder, 'should set `placeholder` text as `value`' ); + assert.strictEqual( $el.prop( 'value' ), '', 'propHooks works properly' ); + assert.strictEqual( $el.val(), '', 'valHooks works properly' ); + assert.ok( $el.hasClass( 'placeholder' ), 'should have `placeholder` class' ); // test on focus $el.focus(); // Re-select the element, as it gets replaced by another one in some browsers - $el = $(selector); - el = $el[0]; + $el = $( selector ); + el = $el[ 0 ]; - assert.strictEqual(el.value, '', '`value` should be the empty string on focus'); - assert.strictEqual($el.prop('value'), '', 'propHooks works properly'); - assert.strictEqual($el.val(), '', 'valHooks works properly'); - assert.ok(!$el.hasClass('placeholder'), 'should not have `placeholder` class on focus'); + assert.strictEqual( el.value, '', '`value` should be the empty string on focus' ); + assert.strictEqual( $el.prop( 'value' ), '', 'propHooks works properly' ); + assert.strictEqual( $el.val(), '', 'valHooks works properly' ); + assert.ok( !$el.hasClass( 'placeholder' ), 'should not have `placeholder` class on focus' ); // and unfocus (blur) again $el.blur(); // Re-select the element, as it gets replaced by another one in some browsers - $el = $(selector); - el = $el[0]; + $el = $( selector ); + el = $el[ 0 ]; - assert.strictEqual(el.value, placeholder, 'should set `placeholder` text as `value`'); - assert.strictEqual($el.prop('value'), '', 'propHooks works properly'); - assert.strictEqual($el.val(), '', 'valHooks works properly'); - assert.ok($el.hasClass('placeholder'), 'should have `placeholder` class'); + assert.strictEqual( el.value, placeholder, 'should set `placeholder` text as `value`' ); + assert.strictEqual( $el.prop( 'value' ), '', 'propHooks works properly' ); + assert.strictEqual( $el.val(), '', 'valHooks works properly' ); + assert.ok( $el.hasClass( 'placeholder' ), 'should have `placeholder` class' ); - }); + } ); - QUnit.test('emulates placeholder for ', 22, function (assert) { - $('
').html(html).appendTo($('#qunit-fixture')); - testElement($('#textarea'), assert); - }); + QUnit.test( 'emulates placeholder for ', 22, function ( assert ) { + $( '
' ).html( html ).appendTo( $( '#qunit-fixture' ) ); + testElement( $( '#textarea' ), assert ); + } ); -}(jQuery)); +}( jQuery ) ); diff --git a/tests/qunit/suites/resources/jquery/jquery.tablesorter.parsers.test.js b/tests/qunit/suites/resources/jquery/jquery.tablesorter.parsers.test.js index 97a3ae12..032551d8 100644 --- a/tests/qunit/suites/resources/jquery/jquery.tablesorter.parsers.test.js +++ b/tests/qunit/suites/resources/jquery/jquery.tablesorter.parsers.test.js @@ -12,32 +12,32 @@ setup: function () { this.liveMonths = mw.language.months; mw.language.months = { - 'keys': { - 'names': ['january', 'february', 'march', 'april', 'may_long', 'june', - 'july', 'august', 'september', 'october', 'november', 'december'], - 'genitive': ['january-gen', 'february-gen', 'march-gen', 'april-gen', 'may-gen', 'june-gen', - 'july-gen', 'august-gen', 'september-gen', 'october-gen', 'november-gen', 'december-gen'], - 'abbrev': ['jan', 'feb', 'mar', 'apr', 'may', 'jun', - 'jul', 'aug', 'sep', 'oct', 'nov', 'dec'] + keys: { + names: [ 'january', 'february', 'march', 'april', 'may_long', 'june', + 'july', 'august', 'september', 'october', 'november', 'december' ], + genitive: [ 'january-gen', 'february-gen', 'march-gen', 'april-gen', 'may-gen', 'june-gen', + 'july-gen', 'august-gen', 'september-gen', 'october-gen', 'november-gen', 'december-gen' ], + abbrev: [ 'jan', 'feb', 'mar', 'apr', 'may', 'jun', + 'jul', 'aug', 'sep', 'oct', 'nov', 'dec' ] }, - 'names': ['January', 'February', 'March', 'April', 'May', 'June', - 'July', 'August', 'September', 'October', 'November', 'December'], - 'genitive': ['January', 'February', 'March', 'April', 'May', 'June', - 'July', 'August', 'September', 'October', 'November', 'December'], - 'abbrev': ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', - 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'] + names: [ 'January', 'February', 'March', 'April', 'May', 'June', + 'July', 'August', 'September', 'October', 'November', 'December' ], + genitive: [ 'January', 'February', 'March', 'April', 'May', 'June', + 'July', 'August', 'September', 'October', 'November', 'December' ], + abbrev: [ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', + 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' ] }; }, teardown: function () { mw.language.months = this.liveMonths; }, config: { - wgContentLanguage: 'en', + wgPageContentLanguage: 'en', /* default date format of the content language */ wgDefaultDateFormat: 'dmy', /* These two are important for numeric interpretations */ - wgSeparatorTransformTable: ['', ''], - wgDigitTransformTable: ['', ''] + wgSeparatorTransformTable: [ '', '' ], + wgDigitTransformTable: [ '', '' ] } } ) ); @@ -57,17 +57,17 @@ QUnit.test( msg, data.length * 2, function ( assert ) { var extractedR, extractedF, parser; - if (callback !== undefined ) { + if ( callback !== undefined ) { callback(); } parser = $.tablesorter.getParser( parserId ); $.each( data, function ( index, testcase ) { - extractedR = parser.is( testcase[0] ); - extractedF = parser.format( testcase[0] ); + extractedR = parser.is( testcase[ 0 ] ); + extractedF = parser.format( testcase[ 0 ] ); - assert.strictEqual( extractedR, testcase[1], 'Detect: ' + testcase[3] ); - assert.strictEqual( extractedF, testcase[2], 'Sortkey: ' + testcase[3] ); + assert.strictEqual( extractedR, testcase[ 1 ], 'Detect: ' + testcase[ 3 ] ); + assert.strictEqual( extractedF, testcase[ 2 ], 'Sortkey: ' + testcase[ 3 ] ); } ); } ); @@ -82,139 +82,139 @@ ipv4 = [ // Some randomly generated fake IPs - ['0.0.0.0', true, 0, 'An IP address' ], - ['255.255.255.255', true, 255255255255, 'An IP address' ], - ['45.238.27.109', true, 45238027109, 'An IP address' ], - ['1.238.27.1', true, 1238027001, 'An IP address with small numbers' ], - ['238.27.1', false, 238027001, 'A malformed IP Address' ], - ['1', false, 1, 'A super malformed IP Address' ], - ['Just text', false, 0, 'A line with just text' ], - ['45.238.27.109Postfix', false, 45238027109, 'An IP address with a connected postfix' ], - ['45.238.27.109 postfix', false, 45238027109, 'An IP address with a seperated postfix' ] + [ '0.0.0.0', true, 0, 'An IP address' ], + [ '255.255.255.255', true, 255255255255, 'An IP address' ], + [ '45.238.27.109', true, 45238027109, 'An IP address' ], + [ '1.238.27.1', true, 1238027001, 'An IP address with small numbers' ], + [ '238.27.1', false, 238027001, 'A malformed IP Address' ], + [ '1', false, 1, 'A super malformed IP Address' ], + [ 'Just text', false, 0, 'A line with just text' ], + [ '45.238.27.109Postfix', false, 45238027109, 'An IP address with a connected postfix' ], + [ '45.238.27.109 postfix', false, 45238027109, 'An IP address with a seperated postfix' ] ]; parserTest( 'IPv4', 'IPAddress', ipv4 ); simpleMDYDatesInMDY = [ - ['January 17, 2010', true, 20100117, 'Long middle endian date'], - ['Jan 17, 2010', true, 20100117, 'Short middle endian date'], - ['1/17/2010', true, 20100117, 'Numeric middle endian date'], - ['01/17/2010', true, 20100117, 'Numeric middle endian date with padding on month'], - ['01/07/2010', true, 20100107, 'Numeric middle endian date with padding on day'], - ['01/07/0010', true, 20100107, 'Numeric middle endian date with padding on year'], - ['5.12.1990', true, 19900512, 'Numeric middle endian date with . separator'] + [ 'January 17, 2010', true, 20100117, 'Long middle endian date' ], + [ 'Jan 17, 2010', true, 20100117, 'Short middle endian date' ], + [ '1/17/2010', true, 20100117, 'Numeric middle endian date' ], + [ '01/17/2010', true, 20100117, 'Numeric middle endian date with padding on month' ], + [ '01/07/2010', true, 20100107, 'Numeric middle endian date with padding on day' ], + [ '01/07/0010', true, 20100107, 'Numeric middle endian date with padding on year' ], + [ '5.12.1990', true, 19900512, 'Numeric middle endian date with . separator' ] ]; parserTest( 'MDY Dates using mdy content language', 'date', simpleMDYDatesInMDY ); simpleMDYDatesInDMY = [ - ['January 17, 2010', true, 20100117, 'Long middle endian date'], - ['Jan 17, 2010', true, 20100117, 'Short middle endian date'], - ['1/17/2010', true, 20101701, 'Numeric middle endian date'], - ['01/17/2010', true, 20101701, 'Numeric middle endian date with padding on month'], - ['01/07/2010', true, 20100701, 'Numeric middle endian date with padding on day'], - ['01/07/0010', true, 20100701, 'Numeric middle endian date with padding on year'], - ['5.12.1990', true, 19901205, 'Numeric middle endian date with . separator'] + [ 'January 17, 2010', true, 20100117, 'Long middle endian date' ], + [ 'Jan 17, 2010', true, 20100117, 'Short middle endian date' ], + [ '1/17/2010', true, 20101701, 'Numeric middle endian date' ], + [ '01/17/2010', true, 20101701, 'Numeric middle endian date with padding on month' ], + [ '01/07/2010', true, 20100701, 'Numeric middle endian date with padding on day' ], + [ '01/07/0010', true, 20100701, 'Numeric middle endian date with padding on year' ], + [ '5.12.1990', true, 19901205, 'Numeric middle endian date with . separator' ] ]; parserTest( 'MDY Dates using dmy content language', 'date', simpleMDYDatesInDMY, function () { mw.config.set( { - 'wgDefaultDateFormat': 'dmy', - 'wgContentLanguage': 'de' + wgDefaultDateFormat: 'dmy', + wgPageContentLanguage: 'de' } ); } ); oldMDYDates = [ - ['January 19, 1400 BC', false, '99999999', 'BC'], - ['January 19, 1400BC', false, '99999999', 'Connected BC'], - ['January, 19 1400 B.C.', false, '99999999', 'B.C.'], - ['January 19, 1400 AD', false, '99999999', 'AD'], - ['January, 19 10', true, 20100119, 'AD'], - ['January, 19 1', false, '99999999', 'AD'] + [ 'January 19, 1400 BC', false, '99999999', 'BC' ], + [ 'January 19, 1400BC', false, '99999999', 'Connected BC' ], + [ 'January, 19 1400 B.C.', false, '99999999', 'B.C.' ], + [ 'January 19, 1400 AD', false, '99999999', 'AD' ], + [ 'January, 19 10', true, 20100119, 'AD' ], + [ 'January, 19 1', false, '99999999', 'AD' ] ]; parserTest( 'Very old MDY dates', 'date', oldMDYDates ); complexMDYDates = [ - ['January, 19 2010', true, 20100119, 'Comma after month'], - ['January 19, 2010', true, 20100119, 'Comma after day'], - ['January/19/2010', true, 20100119, 'Forward slash separator'], - ['04 22 1991', true, 19910422, 'Month with 0 padding'], - ['April 21 1991', true, 19910421, 'Space separation'], - ['04 22 1991', true, 19910422, 'Month with 0 padding'], - ['December 12 \'10', true, 20101212, ''], - ['Dec 12 \'10', true, 20101212, ''], - ['Dec. 12 \'10', true, 20101212, ''] + [ 'January, 19 2010', true, 20100119, 'Comma after month' ], + [ 'January 19, 2010', true, 20100119, 'Comma after day' ], + [ 'January/19/2010', true, 20100119, 'Forward slash separator' ], + [ '04 22 1991', true, 19910422, 'Month with 0 padding' ], + [ 'April 21 1991', true, 19910421, 'Space separation' ], + [ '04 22 1991', true, 19910422, 'Month with 0 padding' ], + [ 'December 12 \'10', true, 20101212, '' ], + [ 'Dec 12 \'10', true, 20101212, '' ], + [ 'Dec. 12 \'10', true, 20101212, '' ] ]; parserTest( 'MDY Dates', 'date', complexMDYDates ); clobberedDates = [ - ['January, 19 2010 - January, 20 2010', false, '99999999', 'Date range with hyphen'], - ['January, 19 2010 — January, 20 2010', false, '99999999', 'Date range with mdash'], - ['prefixJanuary, 19 2010', false, '99999999', 'Connected prefix'], - ['prefix January, 19 2010', false, '99999999', 'Prefix'], - ['December 12 2010postfix', false, '99999999', 'ConnectedPostfix'], - ['December 12 2010 postfix', false, '99999999', 'Postfix'], - ['A simple text', false, '99999999', 'Plain text in date sort'], - ['04l22l1991', false, '99999999', 'l char as separator'], - ['January\\19\\2010', false, '99999999', 'backslash as date separator'] + [ 'January, 19 2010 - January, 20 2010', false, '99999999', 'Date range with hyphen' ], + [ 'January, 19 2010 — January, 20 2010', false, '99999999', 'Date range with mdash' ], + [ 'prefixJanuary, 19 2010', false, '99999999', 'Connected prefix' ], + [ 'prefix January, 19 2010', false, '99999999', 'Prefix' ], + [ 'December 12 2010postfix', false, '99999999', 'ConnectedPostfix' ], + [ 'December 12 2010 postfix', false, '99999999', 'Postfix' ], + [ 'A simple text', false, '99999999', 'Plain text in date sort' ], + [ '04l22l1991', false, '99999999', 'l char as separator' ], + [ 'January\\19\\2010', false, '99999999', 'backslash as date separator' ] ]; parserTest( 'Clobbered Dates', 'date', clobberedDates ); MYDates = [ - ['December 2010', false, '99999999', 'Plain month year'], - ['Dec 2010', false, '99999999', 'Abreviated month year'], - ['12 2010', false, '99999999', 'Numeric month year'] + [ 'December 2010', false, '99999999', 'Plain month year' ], + [ 'Dec 2010', false, '99999999', 'Abreviated month year' ], + [ '12 2010', false, '99999999', 'Numeric month year' ] ]; parserTest( 'MY Dates', 'date', MYDates ); YDates = [ - ['2010', false, '99999999', 'Plain 4-digit year'], - ['876', false, '99999999', '3-digit year'], - ['76', false, '99999999', '2-digit year'], - ['\'76', false, '99999999', '2-digit millenium bug year'], - ['2010 BC', false, '99999999', '4-digit year BC'] + [ '2010', false, '99999999', 'Plain 4-digit year' ], + [ '876', false, '99999999', '3-digit year' ], + [ '76', false, '99999999', '2-digit year' ], + [ '\'76', false, '99999999', '2-digit millenium bug year' ], + [ '2010 BC', false, '99999999', '4-digit year BC' ] ]; parserTest( 'Y Dates', 'date', YDates ); currencyData = [ - ['1.02 $', true, 1.02, ''], - ['$ 3.00', true, 3, ''], - ['€ 2,99', true, 299, ''], - ['$ 1.00', true, 1, ''], - ['$3.50', true, 3.50, ''], - ['$ 1.50', true, 1.50, ''], - ['€ 0.99', true, 0.99, ''], - ['$ 299.99', true, 299.99, ''], - ['$ 2,299.99', true, 2299.99, ''], - ['$ 2,989', true, 2989, ''], - ['$ 2 299.99', true, 2299.99, ''], - ['$ 2 989', true, 2989, ''], - ['$ 2.989', true, 2.989, ''] + [ '1.02 $', true, 1.02, '' ], + [ '$ 3.00', true, 3, '' ], + [ '€ 2,99', true, 299, '' ], + [ '$ 1.00', true, 1, '' ], + [ '$3.50', true, 3.50, '' ], + [ '$ 1.50', true, 1.50, '' ], + [ '€ 0.99', true, 0.99, '' ], + [ '$ 299.99', true, 299.99, '' ], + [ '$ 2,299.99', true, 2299.99, '' ], + [ '$ 2,989', true, 2989, '' ], + [ '$ 2 299.99', true, 2299.99, '' ], + [ '$ 2 989', true, 2989, '' ], + [ '$ 2.989', true, 2.989, '' ] ]; parserTest( 'Currency', 'currency', currencyData ); transformedCurrencyData = [ - ['1.02 $', true, 102, ''], - ['$ 3.00', true, 300, ''], - ['€ 2,99', true, 2.99, ''], - ['$ 1.00', true, 100, ''], - ['$3.50', true, 350, ''], - ['$ 1.50', true, 150, ''], - ['€ 0.99', true, 99, ''], - ['$ 299.99', true, 29999, ''], - ['$ 2\'299,99', true, 2299.99, ''], - ['$ 2,989', true, 2.989, ''], - ['$ 2 299.99', true, 229999, ''], - ['2 989 $', true, 2989, ''], - ['299.99 $', true, 29999, ''], - ['2\'299,99 $', true, 2299.99, ''], - ['2,989 $', true, 2.989, ''], - ['2 299.99 $', true, 229999, ''], - ['2 989 $', true, 2989, ''] + [ '1.02 $', true, 102, '' ], + [ '$ 3.00', true, 300, '' ], + [ '€ 2,99', true, 2.99, '' ], + [ '$ 1.00', true, 100, '' ], + [ '$3.50', true, 350, '' ], + [ '$ 1.50', true, 150, '' ], + [ '€ 0.99', true, 99, '' ], + [ '$ 299.99', true, 29999, '' ], + [ '$ 2\'299,99', true, 2299.99, '' ], + [ '$ 2,989', true, 2.989, '' ], + [ '$ 2 299.99', true, 229999, '' ], + [ '2 989 $', true, 2989, '' ], + [ '299.99 $', true, 29999, '' ], + [ '2\'299,99 $', true, 2299.99, '' ], + [ '2,989 $', true, 2.989, '' ], + [ '2 299.99 $', true, 229999, '' ], + [ '2 989 $', true, 2989, '' ] ]; parserTest( 'Currency with european separators', 'currency', transformedCurrencyData, function () { mw.config.set( { // We expect 22'234.444,22 // Map from ascii separators => localized separators - wgSeparatorTransformTable: [', . ,', '\' , .'], - wgDigitTransformTable: ['', ''] + wgSeparatorTransformTable: [ ', . ,', '\' , .' ], + wgDigitTransformTable: [ '', '' ] } ); } ); diff --git a/tests/qunit/suites/resources/jquery/jquery.tablesorter.test.js b/tests/qunit/suites/resources/jquery/jquery.tablesorter.test.js index f63aa27a..d4d0ce1e 100644 --- a/tests/qunit/suites/resources/jquery/jquery.tablesorter.test.js +++ b/tests/qunit/suites/resources/jquery/jquery.tablesorter.test.js @@ -1,5 +1,19 @@ ( function ( $, mw ) { - var header, + var header = [ 'Planet', 'Radius (km)' ], + + // Data set "planets" + mercury = [ 'Mercury', '2439.7' ], + venus = [ 'Venus', '6051.8' ], + earth = [ 'Earth', '6371.0' ], + mars = [ 'Mars', '3390.0' ], + jupiter = [ 'Jupiter', '69911' ], + saturn = [ 'Saturn', '58232' ], + planets = [ mercury, venus, earth, mars, jupiter, saturn ], + planetsAscName = [ earth, jupiter, mars, mercury, saturn, venus ], + planetsAscRadius = [ mercury, mars, venus, earth, saturn, jupiter ], + planetsRowspan, + planetsRowspanII, + planetsAscNameLegacy, // Data set "simple" a1 = [ 'A', '1' ], @@ -8,11 +22,12 @@ b1 = [ 'B', '1' ], b2 = [ 'B', '2' ], b3 = [ 'B', '3' ], - simple = [a2, b3, a1, a3, b2, b1], - simpleAsc = [a1, a2, a3, b1, b2, b3], - simpleDescasc = [b1, b2, b3, a1, a2, a3], + simple = [ a2, b3, a1, a3, b2, b1 ], + simpleAsc = [ a1, a2, a3, b1, b2, b3 ], + simpleDescasc = [ b1, b2, b3, a1, a2, a3 ], // Data set "colspan" + header4 = [ 'column1a', 'column1b', 'column1c', 'column2' ], aaa1 = [ 'A', 'A', 'A', '1' ], aab5 = [ 'A', 'A', 'B', '5' ], abc3 = [ 'A', 'B', 'C', '3' ], @@ -20,159 +35,145 @@ caa4 = [ 'C', 'A', 'A', '4' ], colspanInitial = [ aab5, aaa1, abc3, bbc2, caa4 ], - // Data set "planets" - mercury = [ 'Mercury', '2439.7' ], - venus = [ 'Venus', '6051.8' ], - earth = [ 'Earth', '6371.0' ], - mars = [ 'Mars', '3390.0' ], - jupiter = [ 'Jupiter', '69911' ], - saturn = [ 'Saturn', '58232' ], - planets = [mercury, venus, earth, mars, jupiter, saturn], - planetsAscName = [earth, jupiter, mars, mercury, saturn, venus], - planetsAscRadius = [mercury, mars, venus, earth, saturn, jupiter], - planetsRowspan, - planetsRowspanII, - planetsAscNameLegacy, - // Data set "ipv4" ipv4 = [ // Some randomly generated fake IPs - ['45.238.27.109'], - ['44.172.9.22'], - ['247.240.82.209'], - ['204.204.132.158'], - ['170.38.91.162'], - ['197.219.164.9'], - ['45.68.154.72'], - ['182.195.149.80'] + [ '45.238.27.109' ], + [ '44.172.9.22' ], + [ '247.240.82.209' ], + [ '204.204.132.158' ], + [ '170.38.91.162' ], + [ '197.219.164.9' ], + [ '45.68.154.72' ], + [ '182.195.149.80' ] ], ipv4Sorted = [ // Sort order should go octet by octet - ['44.172.9.22'], - ['45.68.154.72'], - ['45.238.27.109'], - ['170.38.91.162'], - ['182.195.149.80'], - ['197.219.164.9'], - ['204.204.132.158'], - ['247.240.82.209'] + [ '44.172.9.22' ], + [ '45.68.154.72' ], + [ '45.238.27.109' ], + [ '170.38.91.162' ], + [ '182.195.149.80' ], + [ '197.219.164.9' ], + [ '204.204.132.158' ], + [ '247.240.82.209' ] ], // Data set "umlaut" umlautWords = [ - ['Günther'], - ['Peter'], - ['Björn'], - ['Bjorn'], - ['Apfel'], - ['Äpfel'], - ['Strasse'], - ['Sträßschen'] + [ 'Günther' ], + [ 'Peter' ], + [ 'Björn' ], + [ 'Bjorn' ], + [ 'Apfel' ], + [ 'Äpfel' ], + [ 'Strasse' ], + [ 'Sträßschen' ] ], umlautWordsSorted = [ - ['Äpfel'], - ['Apfel'], - ['Björn'], - ['Bjorn'], - ['Günther'], - ['Peter'], - ['Sträßschen'], - ['Strasse'] + [ 'Äpfel' ], + [ 'Apfel' ], + [ 'Björn' ], + [ 'Bjorn' ], + [ 'Günther' ], + [ 'Peter' ], + [ 'Sträßschen' ], + [ 'Strasse' ] ], complexMDYDates = [ - ['January, 19 2010'], - ['April 21 1991'], - ['04 22 1991'], - ['5.12.1990'], - ['December 12 \'10'] + [ 'January, 19 2010' ], + [ 'April 21 1991' ], + [ '04 22 1991' ], + [ '5.12.1990' ], + [ 'December 12 \'10' ] ], complexMDYSorted = [ - ['5.12.1990'], - ['April 21 1991'], - ['04 22 1991'], - ['January, 19 2010'], - ['December 12 \'10'] + [ '5.12.1990' ], + [ 'April 21 1991' ], + [ '04 22 1991' ], + [ 'January, 19 2010' ], + [ 'December 12 \'10' ] ], currencyUnsorted = [ - ['1.02 $'], - ['$ 3.00'], - ['€ 2,99'], - ['$ 1.00'], - ['$3.50'], - ['$ 1.50'], - ['€ 0.99'] + [ '1.02 $' ], + [ '$ 3.00' ], + [ '€ 2,99' ], + [ '$ 1.00' ], + [ '$3.50' ], + [ '$ 1.50' ], + [ '€ 0.99' ] ], currencySorted = [ - ['€ 0.99'], - ['$ 1.00'], - ['1.02 $'], - ['$ 1.50'], - ['$ 3.00'], - ['$3.50'], + [ '€ 0.99' ], + [ '$ 1.00' ], + [ '1.02 $' ], + [ '$ 1.50' ], + [ '$ 3.00' ], + [ '$3.50' ], // Comma's sort after dots // Not intentional but test to detect changes - ['€ 2,99'] + [ '€ 2,99' ] ], numbers = [ - [ '12' ], - [ '7' ], - [ '13,000'], - [ '9' ], - [ '14' ], - [ '8.0' ] + [ '12' ], + [ '7' ], + [ '13,000' ], + [ '9' ], + [ '14' ], + [ '8.0' ] ], numbersAsc = [ - [ '7' ], - [ '8.0' ], - [ '9' ], - [ '12' ], - [ '14' ], - [ '13,000'] + [ '7' ], + [ '8.0' ], + [ '9' ], + [ '12' ], + [ '14' ], + [ '13,000' ] ], correctDateSorting1 = [ - ['01 January 2010'], - ['05 February 2010'], - ['16 January 2010'] + [ '01 January 2010' ], + [ '05 February 2010' ], + [ '16 January 2010' ] ], correctDateSortingSorted1 = [ - ['01 January 2010'], - ['16 January 2010'], - ['05 February 2010'] + [ '01 January 2010' ], + [ '16 January 2010' ], + [ '05 February 2010' ] ], correctDateSorting2 = [ - ['January 01 2010'], - ['February 05 2010'], - ['January 16 2010'] + [ 'January 01 2010' ], + [ 'February 05 2010' ], + [ 'January 16 2010' ] ], correctDateSortingSorted2 = [ - ['January 01 2010'], - ['January 16 2010'], - ['February 05 2010'] + [ 'January 01 2010' ], + [ 'January 16 2010' ], + [ 'February 05 2010' ] ]; QUnit.module( 'jquery.tablesorter', QUnit.newMwEnvironment( { setup: function () { this.liveMonths = mw.language.months; mw.language.months = { - 'keys': { - 'names': ['january', 'february', 'march', 'april', 'may_long', 'june', - 'july', 'august', 'september', 'october', 'november', 'december'], - 'genitive': ['january-gen', 'february-gen', 'march-gen', 'april-gen', 'may-gen', 'june-gen', - 'july-gen', 'august-gen', 'september-gen', 'october-gen', 'november-gen', 'december-gen'], - 'abbrev': ['jan', 'feb', 'mar', 'apr', 'may', 'jun', - 'jul', 'aug', 'sep', 'oct', 'nov', 'dec'] + keys: { + names: [ 'january', 'february', 'march', 'april', 'may_long', 'june', + 'july', 'august', 'september', 'october', 'november', 'december' ], + genitive: [ 'january-gen', 'february-gen', 'march-gen', 'april-gen', 'may-gen', 'june-gen', + 'july-gen', 'august-gen', 'september-gen', 'october-gen', 'november-gen', 'december-gen' ], + abbrev: [ 'jan', 'feb', 'mar', 'apr', 'may', 'jun', + 'jul', 'aug', 'sep', 'oct', 'nov', 'dec' ] }, - 'names': ['January', 'February', 'March', 'April', 'May', 'June', - 'July', 'August', 'September', 'October', 'November', 'December'], - 'genitive': ['January', 'February', 'March', 'April', 'May', 'June', - 'July', 'August', 'September', 'October', 'November', 'December'], - 'abbrev': ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', - 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'] + names: [ 'January', 'February', 'March', 'April', 'May', 'June', + 'July', 'August', 'September', 'October', 'November', 'December' ], + genitive: [ 'January', 'February', 'March', 'April', 'May', 'June', + 'July', 'August', 'September', 'October', 'November', 'December' ], + abbrev: [ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', + 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' ] }; }, teardown: function () { @@ -180,9 +181,9 @@ }, config: { wgDefaultDateFormat: 'dmy', - wgSeparatorTransformTable: ['', ''], - wgDigitTransformTable: ['', ''], - wgContentLanguage: 'en' + wgSeparatorTransformTable: [ '', '' ], + wgDigitTransformTable: [ '', '' ], + wgPageContentLanguage: 'en' } } ) ); @@ -192,7 +193,7 @@ * * @param {String[]} header * @param {String[][]} data - * @return jQuery + * @return {jQuery} */ function tableCreate( header, data ) { var i, @@ -210,7 +211,7 @@ for ( i = 0; i < data.length; i++ ) { /*jshint loopfunc: true */ $tr = $( '' ); - $.each( data[i], function ( j, str ) { + $.each( data[ i ], function ( j, str ) { var $td = $( '' ); $td.text( str ).appendTo( $tr ); } ); @@ -223,7 +224,7 @@ * Extract text from table. * * @param {jQuery} $table - * @return String[][] + * @return {String[][]} */ function tableExtract( $table ) { var data = []; @@ -302,7 +303,6 @@ } // Sample data set using planets named and their radius - header = [ 'Planet', 'Radius (km)']; tableTest( 'Basic planet table: sorting initially - ascending by name', @@ -388,9 +388,6 @@ $table.find( '.headerSort:eq(1)' ).click().click(); } ); - - header = [ 'column1', 'column2' ]; - tableTest( 'Sorting multiple columns by passing sort list', header, @@ -466,7 +463,7 @@ // Pretend to click while pressing the multi-sort key var event = $.Event( 'click' ); - event[$table.data( 'tablesorter' ).config.sortMultiSortKey] = true; + event[ $table.data( 'tablesorter' ).config.sortMultiSortKey ] = true; $table.find( '.headerSort:eq(1)' ).trigger( event ); } ); @@ -500,10 +497,9 @@ } ); // Sorting with colspans - header = [ 'column1a', 'column1b', 'column1c', 'column2' ]; tableTest( 'Sorting with colspanned headers: spanned column', - header, + header4, colspanInitial, [ aaa1, aab5, abc3, bbc2, caa4 ], function ( $table ) { @@ -516,7 +512,7 @@ } ); tableTest( 'Sorting with colspanned headers: sort spanned column twice', - header, + header4, colspanInitial, [ caa4, bbc2, abc3, aab5, aaa1 ], function ( $table ) { @@ -530,7 +526,7 @@ } ); tableTest( 'Sorting with colspanned headers: subsequent column', - header, + header4, colspanInitial, [ aaa1, bbc2, abc3, caa4, aab5 ], function ( $table ) { @@ -543,7 +539,7 @@ } ); tableTest( 'Sorting with colspanned headers: sort subsequent column twice', - header, + header4, colspanInitial, [ aab5, caa4, abc3, bbc2, aaa1 ], function ( $table ) { @@ -557,42 +553,60 @@ } ); - tableTest( - 'Basic planet table: one unsortable column', - header, - planets, - planets, - function ( $table ) { - $table.find( 'tr:eq(0) > th:eq(0)' ).addClass( 'unsortable' ); + QUnit.test( 'Basic planet table: one unsortable column', 3, function ( assert ) { + var $table = tableCreate( header, planets ), + $cell; + $table.find( 'tr:eq(0) > th:eq(0)' ).addClass( 'unsortable' ); - $table.tablesorter(); - $table.find( 'tr:eq(0) > th:eq(0)' ).click(); - } - ); + $table.tablesorter(); + $table.find( 'tr:eq(0) > th:eq(0)' ).click(); + + assert.deepEqual( + tableExtract( $table ), + planets, + 'table not sorted' + ); + + $cell = $table.find( 'tr:eq(0) > th:eq(0)' ); + $table.find( 'tr:eq(0) > th:eq(1)' ).click(); + + assert.equal( + $cell.hasClass( 'headerSortUp' ) || $cell.hasClass( 'headerSortDown' ), + false, + 'after sort: no class headerSortUp or headerSortDown' + ); + + assert.equal( + $cell.attr( 'title' ), + undefined, + 'after sort: no title tag added' + ); + + } ); // Regression tests! tableTest( 'Bug 28775: German-style (dmy) short numeric dates', - ['Date'], + [ 'Date' ], [ // German-style dates are day-month-year - ['11.11.2011'], - ['01.11.2011'], - ['02.10.2011'], - ['03.08.2011'], - ['09.11.2011'] + [ '11.11.2011' ], + [ '01.11.2011' ], + [ '02.10.2011' ], + [ '03.08.2011' ], + [ '09.11.2011' ] ], [ // Sorted by ascending date - ['03.08.2011'], - ['02.10.2011'], - ['01.11.2011'], - ['09.11.2011'], - ['11.11.2011'] + [ '03.08.2011' ], + [ '02.10.2011' ], + [ '01.11.2011' ], + [ '09.11.2011' ], + [ '11.11.2011' ] ], function ( $table ) { mw.config.set( 'wgDefaultDateFormat', 'dmy' ); - mw.config.set( 'wgContentLanguage', 'de' ); + mw.config.set( 'wgPageContentLanguage', 'de' ); $table.tablesorter(); $table.find( '.headerSort:eq(0)' ).click(); @@ -601,22 +615,22 @@ tableTest( 'Bug 28775: American-style (mdy) short numeric dates', - ['Date'], + [ 'Date' ], [ // American-style dates are month-day-year - ['11.11.2011'], - ['01.11.2011'], - ['02.10.2011'], - ['03.08.2011'], - ['09.11.2011'] + [ '11.11.2011' ], + [ '01.11.2011' ], + [ '02.10.2011' ], + [ '03.08.2011' ], + [ '09.11.2011' ] ], [ // Sorted by ascending date - ['01.11.2011'], - ['02.10.2011'], - ['03.08.2011'], - ['09.11.2011'], - ['11.11.2011'] + [ '01.11.2011' ], + [ '02.10.2011' ], + [ '03.08.2011' ], + [ '09.11.2011' ], + [ '11.11.2011' ] ], function ( $table ) { mw.config.set( 'wgDefaultDateFormat', 'mdy' ); @@ -628,7 +642,7 @@ tableTest( 'Bug 17141: IPv4 address sorting', - ['IP'], + [ 'IP' ], ipv4, ipv4Sorted, function ( $table ) { @@ -638,7 +652,7 @@ ); tableTest( 'Bug 17141: IPv4 address sorting (reverse)', - ['IP'], + [ 'IP' ], ipv4, reversed( ipv4Sorted ), function ( $table ) { @@ -649,15 +663,15 @@ tableTest( 'Accented Characters with custom collation', - ['Name'], + [ 'Name' ], umlautWords, umlautWordsSorted, function ( $table ) { mw.config.set( 'tableSorterCollation', { - 'ä': 'ae', - 'ö': 'oe', - 'ß': 'ss', - 'ü': 'ue' + ä: 'ae', + ö: 'oe', + ß: 'ss', + ü: 'ue' } ); $table.tablesorter(); @@ -749,7 +763,7 @@ tableTest( 'Complex date parsing I', - ['date'], + [ 'date' ], complexMDYDates, complexMDYSorted, function ( $table ) { @@ -762,7 +776,7 @@ tableTest( 'Currency parsing I', - ['currency'], + [ 'currency' ], currencyUnsorted, currencySorted, function ( $table ) { @@ -772,7 +786,7 @@ ); planetsAscNameLegacy = planetsAscName.slice( 0 ); - planetsAscNameLegacy[4] = planetsAscNameLegacy[5]; + planetsAscNameLegacy[ 4 ] = planetsAscNameLegacy[ 5 ]; planetsAscNameLegacy.pop(); tableTest( @@ -801,7 +815,7 @@ $table.find( '.headerSort:eq(0)' ).click(); assert.equal( - $table.data( 'tablesorter' ).config.parsers[0].id, + $table.data( 'tablesorter' ).config.parsers[ 0 ].id, 'number', 'Correctly detected column content skipping sortbottom' ); @@ -989,7 +1003,7 @@ } ); tableTest( 'bug 8115: sort numbers with commas (ascending)', - ['Numbers'], numbers, numbersAsc, + [ 'Numbers' ], numbers, numbersAsc, function ( $table ) { $table.tablesorter(); $table.find( '.headerSort:eq(0)' ).click(); @@ -997,7 +1011,7 @@ ); tableTest( 'bug 8115: sort numbers with commas (descending)', - ['Numbers'], numbers, reversed( numbersAsc ), + [ 'Numbers' ], numbers, reversed( numbersAsc ), function ( $table ) { $table.tablesorter(); $table.find( '.headerSort:eq(0)' ).click().click(); @@ -1032,7 +1046,7 @@ tableTest( 'Correct date sorting I', - ['date'], + [ 'date' ], correctDateSorting1, correctDateSortingSorted1, function ( $table ) { @@ -1045,7 +1059,7 @@ tableTest( 'Correct date sorting II', - ['date'], + [ 'date' ], correctDateSorting2, correctDateSortingSorted2, function ( $table ) { @@ -1238,7 +1252,7 @@ '' ); $table.tablesorter(); - assert.equal( $table.find( 'tr:eq(1) th:eq(1)').data('headerIndex'), + assert.equal( $table.find( 'tr:eq(1) th:eq(1)' ).data( 'headerIndex' ), 2, 'Incorrect index of sort header' ); @@ -1262,14 +1276,14 @@ tableTestHTML( 'Rowspan exploding with colspanned cells (2)', '' + - '' + + '' + '' + - '' + - '' + + '' + + '' + '
nfoobarbazquux
nfoobarbazn2
1foobarbazquux
2foobarquux
1foobarbaz2
2foobar1
', [ - [ '1', 'foo', 'bar', 'baz', 'quux' ], - [ '2', 'foobar', 'baz', 'quux' ] + [ '2', 'foobar', 'baz', '1' ], + [ '1', 'foo', 'bar', 'baz', '2' ] ] ); @@ -1345,4 +1359,39 @@ ] ); + QUnit.test( 'bug 105731 - incomplete rows in table body', 3, function ( assert ) { + var $table, parsers; + $table = $( + '' + + '' + + '' + + '' + + '
AB
3
12
' + ); + $table.tablesorter(); + $table.find( '.headerSort:eq(0)' ).click(); + // now the first row have 2 columns + $table.find( '.headerSort:eq(1)' ).click(); + + parsers = $table.data( 'tablesorter' ).config.parsers; + + assert.equal( + parsers.length, + 2, + 'detectParserForColumn() detect 2 parsers' + ); + + assert.equal( + parsers[ 1 ].id, + 'number', + 'detectParserForColumn() detect parser.id "number" for second column' + ); + + assert.equal( + parsers[ 1 ].format( $table.find( 'tbody > tr > td:eq(1)' ).text() ), + 0, + 'empty cell is sorted as number 0' + ); + + } ); }( jQuery, mediaWiki ) ); diff --git a/tests/qunit/suites/resources/jquery/jquery.textSelection.test.js b/tests/qunit/suites/resources/jquery/jquery.textSelection.test.js index 56b0fa92..2e6f05ed 100644 --- a/tests/qunit/suites/resources/jquery/jquery.textSelection.test.js +++ b/tests/qunit/suites/resources/jquery/jquery.textSelection.test.js @@ -5,13 +5,13 @@ /** * Test factory for $.fn.textSelection( 'encapsulateText' ) * - * @param options {object} associative array containing: - * description {string} - * input {string} - * output {string} - * start {int} starting char for selection - * end {int} ending char for selection - * params {object} add'l parameters for $().textSelection( 'encapsulateText' ) + * @param {Object} options Associative configuration array + * @param {string} options.description Description + * @param {string} options.input Input + * @param {string} options.output Output + * @param {int} options.start Starting char for selection + * @param {int} options.end Ending char for selection + * @param {object} options.params Additional parameters for $().textSelection( 'encapsulateText' ) */ function encapsulateTest( options ) { var opt = $.extend( { @@ -237,23 +237,22 @@ } pos = $textarea.textSelection( 'getCaretPosition', { startAndEnd: true } ); - among( pos[0], options.start, 'Caret start should be where we set it.' ); - among( pos[1], options.end, 'Caret end should be where we set it.' ); + among( pos[ 0 ], options.start, 'Caret start should be where we set it.' ); + among( pos[ 1 ], options.end, 'Caret end should be where we set it.' ); } ); } caretSample = 'Some big text that we like to work with. Nothing fancy... you know what I mean?'; -/* - // @broken: Disabled per bug 34820 + /* @broken: Disabled per bug 34820 caretTest({ - description: 'getCaretPosition with original/empty selection - bug 31847 with IE 6/7/8', - text: caretSample, - start: [0, caretSample.length], // Opera and Firefox (prior to FF 6.0) default caret to the end of the box (caretSample.length) - end: [0, caretSample.length], // Other browsers default it to the beginning (0), so check both. - mode: 'get' + description: 'getCaretPosition with original/empty selection - bug 31847 with IE 6/7/8', + text: caretSample, + start: [0, caretSample.length], // Opera and Firefox (prior to FF 6.0) default caret to the end of the box (caretSample.length) + end: [0, caretSample.length], // Other browsers default it to the beginning (0), so check both. + mode: 'get' }); -*/ + */ caretTest( { description: 'set/getCaretPosition with forced empty selection', diff --git a/tests/qunit/suites/resources/mediawiki.api/mediawiki.ForeignApi.test.js b/tests/qunit/suites/resources/mediawiki.api/mediawiki.ForeignApi.test.js new file mode 100644 index 00000000..9d0fdf54 --- /dev/null +++ b/tests/qunit/suites/resources/mediawiki.api/mediawiki.ForeignApi.test.js @@ -0,0 +1,39 @@ +( function ( mw ) { + QUnit.module( 'mediawiki.ForeignApi', QUnit.newMwEnvironment( { + setup: function () { + this.server = this.sandbox.useFakeServer(); + this.server.respondImmediately = true; + this.clock = this.sandbox.useFakeTimers(); + }, + teardown: function () { + // https://github.com/jquery/jquery/issues/2453 + this.clock.tick(); + } + } ) ); + + QUnit.test( 'origin is included in GET requests', function ( assert ) { + QUnit.expect( 1 ); + var api = new mw.ForeignApi( '//localhost:4242/w/api.php' ); + + this.server.respond( function ( request ) { + assert.ok( request.url.match( /origin=/ ), 'origin is included in GET requests' ); + request.respond( 200, { 'Content-Type': 'application/json' }, '[]' ); + } ); + + api.get( {} ); + } ); + + QUnit.test( 'origin is included in POST requests', function ( assert ) { + QUnit.expect( 2 ); + var api = new mw.ForeignApi( '//localhost:4242/w/api.php' ); + + this.server.respond( function ( request ) { + assert.ok( request.requestBody.match( /origin=/ ), 'origin is included in POST request body' ); + assert.ok( request.url.match( /origin=/ ), 'origin is included in POST request URL, too' ); + request.respond( 200, { 'Content-Type': 'application/json' }, '[]' ); + } ); + + api.post( {} ); + } ); + +}( mediaWiki ) ); diff --git a/tests/qunit/suites/resources/mediawiki.api/mediawiki.api.test.js b/tests/qunit/suites/resources/mediawiki.api/mediawiki.api.test.js index b89526fb..56a346fb 100644 --- a/tests/qunit/suites/resources/mediawiki.api/mediawiki.api.test.js +++ b/tests/qunit/suites/resources/mediawiki.api/mediawiki.api.test.js @@ -1,15 +1,40 @@ -( function ( mw ) { +( function ( mw, $ ) { QUnit.module( 'mediawiki.api', QUnit.newMwEnvironment( { setup: function () { this.server = this.sandbox.useFakeServer(); + this.server.respondImmediately = true; + this.clock = this.sandbox.useFakeTimers(); + }, + teardown: function () { + // https://github.com/jquery/jquery/issues/2453 + this.clock.tick(); } } ) ); + function sequence( responses ) { + var i = 0; + return function ( request ) { + var response = responses[ i ]; + if ( response ) { + i++; + request.respond.apply( request, response ); + } + }; + } + + function sequenceBodies( status, headers, bodies ) { + jQuery.each( bodies, function ( i, body ) { + bodies[ i ] = [ status, headers, body ]; + } ); + return sequence( bodies ); + } + QUnit.test( 'Basic functionality', function ( assert ) { QUnit.expect( 2 ); - var api = new mw.Api(); + this.server.respond( [ 200, { 'Content-Type': 'application/json' }, '[]' ] ); + api.get( {} ) .done( function ( data ) { assert.deepEqual( data, [], 'If request succeeds without errors, resolve deferred' ); @@ -19,36 +44,26 @@ .done( function ( data ) { assert.deepEqual( data, [], 'Simple POST request' ); } ); - - this.server.respond( function ( request ) { - request.respond( 200, { 'Content-Type': 'application/json' }, '[]' ); - } ); } ); QUnit.test( 'API error', function ( assert ) { QUnit.expect( 1 ); - var api = new mw.Api(); + this.server.respond( [ 200, { 'Content-Type': 'application/json' }, + '{ "error": { "code": "unknown_action" } }' + ] ); + api.get( { action: 'doesntexist' } ) .fail( function ( errorCode ) { assert.equal( errorCode, 'unknown_action', 'API error should reject the deferred' ); } ); - - this.server.respond( function ( request ) { - request.respond( 200, { 'Content-Type': 'application/json' }, - '{ "error": { "code": "unknown_action" } }' - ); - } ); } ); QUnit.test( 'FormData support', function ( assert ) { QUnit.expect( 2 ); - var api = new mw.Api(); - api.post( { action: 'test' }, { contentType: 'multipart/form-data' } ); - this.server.respond( function ( request ) { if ( window.FormData ) { assert.ok( !request.url.match( /action=/ ), 'Request has no query string' ); @@ -59,27 +74,41 @@ } request.respond( 200, { 'Content-Type': 'application/json' }, '[]' ); } ); + + api.post( { action: 'test' }, { contentType: 'multipart/form-data' } ); } ); QUnit.test( 'Converting arrays to pipe-separated', function ( assert ) { QUnit.expect( 1 ); - var api = new mw.Api(); - api.get( { test: [ 'foo', 'bar', 'baz' ] } ); this.server.respond( function ( request ) { assert.ok( request.url.match( /test=foo%7Cbar%7Cbaz/ ), 'Pipe-separated value was submitted' ); request.respond( 200, { 'Content-Type': 'application/json' }, '[]' ); } ); + + api.get( { test: [ 'foo', 'bar', 'baz' ] } ); } ); - QUnit.test( 'getToken( pre-populated )', function ( assert ) { + QUnit.test( 'Omitting false booleans', function ( assert ) { QUnit.expect( 2 ); + var api = new mw.Api(); + + this.server.respond( function ( request ) { + assert.ok( !request.url.match( /foo/ ), 'foo query parameter is not present' ); + assert.ok( request.url.match( /bar=true/ ), 'bar query parameter is present with value true' ); + request.respond( 200, { 'Content-Type': 'application/json' }, '[]' ); + } ); + api.get( { foo: false, bar: true } ); + } ); + + QUnit.test( 'getToken() - cached', function ( assert ) { + QUnit.expect( 2 ); var api = new mw.Api(); // Get editToken for local wiki, this should not make - // a request as it should be retrieved from user.tokens. + // a request as it should be retrieved from mw.user.tokens. api.getToken( 'edit' ) .done( function ( token ) { assert.ok( token.length, 'Got a token' ); @@ -91,112 +120,128 @@ assert.equal( this.server.requests.length, 0, 'Requests made' ); } ); - QUnit.test( 'getToken()', function ( assert ) { - QUnit.expect( 5 ); + QUnit.test( 'getToken() - uncached', function ( assert ) { + QUnit.expect( 3 ); + var api = new mw.Api(); - var test = this, - api = new mw.Api(); + this.server.respondWith( /type=testuncached/, [ 200, { 'Content-Type': 'application/json' }, + '{ "tokens": { "testuncachedtoken": "good" } }' + ] ); // Get a token of a type that isn't prepopulated by user.tokens. // Could use "block" or "delete" here, but those could in theory // be added to user.tokens, use a fake one instead. - api.getToken( 'testaction' ) + api.getToken( 'testuncached' ) .done( function ( token ) { - assert.ok( token.length, 'Got testaction token' ); + assert.equal( token, 'good', 'The token' ); } ) .fail( function ( err ) { assert.equal( err, '', 'API error' ); } ); - api.getToken( 'testaction' ) + + api.getToken( 'testuncached' ) .done( function ( token ) { - assert.ok( token.length, 'Got testaction token (cached)' ); + assert.equal( token, 'good', 'The cached token' ); } ) .fail( function ( err ) { assert.equal( err, '', 'API error' ); } ); + assert.equal( this.server.requests.length, 1, 'Requests made' ); + } ); + + QUnit.test( 'getToken() - error', function ( assert ) { + QUnit.expect( 2 ); + var api = new mw.Api(); + + this.server.respondWith( /type=testerror/, sequenceBodies( 200, { 'Content-Type': 'application/json' }, + [ + '{ "error": { "code": "bite-me", "info": "Smite me, O Mighty Smiter" } }', + '{ "tokens": { "testerrortoken": "good" } }' + ] + ) ); + // Don't cache error (bug 65268) - api.getToken( 'testaction2' ) - .fail( function ( err ) { - assert.equal( err, 'bite-me', 'Expected error' ); - } ) - .always( function () { - // Make this request after the first one has finished. - // If we make it simultaneously we still want it to share - // the cache, but as soon as it is fulfilled as error we - // reject it so that the next one tries fresh. - api.getToken( 'testaction2' ) - .done( function ( token ) { - assert.ok( token.length, 'Got testaction2 token (error was not be cached)' ); - } ) - .fail( function ( err ) { - assert.equal( err, '', 'API error' ); - } ); - - assert.equal( test.server.requests.length, 3, 'Requests made' ); - - test.server.requests[2].respond( 200, { 'Content-Type': 'application/json' }, - '{ "tokens": { "testaction2token": "0123abc" } }' - ); + api.getToken( 'testerror' ).fail( function ( err ) { + assert.equal( err, 'bite-me', 'Expected error' ); + + // Make this request after the first one has finished. + // If we make it simultaneously we still want it to share + // the cache, but as soon as it is fulfilled as error we + // reject it so that the next one tries fresh. + api.getToken( 'testerror' ).done( function ( token ) { + assert.equal( token, 'good', 'The token' ); } ); + } ); + } ); - this.server.requests[0].respond( 200, { 'Content-Type': 'application/json' }, - '{ "tokens": { "testactiontoken": "0123abc" } }' - ); + QUnit.test( 'badToken()', function ( assert ) { + QUnit.expect( 2 ); + var api = new mw.Api(), + test = this; + + this.server.respondWith( /type=testbad/, sequenceBodies( 200, { 'Content-Type': 'application/json' }, + [ + '{ "tokens": { "testbadtoken": "bad" } }', + '{ "tokens": { "testbadtoken": "good" } }' + ] + ) ); + + api.getToken( 'testbad' ) + .then( function () { + api.badToken( 'testbad' ); + return api.getToken( 'testbad' ); + } ) + .then( function ( token ) { + assert.equal( token, 'good', 'The token' ); + assert.equal( test.server.requests.length, 2, 'Requests made' ); + } ); - this.server.requests[1].respond( 200, { 'Content-Type': 'application/json' }, - '{ "error": { "code": "bite-me", "info": "Smite me, O Mighty Smiter" } }' - ); } ); QUnit.test( 'postWithToken( tokenType, params )', function ( assert ) { QUnit.expect( 1 ); - var api = new mw.Api( { ajax: { url: '/postWithToken/api.php' } } ); - // - Requests token - // - Performs action=example - api.postWithToken( 'testsimpletoken', { action: 'example', key: 'foo' } ) + this.server.respondWith( 'GET', /type=testpost/, [ 200, { 'Content-Type': 'application/json' }, + '{ "tokens": { "testposttoken": "good" } }' + ] ); + this.server.respondWith( 'POST', /api/, function ( request ) { + if ( request.requestBody.match( /token=good/ ) ) { + request.respond( 200, { 'Content-Type': 'application/json' }, + '{ "example": { "foo": "quux" } }' + ); + } + } ); + + api.postWithToken( 'testpost', { action: 'example', key: 'foo' } ) .done( function ( data ) { assert.deepEqual( data, { example: { foo: 'quux' } } ); } ); - - this.server.requests[0].respond( 200, { 'Content-Type': 'application/json' }, - '{ "tokens": { "testsimpletokentoken": "a-bad-token" } }' - ); - - this.server.requests[1].respond( 200, { 'Content-Type': 'application/json' }, - '{ "example": { "foo": "quux" } }' - ); } ); QUnit.test( 'postWithToken( tokenType, params with assert )', function ( assert ) { QUnit.expect( 2 ); - var api = new mw.Api( { ajax: { url: '/postWithToken/api.php' } } ); - api.postWithToken( 'testasserttoken', { action: 'example', key: 'foo', assert: 'user' } ) + this.server.respondWith( /assert=user/, [ 200, { 'Content-Type': 'application/json' }, + '{ "error": { "code": "assertuserfailed", "info": "Assertion failed" } }' + ] ); + + api.postWithToken( 'testassertpost', { action: 'example', key: 'foo', assert: 'user' } ) .fail( function ( errorCode ) { assert.equal( errorCode, 'assertuserfailed', 'getToken fails assert' ); } ); - assert.equal( this.server.requests.length, 1, 'Request for token made' ); - this.server.respondWith( /assert=user/, function ( request ) { - request.respond( - 200, - { 'Content-Type': 'application/json' }, - '{ "error": { "code": "assertuserfailed", "info": "Assertion failed" } }' - ); - } ); - - this.server.respond(); + assert.equal( this.server.requests.length, 1, 'Requests made' ); } ); QUnit.test( 'postWithToken( tokenType, params, ajaxOptions )', function ( assert ) { QUnit.expect( 3 ); - var api = new mw.Api(); + this.server.respond( [ 200, { 'Content-Type': 'application/json' }, '{ "example": "quux" }' ] ); + api.postWithToken( 'edit', { @@ -223,86 +268,111 @@ } ); assert.equal( this.server.requests.length, 2, 'Request made' ); - assert.equal( this.server.requests[0].requestHeaders['X-Foo'], 'Bar', 'Header sent' ); - - this.server.respond( function ( request ) { - request.respond( 200, { 'Content-Type': 'application/json' }, '{ "example": "quux" }' ); - } ); + assert.equal( this.server.requests[ 0 ].requestHeaders[ 'X-Foo' ], 'Bar', 'Header sent' ); } ); QUnit.test( 'postWithToken() - badtoken', function ( assert ) { QUnit.expect( 1 ); - var api = new mw.Api(); - // - Request: token + this.server.respondWith( /type=testbadtoken/, sequenceBodies( 200, { 'Content-Type': 'application/json' }, + [ + '{ "tokens": { "testbadtokentoken": "bad" } }', + '{ "tokens": { "testbadtokentoken": "good" } }' + ] + ) ); + this.server.respondWith( 'POST', /api/, function ( request ) { + if ( request.requestBody.match( /token=bad/ ) ) { + request.respond( 200, { 'Content-Type': 'application/json' }, + '{ "error": { "code": "badtoken" } }' + ); + } + if ( request.requestBody.match( /token=good/ ) ) { + request.respond( 200, { 'Content-Type': 'application/json' }, + '{ "example": { "foo": "quux" } }' + ); + } + } ); + + // - Request: new token -> bad // - Request: action=example -> badtoken error - // - Request: new token - // - Request: action=example + // - Request: new token -> good + // - Request: action=example -> success api.postWithToken( 'testbadtoken', { action: 'example', key: 'foo' } ) .done( function ( data ) { assert.deepEqual( data, { example: { foo: 'quux' } } ); } ); - - this.server.requests[0].respond( 200, { 'Content-Type': 'application/json' }, - '{ "tokens": { "testbadtokentoken": "a-bad-token" } }' - ); - - this.server.requests[1].respond( 200, { 'Content-Type': 'application/json' }, - '{ "error": { "code": "badtoken" } }' - ); - - this.server.requests[2].respond( 200, { 'Content-Type': 'application/json' }, - '{ "tokens": { "testbadtokentoken": "a-good-token" } }' - ); - - this.server.requests[3].respond( 200, { 'Content-Type': 'application/json' }, - '{ "example": { "foo": "quux" } }' - ); - } ); QUnit.test( 'postWithToken() - badtoken-cached', function ( assert ) { QUnit.expect( 2 ); + var sequenceA, + api = new mw.Api(); - var api = new mw.Api(); + this.server.respondWith( /type=testonce/, sequenceBodies( 200, { 'Content-Type': 'application/json' }, + [ + '{ "tokens": { "testoncetoken": "good-A" } }', + '{ "tokens": { "testoncetoken": "good-B" } }' + ] + ) ); + sequenceA = sequenceBodies( 200, { 'Content-Type': 'application/json' }, + [ + '{ "example": { "value": "A" } }', + '{ "error": { "code": "badtoken" } }' + ] + ); + this.server.respondWith( 'POST', /api/, function ( request ) { + if ( request.requestBody.match( /token=good-A/ ) ) { + sequenceA( request ); + } else if ( request.requestBody.match( /token=good-B/ ) ) { + request.respond( 200, { 'Content-Type': 'application/json' }, + '{ "example": { "value": "B" } }' + ); + } + } ); - // - Request: token + // - Request: new token -> A // - Request: action=example - api.postWithToken( 'testbadtokencache', { action: 'example', key: 'foo' } ) + api.postWithToken( 'testonce', { action: 'example', key: 'foo' } ) .done( function ( data ) { - assert.deepEqual( data, { example: { foo: 'quux' } } ); + assert.deepEqual( data, { example: { value: 'A' } } ); } ); - // - Cache: Try previously cached token - // - Request: action=example -> badtoken error - // - Request: new token - // - Request: action=example - api.postWithToken( 'testbadtokencache', { action: 'example', key: 'bar' } ) + // - Request: action=example w/ token A -> badtoken error + // - Request: new token -> B + // - Request: action=example w/ token B -> success + api.postWithToken( 'testonce', { action: 'example', key: 'bar' } ) .done( function ( data ) { - assert.deepEqual( data, { example: { bar: 'quux' } } ); + assert.deepEqual( data, { example: { value: 'B' } } ); } ); + } ); - this.server.requests[0].respond( 200, { 'Content-Type': 'application/json' }, - '{ "tokens": { "testbadtokencachetoken": "a-good-token-once" } }' - ); - - this.server.requests[1].respond( 200, { 'Content-Type': 'application/json' }, - '{ "example": { "foo": "quux" } }' - ); - - this.server.requests[2].respond( 200, { 'Content-Type': 'application/json' }, - '{ "error": { "code": "badtoken" } }' - ); - - this.server.requests[3].respond( 200, { 'Content-Type': 'application/json' }, - '{ "tokens": { "testbadtokencachetoken": "a-good-new-token" } }' - ); - - this.server.requests[4].respond( 200, { 'Content-Type': 'application/json' }, - '{ "example": { "bar": "quux" } }' - ); - + QUnit.module( 'mediawiki.api (2)', { + setup: function () { + var self = this, + requests = this.requests = []; + this.api = new mw.Api(); + this.sandbox.stub( jQuery, 'ajax', function () { + var request = $.extend( { + abort: self.sandbox.spy() + }, $.Deferred() ); + requests.push( request ); + return request; + } ); + } } ); -}( mediaWiki ) ); + QUnit.test( '#abort', 3, function ( assert ) { + this.api.get( { + a: 1 + } ); + this.api.post( { + b: 2 + } ); + this.api.abort(); + assert.ok( this.requests.length === 2, 'Check both requests triggered' ); + $.each( this.requests, function ( i, request ) { + assert.ok( request.abort.calledOnce, 'abort request number ' + i ); + } ); + } ); +}( mediaWiki, jQuery ) ); diff --git a/tests/qunit/suites/resources/mediawiki.api/mediawiki.api.upload.test.js b/tests/qunit/suites/resources/mediawiki.api/mediawiki.api.upload.test.js new file mode 100644 index 00000000..10fcd5da --- /dev/null +++ b/tests/qunit/suites/resources/mediawiki.api/mediawiki.api.upload.test.js @@ -0,0 +1,35 @@ +( function ( mw, $ ) { + QUnit.module( 'mediawiki.api.upload', QUnit.newMwEnvironment( {} ) ); + + QUnit.test( 'Basic functionality', function ( assert ) { + QUnit.expect( 2 ); + var api = new mw.Api(); + assert.ok( api.upload ); + assert.throws( function () { + api.upload(); + } ); + } ); + + QUnit.test( 'Set up iframe upload', function ( assert ) { + QUnit.expect( 5 ); + var $iframe, $form, $input, + api = new mw.Api(); + + this.sandbox.stub( api, 'getEditToken', function () { + return $.Deferred().promise(); + } ); + + api.uploadWithIframe( $( '' )[ 0 ], { filename: 'Testing API upload.jpg' } ); + + $iframe = $( 'iframe' ); + $form = $( 'form.mw-api-upload-form' ); + $input = $form.find( 'input[name=filename]' ); + + assert.ok( $form.length > 0 ); + assert.ok( $input.length > 0 ); + assert.ok( $iframe.length > 0 ); + assert.strictEqual( $form.prop( 'target' ), $iframe.prop( 'id' ) ); + assert.strictEqual( $input.val(), 'Testing API upload.jpg' ); + } ); + +}( mediaWiki, jQuery ) ); diff --git a/tests/qunit/suites/resources/mediawiki.api/mediawiki.api.watch.test.js b/tests/qunit/suites/resources/mediawiki.api/mediawiki.api.watch.test.js index 5965ab7b..64a51847 100644 --- a/tests/qunit/suites/resources/mediawiki.api/mediawiki.api.watch.test.js +++ b/tests/qunit/suites/resources/mediawiki.api/mediawiki.api.watch.test.js @@ -17,12 +17,12 @@ } ); api.watch( [ 'Foo' ] ).done( function ( items ) { - assert.equal( items[0].title, 'Foo' ); + assert.equal( items[ 0 ].title, 'Foo' ); } ); api.watch( [ 'Foo', 'Bar' ] ).done( function ( items ) { - assert.equal( items[0].title, 'Foo' ); - assert.equal( items[1].title, 'Bar' ); + assert.equal( items[ 0 ].title, 'Foo' ); + assert.equal( items[ 1 ].title, 'Bar' ); } ); // Requests are POST, match requestBody instead of url diff --git a/tests/qunit/suites/resources/mediawiki/mediawiki.RegExp.test.js b/tests/qunit/suites/resources/mediawiki/mediawiki.RegExp.test.js new file mode 100644 index 00000000..2388497a --- /dev/null +++ b/tests/qunit/suites/resources/mediawiki/mediawiki.RegExp.test.js @@ -0,0 +1,38 @@ +( function ( mw, $ ) { + QUnit.module( 'mediawiki.RegExp' ); + + QUnit.test( 'escape', 16, function ( assert ) { + var specials, normal; + + specials = [ + '\\', + '{', + '}', + '(', + ')', + '[', + ']', + '|', + '.', + '?', + '*', + '+', + '-', + '^', + '$' + ]; + + normal = [ + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', + 'abcdefghijklmnopqrstuvwxyz', + '0123456789' + ].join( '' ); + + $.each( specials, function ( i, str ) { + assert.propEqual( str.match( new RegExp( mw.RegExp.escape( str ) ) ), [ str ], 'Match ' + str ); + } ); + + assert.equal( mw.RegExp.escape( normal ), normal, 'Alphanumerals are left alone' ); + } ); + +}( mediaWiki, jQuery ) ); diff --git a/tests/qunit/suites/resources/mediawiki/mediawiki.Title.test.js b/tests/qunit/suites/resources/mediawiki/mediawiki.Title.test.js index c0afe07c..641a5a5d 100644 --- a/tests/qunit/suites/resources/mediawiki/mediawiki.Title.test.js +++ b/tests/qunit/suites/resources/mediawiki/mediawiki.Title.test.js @@ -100,33 +100,35 @@ // testing custom / localized namespace 100: 'Penguins' }, + // jscs: disable requireCamelCaseOrUpperCaseIdentifiers wgNamespaceIds: { - 'media': -2, - 'special': -1, + media: -2, + special: -1, '': 0, - 'talk': 1, - 'user': 2, - 'user_talk': 3, - 'wikipedia': 4, - 'wikipedia_talk': 5, - 'file': 6, - 'file_talk': 7, - 'mediawiki': 8, - 'mediawiki_talk': 9, - 'template': 10, - 'template_talk': 11, - 'help': 12, - 'help_talk': 13, - 'category': 14, - 'category_talk': 15, - 'image': 6, - 'image_talk': 7, - 'project': 4, - 'project_talk': 5, + talk: 1, + user: 2, + user_talk: 3, + wikipedia: 4, + wikipedia_talk: 5, + file: 6, + file_talk: 7, + mediawiki: 8, + mediawiki_talk: 9, + template: 10, + template_talk: 11, + help: 12, + help_talk: 13, + category: 14, + category_talk: 15, + image: 6, + image_talk: 7, + project: 4, + project_talk: 5, // Testing custom namespaces and aliases - 'penguins': 100, - 'antarctic_waterfowl': 100 + penguins: 100, + antarctic_waterfowl: 100 }, + // jscs: enable requireCamelCaseOrUpperCaseIdentifiers wgCaseSensitiveNamespaces: [] } } ) ); @@ -134,14 +136,14 @@ QUnit.test( 'constructor', cases.invalid.length, function ( assert ) { var i, title; for ( i = 0; i < cases.valid.length; i++ ) { - title = new mw.Title( cases.valid[i] ); + title = new mw.Title( cases.valid[ i ] ); } for ( i = 0; i < cases.invalid.length; i++ ) { /*jshint loopfunc:true */ - title = cases.invalid[i]; + title = cases.invalid[ i ]; assert.throws( function () { return new mw.Title( title ); - }, cases.invalid[i] ); + }, cases.invalid[ i ] ); } } ); @@ -149,21 +151,21 @@ var i; for ( i = 0; i < cases.valid.length; i++ ) { assert.equal( - $.type( mw.Title.newFromText( cases.valid[i] ) ), + $.type( mw.Title.newFromText( cases.valid[ i ] ) ), 'object', - cases.valid[i] + cases.valid[ i ] ); } for ( i = 0; i < cases.invalid.length; i++ ) { assert.equal( - $.type( mw.Title.newFromText( cases.invalid[i] ) ), + $.type( mw.Title.newFromText( cases.invalid[ i ] ) ), 'null', - cases.invalid[i] + cases.invalid[ i ] ); } } ); - QUnit.test( 'Basic parsing', 12, function ( assert ) { + QUnit.test( 'Basic parsing', 21, function ( assert ) { var title; title = new mw.Title( 'File:Foo_bar.JPG' ); @@ -181,6 +183,17 @@ title = new mw.Title( 'Foo#bar' ); assert.equal( title.getPrefixedText(), 'Foo' ); assert.equal( title.getFragment(), 'bar' ); + + title = new mw.Title( '.foo' ); + assert.equal( title.getPrefixedText(), '.foo' ); + assert.equal( title.getName(), '' ); + assert.equal( title.getNameText(), '' ); + assert.equal( title.getExtension(), 'foo' ); + assert.equal( title.getDotExtension(), '.foo' ); + assert.equal( title.getMain(), '.foo' ); + assert.equal( title.getMainText(), '.foo' ); + assert.equal( title.getPrefixedDb(), '.foo' ); + assert.equal( title.getPrefixedText(), '.foo' ); } ); QUnit.test( 'Transformation', 11, function ( assert ) { @@ -266,7 +279,7 @@ assert.equal( title.toString(), 'Article', 'Default config: No sensitive namespaces by default. First-letter becomes uppercase' ); // $wgCapitalLinks = false; - mw.config.set( 'wgCaseSensitiveNamespaces', [0, -2, 1, 4, 5, 6, 7, 10, 11, 12, 13, 14, 15] ); + mw.config.set( 'wgCaseSensitiveNamespaces', [ 0, -2, 1, 4, 5, 6, 7, 10, 11, 12, 13, 14, 15 ] ); title = new mw.Title( 'article' ); assert.equal( title.toString(), 'article', '$wgCapitalLinks=false: Article namespace is sensitive, first-letter case stays lowercase' ); @@ -309,8 +322,8 @@ assert.strictEqual( title.exists(), null, 'Return null with empty existance registry' ); // Basic registry, checks default to boolean - mw.Title.exist.set( ['Does_exist', 'User_talk:NeilK', 'Wikipedia:Sandbox_rules'], true ); - mw.Title.exist.set( ['Does_not_exist', 'User:John', 'Foobar'], false ); + mw.Title.exist.set( [ 'Does_exist', 'User_talk:NeilK', 'Wikipedia:Sandbox_rules' ], true ); + mw.Title.exist.set( [ 'Does_not_exist', 'User:John', 'Foobar' ], false ); title = new mw.Title( 'Project:Sandbox rules' ); assert.assertTrue( title.exists(), 'Return true for page titles marked as existing' ); @@ -327,7 +340,7 @@ title = new mw.Title( 'Foobar' ); assert.equal( title.getUrl(), '/wiki/Foobar', 'Basic functionality, getUrl uses mw.util.getUrl' ); - assert.equal( title.getUrl({ action: 'edit' }), '/wiki/Foobar?action=edit', 'Basic functionality, \'params\' parameter' ); + assert.equal( title.getUrl( { action: 'edit' } ), '/wiki/Foobar?action=edit', 'Basic functionality, \'params\' parameter' ); title = new mw.Title( 'John Doe', 3 ); assert.equal( title.getUrl(), '/wiki/User_talk:John_Doe', 'Escaping in title and namespace for urls' ); @@ -420,7 +433,7 @@ ]; for ( i = 0; i < cases.length; i++ ) { - thisCase = cases[i]; + thisCase = cases[ i ]; title = mw.Title.newFromImg( { src: thisCase.url } ); if ( thisCase.nameText !== undefined ) { @@ -467,28 +480,62 @@ ]; for ( i = 0; i < cases.length; i++ ) { - thisCase = cases[i]; + thisCase = cases[ i ]; title = mw.Title.newFromText( thisCase.text ); assert.equal( title.getRelativeText( thisCase.relativeTo ), thisCase.expectedResult ); } } ); - QUnit.test( 'newFromUserInput', 8, function ( assert ) { + QUnit.test( 'normalizeExtension', 5, function ( assert ) { + var extension, i, thisCase, prefix, + cases = [ + { + extension: 'png', + expected: 'png', + description: 'Extension already in canonical form' + }, + { + extension: 'PNG', + expected: 'png', + description: 'Extension lowercased in canonical form' + }, + { + extension: 'jpeg', + expected: 'jpg', + description: 'Extension changed in canonical form' + }, + { + extension: 'JPEG', + expected: 'jpg', + description: 'Extension lowercased and changed in canonical form' + }, + { + extension: '~~~', + expected: '', + description: 'Extension invalid and discarded' + } + ]; + + for ( i = 0; i < cases.length; i++ ) { + thisCase = cases[ i ]; + extension = mw.Title.normalizeExtension( thisCase.extension ); + + prefix = '[' + thisCase.description + '] '; + assert.equal( extension, thisCase.expected, prefix + 'Extension as expected' ); + } + } ); + + QUnit.test( 'newFromUserInput', 12, function ( assert ) { var title, i, thisCase, prefix, cases = [ { title: 'DCS0001557854455.JPG', - defaultNamespace: 0, - options: { - fileExtension: 'PNG' - }, expected: 'DCS0001557854455.JPG', description: 'Title in normal namespace without anything invalid but with "file extension"' }, { title: 'MediaWiki:Msg-awesome', - defaultNamespace: undefined, expected: 'MediaWiki:Msg-awesome', description: 'Full title (page in MediaWiki namespace) supplied as string' }, @@ -506,11 +553,21 @@ }, expected: 'File:The/Mw/Sound.kml', description: 'Page in File-namespace without explicit options' + }, + { + title: 'File:Foo.JPEG', + expected: 'File:Foo.JPEG', + description: 'Page in File-namespace with non-canonical extension' + }, + { + title: 'File:Foo.JPEG ', + expected: 'File:Foo.JPEG', + description: 'Page in File-namespace with trailing whitespace' } ]; for ( i = 0; i < cases.length; i++ ) { - thisCase = cases[i]; + thisCase = cases[ i ]; title = mw.Title.newFromUserInput( thisCase.title, thisCase.defaultNamespace, thisCase.options ); if ( thisCase.expected !== undefined ) { @@ -524,15 +581,14 @@ } } ); - QUnit.test( 'newFromFileName', 62, function ( assert ) { + QUnit.test( 'newFromFileName', 54, function ( assert ) { var title, i, thisCase, prefix, cases = [ { fileName: 'DCS0001557854455.JPG', typeOfName: 'Standard camera output', nameText: 'DCS0001557854455', - prefixedText: 'File:DCS0001557854455.JPG', - extensionDesired: 'jpg' + prefixedText: 'File:DCS0001557854455.JPG' }, { fileName: 'File:Sample.png', @@ -544,8 +600,7 @@ fileName: 'Treppe 2222 Test upload.jpg', typeOfName: 'File name with spaces in it and lower case file extension', nameText: 'Treppe 2222 Test upload', - prefixedText: 'File:Treppe 2222 Test upload.jpg', - extensionDesired: 'JPG' + prefixedText: 'File:Treppe 2222 Test upload.jpg' }, { fileName: 'I contain a \ttab.jpg', @@ -601,20 +656,6 @@ nameText: 'Dot. dot', prefixedText: 'File:Dot. dot. dot' }, - { - fileName: 'dot. dot ._dot', - typeOfName: 'File name with different file extension desired', - nameText: 'Dot. dot . dot', - prefixedText: 'File:Dot. dot . dot.png', - extensionDesired: 'png' - }, - { - fileName: 'fileWOExt', - typeOfName: 'File W/O extension with extension desired', - nameText: 'FileWOExt', - prefixedText: 'File:FileWOExt.png', - extensionDesired: 'png' - }, { fileName: '𠜎𠜱𠝹𠱓𠱸𠲖𠳏𠳕𠴕𠵼𠵿𠸎𠸏𠹷𠺝𠺢𠻗𠻹𠻺𠼭𠼮𠽌𠾴𠾼𠿪𡁜𡁯𡁵𡁶𡁻𡃁𡃉𡇙𢃇𢞵𢫕𢭃𢯊𢱑𢱕𢳂𠻹𠻺𠼭𠼮𠽌𠾴𠾼𠿪𡁜𡁯𡁵𡁶𡁻𡃁𡃉𡇙𢃇𢞵𢫕𢭃𢯊𢱑𢱕𢳂.png', typeOfName: 'File name longer than 240 bytes', @@ -632,8 +673,8 @@ ]; for ( i = 0; i < cases.length; i++ ) { - thisCase = cases[i]; - title = mw.Title.newFromFileName( thisCase.fileName, thisCase.extensionDesired ); + thisCase = cases[ i ]; + title = mw.Title.newFromFileName( thisCase.fileName ); if ( thisCase.nameText !== undefined ) { prefix = '[' + thisCase.typeOfName + '] '; diff --git a/tests/qunit/suites/resources/mediawiki/mediawiki.Uri.test.js b/tests/qunit/suites/resources/mediawiki/mediawiki.Uri.test.js index ba366553..51374bd8 100644 --- a/tests/qunit/suites/resources/mediawiki/mediawiki.Uri.test.js +++ b/tests/qunit/suites/resources/mediawiki/mediawiki.Uri.test.js @@ -11,7 +11,7 @@ } } ) ); - $.each( [true, false], function ( i, strictMode ) { + $.each( [ true, false ], function ( i, strictMode ) { QUnit.test( 'Basic construction and properties (' + ( strictMode ? '' : 'non-' ) + 'strict mode)', 2, function ( assert ) { var uriString, uri; uriString = 'http://www.ietf.org/rfc/rfc2396.txt'; @@ -76,8 +76,8 @@ } ); assert.strictEqual( uri.query.n, '1', 'Simple parameter with overrideKeys:false' ); - assert.strictEqual( uri.query.m[0], 'foo', 'Order of multi-value parameters with overrideKeys:true' ); - assert.strictEqual( uri.query.m[1], 'bar', 'Order of multi-value parameters with overrideKeys:true' ); + assert.strictEqual( uri.query.m[ 0 ], 'foo', 'Order of multi-value parameters with overrideKeys:true' ); + assert.strictEqual( uri.query.m[ 1 ], 'bar', 'Order of multi-value parameters with overrideKeys:true' ); assert.strictEqual( uri.query.m.length, 2, 'Number of mult-value field is correct' ); uri = new mw.Uri( 'ftp://usr:pwd@192.0.2.16/' ); @@ -270,7 +270,7 @@ assert.deepEqual( original.query, - { 'one': '1', 'two': '2' }, + { one: '1', two: '2' }, 'Properties is deep cloned (bug 37708)' ); } ); @@ -367,7 +367,7 @@ host: 'example.com', port: undefined, path: '/wiki/Foo', - query: { 'v': '2' }, + query: { v: '2' }, fragment: undefined }, 'basic object properties' diff --git a/tests/qunit/suites/resources/mediawiki/mediawiki.cldr.test.js b/tests/qunit/suites/resources/mediawiki/mediawiki.cldr.test.js index 779a0ed4..399db914 100644 --- a/tests/qunit/suites/resources/mediawiki/mediawiki.cldr.test.js +++ b/tests/qunit/suites/resources/mediawiki/mediawiki.cldr.test.js @@ -65,9 +65,9 @@ QUnit.test( 'Plural Test for ' + langCode, tests.length, function ( assert ) { for ( var i = 0; i < tests.length; i++ ) { assert.equal( - mw.language.convertPlural( tests[i][0], tests[i][1] ), - tests[i][2], - tests[i][3] + mw.language.convertPlural( tests[ i ][ 0 ], tests[ i ][ 1 ] ), + tests[ i ][ 2 ], + tests[ i ][ 3 ] ); } } ); diff --git a/tests/qunit/suites/resources/mediawiki/mediawiki.cookie.test.js b/tests/qunit/suites/resources/mediawiki/mediawiki.cookie.test.js index f5f199ea..7a13f0f7 100644 --- a/tests/qunit/suites/resources/mediawiki/mediawiki.cookie.test.js +++ b/tests/qunit/suites/resources/mediawiki/mediawiki.cookie.test.js @@ -102,7 +102,7 @@ } ); call = $.cookie.lastCall.args; - assert.strictEqual( call[0], 'myPrefixfoo' ); + assert.strictEqual( call[ 0 ], 'myPrefixfoo' ); assert.deepEqual( call[ 2 ], { expires: expiryDate, domain: 'myDomain', @@ -122,7 +122,7 @@ } ); call = $.cookie.lastCall.args; - assert.strictEqual( call[0], 'myPrefixfoo' ); + assert.strictEqual( call[ 0 ], 'myPrefixfoo' ); assert.deepEqual( call[ 2 ], { expires: date, domain: 'myDomain', diff --git a/tests/qunit/suites/resources/mediawiki/mediawiki.errorLogger.test.js b/tests/qunit/suites/resources/mediawiki/mediawiki.errorLogger.test.js index 7c3f1ecb..587c8931 100644 --- a/tests/qunit/suites/resources/mediawiki/mediawiki.errorLogger.test.js +++ b/tests/qunit/suites/resources/mediawiki/mediawiki.errorLogger.test.js @@ -7,7 +7,7 @@ errorUrl = 'http://example.com', errorLine = '123', errorColumn = '45', - errorObject = new Error( 'Foo'), + errorObject = new Error( 'Foo' ), oldHandler = this.sandbox.stub(); this.sandbox.stub( mw, 'track' ); diff --git a/tests/qunit/suites/resources/mediawiki/mediawiki.experiments.test.js b/tests/qunit/suites/resources/mediawiki/mediawiki.experiments.test.js new file mode 100644 index 00000000..774b2053 --- /dev/null +++ b/tests/qunit/suites/resources/mediawiki/mediawiki.experiments.test.js @@ -0,0 +1,63 @@ +( function ( mw ) { + + var getBucket = mw.experiments.getBucket; + + function createExperiment() { + return { + name: 'experiment', + enabled: true, + buckets: { + control: 0.25, + A: 0.25, + B: 0.25, + C: 0.25 + } + }; + } + + QUnit.module( 'mediawiki.experiments' ); + + QUnit.test( 'getBucket( experiment, token )', 4, function ( assert ) { + var experiment = createExperiment(), + token = '123457890'; + + assert.equal( + getBucket( experiment, token ), + getBucket( experiment, token ), + 'It returns the same bucket for the same experiment-token pair.' + ); + + // -------- + experiment = createExperiment(); + experiment.buckets = { + A: 0.314159265359 + }; + + assert.equal( + 'A', + getBucket( experiment, token ), + 'It returns the bucket if only one is defined.' + ); + + // -------- + experiment = createExperiment(); + experiment.enabled = false; + + assert.equal( + 'control', + getBucket( experiment, token ), + 'It returns "control" if the experiment is disabled.' + ); + + // -------- + experiment = createExperiment(); + experiment.buckets = {}; + + assert.equal( + 'control', + getBucket( experiment, token ), + 'It returns "control" if the experiment doesn\'t have any buckets.' + ); + } ); + +}( mediaWiki ) ); diff --git a/tests/qunit/suites/resources/mediawiki/mediawiki.jqueryMsg.test.js b/tests/qunit/suites/resources/mediawiki/mediawiki.jqueryMsg.test.js index 7e23e2ff..c088554a 100644 --- a/tests/qunit/suites/resources/mediawiki/mediawiki.jqueryMsg.test.js +++ b/tests/qunit/suites/resources/mediawiki/mediawiki.jqueryMsg.test.js @@ -1,5 +1,6 @@ ( function ( mw, $ ) { - var formatText, formatParse, formatnumTests, specialCharactersPageName, expectedListUsers, expectedEntrypoints, + var formatText, formatParse, formatnumTests, specialCharactersPageName, expectedListUsers, + expectedListUsersSitename, expectedEntrypoints, mwLanguageCache = {}, hasOwn = Object.hasOwnProperty; @@ -16,6 +17,8 @@ specialCharactersPageName = '"Who" wants to be a millionaire & live on \'Exotic Island\'?'; expectedListUsers = '注册用户'; + expectedListUsersSitename = '注册用户' + + mw.config.get( 'wgSiteName' ) + ''; expectedEntrypoints = 'index.php'; @@ -52,6 +55,7 @@ 'see-portal-url': '{{Int:portal-url}} is an important community page.', 'jquerymsg-test-statistics-users': '注册[[Special:ListUsers|用户]]', + 'jquerymsg-test-statistics-users-sitename': '注册[[Special:ListUsers|用户{{SITENAME}}]]', 'jquerymsg-test-version-entrypoints-index-php': '[https://www.mediawiki.org/wiki/Manual:index.php index.php]', @@ -70,7 +74,7 @@ */ function getMwLanguage( langCode ) { if ( !hasOwn.call( mwLanguageCache, langCode ) ) { - mwLanguageCache[langCode] = $.ajax( { + mwLanguageCache[ langCode ] = $.ajax( { url: mw.util.wikiScript( 'load' ), data: { skin: mw.config.get( 'skin' ), @@ -88,25 +92,37 @@ return mw.language; } ); } - return mwLanguageCache[langCode]; + return mwLanguageCache[ langCode ]; } /** * @param {Function[]} tasks List of functions that perform tasks * that may be asynchronous. Invoke the callback parameter when done. - * @param {Function} done When all tasks are done. - * @return + * @param {Function} complete Called when all tasks are done, or when the sequence is aborted. */ - function process( tasks, done ) { - function run() { + function process( tasks, complete ) { + /*jshint latedef:false */ + function abort() { + tasks.splice( 0, tasks.length ); + next(); + } + function next() { + if ( !tasks ) { + // This happens if after the process is completed, one of our callbacks is + // invoked. This can happen if a test timed out but the process was still + // running. In that case, ignore it. Don't invoke complete() a second time. + return; + } var task = tasks.shift(); if ( task ) { - task( run ); + task( next, abort ); } else { - done(); + // Remove tasks list to indicate the process is final. + tasks = null; + complete(); } } - run(); + next(); } QUnit.test( 'Replace', 16, function ( assert ) { @@ -306,9 +322,9 @@ QUnit.test( 'Match PHP parser', mw.libs.phpParserData.tests.length, function ( assert ) { mw.messages.set( mw.libs.phpParserData.messages ); var tasks = $.map( mw.libs.phpParserData.tests, function ( test ) { - return function ( next ) { + return function ( next, abort ) { getMwLanguage( test.lang ) - .done( function ( langClass ) { + .then( function ( langClass ) { mw.config.set( 'wgUserLanguage', test.lang ); var parser = new mw.jqueryMsg.parser( { language: langClass } ); assert.equal( @@ -316,11 +332,10 @@ test.result, test.name ); - } ) - .fail( function () { + }, function () { assert.ok( false, 'Language "' + test.lang + '" failed to load.' ); } ) - .always( next ); + .then( next, abort ); }; } ); @@ -328,8 +343,9 @@ process( tasks, QUnit.start ); } ); - QUnit.test( 'Links', 6, function ( assert ) { - var expectedDisambiguationsText, + QUnit.test( 'Links', 14, function ( assert ) { + var testCases, + expectedDisambiguationsText, expectedMultipleBars, expectedSpecialCharacters; @@ -360,12 +376,24 @@ // Pipe trick is not supported currently, but should not parse as text either. mw.messages.set( 'pipe-trick', '[[Tampa, Florida|]]' ); + mw.messages.set( 'reverse-pipe-trick', '[[|Tampa, Florida]]' ); + mw.messages.set( 'empty-link', '[[]]' ); this.suppressWarnings(); assert.equal( formatParse( 'pipe-trick' ), '[[Tampa, Florida|]]', 'Pipe trick should not be parsed.' ); + assert.equal( + formatParse( 'reverse-pipe-trick' ), + '[[|Tampa, Florida]]', + 'Reverse pipe trick should not be parsed.' + ); + assert.equal( + formatParse( 'empty-link' ), + '[[]]', + 'Empty link should not be parsed.' + ); this.restoreWarnings(); expectedMultipleBars = 'Main|Page'; @@ -384,20 +412,159 @@ expectedSpecialCharacters, 'Special characters' ); + + mw.messages.set( 'leading-colon', '[[:File:Foo.jpg]]' ); + assert.htmlEqual( + formatParse( 'leading-colon' ), + 'File:Foo.jpg', + 'Leading colon in links is stripped' + ); + + assert.htmlEqual( + formatParse( 'jquerymsg-test-statistics-users-sitename' ), + expectedListUsersSitename, + 'Piped wikilink with parser function in the text' + ); + + testCases = [ + [ + 'extlink-html-full', + 'asd [http://example.org Example] asd', + 'asd Example asd' + ], + [ + 'extlink-html-partial', + 'asd [http://example.org foo Example bar] asd', + 'asd foo Example bar asd' + ], + [ + 'wikilink-html-full', + 'asd [[Example|Example]] asd', + 'asd Example asd' + ], + [ + 'wikilink-html-partial', + 'asd [[Example|foo Example bar]] asd', + 'asd foo Example bar asd' + ] + ]; + + $.each( testCases, function () { + var + key = this[ 0 ], + input = this[ 1 ], + output = this[ 2 ]; + mw.messages.set( key, input ); + assert.htmlEqual( + formatParse( key ), + output, + 'HTML in links: ' + key + ); + } ); + } ); + + QUnit.test( 'Replacements in links', 14, function ( assert ) { + var testCases = [ + [ + 'extlink-param-href-full', + 'asd [$1 Example] asd', + 'asd Example asd' + ], + [ + 'extlink-param-href-partial', + 'asd [$1/example Example] asd', + 'asd Example asd' + ], + [ + 'extlink-param-text-full', + 'asd [http://example.org $2] asd', + 'asd Text asd' + ], + [ + 'extlink-param-text-partial', + 'asd [http://example.org Example $2] asd', + 'asd Example Text asd' + ], + [ + 'extlink-param-both-full', + 'asd [$1 $2] asd', + 'asd Text asd' + ], + [ + 'extlink-param-both-partial', + 'asd [$1/example Example $2] asd', + 'asd Example Text asd' + ], + [ + 'wikilink-param-href-full', + 'asd [[$1|Example]] asd', + 'asd Example asd' + ], + [ + 'wikilink-param-href-partial', + 'asd [[$1/Test|Example]] asd', + 'asd Example asd' + ], + [ + 'wikilink-param-text-full', + 'asd [[Example|$2]] asd', + 'asd Text asd' + ], + [ + 'wikilink-param-text-partial', + 'asd [[Example|Example $2]] asd', + 'asd Example Text asd' + ], + [ + 'wikilink-param-both-full', + 'asd [[$1|$2]] asd', + 'asd Text asd' + ], + [ + 'wikilink-param-both-partial', + 'asd [[$1/Test|Example $2]] asd', + 'asd Example Text asd' + ], + [ + 'wikilink-param-unpiped-full', + 'asd [[$1]] asd', + 'asd Example asd' + ], + [ + 'wikilink-param-unpiped-partial', + 'asd [[$1/Test]] asd', + 'asd Example/Test asd' + ] + ]; + + $.each( testCases, function () { + var + key = this[ 0 ], + input = this[ 1 ], + output = this[ 2 ], + paramHref = key.slice( 0, 8 ) === 'wikilink' ? 'Example' : 'http://example.com', + paramText = 'Text'; + mw.messages.set( key, input ); + assert.htmlEqual( + formatParse( key, paramHref, paramText ), + output, + 'Replacements in links: ' + key + ); + } ); } ); -// Tests that {{-transformation vs. general parsing are done as requested + // Tests that {{-transformation vs. general parsing are done as requested QUnit.test( 'Curly brace transformation', 16, function ( assert ) { var oldUserLang = mw.config.get( 'wgUserLanguage' ); - assertBothModes( assert, ['gender-msg', 'Bob', 'male'], 'Bob: blue', 'gender is resolved' ); + assertBothModes( assert, [ 'gender-msg', 'Bob', 'male' ], 'Bob: blue', 'gender is resolved' ); - assertBothModes( assert, ['plural-msg', 5], 'Found 5 items', 'plural is resolved' ); + assertBothModes( assert, [ 'plural-msg', 5 ], 'Found 5 items', 'plural is resolved' ); - assertBothModes( assert, ['grammar-msg'], 'Przeszukaj ' + mw.config.get( 'wgSiteName' ), 'grammar is resolved' ); + assertBothModes( assert, [ 'grammar-msg' ], 'Przeszukaj ' + mw.config.get( 'wgSiteName' ), 'grammar is resolved' ); mw.config.set( 'wgUserLanguage', 'en' ); - assertBothModes( assert, ['formatnum-msg', '987654321.654321'], '987,654,321.654', 'formatnum is resolved' ); + assertBothModes( assert, [ 'formatnum-msg', '987654321.654321' ], '987,654,321.654', 'formatnum is resolved' ); // Test non-{{ wikitext, where behavior differs @@ -501,7 +668,7 @@ 'curly-brace': '{{int:message}}', 'single-square-bracket': '[https://www.mediawiki.org/ MediaWiki]', 'double-square-bracket': '[[Some page]]', - 'regular': 'Other message' + regular: 'Other message' } ); oldGMF = mw.jqueryMsg.getMessageFunction; @@ -518,7 +685,7 @@ outerCalled = false; innerCalled = false; message = mw.message( key ); - message[format](); + message[ format ](); assert.strictEqual( outerCalled, shouldCall, 'Outer function called for ' + key ); assert.strictEqual( innerCalled, shouldCall, 'Inner function called for ' + key ); } @@ -645,9 +812,9 @@ mw.messages.set( 'formatnum-msg', '{{formatnum:$1}}' ); mw.messages.set( 'formatnum-msg-int', '{{formatnum:$1|R}}' ); var queue = $.map( formatnumTests, function ( test ) { - return function ( next ) { + return function ( next, abort ) { getMwLanguage( test.lang ) - .done( function ( langClass ) { + .then( function ( langClass ) { mw.config.set( 'wgUserLanguage', test.lang ); var parser = new mw.jqueryMsg.parser( { language: langClass } ); assert.equal( @@ -656,11 +823,10 @@ test.result, test.description ); - } ) - .fail( function () { + }, function () { assert.ok( false, 'Language "' + test.lang + '" failed to load' ); } ) - .always( next ); + .then( next, abort ); }; } ); QUnit.stop(); @@ -671,16 +837,16 @@ QUnit.test( 'HTML', 26, function ( assert ) { mw.messages.set( 'jquerymsg-italics-msg', 'Very important' ); - assertBothModes( assert, ['jquerymsg-italics-msg'], mw.messages.get( 'jquerymsg-italics-msg' ), 'Simple italics unchanged' ); + assertBothModes( assert, [ 'jquerymsg-italics-msg' ], mw.messages.get( 'jquerymsg-italics-msg' ), 'Simple italics unchanged' ); mw.messages.set( 'jquerymsg-bold-msg', 'Strong speaker' ); - assertBothModes( assert, ['jquerymsg-bold-msg'], mw.messages.get( 'jquerymsg-bold-msg' ), 'Simple bold unchanged' ); + assertBothModes( assert, [ 'jquerymsg-bold-msg' ], mw.messages.get( 'jquerymsg-bold-msg' ), 'Simple bold unchanged' ); mw.messages.set( 'jquerymsg-bold-italics-msg', 'It is key' ); - assertBothModes( assert, ['jquerymsg-bold-italics-msg'], mw.messages.get( 'jquerymsg-bold-italics-msg' ), 'Bold and italics nesting order preserved' ); + assertBothModes( assert, [ 'jquerymsg-bold-italics-msg' ], mw.messages.get( 'jquerymsg-bold-italics-msg' ), 'Bold and italics nesting order preserved' ); mw.messages.set( 'jquerymsg-italics-bold-msg', 'It is vital' ); - assertBothModes( assert, ['jquerymsg-italics-bold-msg'], mw.messages.get( 'jquerymsg-italics-bold-msg' ), 'Italics and bold nesting order preserved' ); + assertBothModes( assert, [ 'jquerymsg-italics-bold-msg' ], mw.messages.get( 'jquerymsg-italics-bold-msg' ), 'Italics and bold nesting order preserved' ); mw.messages.set( 'jquerymsg-italics-with-link', 'An italicized [[link|wiki-link]]' ); @@ -737,19 +903,17 @@ 'Mismatched HTML start and end tag treated as text' ); - // TODO (mattflaschen, 2013-03-18): It's not a security issue, but there's no real - // reason the htmlEmitter span needs to be here. It's an artifact of how emitting works. mw.messages.set( 'jquerymsg-script-and-external-link', ' [http://example.com Foo bar]' ); assert.htmlEqual( formatParse( 'jquerymsg-script-and-external-link' ), - '<script>alert( "jquerymsg-script-and-external-link test" );</script> Foo bar', + '<script>alert( "jquerymsg-script-and-external-link test" );</script> Foo bar', 'HTML tags in external links not interfering with escaping of other tags' ); mw.messages.set( 'jquerymsg-link-script', '[http://example.com ]' ); assert.htmlEqual( formatParse( 'jquerymsg-link-script' ), - '<script>alert( "jquerymsg-link-script test" );</script>', + '<script>alert( "jquerymsg-link-script test" );</script>', 'Non-whitelisted HTML tag in external link anchor treated as text' ); @@ -792,7 +956,7 @@ mw.messages.set( 'jquerymsg-wikitext-contents-script', '' ); assert.htmlEqual( formatParse( 'jquerymsg-wikitext-contents-script' ), - '<script>Script inside</script>', + '<script>Script inside</script>', 'Contents of valid tag are treated as wikitext, so invalid HTML element is treated as text' ); @@ -832,4 +996,33 @@ assert.equal( logSpy.callCount, 2, 'mw.log.warn calls' ); } ); + QUnit.test( 'Integration', 4, function ( assert ) { + var expected, logSpy; + + expected = 'Bold!'; + mw.messages.set( 'integration-test', '[[Bold]]!' ); + + this.suppressWarnings(); + logSpy = this.sandbox.spy( mw.log, 'warn' ); + assert.equal( + window.gM( 'integration-test' ), + expected, + 'Global function gM() works correctly' + ); + assert.equal( logSpy.callCount, 1, 'mw.log.warn called' ); + this.restoreWarnings(); + + assert.equal( + mw.message( 'integration-test' ).parse(), + expected, + 'mw.message().parse() works correctly' + ); + + assert.equal( + $( '' ).msg( 'integration-test' ).html(), + expected, + 'jQuery plugin $.fn.msg() works correctly' + ); + } ); + }( mediaWiki, jQuery ) ); diff --git a/tests/qunit/suites/resources/mediawiki/mediawiki.jscompat.test.js b/tests/qunit/suites/resources/mediawiki/mediawiki.jscompat.test.js index 3328ce3f..3b5915ac 100644 --- a/tests/qunit/suites/resources/mediawiki/mediawiki.jscompat.test.js +++ b/tests/qunit/suites/resources/mediawiki/mediawiki.jscompat.test.js @@ -61,7 +61,7 @@ expected = repeat( '\n', n ) + 'some text'; $textarea = $( '' ); - assert.equal( $textarea.val(), expected, 'Expecting ' + n + ' newlines (HTML contained ' + (n + 1) + ')' ); + assert.equal( $textarea.val(), expected, 'Expecting ' + n + ' newlines (HTML contained ' + ( n + 1 ) + ')' ); $textarea = $( '