diff options
Diffstat (limited to 'tests/qunit')
24 files changed, 1547 insertions, 313 deletions
diff --git a/tests/qunit/QUnitTestResources.php b/tests/qunit/QUnitTestResources.php new file mode 100644 index 00000000..670e3d11 --- /dev/null +++ b/tests/qunit/QUnitTestResources.php @@ -0,0 +1,52 @@ +<?php + +return array( + + /* Test suites for MediaWiki core modules */ + + 'mediawiki.tests.qunit.suites' => array( + 'scripts' => array( + 'tests/qunit/suites/resources/jquery/jquery.autoEllipsis.test.js', + 'tests/qunit/suites/resources/jquery/jquery.byteLength.test.js', + 'tests/qunit/suites/resources/jquery/jquery.byteLimit.test.js', + 'tests/qunit/suites/resources/jquery/jquery.client.test.js', + 'tests/qunit/suites/resources/jquery/jquery.colorUtil.test.js', + 'tests/qunit/suites/resources/jquery/jquery.delayedBind.test.js', + 'tests/qunit/suites/resources/jquery/jquery.getAttrs.test.js', + 'tests/qunit/suites/resources/jquery/jquery.highlightText.test.js', + 'tests/qunit/suites/resources/jquery/jquery.localize.test.js', + 'tests/qunit/suites/resources/jquery/jquery.mwExtension.test.js', + 'tests/qunit/suites/resources/jquery/jquery.tabIndex.test.js', + 'tests/qunit/suites/resources/jquery/jquery.tablesorter.test.js', + 'tests/qunit/suites/resources/jquery/jquery.textSelection.test.js', + 'tests/qunit/suites/resources/mediawiki/mediawiki.jscompat.test.js', + 'tests/qunit/suites/resources/mediawiki/mediawiki.test.js', + 'tests/qunit/suites/resources/mediawiki/mediawiki.Title.test.js', + 'tests/qunit/suites/resources/mediawiki/mediawiki.user.test.js', + 'tests/qunit/suites/resources/mediawiki/mediawiki.util.test.js', + 'tests/qunit/suites/resources/mediawiki.special/mediawiki.special.recentchanges.test.js', + "tests/qunit/suites/resources/mediawiki/mediawiki.jqueryMsg.test.js", + ), + 'dependencies' => array( + 'jquery.autoEllipsis', + 'jquery.byteLength', + 'jquery.byteLimit', + 'jquery.client', + 'jquery.colorUtil', + 'jquery.delayedBind', + 'jquery.getAttrs', + 'jquery.highlightText', + 'jquery.localize', + 'jquery.mwExtension', + 'jquery.tabIndex', + 'jquery.tablesorter', + 'jquery.textSelection', + 'mediawiki', + 'mediawiki.Title', + 'mediawiki.user', + 'mediawiki.util', + 'mediawiki.special.recentchanges', + 'mediawiki.jqueryMsg', + ), + ) +); diff --git a/tests/qunit/data/qunitOkCall.js b/tests/qunit/data/qunitOkCall.js new file mode 100644 index 00000000..2fb6e01d --- /dev/null +++ b/tests/qunit/data/qunitOkCall.js @@ -0,0 +1,2 @@ +start(); +ok( true, 'Successfully loaded!'); diff --git a/tests/qunit/data/testrunner.js b/tests/qunit/data/testrunner.js index dbfe9fad..fdd3116b 100644 --- a/tests/qunit/data/testrunner.js +++ b/tests/qunit/data/testrunner.js @@ -1,36 +1,61 @@ -( function( $ ) { +( function ( $, mw, QUnit, undefined ) { +"use strict"; + +var mwTestIgnore, mwTester, addons; /** * Add bogus to url to prevent IE crazy caching * - * @param value {String} a relative path (eg. 'data/defineTestCallback.js' or 'data/test.php?foo=bar') + * @param value {String} a relative path (eg. 'data/defineTestCallback.js' + * or 'data/test.php?foo=bar'). * @return {String} Such as 'data/defineTestCallback.js?131031765087663960' */ -QUnit.fixurl = function(value) { - return value + (/\?/.test(value) ? "&" : "?") + new Date().getTime() + "" + parseInt(Math.random()*100000); +QUnit.fixurl = function (value) { + return value + (/\?/.test( value ) ? '&' : '?') + + String( new Date().getTime() ) + + String( parseInt( Math.random()*100000, 10 ) ); }; /** + * Configuration + */ +QUnit.config.testTimeout = 5000; + +/** + * MediaWiki debug mode + */ +QUnit.config.urlConfig.push( 'debug' ); + +/** * Load TestSwarm agent */ if ( QUnit.urlParams.swarmURL ) { - document.write("<scr" + "ipt src='" + QUnit.fixurl( 'data/testwarm.inject.js' ) + "'></scr" + "ipt>"); + document.write( "<scr" + "ipt src='" + QUnit.fixurl( mw.config.get( 'wgScriptPath' ) + + '/tests/qunit/data/testwarm.inject.js' ) + "'></scr" + "ipt>" ); } /** - * Load completenesstest + * CompletenessTest */ +// Adds toggle checkbox to header +QUnit.config.urlConfig.push( 'completenesstest' ); + +// Initiate when enabled if ( QUnit.urlParams.completenesstest ) { // Return true to ignore - var mwTestIgnore = function( val, tester, funcPath ) { + mwTestIgnore = function ( val, tester, funcPath ) { // Don't record methods of the properties of constructors, // to avoid getting into a loop (prototype.constructor.prototype..). // Since we're therefor skipping any injection for // "new mw.Foo()", manually set it to true here. if ( val instanceof mw.Map ) { - tester.methodCallTracker['Map'] = true; + tester.methodCallTracker.Map = true; + return true; + } + if ( val instanceof mw.Title ) { + tester.methodCallTracker.Title = true; return true; } @@ -42,42 +67,113 @@ if ( QUnit.urlParams.completenesstest ) { return false; }; - var mwTester = new CompletenessTest( mw, mwTestIgnore ); + mwTester = new CompletenessTest( mw, mwTestIgnore ); } /** + * Test environment recommended for all QUnit test modules + */ +// Whether to log environment changes to the console +QUnit.config.urlConfig.push( 'mwlogenv' ); + +/** + * Reset mw.config to a fresh copy of the live config for each test(); + * @param override {Object} [optional] + * @example: + * <code> + * module( .., newMwEnvironment() ); + * + * test( .., function () { + * mw.config.set( 'foo', 'bar' ); // just for this test + * } ); + * + * test( .., function () { + * mw.config.get( 'foo' ); // doesn't exist + * } ); + * + * + * module( .., newMwEnvironment({ quux: 'corge' }) ); + * + * test( .., function () { + * mw.config.get( 'quux' ); // "corge" + * mw.config.set( 'quux', "grault" ); + * } ); + * + * test( .., function () { + * mw.config.get( 'quux' ); // "corge" + * } ); + * </code> + */ +QUnit.newMwEnvironment = ( function () { + var liveConfig, freshConfigCopy, log; + + liveConfig = mw.config.values; + + freshConfigCopy = function ( custom ) { + // "deep=true" is important here. + // Otherwise we just create a new object with values referring to live config. + // e.g. mw.config.set( 'wgFileExtensions', [] ) would not effect liveConfig, + // but mw.config.get( 'wgFileExtensions' ).push( 'png' ) would as the array + // was passed by reference in $.extend's loop. + return $.extend({}, liveConfig, custom, /*deep=*/true ); + }; + + log = QUnit.urlParams.mwlogenv ? mw.log : function () {}; + + return function ( override ) { + override = override || {}; + + return { + setup: function () { + log( 'MwEnvironment> SETUP for "' + QUnit.config.current.module + + ': ' + QUnit.config.current.testName + '"' ); + // Greetings, mock configuration! + mw.config.values = freshConfigCopy( override ); + }, + + teardown: function () { + log( 'MwEnvironment> TEARDOWN for "' + QUnit.config.current.module + + ': ' + QUnit.config.current.testName + '"' ); + // Farewell, mock configuration! + mw.config.values = liveConfig; + } + }; + }; +}() ); + +/** * Add-on assertion helpers */ // Define the add-ons -var addons = { +addons = { // Expect boolean true - assertTrue: function( actual, message ) { + assertTrue: function ( actual, message ) { strictEqual( actual, true, message ); }, // Expect boolean false - assertFalse: function( actual, message ) { + assertFalse: function ( actual, message ) { strictEqual( actual, false, message ); }, // Expect numerical value less than X - lt: function( actual, expected, message ) { + lt: function ( actual, expected, message ) { QUnit.push( actual < expected, actual, 'less than ' + expected, message ); }, // Expect numerical value less than or equal to X - ltOrEq: function( actual, expected, message ) { + ltOrEq: function ( actual, expected, message ) { QUnit.push( actual <= expected, actual, 'less than or equal to ' + expected, message ); }, // Expect numerical value greater than X - gt: function( actual, expected, message ) { + gt: function ( actual, expected, message ) { QUnit.push( actual > expected, actual, 'greater than ' + expected, message ); }, // Expect numerical value greater than or equal to X - gtOrEq: function( actual, expected, message ) { + gtOrEq: function ( actual, expected, message ) { QUnit.push( actual >= expected, actual, 'greater than or equal to ' + expected, message ); }, @@ -89,4 +185,4 @@ var addons = { $.extend( QUnit, addons ); $.extend( window, addons ); -})( jQuery ); +})( jQuery, mediaWiki, QUnit ); diff --git a/tests/qunit/index.html b/tests/qunit/index.html index f748b87f..ef7ff8de 100644 --- a/tests/qunit/index.html +++ b/tests/qunit/index.html @@ -9,6 +9,43 @@ <script> function startUp(){ mw.config = new mw.Map( false ); + + /** + * Simulate an average mw.config context + */ + /* StartUp module */ + mw.config.set({"wgLoadScript": "/mw/trunk/phase3/load.php", "debug": true, "skin": "vector", "stylepath": "/mw/trunk/phase3/skins", "wgUrlProtocols": "http\\:\\/\\/|https\\:\\/\\/|ftp\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|gopher\\:\\/\\/|telnet\\:\\/\\/|nntp\\:\\/\\/|worldwind\\:\\/\\/|mailto\\:|news\\:|svn\\:\\/\\/|git\\:\\/\\/|mms\\:\\/\\/|\\/\\/", "wgArticlePath": "/mw/trunk/phase3/index.php/$1", "wgScriptPath": "/mw/trunk/phase3", "wgScriptExtension": ".php", "wgScript": "/mw/trunk/phase3/index.php", "wgVariantArticlePath": false, "wgActionPaths": [], "wgServer": "http://localhost", "wgUserLanguage": "en", "wgContentLanguage": "en", "wgVersion": "1.19alpha", "wgEnableAPI": true, "wgEnableWriteAPI": true, "wgDefaultDateFormat": "dmy", "wgMonthNames": ["", "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"], "wgMonthNamesShort": ["", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"], "wgMainPageTitle": "Main Page", "wgFormattedNamespaces": {"-2": "Media", "-1": "Special", "0": "", "1": "Talk", "2": "User", "3": "User talk", "4": "Testopedia", "5": "Testopedia talk", "6": "File", "7": "File talk", "8": "MediaWiki", "9": "MediaWiki talk", "10": "Template", "11": "Template talk", "12": "Help", "13": "Help talk", "14": "Category", "15": "Category talk"}, "wgNamespaceIds": {"media": -2, "special": -1, "": 0, "talk": 1, "user": 2, "user_talk": 3, "testopedia": 4, "testopedia_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}, "wgSiteName": "Testopedia", "wgFileExtensions": ["png", "gif", "jpg", "jpeg"], "wgDBname": "mediawiki", "wgFileCanRotate": true, "wgAvailableSkins": {"chick": "Chick", "cologneblue": "CologneBlue", "modern": "Modern", "monobook": "MonoBook", "myskin": "MySkin", "nostalgia": "Nostalgia", "simple": "Simple", "standard": "Standard", "vector": "Vector"}, "wgExtensionAssetsPath": "/mw/trunk/phase3/extensions", "wgCookiePrefix": "mediawiki", "wgResourceLoaderMaxQueryLength": -1, "wgCaseSensitiveNamespaces": []}); + + /* WikiPage specific */ + mw.config.set({"wgCanonicalNamespace": "", "wgCanonicalSpecialPageName": false, "wgNamespaceNumber": 0, "wgPageName": "Sandbox", "wgTitle": "Sandbox", "wgCurRevisionId": 486, "wgArticleId": 84, "wgIsArticle": true, "wgAction": "view", "wgUserName": null, "wgUserGroups": ["*"], "wgCategories": [], "wgBreakFrames": false, "wgPageContentLanguage": "en", "wgSeparatorTransformTable": ["", ""], "wgDigitTransformTable": ["", ""], "wgRestrictionEdit": [], "wgRestrictionMove": [], "wgRedirectedFrom": "Sandbox"}); + + /** + * Fix wgScriptPath and the like to the real thing, + * instead of fake ones (for access to /tests/qunit/data/) + */ + + // Regular expression to extract the path for the QUnit tests + // Takes into account that tests could be run from a file:// URL + // by excluding the 'index.html' part from the URL + var rePath = /(?:[^#\?](?!index.html))*\/?/; + + // Extract path to /tests/qunit/ + var qunitTestsPath = rePath.exec( location.pathname )[0]; + + // Traverse up to script path + var pathParts = qunitTestsPath.split( '/' ); + pathParts.pop(); pathParts.pop(); pathParts.pop(); + var scriptPath = pathParts.join( '/' ); + + mw.config.set({ + "wgServer": location.protocol + '//' + location.host, + "wgScriptPath": scriptPath, + "wgLoadScript": scriptPath + '/load.php', + "stylepath": scriptPath + '/skins', + "wgArticlePath": scriptPath + '/index.php/$1', + "wgScript": scriptPath + '/index.php', + "wgExtensionAssetsPath": scriptPath + '/extensions' + }); } </script> @@ -18,15 +55,15 @@ <!-- MW: mediawiki.page.startup --> <script src="../../resources/jquery/jquery.client.js"></script> + <script src="../../resources/mediawiki/mediawiki.util.js"></script> <script src="../../resources/mediawiki.page/mediawiki.page.startup.js"></script> - <!-- MW: mediawiki.user|mediawiki.util|mediawiki.page.ready --> + <!-- MW: mediawiki.user|mediawiki.page.ready --> <script src="../../resources/jquery/jquery.cookie.js"></script> <script src="../../resources/mediawiki/mediawiki.user.js"></script> <script src="../../resources/jquery/jquery.messageBox.js"></script> - <script src="../../resources/jquery/jquery.mwPrototypes.js"></script> - <script src="../../resources/mediawiki/mediawiki.util.js"></script> + <script src="../../resources/jquery/jquery.mwExtension.js"></script> <script src="../../resources/jquery/jquery.checkboxShiftClick.js"></script> <script src="../../resources/jquery/jquery.makeCollapsible.js"></script> @@ -45,9 +82,14 @@ <script src="../../resources/jquery/jquery.colorUtil.js"></script> <script src="../../resources/jquery/jquery.delayedBind.js"></script> <script src="../../resources/jquery/jquery.getAttrs.js"></script> + <script src="../../resources/jquery/jquery.highlightText.js"></script> <script src="../../resources/jquery/jquery.localize.js"></script> <script src="../../resources/jquery/jquery.tabIndex.js"></script> <script src="../../resources/jquery/jquery.tablesorter.js"></script> + <script src="../../resources/jquery/jquery.textSelection.js"></script> + <script src="../../resources/mediawiki/mediawiki.Title.js"></script> + <script src="../../resources/mediawiki.language/mediawiki.language.js"></script> + <script src="../../resources/mediawiki/mediawiki.jqueryMsg.js"></script> <script src="../../resources/mediawiki.special/mediawiki.special.js"></script> <script src="../../resources/mediawiki.special/mediawiki.special.recentchanges.js"></script> @@ -59,23 +101,27 @@ <!-- QUnit: Load test suites (maintain the same order as above please) --> <script src="suites/resources/mediawiki/mediawiki.jscompat.test.js"></script> - <script src="suites/resources/mediawiki/mediawiki.js"></script> - <script src="suites/resources/mediawiki/mediawiki.user.js"></script> + <script src="suites/resources/mediawiki/mediawiki.test.js"></script> + <script src="suites/resources/mediawiki/mediawiki.user.test.js"></script> - <script src="suites/resources/jquery/jquery.client.js"></script> - <script src="suites/resources/jquery/jquery.mwPrototypes.js"></script> - <script src="suites/resources/mediawiki/mediawiki.util.js"></script> + <script src="suites/resources/jquery/jquery.client.test.js"></script> + <script src="suites/resources/jquery/jquery.mwExtension.test.js"></script> + <script src="suites/resources/mediawiki/mediawiki.util.test.js"></script> - <script src="suites/resources/jquery/jquery.autoEllipsis.js"></script> - <script src="suites/resources/jquery/jquery.byteLength.js"></script> - <script src="suites/resources/jquery/jquery.byteLimit.js"></script> - <script src="suites/resources/jquery/jquery.colorUtil.js"></script> + <script src="suites/resources/jquery/jquery.autoEllipsis.test.js"></script> + <script src="suites/resources/jquery/jquery.byteLength.test.js"></script> + <script src="suites/resources/jquery/jquery.byteLimit.test.js"></script> + <script src="suites/resources/jquery/jquery.colorUtil.test.js"></script> <script src="suites/resources/jquery/jquery.delayedBind.test.js"></script> - <script src="suites/resources/jquery/jquery.getAttrs.js"></script> - <script src="suites/resources/jquery/jquery.localize.js"></script> - <script src="suites/resources/jquery/jquery.tabIndex.js"></script> + <script src="suites/resources/jquery/jquery.getAttrs.test.js"></script> + <script src="suites/resources/jquery/jquery.highlightText.test.js"></script> + <script src="suites/resources/jquery/jquery.localize.test.js"></script> + <script src="suites/resources/jquery/jquery.tabIndex.test.js"></script> <script src="suites/resources/jquery/jquery.tablesorter.test.js" charset="UTF-8"></script> - <script src="suites/resources/mediawiki.special/mediawiki.special.recentchanges.js"></script> + <script src="suites/resources/jquery/jquery.textSelection.test.js" charset="UTF-8"></script> + <script src="suites/resources/mediawiki/mediawiki.Title.test.js"></script> + <script src="suites/resources/mediawiki/mediawiki.jqueryMsg.test.js"></script> + <script src="suites/resources/mediawiki.special/mediawiki.special.recentchanges.test.js"></script> </head> <body> <h1 id="qunit-header">MediaWiki JavaScript Test Suite</h1> @@ -85,6 +131,7 @@ </div> <h2 id="qunit-userAgent"></h2> <ol id="qunit-tests"></ol> + <div id="qunit-fixture"></div> <!-- Scripts inserting stuff here shall remove it themselfs! --> <div id="content"></div> diff --git a/tests/qunit/suites/resources/jquery/jquery.autoEllipsis.js b/tests/qunit/suites/resources/jquery/jquery.autoEllipsis.test.js index caf5a6f1..6e371384 100644 --- a/tests/qunit/suites/resources/jquery/jquery.autoEllipsis.js +++ b/tests/qunit/suites/resources/jquery/jquery.autoEllipsis.test.js @@ -1,4 +1,4 @@ -module( 'jquery.autoEllipsis.js' ); +module( 'jquery.autoEllipsis', QUnit.newMwEnvironment() ); test( '-- Initial check', function() { expect(1); @@ -6,8 +6,8 @@ test( '-- Initial check', function() { }); function createWrappedDiv( text, width ) { - var $wrapper = $( '<div />' ).css( 'width', width ); - var $div = $( '<div />' ).text( text ); + var $wrapper = $( '<div>' ).css( 'width', width ); + var $div = $( '<div>' ).text( text ); $wrapper.append( $div ); return $wrapper; } @@ -26,7 +26,7 @@ test( 'Position right', function() { // We need this thing to be visible, so append it to the DOM var origText = 'This is a really long random string and there is no way it fits in 100 pixels.'; var $wrapper = createWrappedDiv( origText, '100px' ); - $( 'body' ).append( $wrapper ); + $( '#qunit-fixture' ).append( $wrapper ); $wrapper.autoEllipsis( { position: 'right' } ); // Verify that, and only one, span element was created @@ -47,12 +47,9 @@ test( 'Position right', function() { // Put this text in the span and verify it doesn't fit $span.text( spanTextNew ); // In IE6 width works like min-width, allow IE6's width to be "equal to" - if ( $.browser.msie && Number( $.browser.version ) == 6 ) { + if ( $.browser.msie && Number( $.browser.version ) === 6 ) { gtOrEq( $span.width(), $span.parent().width(), 'Fit is maximal (adding two characters makes it not fit any more) - IE6: Maybe equal to as well due to width behaving like min-width in IE6' ); } else { gt( $span.width(), $span.parent().width(), 'Fit is maximal (adding two characters makes it not fit any more)' ); } - - // Clean up - $wrapper.remove(); }); diff --git a/tests/qunit/suites/resources/jquery/jquery.byteLength.js b/tests/qunit/suites/resources/jquery/jquery.byteLength.test.js index f82fda27..15fac691 100644 --- a/tests/qunit/suites/resources/jquery/jquery.byteLength.js +++ b/tests/qunit/suites/resources/jquery/jquery.byteLength.test.js @@ -1,4 +1,4 @@ -module( 'jquery.byteLength.js' ); +module( 'jquery.byteLength', QUnit.newMwEnvironment() ); test( '-- Initial check', function() { expect(1); @@ -25,7 +25,7 @@ test( 'Simple text', function() { test( 'Special text', window.foo = function() { expect(5); - // http://en.wikipedia.org/wiki/UTF-8 + // http://en.wikipedia.org/wiki/UTF-8 var U_0024 = '\u0024', U_00A2 = '\u00A2', U_20AC = '\u20AC', diff --git a/tests/qunit/suites/resources/jquery/jquery.byteLimit.js b/tests/qunit/suites/resources/jquery/jquery.byteLimit.test.js index 461ea49b..3346c2d5 100644 --- a/tests/qunit/suites/resources/jquery/jquery.byteLimit.js +++ b/tests/qunit/suites/resources/jquery/jquery.byteLimit.test.js @@ -1,4 +1,6 @@ -module( 'jquery.byteLimit.js' ); +( function () { + +module( 'jquery.byteLimit', QUnit.newMwEnvironment() ); test( '-- Initial check', function() { expect(1); @@ -23,46 +25,47 @@ $.addChars = function( $input, charstr ) { } } }; -var blti = 0; + /** * Test factory for $.fn.byteLimit * * @param $input {jQuery} jQuery object in an input element - * @param useLimit {Boolean} Wether a limit should apply at all + * @param hasLimit {Boolean} Wether a limit should apply at all * @param limit {Number} Limit (if used) otherwise undefined - * The limit should be less than 20 (the sample data's length) + * The limit should be less than 20 (the sample data's length) */ var byteLimitTest = function( options ) { var opt = $.extend({ description: '', $input: null, sample: '', - useLimit: false, - expected: 0, + hasLimit: false, + expected: '', limit: null }, options); - var i = blti++; test( opt.description, function() { - opt.$input.appendTo( 'body' ); - + opt.$input.appendTo( '#qunit-fixture' ); + // Simulate pressing keys for each of the sample characters $.addChars( opt.$input, opt.sample ); - var newVal = opt.$input.val(); - - if ( opt.useLimit ) { - expect(2); - + var rawVal = opt.$input.val(), + fn = opt.$input.data( 'byteLimit-callback' ), + newVal = $.isFunction( fn ) ? fn( rawVal ) : rawVal; + + if ( opt.hasLimit ) { + expect(3); + ltOrEq( $.byteLength( newVal ), opt.limit, 'Prevent keypresses after byteLimit was reached, length never exceeded the limit' ); - equal( $.byteLength( newVal ), opt.expected, 'Not preventing keypresses too early, length has reached the expected length' ); - + equal( $.byteLength( rawVal ), $.byteLength( opt.expected ), 'Not preventing keypresses too early, length has reached the expected length' ); + equal( rawVal, opt.expected, 'New value matches the expected string' ); + } else { - expect(1); - equal( $.byteLength( newVal ), opt.expected, 'Unlimited scenarios are not affected, expected length reached' ); + expect(2); + equal( newVal, opt.expected, 'New value matches the expected string' ); + equal( $.byteLength( newVal ), $.byteLength( opt.expected ), 'Unlimited scenarios are not affected, expected length reached' ); } - - opt.$input.remove(); } ); }; @@ -79,77 +82,106 @@ var byteLimitTest({ description: 'Plain text input', $input: $( '<input>' ) - .attr( { - 'type': 'text' - }), + .attr( 'type', 'text' ), sample: simpleSample, - useLimit: false, - expected: $.byteLength( simpleSample ) + hasLimit: false, + expected: simpleSample }); byteLimitTest({ description: 'Limit using the maxlength attribute', $input: $( '<input>' ) - .attr( { - 'type': 'text', - 'maxlength': '10' - }) + .attr( 'type', 'text' ) + .prop( 'maxLength', '10' ) .byteLimit(), sample: simpleSample, - useLimit: true, + hasLimit: true, limit: 10, - expected: 10 + expected: '1234567890' }); byteLimitTest({ description: 'Limit using a custom value', $input: $( '<input>' ) - .attr( { - 'type': 'text' - }) + .attr( 'type', 'text' ) .byteLimit( 10 ), sample: simpleSample, - useLimit: true, + hasLimit: true, limit: 10, - expected: 10 + expected: '1234567890' }); byteLimitTest({ description: 'Limit using a custom value, overriding maxlength attribute', $input: $( '<input>' ) - .attr( { - 'type': 'text', - 'maxLength': '10' - }) + .attr( 'type', 'text' ) + .prop( 'maxLength', '10' ) .byteLimit( 15 ), sample: simpleSample, - useLimit: true, + hasLimit: true, limit: 15, - expected: 15 + expected: '123456789012345' }); byteLimitTest({ description: 'Limit using a custom value (multibyte)', $input: $( '<input>' ) - .attr( { - 'type': 'text' - }) + .attr( 'type', 'text' ) .byteLimit( 14 ), sample: mbSample, - useLimit: true, + hasLimit: true, limit: 14, - expected: 14 // (10 x 1-byte char) + (1 x 3-byte char) + (1 x 1-byte char) + expected: '1234567890' + U_20AC + '1' }); byteLimitTest({ description: 'Limit using a custom value (multibyte) overlapping a byte', $input: $( '<input>' ) - .attr( { - 'type': 'text' - }) + .attr( 'type', 'text' ) .byteLimit( 12 ), sample: mbSample, - useLimit: true, + hasLimit: true, limit: 12, - expected: 12 // 10 x 1-byte char. The next 3-byte char exceeds limit of 12, but 2 more 1-byte chars come in after. + expected: '1234567890' + '12' }); + +byteLimitTest({ + description: 'Pass the limit and a callback as input filter', + $input: $( '<input>' ) + .attr( 'type', 'text' ) + .byteLimit( 6, function( val ) { + // Invalid title + if ( val == '' ) { + return ''; + } + + // Return without namespace prefix + return new mw.Title( '' + val ).getMain(); + } ), + sample: 'User:Sample', + hasLimit: true, + limit: 6, // 'Sample' length + expected: 'User:Sample' +}); + +byteLimitTest({ + description: 'Limit using the maxlength attribute and pass a callback as input filter', + $input: $( '<input>' ) + .attr( 'type', 'text' ) + .prop( 'maxLength', '6' ) + .byteLimit( function( val ) { + // Invalid title + if ( val === '' ) { + return ''; + } + + // Return without namespace prefix + return new mw.Title( '' + val ).getMain(); + } ), + sample: 'User:Sample', + hasLimit: true, + limit: 6, // 'Sample' length + expected: 'User:Sample' +}); + +}() );
\ No newline at end of file diff --git a/tests/qunit/suites/resources/jquery/jquery.client.js b/tests/qunit/suites/resources/jquery/jquery.client.test.js index 50df2928..7be41971 100644 --- a/tests/qunit/suites/resources/jquery/jquery.client.js +++ b/tests/qunit/suites/resources/jquery/jquery.client.test.js @@ -1,12 +1,14 @@ -module( 'jquery.client.js' ); +module( 'jquery.client', QUnit.newMwEnvironment() ); test( '-- Initial check', function() { expect(1); ok( jQuery.client, 'jQuery.client defined' ); }); -test( 'profile userAgent support', function() { - expect(8); +/** Number of user-agent defined */ +var uacount = 0; + +var uas = (function() { // Object keyed by userAgent. Value is an array (human-readable name, client-profile object, navigator.platform value) // Info based on results from http://toolserver.org/~krinkle/testswarm/job/174/ @@ -24,11 +26,32 @@ test( 'profile userAgent support', function() { "version": "7.0", "versionBase": "7", "versionNumber": 7 + }, + wikiEditor: { + ltr: true, + rtl: false } }, // Internet Explorer 8 // Internet Explorer 9 // Internet Explorer 10 + 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)': { + title: 'Internet Explorer 10', + platform: 'Win32', + profile: { + "name": "msie", + "layout": "trident", + "layoutVersion": "unknown", // should be able to report 6? + "platform": "win", + "version": "10.0", + "versionBase": "10", + "versionNumber": 10 + }, + wikiEditor: { + ltr: true, + rtl: true + } + }, // Firefox 2 // Firefox 3.5 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1.19) Gecko/20110420 Firefox/3.5.19': { @@ -42,6 +65,10 @@ test( 'profile userAgent support', function() { "version": "3.5.19", "versionBase": "3", "versionNumber": 3.5 + }, + wikiEditor: { + ltr: true, + rtl: true } }, // Firefox 3.6 @@ -56,6 +83,10 @@ test( 'profile userAgent support', function() { "version": "3.6.17", "versionBase": "3", "versionNumber": 3.6 + }, + wikiEditor: { + ltr: true, + rtl: true } }, // Firefox 4 @@ -70,7 +101,29 @@ test( 'profile userAgent support', function() { "version": "4.0.1", "versionBase": "4", "versionNumber": 4 - } + }, + wikiEditor: { + ltr: true, + rtl: true + } + }, + // Firefox 10 nightly build + 'Mozilla/5.0 (X11; Linux x86_64; rv:10.0a1) Gecko/20111103 Firefox/10.0a1': { + title: 'Firefox 10 nightly', + platform: 'Linux', + profile: { + "name": "firefox", + "layout": "gecko", + "layoutVersion": 20111103, + "platform": "linux", + "version": "10.0a1", + "versionBase": "10", + "versionNumber": 10 + }, + wikiEditor: { + ltr: true, + rtl: true + } }, // Firefox 5 // Safari 3 @@ -86,7 +139,11 @@ test( 'profile userAgent support', function() { "version": "4.0.5", "versionBase": "4", "versionNumber": 4 - } + }, + wikiEditor: { + ltr: true, + rtl: true + } }, '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': { title: 'Safari 4', @@ -99,6 +156,10 @@ test( 'profile userAgent support', function() { "version": "4.0.5", "versionBase": "4", "versionNumber": 4 + }, + wikiEditor: { + ltr: true, + rtl: true } }, // Safari 5 @@ -122,6 +183,10 @@ test( 'profile userAgent support', function() { "version": "12.0.742.112", "versionBase": "12", "versionNumber": 12 + }, + wikiEditor: { + ltr: true, + rtl: true } }, 'Mozilla/5.0 (X11; Linux i686) AppleWebKit/534.30 (KHTML, like Gecko) Chrome/12.0.742.68 Safari/534.30': { @@ -135,9 +200,19 @@ test( 'profile userAgent support', function() { "version": "12.0.742.68", "versionBase": "12", "versionNumber": 12 + }, + wikiEditor: { + ltr: true, + rtl: true } } }; + $.each( uas, function() { uacount++ }); + return uas; +})(); + +test( 'profile userAgent support', function() { + expect(uacount); // Generate a client profile object and compare recursively var uaTest = function( rawUserAgent, data ) { @@ -168,34 +243,37 @@ test( 'profile return validation for current user agent', function() { equal( typeof p.versionNumber, 'number', 'p.versionNumber is a number' ); }); +// Example from WikiEditor +// Make sure to use raw numbers, a string like "7.0" would fail on a +// version 10 browser since in string comparaison "10" is before "7.0" :) +var testMap = { + 'ltr': { + 'msie': [['>=', 7.0]], + 'firefox': [['>=', 2]], + 'opera': [['>=', 9.6]], + 'safari': [['>=', 3]], + 'chrome': [['>=', 3]], + 'netscape': [['>=', 9]], + 'blackberry': false, + 'ipod': false, + 'iphone': false + }, + 'rtl': { + 'msie': [['>=', 8]], + 'firefox': [['>=', 2]], + 'opera': [['>=', 9.6]], + 'safari': [['>=', 3]], + 'chrome': [['>=', 3]], + 'netscape': [['>=', 9]], + 'blackberry': false, + 'ipod': false, + 'iphone': false + } +}; + test( 'test', function() { expect(1); - // Example from WikiEditor - var testMap = { - 'ltr': { - 'msie': [['>=', 7]], - 'firefox': [['>=', 2]], - 'opera': [['>=', 9.6]], - 'safari': [['>=', 3]], - 'chrome': [['>=', 3]], - 'netscape': [['>=', 9]], - 'blackberry': false, - 'ipod': false, - 'iphone': false - }, - 'rtl': { - 'msie': [['>=', 8]], - 'firefox': [['>=', 2]], - 'opera': [['>=', 9.6]], - 'safari': [['>=', 3]], - 'chrome': [['>=', 3]], - 'netscape': [['>=', 9]], - 'blackberry': false, - 'ipod': false, - 'iphone': false - } - }; // .test() uses eval, make sure no exceptions are thrown // then do a basic return value type check var testMatch = $.client.test( testMap ); @@ -203,3 +281,29 @@ test( 'test', function() { equal( typeof testMatch, 'boolean', 'test returns a boolean value' ); }); + +test( 'User-agent matches against WikiEditor\'s compatibility map', function() { + expect( uacount * 2 ); // double since we test both LTR and RTL + + var $body = $( 'body' ), + bodyClasses = $body.attr( 'class' ); + + // Loop through and run tests + $.each( uas, function ( agent, data ) { + $.each( ['ltr', 'rtl'], function ( i, dir ) { + $body.removeClass( 'ltr rtl' ).addClass( dir ); + var profile = $.client.profile( { + userAgent: agent, + platform: data.platform + } ); + var testMatch = $.client.test( testMap, profile ); + $body.removeClass( dir ); + + equal( testMatch, data.wikiEditor[dir], 'testing comparison based on ' + dir + ', ' + agent ); + }); + }); + + // Restore body classes + $body.attr( 'class', bodyClasses ); +}); + diff --git a/tests/qunit/suites/resources/jquery/jquery.colorUtil.js b/tests/qunit/suites/resources/jquery/jquery.colorUtil.test.js index 93f12b82..655ee564 100644 --- a/tests/qunit/suites/resources/jquery/jquery.colorUtil.js +++ b/tests/qunit/suites/resources/jquery/jquery.colorUtil.test.js @@ -1,4 +1,4 @@ -module( 'jquery.colorUtil.js' ); +module( 'jquery.colorUtil', QUnit.newMwEnvironment() ); test( '-- Initial check', function() { expect(1); diff --git a/tests/qunit/suites/resources/jquery/jquery.delayedBind.test.js b/tests/qunit/suites/resources/jquery/jquery.delayedBind.test.js index 8688f12e..6489a1f1 100644 --- a/tests/qunit/suites/resources/jquery/jquery.delayedBind.test.js +++ b/tests/qunit/suites/resources/jquery/jquery.delayedBind.test.js @@ -1,5 +1,5 @@ test('jquery.delayedBind with data option', function() { - var $fixture = $('<div>').appendTo('body'), + var $fixture = $('<div>').appendTo('#qunit-fixture'), data = { magic: "beeswax" }, delay = 50; @@ -20,7 +20,7 @@ test('jquery.delayedBind with data option', function() { }); test('jquery.delayedBind without data option', function() { - var $fixture = $('<div>').appendTo('body'), + var $fixture = $('<div>').appendTo('#qunit-fixture'), data = { magic: "beeswax" }, delay = 50; diff --git a/tests/qunit/suites/resources/jquery/jquery.getAttrs.js b/tests/qunit/suites/resources/jquery/jquery.getAttrs.test.js index 3d3d01e1..9377a2f6 100644 --- a/tests/qunit/suites/resources/jquery/jquery.getAttrs.js +++ b/tests/qunit/suites/resources/jquery/jquery.getAttrs.test.js @@ -1,4 +1,4 @@ -module( 'jquery.getAttrs.js' ); +module( 'jquery.getAttrs', QUnit.newMwEnvironment() ); test( '-- Initial check', function() { expect(1); diff --git a/tests/qunit/suites/resources/jquery/jquery.highlightText.test.js b/tests/qunit/suites/resources/jquery/jquery.highlightText.test.js new file mode 100644 index 00000000..4750d2b8 --- /dev/null +++ b/tests/qunit/suites/resources/jquery/jquery.highlightText.test.js @@ -0,0 +1,239 @@ +module( 'jquery.highlightText', QUnit.newMwEnvironment() ); + +test( '-- Initial check', function() { + expect(1); + ok( $.fn.highlightText, 'jQuery.fn.highlightText defined' ); +} ); + +test( 'Check', function() { + var cases = [ + { + desc: 'Test 001', + text: 'Blue Öyster Cult', + highlight: 'Blue', + expected: '<span class="highlight">Blue</span> Öyster Cult' + }, + { + desc: 'Test 002', + text: 'Blue Öyster Cult', + highlight: 'Blue ', + expected: '<span class="highlight">Blue</span> Öyster Cult' + }, + { + desc: 'Test 003', + text: 'Blue Öyster Cult', + highlight: 'Blue Ö', + expected: '<span class="highlight">Blue</span> <span class="highlight">Ö</span>yster Cult' + }, + { + desc: 'Test 004', + text: 'Blue Öyster Cult', + highlight: 'Blue Öy', + expected: '<span class="highlight">Blue</span> <span class="highlight">Öy</span>ster Cult' + }, + { + desc: 'Test 005', + text: 'Blue Öyster Cult', + highlight: ' Blue', + expected: '<span class="highlight">Blue</span> Öyster Cult' + }, + { + desc: 'Test 006', + text: 'Blue Öyster Cult', + highlight: ' Blue ', + expected: '<span class="highlight">Blue</span> Öyster Cult' + }, + { + desc: 'Test 007', + text: 'Blue Öyster Cult', + highlight: ' Blue Ö', + expected: '<span class="highlight">Blue</span> <span class="highlight">Ö</span>yster Cult' + }, + { + desc: 'Test 008', + text: 'Blue Öyster Cult', + highlight: ' Blue Öy', + expected: '<span class="highlight">Blue</span> <span class="highlight">Öy</span>ster Cult' + }, + { + desc: 'Test 009: Highlighter broken on starting Umlaut?', + text: 'Österreich', + highlight: 'Österreich', + expected: '<span class="highlight">Österreich</span>' + }, + { + desc: 'Test 010: Highlighter broken on starting Umlaut?', + text: 'Österreich', + highlight: 'Ö', + expected: '<span class="highlight">Ö</span>sterreich' + }, + { + desc: 'Test 011: Highlighter broken on starting Umlaut?', + text: 'Österreich', + highlight: 'Öst', + expected: '<span class="highlight">Öst</span>erreich' + }, + { + desc: 'Test 012: Highlighter broken on starting Umlaut?', + text: 'Österreich', + highlight: 'Oe', + expected: 'Österreich' + }, + { + desc: 'Test 013: Highlighter broken on punctuation mark?', + text: 'So good. To be there', + highlight: 'good', + expected: 'So <span class="highlight">good</span>. To be there' + }, + { + desc: 'Test 014: Highlighter broken on space?', + text: 'So good. To be there', + highlight: 'be', + expected: 'So good. To <span class="highlight">be</span> there' + }, + { + desc: 'Test 015: Highlighter broken on space?', + text: 'So good. To be there', + highlight: ' be', + expected: 'So good. To <span class="highlight">be</span> there' + }, + { + desc: 'Test 016: Highlighter broken on space?', + text: 'So good. To be there', + highlight: 'be ', + expected: 'So good. To <span class="highlight">be</span> there' + }, + { + desc: 'Test 017: Highlighter broken on space?', + text: 'So good. To be there', + highlight: ' be ', + expected: 'So good. To <span class="highlight">be</span> there' + }, + { + desc: 'Test 018: en de Highlighter broken on special character at the end?', + text: 'So good. xbß', + highlight: 'xbß', + expected: 'So good. <span class="highlight">xbß</span>' + }, + { + desc: 'Test 019: en de Highlighter broken on special character at the end?', + text: 'So good. xbß.', + highlight: 'xbß.', + expected: 'So good. <span class="highlight">xbß.</span>' + }, + { + desc: 'Test 020: RTL he Hebrew', + text: 'חסיד אומות העולם', + highlight: 'חסיד אומות העולם', + expected: '<span class="highlight">חסיד</span> <span class="highlight">אומות</span> <span class="highlight">העולם</span>' + }, + { + desc: 'Test 021: RTL he Hebrew', + text: 'חסיד אומות העולם', + highlight: 'חסי', + expected: '<span class="highlight">חסי</span>ד אומות העולם' + }, + { + desc: 'Test 022: ja Japanese', + text: '諸国民の中の正義の人', + highlight: '諸国民の中の正義の人', + expected: '<span class="highlight">諸国民の中の正義の人</span>' + }, + { + desc: 'Test 023: ja Japanese', + text: '諸国民の中の正義の人', + highlight: '諸国', + expected: '<span class="highlight">諸国</span>民の中の正義の人' + }, + { + desc: 'Test 024: fr French text and « french quotes » (guillemets)', + text: "« L'oiseau est sur l’île »", + highlight: "« L'oiseau est sur l’île »", + expected: '<span class="highlight">«</span> <span class="highlight">L\'oiseau</span> <span class="highlight">est</span> <span class="highlight">sur</span> <span class="highlight">l’île</span> <span class="highlight">»</span>' + }, + { + desc: 'Test 025: fr French text and « french quotes » (guillemets)', + text: "« L'oiseau est sur l’île »", + highlight: "« L'oise", + expected: '<span class="highlight">«</span> <span class="highlight">L\'oise</span>au est sur l’île »' + }, + { + desc: 'Test 025a: fr French text and « french quotes » (guillemets) - does it match the single strings "«" and "L" separately?', + text: "« L'oiseau est sur l’île »", + highlight: "« L", + expected: '<span class="highlight">«</span> <span class="highlight">L</span>\'oiseau est sur <span class="highlight">l</span>’île »' + }, + { + desc: 'Test 026: ru Russian', + text: 'Праведники мира', + highlight: 'Праведники мира', + expected: '<span class="highlight">Праведники</span> <span class="highlight">мира</span>' + }, + { + desc: 'Test 027: ru Russian', + text: 'Праведники мира', + highlight: 'Праве', + expected: '<span class="highlight">Праве</span>дники мира' + }, + { + desc: 'Test 028 ka Georgian', + text: 'მთავარი გვერდი', + highlight: 'მთავარი გვერდი', + expected: '<span class="highlight">მთავარი</span> <span class="highlight">გვერდი</span>' + }, + { + desc: 'Test 029 ka Georgian', + text: 'მთავარი გვერდი', + highlight: 'მთა', + expected: '<span class="highlight">მთა</span>ვარი გვერდი' + }, + { + desc: 'Test 030 hy Armenian', + text: 'Նոնա Գափրինդաշվիլի', + highlight: 'Նոնա Գափրինդաշվիլի', + expected: '<span class="highlight">Նոնա</span> <span class="highlight">Գափրինդաշվիլի</span>' + }, + { + desc: 'Test 031 hy Armenian', + text: 'Նոնա Գափրինդաշվիլի', + highlight: 'Նոն', + expected: '<span class="highlight">Նոն</span>ա Գափրինդաշվիլի' + }, + { + desc: 'Test 032: th Thai', + text: 'พอล แอร์ดิช', + highlight: 'พอล แอร์ดิช', + expected: '<span class="highlight">พอล</span> <span class="highlight">แอร์ดิช</span>' + }, + { + desc: 'Test 033: th Thai', + text: 'พอล แอร์ดิช', + highlight: 'พอ', + expected: '<span class="highlight">พอ</span>ล แอร์ดิช' + }, + { + desc: 'Test 034: RTL ar Arabic', + text: 'بول إيردوس', + highlight: 'بول إيردوس', + expected: '<span class="highlight">بول</span> <span class="highlight">إيردوس</span>' + }, + { + desc: 'Test 035: RTL ar Arabic', + text: 'بول إيردوس', + highlight: 'بو', + expected: '<span class="highlight">بو</span>ل إيردوس' + } + ]; + expect(cases.length); + var $fixture; + + $.each(cases, function( i, item ) { + $fixture = $( '<p></p>' ).text( item.text ); + $fixture.highlightText( item.highlight ); + equals( + $fixture.html(), + $('<p>' + item.expected + '</p>').html(), // re-parse to normalize! + item.desc || undefined + ); + } ); +} ); diff --git a/tests/qunit/suites/resources/jquery/jquery.localize.js b/tests/qunit/suites/resources/jquery/jquery.localize.test.js index 40b58687..cd828634 100644 --- a/tests/qunit/suites/resources/jquery/jquery.localize.js +++ b/tests/qunit/suites/resources/jquery/jquery.localize.test.js @@ -1,4 +1,4 @@ -module( 'jquery.localize.js' ); +module( 'jquery.localize', QUnit.newMwEnvironment() ); test( '-- Initial check', function() { expect(1); diff --git a/tests/qunit/suites/resources/jquery/jquery.mwPrototypes.js b/tests/qunit/suites/resources/jquery/jquery.mwExtension.test.js index bb6d2a1b..3a2d0d83 100644 --- a/tests/qunit/suites/resources/jquery/jquery.mwPrototypes.js +++ b/tests/qunit/suites/resources/jquery/jquery.mwExtension.test.js @@ -1,10 +1,10 @@ -module( 'jquery.mwPrototypes.js' ); +module( 'jquery.mwExtension', QUnit.newMwEnvironment() ); test( 'String functions', function() { equal( $.trimLeft( ' foo bar ' ), 'foo bar ', 'trimLeft' ); equal( $.trimRight( ' foo bar ' ), ' foo bar', 'trimRight' ); - equal( $.ucFirst( 'foo'), 'Foo', 'ucFirst' ); + equal( $.ucFirst( 'foo' ), 'Foo', 'ucFirst' ); equal( $.escapeRE( '<!-- ([{+mW+}]) $^|?>' ), '<!\\-\\- \\(\\[\\{\\+mW\\+\\}\\]\\) \\$\\^\\|\\?>', 'escapeRE - Escape specials' ); @@ -36,6 +36,8 @@ test( 'Is functions', function() { strictEqual( $.isEmpty( 'string' ), false, 'isEmptry: "string"' ); strictEqual( $.isEmpty( '0' ), true, 'isEmptry: "0"' ); + strictEqual( $.isEmpty( '' ), true, 'isEmptry: ""' ); + strictEqual( $.isEmpty( 1 ), false, 'isEmptry: 1' ); strictEqual( $.isEmpty( [] ), true, 'isEmptry: []' ); strictEqual( $.isEmpty( {} ), true, 'isEmptry: {}' ); diff --git a/tests/qunit/suites/resources/jquery/jquery.tabIndex.js b/tests/qunit/suites/resources/jquery/jquery.tabIndex.test.js index 1ff81e58..98ff5508 100644 --- a/tests/qunit/suites/resources/jquery/jquery.tabIndex.js +++ b/tests/qunit/suites/resources/jquery/jquery.tabIndex.test.js @@ -1,4 +1,4 @@ -module( 'jquery.tabIndex.js' ); +module( 'jquery.tabIndex', QUnit.newMwEnvironment() ); test( '-- Initial check', function() { expect(2); @@ -18,14 +18,11 @@ test( 'firstTabIndex', function() { '<textarea tabindex="5">Foobar</textarea>' + '</form>'; - var $testA = $( '<div>' ).html( testEnvironment ).appendTo( 'body' ); + var $testA = $( '<div>' ).html( testEnvironment ).appendTo( '#qunit-fixture' ); strictEqual( $testA.firstTabIndex(), 2, 'First tabindex should be 2 within this context.' ); var $testB = $( '<div>' ); strictEqual( $testB.firstTabIndex(), null, 'Return null if none available.' ); - - // Clean up - $testA.add( $testB ).remove(); }); test( 'lastTabIndex', function() { @@ -39,12 +36,9 @@ test( 'lastTabIndex', function() { '<textarea tabindex="5">Foobar</textarea>' + '</form>'; - var $testA = $( '<div>' ).html( testEnvironment ).appendTo( 'body' ); + var $testA = $( '<div>' ).html( testEnvironment ).appendTo( '#qunit-fixture' ); strictEqual( $testA.lastTabIndex(), 9, 'Last tabindex should be 9 within this context.' ); var $testB = $( '<div>' ); strictEqual( $testB.lastTabIndex(), null, 'Return null if none available.' ); - - // Clean up - $testA.add( $testB ).remove(); }); diff --git a/tests/qunit/suites/resources/jquery/jquery.tablesorter.test.js b/tests/qunit/suites/resources/jquery/jquery.tablesorter.test.js index f47b7f40..7ecdc4b1 100644 --- a/tests/qunit/suites/resources/jquery/jquery.tablesorter.test.js +++ b/tests/qunit/suites/resources/jquery/jquery.tablesorter.test.js @@ -1,11 +1,13 @@ -(function() { +( function () { -module( 'jquery.tablesorter.test.js' ); +var config = { + wgMonthNames: ['', 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'], + wgMonthNamesShort: ['', 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], + wgDefaultDateFormat: 'dmy', + wgContentLanguage: 'en' +}; -// setup hack -mw.config.set('wgMonthNames', window.wgMonthNames = ['', 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']); -mw.config.set('wgMonthNamesShort', window.wgMonthNamesShort = ['', 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']); -mw.config.set('wgDefaultDateFormat', window.wgDefaultDateFormat = 'dmy'); +module( 'jquery.tablesorter', QUnit.newMwEnvironment( config ) ); test( '-- Initial check', function() { expect(1); @@ -21,23 +23,24 @@ test( '-- Initial check', function() { * @return jQuery */ var tableCreate = function( header, data ) { - var $table = $('<table class="sortable"><thead></thead><tbody></tbody></table>'), - $thead = $table.find('thead'), - $tbody = $table.find('tbody'); - var $tr = $('<tr>'); - $.each(header, function(i, str) { - var $th = $('<th>'); - $th.text(str).appendTo($tr); + var $table = $( '<table class="sortable"><thead></thead><tbody></tbody></table>' ), + $thead = $table.find( 'thead' ), + $tbody = $table.find( 'tbody' ), + $tr = $( '<tr>' ); + + $.each( header, function( i, str ) { + var $th = $( '<th>' ); + $th.text( str ).appendTo( $tr ); }); - $tr.appendTo($thead); + $tr.appendTo( $thead ); for (var i = 0; i < data.length; i++) { - $tr = $('<tr>'); - $.each(data[i], function(j, str) { - var $td = $('<td>'); - $td.text(str).appendTo($tr); + $tr = $( '<tr>' ); + $.each( data[i], function( j, str ) { + var $td = $( '<td>' ); + $td.text( str ).appendTo( $tr ); }); - $tr.appendTo($tbody); + $tr.appendTo( $tbody ); } return $table; }; @@ -50,12 +53,13 @@ var tableCreate = function( header, data ) { */ var tableExtract = function( $table ) { var data = []; - $table.find('tbody').find('tr').each(function(i, tr) { + + $table.find( 'tbody' ).find( 'tr' ).each( function( i, tr ) { var row = []; - $(tr).find('td,th').each(function(i, td) { - row.push($(td).text()); + $( tr ).find( 'td,th' ).each( function( i, td ) { + row.push( $( td ).text() ); }); - data.push(row); + data.push( row ); }); return data; }; @@ -75,7 +79,6 @@ var tableTest = function( msg, header, data, expected, callback ) { expect(1); var $table = tableCreate( header, data ); - //$('body').append($table); // Give caller a chance to set up sorting and manipulate the table. callback( $table ); @@ -93,18 +96,18 @@ var reversed = function(arr) { return arr2; }; -// Sample data set: some planets! -var header = ['Planet', 'Radius (km)'], - mercury = ['Mercury', '2439.7'], - venus = ['Venus', '6051.8'], - earth = ['Earth', '6371.0'], - mars = ['Mars', '3390.0'], - jupiter = ['Jupiter', '69911'], - saturn = ['Saturn', '58232']; +// Sample data set using planets named and their radius +var header = [ 'Planet' , 'Radius (km)'], + mercury = [ 'Mercury', '2439.7' ], + venus = [ 'Venus' , '6051.8' ], + earth = [ 'Earth' , '6371.0' ], + mars = [ 'Mars' , '3390.0' ], + jupiter = [ 'Jupiter', '69911' ], + saturn = [ 'Saturn' , '58232' ]; // Initial data set -var planets = [mercury, venus, earth, mars, jupiter, saturn]; -var ascendingName = [earth, jupiter, mars, mercury, saturn, venus]; +var planets = [mercury, venus, earth, mars, jupiter, saturn]; +var ascendingName = [earth, jupiter, mars, mercury, saturn, venus]; var ascendingRadius = [mercury, mars, venus, earth, saturn, jupiter]; tableTest( @@ -114,7 +117,7 @@ tableTest( ascendingName, function( $table ) { $table.tablesorter(); - $table.find('.headerSort:eq(0)').click(); + $table.find( '.headerSort:eq(0)' ).click(); } ); tableTest( @@ -124,7 +127,7 @@ tableTest( ascendingName, function( $table ) { $table.tablesorter(); - $table.find('.headerSort:eq(0)').click(); + $table.find( '.headerSort:eq(0)' ).click(); } ); tableTest( @@ -134,7 +137,7 @@ tableTest( reversed(ascendingName), function( $table ) { $table.tablesorter(); - $table.find('.headerSort:eq(0)').click().click(); + $table.find( '.headerSort:eq(0)' ).click().click(); } ); tableTest( @@ -144,7 +147,7 @@ tableTest( ascendingRadius, function( $table ) { $table.tablesorter(); - $table.find('.headerSort:eq(1)').click(); + $table.find( '.headerSort:eq(1)' ).click(); } ); tableTest( @@ -154,25 +157,23 @@ tableTest( reversed(ascendingRadius), function( $table ) { $table.tablesorter(); - $table.find('.headerSort:eq(1)').click().click(); + $table.find( '.headerSort:eq(1)' ).click().click(); } ); // Regression tests! tableTest( - 'Bug 28775: German-style short numeric dates', + 'Bug 28775: German-style (dmy) short numeric dates', ['Date'], - [ - // German-style dates are day-month-year + [ // German-style dates are day-month-year ['11.11.2011'], ['01.11.2011'], ['02.10.2011'], ['03.08.2011'], ['09.11.2011'] ], - [ - // Sorted by ascending date + [ // Sorted by ascending date ['03.08.2011'], ['02.10.2011'], ['01.11.2011'], @@ -180,25 +181,25 @@ tableTest( ['11.11.2011'] ], function( $table ) { - // @fixme reset it at end or change module to allow us to override it - mw.config.set('wgDefaultDateFormat', window.wgDefaultDateFormat = 'dmy'); + mw.config.set( 'wgDefaultDateFormat', 'dmy' ); + mw.config.set( 'wgContentLanguage', 'de' ); + $table.tablesorter(); - $table.find('.headerSort:eq(0)').click(); + $table.find( '.headerSort:eq(0)' ).click(); } ); + tableTest( - 'Bug 28775: American-style short numeric dates', + 'Bug 28775: American-style (mdy) short numeric dates', ['Date'], - [ - // American-style dates are month-day-year + [ // American-style dates are month-day-year ['11.11.2011'], ['01.11.2011'], ['02.10.2011'], ['03.08.2011'], ['09.11.2011'] ], - [ - // Sorted by ascending date + [ // Sorted by ascending date ['01.11.2011'], ['02.10.2011'], ['03.08.2011'], @@ -206,10 +207,10 @@ tableTest( ['11.11.2011'] ], function( $table ) { - // @fixme reset it at end or change module to allow us to override it - mw.config.set('wgDefaultDateFormat', window.wgDefaultDateFormat = 'mdy'); + mw.config.set( 'wgDefaultDateFormat', 'mdy' ); + $table.tablesorter(); - $table.find('.headerSort:eq(0)').click(); + $table.find( '.headerSort:eq(0)' ).click(); } ); @@ -235,6 +236,7 @@ var ipv4Sorted = [ ['204.204.132.158'], ['247.240.82.209'] ]; + tableTest( 'Bug 17141: IPv4 address sorting', ['IP'], @@ -242,7 +244,7 @@ tableTest( ipv4Sorted, function( $table ) { $table.tablesorter(); - $table.find('.headerSort:eq(0)').click(); + $table.find( '.headerSort:eq(0)' ).click(); } ); tableTest( @@ -252,7 +254,7 @@ tableTest( reversed(ipv4Sorted), function( $table ) { $table.tablesorter(); - $table.find('.headerSort:eq(0)').click().click(); + $table.find( '.headerSort:eq(0)' ).click().click(); } ); @@ -286,27 +288,36 @@ tableTest( umlautWords, umlautWordsSorted, function( $table ) { - mw.config.set('tableSorterCollation', {'ä':'ae', 'ö' : 'oe', 'ß': 'ss', 'ü':'ue'}); + mw.config.set( 'tableSorterCollation', { + 'ä': 'ae', + 'ö': 'oe', + 'ß': 'ss', + 'ü':'ue' + } ); + $table.tablesorter(); - $table.find('.headerSort:eq(0)').click(); - mw.config.set('tableSorterCollation', {}); + $table.find( '.headerSort:eq(0)' ).click(); } ); -var planetsRowspan =[["Earth","6051.8"], jupiter, ["Mars","6051.8"], mercury, saturn, venus]; -var planetsRowspanII =[jupiter, mercury, saturn, ['Venus', '6371.0'], venus, ['Venus', '3390.0']]; +var planetsRowspan = [["Earth","6051.8"], jupiter, ["Mars","6051.8"], mercury, saturn, venus]; +var planetsRowspanII = [jupiter, mercury, saturn, ['Venus', '6371.0'], venus, ['Venus', '3390.0']]; tableTest( - 'Basic planet table: Same value for multiple rows via rowspan', + 'Basic planet table: same value for multiple rows via rowspan', header, planets, planetsRowspan, function( $table ) { - //Quick&Dirty mod - $table.find('tr:eq(3) td:eq(1), tr:eq(4) td:eq(1)').remove(); - $table.find('tr:eq(2) td:eq(1)').attr('rowspan', '3'); + // Modify the table to have a multiuple-row-spanning cell: + // - Remove 2nd cell of 4th row, and, 2nd cell or 5th row. + $table.find( 'tr:eq(3) td:eq(1), tr:eq(4) td:eq(1)' ).remove(); + // - Set rowspan for 2nd cell of 3rd row to 3. + // This covers the removed cell in the 4th and 5th row. + $table.find( 'tr:eq(2) td:eq(1)' ).prop( 'rowspan', '3' ); + $table.tablesorter(); - $table.find('.headerSort:eq(0)').click(); + $table.find( '.headerSort:eq(0)' ).click(); } ); tableTest( @@ -315,11 +326,15 @@ tableTest( planets, planetsRowspanII, function( $table ) { - //Quick&Dirty mod - $table.find('tr:eq(3) td:eq(0), tr:eq(4) td:eq(0)').remove(); - $table.find('tr:eq(2) td:eq(0)').attr('rowspan', '3'); + // Modify the table to have a multiuple-row-spanning cell: + // - Remove 1st cell of 4th row, and, 1st cell or 5th row. + $table.find( 'tr:eq(3) td:eq(0), tr:eq(4) td:eq(0)' ).remove(); + // - Set rowspan for 1st cell of 3rd row to 3. + // This covers the removed cell in the 4th and 5th row. + $table.find( 'tr:eq(2) td:eq(0)' ).prop( 'rowspan', '3' ); + $table.tablesorter(); - $table.find('.headerSort:eq(0)').click(); + $table.find( '.headerSort:eq(0)' ).click(); } ); @@ -346,9 +361,10 @@ tableTest( complexMDYDates, complexMDYSorted, function( $table ) { - mw.config.set('wgDefaultDateFormat', window.wgDefaultDateFormat = 'mdy'); + mw.config.set( 'wgDefaultDateFormat', 'mdy' ); + $table.tablesorter(); - $table.find('.headerSort:eq(0)').click(); + $table.find( '.headerSort:eq(0)' ).click(); } ); @@ -362,9 +378,9 @@ tableTest( planets, ascendingNameLegacy, function( $table ) { - $table.find('tr:last').addClass('sortbottom'); + $table.find( 'tr:last' ).addClass( 'sortbottom' ); $table.tablesorter(); - $table.find('.headerSort:eq(0)').click(); + $table.find( '.headerSort:eq(0)' ).click(); } ); @@ -472,4 +488,65 @@ test( 'data-sort-value attribute, when available, should override sorting positi }); +var numbers = [ + [ '12' ], + [ '7' ], + [ '13,000'], + [ '9' ], + [ '14' ], + [ '8.0' ] +]; +var numbersAsc = [ + [ '7' ], + [ '8.0' ], + [ '9' ], + [ '12' ], + [ '14' ], + [ '13,000'] +]; + +tableTest( 'bug 8115: sort numbers with commas (ascending)', + ['Numbers'], numbers, numbersAsc, + function( $table ) { + $table.tablesorter(); + $table.find( '.headerSort:eq(0)' ).click(); + } +); + +tableTest( 'bug 8115: sort numbers with commas (descending)', + ['Numbers'], numbers, reversed(numbersAsc), + function( $table ) { + $table.tablesorter(); + $table.find( '.headerSort:eq(0)' ).click().click(); + } +); +// TODO add numbers sorting tests for bug 8115 with a different language + +test( 'bug 32888 - Tables inside a tableheader cell', function() { + expect(2); + + var $table; + $table = $( + '<table class="sortable" id="32888">' + + '<tr><th>header<table id="32888-2">'+ + '<tr><th>1</th><th>2</th></tr>' + + '</table></th></tr>' + + '<tr><td>A</td></tr>' + + '<tr><td>B</td></tr>' + + '</table>' + ); + $table.tablesorter(); + + equals( + $table.find('> thead:eq(0) > tr > th.headerSort').length, + 1, + 'Child tables inside a headercell should not interfere with sortable headers (bug 32888)' + ); + equals( + $('#32888-2').find('th.headerSort').length, + 0, + 'The headers of child tables inside a headercell should not be sortable themselves (bug 32888)' + ); +}); + })(); diff --git a/tests/qunit/suites/resources/jquery/jquery.textSelection.test.js b/tests/qunit/suites/resources/jquery/jquery.textSelection.test.js new file mode 100644 index 00000000..1b2f3024 --- /dev/null +++ b/tests/qunit/suites/resources/jquery/jquery.textSelection.test.js @@ -0,0 +1,279 @@ +module( 'jquery.textSelection', QUnit.newMwEnvironment() ); + +test( '-- Initial check', function() { + expect(1); + ok( $.fn.textSelection, 'jQuery.fn.textSelection defined' ); +} ); + +/** + * 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' ) + */ +var encapsulateTest = function( options ) { + var opt = $.extend({ + description: '', + before: {}, + after: {}, + replace: {} + }, options); + + opt.before = $.extend({ + text: '', + start: 0, + end: 0 + }, opt.before); + opt.after = $.extend({ + text: '', + selected: null + }, opt.after); + + test( opt.description, function() { + var tests = 1; + if ( opt.after.selected !== null ) { + tests++; + } + expect( tests ); + + var $textarea = $( '<textarea>' ); + + $( '#qunit-fixture' ).append( $textarea ); + + //$textarea.textSelection( 'setContents', opt.before.text); // this method is actually missing atm... + $textarea.val( opt.before.text ); // won't work with the WikiEditor iframe? + + var start = opt.before.start, + end = opt.before.end; + if ( window.opera ) { + // Compensate for Opera's craziness converting "\n" to "\r\n" and counting that as two chars + var newLinesBefore = opt.before.text.substring( 0, start ).split( "\n" ).length - 1, + newLinesInside = opt.before.text.substring( start, end ).split( "\n" ).length - 1; + start += newLinesBefore; + end += newLinesBefore + newLinesInside; + } + + var options = $.extend( {}, opt.replace ); // Clone opt.replace + options.selectionStart = start; + options.selectionEnd = end; + $textarea.textSelection( 'encapsulateSelection', options ); + + var text = $textarea.textSelection( 'getContents' ).replace( /\r\n/g, "\n" ); + + equal( text, opt.after.text, 'Checking full text after encapsulation' ); + + if (opt.after.selected !== null) { + var selected = $textarea.textSelection( 'getSelection' ); + equal( selected, opt.after.selected, 'Checking selected text after encapsulation.' ); + } + + } ); +}; + +var sig = { + 'pre': "--~~~~" +}, bold = { + pre: "'''", + peri: 'Bold text', + post: "'''" +}, h2 = { + 'pre': '== ', + 'peri': 'Heading 2', + 'post': ' ==', + 'regex': /^(\s*)(={1,6})(.*?)\2(\s*)$/, + 'regexReplace': "\$1==\$3==\$4", + 'ownline': true +}, ulist = { + 'pre': "* ", + 'peri': 'Bulleted list item', + 'post': "", + 'ownline': true, + 'splitlines': true +}; + +encapsulateTest({ + description: "Adding sig to end of text", + before: { + text: "Wikilove dude! ", + start: 15, + end: 15 + }, + after: { + text: "Wikilove dude! --~~~~", + selected: "" + }, + replace: sig +}); + +encapsulateTest({ + description: "Adding bold to empty", + before: { + text: "", + start: 0, + end: 0 + }, + after: { + text: "'''Bold text'''", + selected: "Bold text" // selected because it's the default + }, + replace: bold +}); + +encapsulateTest({ + description: "Adding bold to existing text", + before: { + text: "Now is the time for all good men to come to the aid of their country", + start: 20, + end: 32 + }, + after: { + text: "Now is the time for '''all good men''' to come to the aid of their country", + selected: "" // empty because it's not the default' + }, + replace: bold +}); + +encapsulateTest({ + description: "ownline option: adding new h2", + before: { + text:"Before\nAfter", + start: 7, + end: 7 + }, + after: { + text: "Before\n== Heading 2 ==\nAfter", + selected: "Heading 2" + }, + replace: h2 +}); + +encapsulateTest({ + description: "ownline option: turn a whole line into new h2", + before: { + text:"Before\nMy heading\nAfter", + start: 7, + end: 17 + }, + after: { + text: "Before\n== My heading ==\nAfter", + selected: "" + }, + replace: h2 +}); + + +encapsulateTest({ + description: "ownline option: turn a partial line into new h2", + before: { + text:"BeforeMy headingAfter", + start: 6, + end: 16 + }, + after: { + text: "Before\n== My heading ==\nAfter", + selected: "" + }, + replace: h2 +}); + + +encapsulateTest({ + description: "splitlines option: no selection, insert new list item", + before: { + text: "Before\nAfter", + start: 7, + end: 7 + }, + after: { + text: "Before\n* Bulleted list item\nAfter" + }, + replace: ulist +}); + +encapsulateTest({ + description: "splitlines option: single partial line selection, insert new list item", + before: { + text: "BeforeMy List ItemAfter", + start: 6, + end: 18 + }, + after: { + text: "Before\n* My List Item\nAfter" + }, + replace: ulist +}); + +encapsulateTest({ + description: "splitlines option: multiple lines", + before: { + text: "Before\nFirst\nSecond\nThird\nAfter", + start: 7, + end: 25 + }, + after: { + text: "Before\n* First\n* Second\n* Third\nAfter" + }, + replace: ulist +}); + + +var caretTest = function(options) { + test(options.description, function() { + expect(2); + + var $textarea = $( '<textarea>' ).text(options.text); + + $( '#qunit-fixture' ).append( $textarea ); + + if (options.mode == 'set') { + $textarea.textSelection('setSelection', { + start: options.start, + end: options.end + }); + } + + var among = function(actual, expected, message) { + if ($.isArray(expected)) { + ok($.inArray(actual, expected) !== -1 , message + ' (got ' + actual + '; expected one of ' + expected.join(', ') + ')'); + } else { + equal(actual, expected, message); + } + }; + + var 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.'); + }); +} + +var caretSample = "Some big text that we like to work with. Nothing fancy... you know what I mean?"; + +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' +}); + +caretTest({ + description: 'set/getCaretPosition with forced empty selection', + text: caretSample, + start: 7, + end: 7, + mode: 'set' +}); + +caretTest({ + description: 'set/getCaretPosition with small selection', + text: caretSample, + start: 6, + end: 11, + mode: 'set' +}); + diff --git a/tests/qunit/suites/resources/mediawiki.special/mediawiki.special.recentchanges.js b/tests/qunit/suites/resources/mediawiki.special/mediawiki.special.recentchanges.test.js index bcc9b96b..d73fe5a6 100644 --- a/tests/qunit/suites/resources/mediawiki.special/mediawiki.special.recentchanges.js +++ b/tests/qunit/suites/resources/mediawiki.special/mediawiki.special.recentchanges.test.js @@ -1,13 +1,9 @@ -module( 'mediawiki.special.recentchanges.js' ); +module( 'mediawiki.special.recentchanges', QUnit.newMwEnvironment() ); test( '-- Initial check', function() { expect( 2 ); - ok( mw.special.recentchanges.init, - 'mw.special.recentchanges.init defined' - ); - ok( mw.special.recentchanges.updateCheckboxes, - 'mw.special.recentchanges.updateCheckboxes defined' - ); + ok( mw.special.recentchanges.init, 'mw.special.recentchanges.init defined' ); + ok( mw.special.recentchanges.updateCheckboxes, 'mw.special.recentchanges.updateCheckboxes defined' ); // TODO: verify checkboxes == [ 'nsassociated', 'nsinvert' ] }); @@ -37,34 +33,34 @@ test( '"all" namespace disable checkboxes', function() { // TODO abstract the double strictEquals // At first checkboxes are enabled - strictEqual( $( '#nsinvert' ).attr( 'disabled' ), undefined ); - strictEqual( $( '#nsassociated' ).attr( 'disabled' ), undefined ); + strictEqual( $( '#nsinvert' ).prop( 'disabled' ), false ); + strictEqual( $( '#nsassociated' ).prop( 'disabled' ), false ); // Initiate the recentchanges module mw.special.recentchanges.init(); // By default - strictEqual( $( '#nsinvert' ).attr( 'disabled' ), 'disabled' ); - strictEqual( $( '#nsassociated' ).attr( 'disabled' ), 'disabled' ); + strictEqual( $( '#nsinvert' ).prop( 'disabled' ), true ); + strictEqual( $( '#nsassociated' ).prop( 'disabled' ), true ); // select second option... var $options = $( '#namespace' ).find( 'option' ); - $options.eq(0).removeAttr( 'selected' ); - $options.eq(1).attr( 'selected', 'selected' ); + $options.eq(0).removeProp( 'selected' ); + $options.eq(1).prop( 'selected', true ); $( '#namespace' ).change(); // ... and checkboxes should be enabled again - strictEqual( $( '#nsinvert' ).attr( 'disabled' ), undefined ); - strictEqual( $( '#nsassociated' ).attr( 'disabled' ), undefined ); + strictEqual( $( '#nsinvert' ).prop( 'disabled' ), false ); + strictEqual( $( '#nsassociated' ).prop( 'disabled' ), false ); // select first option ( 'all' namespace)... - $options.eq(1).removeAttr( 'selected' ); - $options.eq(0).attr( 'selected', 'selected' );; + $options.eq(1).removeProp( 'selected' ); + $options.eq(0).prop( 'selected', true ); $( '#namespace' ).change(); - + // ... and checkboxes should now be disabled - strictEqual( $( '#nsinvert' ).attr( 'disabled' ), 'disabled' ); - strictEqual( $( '#nsassociated' ).attr( 'disabled' ), 'disabled' ); + strictEqual( $( '#nsinvert' ).prop( 'disabled' ), true ); + strictEqual( $( '#nsassociated' ).prop( 'disabled' ), true ); // DOM cleanup $env.remove(); diff --git a/tests/qunit/suites/resources/mediawiki/mediawiki.Title.test.js b/tests/qunit/suites/resources/mediawiki/mediawiki.Title.test.js new file mode 100644 index 00000000..e04111f1 --- /dev/null +++ b/tests/qunit/suites/resources/mediawiki/mediawiki.Title.test.js @@ -0,0 +1,201 @@ +( function () { + +// mw.Title relies on these three config vars +// Restore them after each test run +var config = { + "wgFormattedNamespaces": { + "-2": "Media", + "-1": "Special", + "0": "", + "1": "Talk", + "2": "User", + "3": "User talk", + "4": "Wikipedia", + "5": "Wikipedia talk", + "6": "File", + "7": "File talk", + "8": "MediaWiki", + "9": "MediaWiki talk", + "10": "Template", + "11": "Template talk", + "12": "Help", + "13": "Help talk", + "14": "Category", + "15": "Category talk", + // testing custom / localized namespace + "100": "Penguins" + }, + "wgNamespaceIds": { + "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, + /* testing custom / alias */ + "penguins": 100, + "antarctic_waterfowl": 100 + }, + "wgCaseSensitiveNamespaces": [] +}; + +module( 'mediawiki.Title', QUnit.newMwEnvironment( config ) ); + +test( '-- Initial check', function () { + expect(1); + ok( mw.Title, 'mw.Title defined' ); +}); + +test( 'Transformation', function () { + expect(8); + + var title; + + title = new mw.Title( 'File:quux pif.jpg' ); + equal( title.getName(), 'Quux_pif' ); + + title = new mw.Title( 'File:Glarg_foo_glang.jpg' ); + equal( title.getNameText(), 'Glarg foo glang' ); + + title = new mw.Title( 'User:ABC.DEF' ); + equal( title.toText(), 'User:ABC.DEF' ); + equal( title.getNamespaceId(), 2 ); + equal( title.getNamespacePrefix(), 'User:' ); + + title = new mw.Title( 'uSEr:hAshAr' ); + equal( title.toText(), 'User:HAshAr' ); + equal( title.getNamespaceId(), 2 ); + + title = new mw.Title( ' MediaWiki: Foo bar .js ' ); + // Don't ask why, it's the way the backend works. One space is kept of each set + equal( title.getName(), 'Foo_bar_.js', "Merge multiple spaces to a single space." ); +}); + +test( 'Main text for filename', function () { + expect(8); + + var title = new mw.Title( 'File:foo_bar.JPG' ); + + equal( title.getNamespaceId(), 6 ); + equal( title.getNamespacePrefix(), 'File:' ); + equal( title.getName(), 'Foo_bar' ); + equal( title.getNameText(), 'Foo bar' ); + equal( title.getMain(), 'Foo_bar.JPG' ); + equal( title.getMainText(), 'Foo bar.JPG' ); + equal( title.getExtension(), 'JPG' ); + equal( title.getDotExtension(), '.JPG' ); +}); + +test( 'Namespace detection and conversion', function () { + expect(6); + + var title; + + title = new mw.Title( 'something.PDF', 6 ); + equal( title.toString(), 'File:Something.PDF' ); + + title = new mw.Title( 'NeilK', 3 ); + equal( title.toString(), 'User_talk:NeilK' ); + equal( title.toText(), 'User talk:NeilK' ); + + title = new mw.Title( 'Frobisher', 100 ); + equal( title.toString(), 'Penguins:Frobisher' ); + + title = new mw.Title( 'antarctic_waterfowl:flightless_yet_cute.jpg' ); + equal( title.toString(), 'Penguins:Flightless_yet_cute.jpg' ); + + title = new mw.Title( 'Penguins:flightless_yet_cute.jpg' ); + equal( title.toString(), 'Penguins:Flightless_yet_cute.jpg' ); +}); + +test( 'Throw error on invalid title', function () { + expect(1); + + raises(function () { + var title = new mw.Title( '' ); + }, 'Throw error on empty string' ); +}); + +test( 'Case-sensivity', function () { + expect(3); + + var title; + + // Default config + mw.config.set( 'wgCaseSensitiveNamespaces', [] ); + + title = new mw.Title( 'article' ); + 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] ); + + title = new mw.Title( 'article' ); + equal( title.toString(), 'article', '$wgCapitalLinks=false: Article namespace is sensitive, first-letter case stays lowercase' ); + + title = new mw.Title( 'john', 2 ); + equal( title.toString(), 'User:John', '$wgCapitalLinks=false: User namespace is insensitive, first-letter becomes uppercase' ); +}); + +test( 'toString / toText', function () { + expect(2); + + var title = new mw.Title( 'Some random page' ); + + equal( title.toString(), title.getPrefixedDb() ); + equal( title.toText(), title.getPrefixedText() ); +}); + +test( 'Exists', function () { + expect(3); + + var title; + + // Empty registry, checks default to null + + title = new mw.Title( 'Some random page', 4 ); + 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 ); + + title = new mw.Title( 'Project:Sandbox rules' ); + assertTrue( title.exists(), 'Return true for page titles marked as existing' ); + title = new mw.Title( 'Foobar' ); + assertFalse( title.exists(), 'Return false for page titles marked as nonexistent' ); + +}); + +test( 'Url', function () { + expect(2); + + var title; + + // Config + mw.config.set( 'wgArticlePath', '/wiki/$1' ); + + title = new mw.Title( 'Foobar' ); + equal( title.getUrl(), '/wiki/Foobar', 'Basic functionally, toString passing to wikiGetlink' ); + + title = new mw.Title( 'John Doe', 3 ); + equal( title.getUrl(), '/wiki/User_talk:John_Doe', 'Escaping in title and namespace for urls' ); +}); + +}() );
\ No newline at end of file diff --git a/tests/qunit/suites/resources/mediawiki/mediawiki.jqueryMsg.test.js b/tests/qunit/suites/resources/mediawiki/mediawiki.jqueryMsg.test.js new file mode 100644 index 00000000..265ec2ae --- /dev/null +++ b/tests/qunit/suites/resources/mediawiki/mediawiki.jqueryMsg.test.js @@ -0,0 +1,43 @@ +module( 'mediawiki.jqueryMsg' ); + +test( '-- Initial check', function() { + expect( 1 ); + ok( mw.jqueryMsg, 'mw.jqueryMsg defined' ); +} ); + +test( 'mw.jqueryMsg Plural', function() { + expect( 5 ); + var parser = mw.jqueryMsg.getMessageFunction(); + ok( parser, 'Parser Function initialized' ); + ok( mw.messages.set( 'plural-msg', 'Found $1 {{PLURAL:$1|item|items}}' ), 'mw.messages.set: Register' ); + equal( parser( 'plural-msg', 0 ) , 'Found 0 items', 'Plural test for english with zero as count' ); + equal( parser( 'plural-msg', 1 ) , 'Found 1 item', 'Singular test for english' ); + equal( parser( 'plural-msg', 2 ) , 'Found 2 items', 'Plural test for english' ); +} ); + + +test( 'mw.jqueryMsg Gender', function() { + expect( 16 ); + //TODO: These tests should be for mw.msg once mw.msg integrated with mw.jqueryMsg + var user = mw.user; + user.options.set( 'gender', 'male' ); + var parser = mw.jqueryMsg.getMessageFunction(); + ok( parser, 'Parser Function initialized' ); + //TODO: English may not be the best language for these tests. Use a language like Arabic or Russian + ok( mw.messages.set( 'gender-msg', '$1 reverted {{GENDER:$2|his|her|their}} last edit' ), 'mw.messages.set: Register' ); + equal( parser( 'gender-msg', 'Bob', 'male' ) , 'Bob reverted his last edit', 'Gender masculine' ); + equal( parser( 'gender-msg', 'Bob', user ) , 'Bob reverted his last edit', 'Gender masculine' ); + user.options.set( 'gender', 'unknown' ); + equal( parser( 'gender-msg', 'They', user ) , 'They reverted their last edit', 'Gender neutral or unknown' ); + equal( parser( 'gender-msg', 'Alice', 'female' ) , 'Alice reverted her last edit', 'Gender feminine' ); + equal( parser( 'gender-msg', 'User' ) , 'User reverted their last edit', 'Gender neutral' ); + equal( parser( 'gender-msg', 'User', 'unknown' ) , 'User reverted their last edit', 'Gender neutral' ); + ok( mw.messages.set( 'gender-msg-one-form', '{{GENDER:$1|User}} reverted last $2 {{PLURAL:$2|edit|edits}}' ), 'mw.messages.set: Register' ); + equal( parser( 'gender-msg-one-form', 'male', 10 ) , 'User reverted last 10 edits', 'Gender neutral and plural form' ); + equal( parser( 'gender-msg-one-form', 'female', 1 ) , 'User reverted last 1 edit', 'Gender neutral and singular form' ); + ok( mw.messages.set( 'gender-msg-lowercase', '{{gender:$1|he|she}} is awesome' ), 'mw.messages.set: Register' ); + equal( parser( 'gender-msg-lowercase', 'male' ) , 'he is awesome', 'Gender masculine' ); + equal( parser( 'gender-msg-lowercase', 'female' ) , 'she is awesome', 'Gender feminine' ); + ok( mw.messages.set( 'gender-msg-wrong', '{{gender}} is awesome' ), 'mw.messages.set: Register' ); + equal( parser( 'gender-msg-wrong', 'female' ) , ' is awesome', 'Wrong syntax used, but ignore the {{gender}}' ); +} ); diff --git a/tests/qunit/suites/resources/mediawiki/mediawiki.jscompat.test.js b/tests/qunit/suites/resources/mediawiki/mediawiki.jscompat.test.js index 52cd32c8..24005b64 100644 --- a/tests/qunit/suites/resources/mediawiki/mediawiki.jscompat.test.js +++ b/tests/qunit/suites/resources/mediawiki/mediawiki.jscompat.test.js @@ -1,6 +1,6 @@ /* Some misc JavaScript compatibility tests, just to make sure the environments we run in are consistent */ -module( 'mediawiki.jscompat' ); +module( 'mediawiki.jscompat', QUnit.newMwEnvironment() ); test( 'Variable with Unicode letter in name', function() { expect(3); @@ -33,3 +33,30 @@ test( 'Keyword workaround: "if" as member variable name using Unicode escapes', deepEqual( foo.\u0069\u0066, orig, 'foo.\\u0069\\u0066' ); }); */ + +test( 'Stripping of single initial newline from textarea\'s literal contents (bug 12130)', function() { + var maxn = 4; + expect(maxn * 2); + + var repeat = function(str, n) { + if (n <= 0) { + return ''; + } else { + var out = Array(n); + for (var i = 0; i < n; i++) { + out[i] = str; + } + return out.join(''); + } + }; + + for (var n = 0; n < maxn; n++) { + var expected = repeat('\n', n) + 'some text'; + + var $textarea = $('<textarea>\n' + expected + '</textarea>'); + equal($textarea.val(), expected, 'Expecting ' + n + ' newlines (HTML contained ' + (n + 1) + ')'); + + var $textarea2 = $('<textarea>').val(expected); + equal($textarea2.val(), expected, 'Expecting ' + n + ' newlines (from DOM set with ' + n + ')'); + } +}); diff --git a/tests/qunit/suites/resources/mediawiki/mediawiki.js b/tests/qunit/suites/resources/mediawiki/mediawiki.test.js index 4beed881..e6934eda 100644 --- a/tests/qunit/suites/resources/mediawiki/mediawiki.js +++ b/tests/qunit/suites/resources/mediawiki/mediawiki.test.js @@ -1,4 +1,4 @@ -module( 'mediawiki.js' ); +module( 'mediawiki', QUnit.newMwEnvironment() ); test( '-- Initial check', function() { expect(8); @@ -80,7 +80,7 @@ test( 'mw.config', function() { }); test( 'mw.message & mw.messages', function() { - expect(17); + expect(20); ok( mw.messages, 'messages defined' ); ok( mw.messages instanceof mw.Map, 'mw.messages instance of mw.Map' ); @@ -88,7 +88,7 @@ test( 'mw.message & mw.messages', function() { var hello = mw.message( 'hello' ); - equal( hello.format, 'parse', 'Message property "format" defaults to "parse"' ); + equal( hello.format, 'plain', 'Message property "format" defaults to "plain"' ); strictEqual( hello.map, mw.messages, 'Message property "map" defaults to the global instance in mw.messages' ); equal( hello.key, 'hello', 'Message property "key" (currect key)' ); deepEqual( hello.parameters, [], 'Message property "parameters" defaults to an empty array' ); @@ -111,59 +111,45 @@ test( 'mw.message & mw.messages', function() { strictEqual( hello.exists(), true, 'Message.exists returns true for existing messages' ); var goodbye = mw.message( 'goodbye' ); - strictEqual( goodbye.exists(), false, 'Message.exists returns false for inexisting messages' ); + strictEqual( goodbye.exists(), false, 'Message.exists returns false for nonexistent messages' ); equal( goodbye.plain(), '<goodbye>', 'Message.toString returns plain <key> if format is "plain" and key does not exist' ); // bug 30684 equal( goodbye.escaped(), '<goodbye>', 'Message.toString returns properly escaped <key> if format is "escaped" and key does not exist' ); + + ok( mw.messages.set( 'pluraltestmsg', 'There {{PLURAL:$1|is|are}} $1 {{PLURAL:$1|result|results}}' ), 'mw.messages.set: Register' ); + var pluralMessage = mw.message( 'pluraltestmsg' , 6 ); + equal( pluralMessage.plain(), 'There are 6 results', 'plural get resolved when format is plain' ); + equal( pluralMessage.parse(), 'There are 6 results', 'plural get resolved when format is parse' ); + }); test( 'mw.msg', function() { - expect(3); + expect(11); ok( mw.messages.set( 'hello', 'Hello <b>awesome</b> world' ), 'mw.messages.set: Register' ); - equal( mw.msg( 'hello' ), 'Hello <b>awesome</b> world', 'Gets message with default options (existing message)' ); - equal( mw.msg( 'goodbye' ), '<goodbye>', 'Gets message with default options (inexisting message)' ); -}); + equal( mw.msg( 'goodbye' ), '<goodbye>', 'Gets message with default options (nonexistent message)' ); -test( 'mw.loader', function() { - expect(5); + ok( mw.messages.set( 'plural-item' , 'Found $1 {{PLURAL:$1|item|items}}' ) ); + equal( mw.msg( 'plural-item', 5 ), 'Found 5 items', 'Apply plural for count 5' ); + equal( mw.msg( 'plural-item', 0 ), 'Found 0 items', 'Apply plural for count 0' ); + equal( mw.msg( 'plural-item', 1 ), 'Found 1 item', 'Apply plural for count 1' ); - // Regular expression to extract the path for the QUnit tests - // Takes into account that tests could be run from a file:// URL - // by excluding the 'index.html' part from the URL - var rePath = /(?:[^#\?](?!index.html))*\/?/; + ok( mw.messages.set('gender-plural-msg' , '{{GENDER:$1|he|she|they}} {{PLURAL:$2|is|are}} awesome' ) ); + equal( mw.msg( 'gender-plural-msg', 'male', 1 ), 'he is awesome', 'Gender test for male, plural count 1' ); + equal( mw.msg( 'gender-plural-msg', 'female', '1' ), 'she is awesome', 'Gender test for female, plural count 1' ); + equal( mw.msg( 'gender-plural-msg', 'unknown', 10 ), 'they are awesome', 'Gender test for neutral, plural count 10' ); - // Four assertions to test the above regular expression: - equal( - rePath.exec( 'http://path/to/tests/?foobar' )[0], - "http://path/to/tests/", - "Extracting path from http URL with query" - ); - equal( - rePath.exec( 'http://path/to/tests/#frag' )[0], - "http://path/to/tests/", - "Extracting path from http URL with fragment" - ); - equal( - rePath.exec( 'file://path/to/tests/index.html?foobar' )[0], - "file://path/to/tests/", - "Extracting path from local URL (file://) with query" - ); - equal( - rePath.exec( 'file://path/to/tests/index.html#frag' )[0], - "file://path/to/tests/", - "Extracting path from local URL (file://) with fragment" - ); +}); - // Asynchronous ahead - stop(5000); +test( 'mw.loader', function() { + expect(1); - // Extract path - var tests_path = rePath.exec( location.href ); + // Asynchronous ahead + stop(); - mw.loader.implement( 'is.awesome', [QUnit.fixurl( tests_path + 'data/defineTestCallback.js')], {}, {} ); + mw.loader.implement( 'is.awesome', [QUnit.fixurl( mw.config.get( 'wgScriptPath' ) + '/tests/qunit/data/defineTestCallback.js' )], {}, {} ); mw.loader.using( 'is.awesome', function() { @@ -185,9 +171,9 @@ test( 'mw.loader.bug29107' , function() { // Message doesn't exist already ok( !mw.messages.exists( 'bug29107' ) ); - // Async! Include a timeout, as failure in this test leads to neither the - // success nor failure callbacks getting called. - stop(5000); + // Async! Failure in this test may lead to neither the success nor error callbacks getting called. + // Due to QUnit's timeout feauture we won't hang here forever if this happends. + stop(); mw.loader.implement( 'bug29107.messages-only', [], {}, {'bug29107': 'loaded'} ); mw.loader.using( 'bug29107.messages-only', function() { @@ -199,8 +185,31 @@ test( 'mw.loader.bug29107' , function() { }); }); +test( 'mw.loader.bug30825', function() { + // This bug was actually already fixed in 1.18 and later when discovered in 1.17. + // Test is for regressions! + + expect(2); + + // Forge an URL to the test callback script + var target = QUnit.fixurl( + mw.config.get( 'wgServer' ) + mw.config.get( 'wgScriptPath' ) + '/tests/qunit/data/qunitOkCall.js' + ); + + // Confirm that mw.loader.load() works with protocol-relative URLs + target = target.replace( /https?:/, '' ); + + equal( target.substr( 0, 2 ), '//', + 'URL must be relative to test relative URLs!' + ); + + // Async! + stop(); + mw.loader.load( target ); +}); + test( 'mw.html', function() { - expect(7); + expect(11); raises( function(){ mw.html.escape(); @@ -214,11 +223,40 @@ test( 'mw.html', function() { equal( mw.html.element( 'div' ), '<div/>', 'html.element DIV (simple)' ); - equal( mw.html.element( 'div', - { id: 'foobar' } ), + equal( + mw.html.element( + 'div', { + id: 'foobar' + } + ), '<div id="foobar"/>', 'html.element DIV (attribs)' ); + equal( mw.html.element( 'p', null, 12 ), '<p>12</p>', 'Numbers are valid content and should be casted to a string' ); + + equal( mw.html.element( 'p', { title: 12 }, '' ), '<p title="12"></p>', 'Numbers are valid attribute values' ); + + equal( + mw.html.element( + 'option', { + selected: true + }, 'Foo' + ), + '<option selected="selected">Foo</option>', + 'Attributes may have boolean values. True copies the attribute name to the value.' + ); + + equal( + mw.html.element( + 'option', { + value: 'foo', + selected: false + }, 'Foo' + ), + '<option value="foo">Foo</option>', + 'Attributes may have boolean values. False keeps the attribute from output.' + ); + equal( mw.html.element( 'div', null, 'a' ), '<div>a</div>', diff --git a/tests/qunit/suites/resources/mediawiki/mediawiki.user.js b/tests/qunit/suites/resources/mediawiki/mediawiki.user.test.js index d5c6baad..15265db5 100644 --- a/tests/qunit/suites/resources/mediawiki/mediawiki.user.js +++ b/tests/qunit/suites/resources/mediawiki/mediawiki.user.test.js @@ -1,4 +1,4 @@ -module( 'mediawiki.user.js' ); +module( 'mediawiki.user', QUnit.newMwEnvironment() ); test( '-- Initial check', function() { expect(1); @@ -16,6 +16,16 @@ test( 'options', function() { test( 'User login status', function() { expect(5); + /** + * Tests can be run under three different conditions: + * 1) From tests/qunit/index.html, user will be anonymous. + * 2) Logged in on [[Special:JavaScriptTest/qunit]] + * 3) Anonymously at the same special page. + */ + + // Forge an anonymous user: + mw.config.set( 'wgUserName', null); + strictEqual( mw.user.name(), null, 'user.name should return null when anonymous' ); ok( mw.user.anonymous(), 'user.anonymous should reutrn true when anonymous' ); diff --git a/tests/qunit/suites/resources/mediawiki/mediawiki.util.js b/tests/qunit/suites/resources/mediawiki/mediawiki.util.test.js index 9c05d9b2..ea28935e 100644 --- a/tests/qunit/suites/resources/mediawiki/mediawiki.util.js +++ b/tests/qunit/suites/resources/mediawiki/mediawiki.util.test.js @@ -1,4 +1,4 @@ -module( 'mediawiki.util.js' ); +module( 'mediawiki.util', QUnit.newMwEnvironment() ); test( '-- Initial check', function() { expect(1); @@ -47,13 +47,12 @@ test( 'wikiScript', function() { equal( mw.util.wikiScript(), mw.config.get( 'wgScript' ), 'Defaults to index.php and is equal to wgScript' ); equal( mw.util.wikiScript( 'api' ), '/w/api.php', 'API path' ); - }); test( 'addCSS', function() { expect(3); - var $testEl = $( '<div>' ).attr( 'id', 'mw-addcsstest' ).appendTo( 'body' ); + var $testEl = $( '<div>' ).attr( 'id', 'mw-addcsstest' ).appendTo( '#qunit-fixture' ); var style = mw.util.addCSS( '#mw-addcsstest { visibility: hidden; }' ); equal( typeof style, 'object', 'addCSS returned an object' ); @@ -62,9 +61,7 @@ test( 'addCSS', function() { equal( $testEl.css( 'visibility' ), 'hidden', 'Added style properties are in effect' ); // Clean up - $( style.ownerNode ) - .add( $testEl ) - .remove(); + $( style.ownerNode ).remove(); }); test( 'toggleToc', function() { @@ -80,7 +77,7 @@ test( 'toggleToc', function() { '</div>' + '<ul><li></li></ul>' + '</td></tr></table>', - $toc = $(tocHtml).appendTo( 'body' ), + $toc = $(tocHtml).appendTo( '#qunit-fixture' ), $toggleLink = $( '#togglelink' ); strictEqual( $toggleLink.length, 1, 'Toggle link is appended to the page.' ); @@ -91,9 +88,6 @@ test( 'toggleToc', function() { var actionC = function() { start(); - - // Clean up - $toc.remove(); }; var actionB = function() { start(); stop(); @@ -109,18 +103,18 @@ test( 'toggleToc', function() { test( 'getParamValue', function() { expect(5); - var url1 = 'http://mediawiki.org/?foo=wrong&foo=right#&foo=bad'; + var url1 = 'http://example.org/?foo=wrong&foo=right#&foo=bad'; equal( mw.util.getParamValue( 'foo', url1 ), 'right', 'Use latest one, ignore hash' ); strictEqual( mw.util.getParamValue( 'bar', url1 ), null, 'Return null when not found' ); - var url2 = 'http://mediawiki.org/#&foo=bad'; + var url2 = 'http://example.org/#&foo=bad'; strictEqual( mw.util.getParamValue( 'foo', url2 ), null, 'Ignore hash if param is not in querystring but in hash (bug 27427)' ); - var url3 = 'example.com?' + $.param({ 'TEST': 'a b+c' }); + var url3 = 'example.org?' + $.param({ 'TEST': 'a b+c' }); strictEqual( mw.util.getParamValue( 'TEST', url3 ), 'a b+c', 'Bug 30441: getParamValue must understand "+" encoding of space' ); - var url4 = 'example.com?' + $.param({ 'TEST': 'a b+c d' }); // check for sloppy code from r95332 :) + var url4 = 'example.org?' + $.param({ 'TEST': 'a b+c d' }); // check for sloppy code from r95332 :) strictEqual( mw.util.getParamValue( 'TEST', url4 ), 'a b+c d', 'Bug 30441: getParamValue must understand "+" encoding of space (multiple spaces)' ); }); @@ -139,51 +133,55 @@ test( '$content', function() { strictEqual( mw.util.$content.length, 1, 'mw.util.$content must have length of 1' ); }); + +/** + * Portlet names are prefixed with 'p-test' to avoid conflict with core + * when running the test suite under a wiki page. + * Previously, test elements where invisible to the selector since only + * one element can have a given id. + */ test( 'addPortletLink', function() { expect(7); var mwPanel = '<div id="mw-panel" class="noprint">\ <h5>Toolbox</h5>\ - <div class="portlet" id="p-tb">\ + <div class="portlet" id="p-test-tb">\ <ul class="body"></ul>\ </div>\ </div>', - vectorTabs = '<div id="p-views" class="vectorTabs">\ + vectorTabs = '<div id="p-test-views" class="vectorTabs">\ <h5>Views</h5>\ <ul></ul>\ </div>', - $mwPanel = $(mwPanel).appendTo( 'body' ), - $vectorTabs = $(vectorTabs).appendTo( 'body' ); + $mwPanel = $(mwPanel).appendTo( '#qunit-fixture' ), + $vectorTabs = $(vectorTabs).appendTo( '#qunit-fixture' ); - var tbRL = mw.util.addPortletLink( 'p-tb', 'http://mediawiki.org/wiki/ResourceLoader', + var tbRL = mw.util.addPortletLink( 'p-test-tb', '//mediawiki.org/wiki/ResourceLoader', 'ResourceLoader', 't-rl', 'More info about ResourceLoader on MediaWiki.org ', 'l' ); ok( $.isDomElement( tbRL ), 'addPortletLink returns a valid DOM Element according to $.isDomElement' ); - var tbMW = mw.util.addPortletLink( 'p-tb', 'http://mediawiki.org/', + var tbMW = mw.util.addPortletLink( 'p-test-tb', '//mediawiki.org/', 'MediaWiki.org', 't-mworg', 'Go to MediaWiki.org ', 'm', tbRL ), $tbMW = $( tbMW ); - + equal( $tbMW.attr( 'id' ), 't-mworg', 'Link has correct ID set' ); - equal( $tbMW.closest( '.portlet' ).attr( 'id' ), 'p-tb', 'Link was inserted within correct portlet' ); + equal( $tbMW.closest( '.portlet' ).attr( 'id' ), 'p-test-tb', 'Link was inserted within correct portlet' ); equal( $tbMW.next().attr( 'id' ), 't-rl', 'Link is in the correct position (by passing nextnode)' ); - var tbRLDM = mw.util.addPortletLink( 'p-tb', 'http://mediawiki.org/wiki/RL/DM', + var tbRLDM = mw.util.addPortletLink( 'p-test-tb', '//mediawiki.org/wiki/RL/DM', 'Default modules', 't-rldm', 'List of all default modules ', 'd', '#t-rl' ); equal( $( tbRLDM ).next().attr( 'id' ), 't-rl', 'Link is in the correct position (by passing CSS selector)' ); - var caFoo = mw.util.addPortletLink( 'p-views', '#', 'Foo' ); + var caFoo = mw.util.addPortletLink( 'p-test-views', '#', 'Foo' ); strictEqual( $tbMW.find( 'span').length, 0, 'No <span> element should be added for porlets without vectorTabs class.' ); strictEqual( $( caFoo ).find( 'span').length, 1, 'A <span> element should be added for porlets with vectorTabs class.' ); - + // Clean up - $( [tbRL, tbMW, tbRLDM, caFoo] ) - .add( $mwPanel ) - .add( $vectorTabs ) - .remove(); + $( [tbRL, tbMW, tbRLDM, caFoo] ).remove(); }); test( 'jsMessage', function() { |