summaryrefslogtreecommitdiff
path: root/tests/qunit
diff options
context:
space:
mode:
Diffstat (limited to 'tests/qunit')
-rw-r--r--tests/qunit/QUnitTestResources.php52
-rw-r--r--tests/qunit/data/qunitOkCall.js2
-rw-r--r--tests/qunit/data/testrunner.js130
-rw-r--r--tests/qunit/index.html79
-rw-r--r--tests/qunit/suites/resources/jquery/jquery.autoEllipsis.test.js (renamed from tests/qunit/suites/resources/jquery/jquery.autoEllipsis.js)13
-rw-r--r--tests/qunit/suites/resources/jquery/jquery.byteLength.test.js (renamed from tests/qunit/suites/resources/jquery/jquery.byteLength.js)4
-rw-r--r--tests/qunit/suites/resources/jquery/jquery.byteLimit.test.js (renamed from tests/qunit/suites/resources/jquery/jquery.byteLimit.js)136
-rw-r--r--tests/qunit/suites/resources/jquery/jquery.client.test.js (renamed from tests/qunit/suites/resources/jquery/jquery.client.js)164
-rw-r--r--tests/qunit/suites/resources/jquery/jquery.colorUtil.test.js (renamed from tests/qunit/suites/resources/jquery/jquery.colorUtil.js)2
-rw-r--r--tests/qunit/suites/resources/jquery/jquery.delayedBind.test.js4
-rw-r--r--tests/qunit/suites/resources/jquery/jquery.getAttrs.test.js (renamed from tests/qunit/suites/resources/jquery/jquery.getAttrs.js)2
-rw-r--r--tests/qunit/suites/resources/jquery/jquery.highlightText.test.js239
-rw-r--r--tests/qunit/suites/resources/jquery/jquery.localize.test.js (renamed from tests/qunit/suites/resources/jquery/jquery.localize.js)2
-rw-r--r--tests/qunit/suites/resources/jquery/jquery.mwExtension.test.js (renamed from tests/qunit/suites/resources/jquery/jquery.mwPrototypes.js)6
-rw-r--r--tests/qunit/suites/resources/jquery/jquery.tabIndex.test.js (renamed from tests/qunit/suites/resources/jquery/jquery.tabIndex.js)12
-rw-r--r--tests/qunit/suites/resources/jquery/jquery.tablesorter.test.js227
-rw-r--r--tests/qunit/suites/resources/jquery/jquery.textSelection.test.js279
-rw-r--r--tests/qunit/suites/resources/mediawiki.special/mediawiki.special.recentchanges.test.js (renamed from tests/qunit/suites/resources/mediawiki.special/mediawiki.special.recentchanges.js)36
-rw-r--r--tests/qunit/suites/resources/mediawiki/mediawiki.Title.test.js201
-rw-r--r--tests/qunit/suites/resources/mediawiki/mediawiki.jqueryMsg.test.js43
-rw-r--r--tests/qunit/suites/resources/mediawiki/mediawiki.jscompat.test.js29
-rw-r--r--tests/qunit/suites/resources/mediawiki/mediawiki.test.js (renamed from tests/qunit/suites/resources/mediawiki/mediawiki.js)130
-rw-r--r--tests/qunit/suites/resources/mediawiki/mediawiki.user.test.js (renamed from tests/qunit/suites/resources/mediawiki/mediawiki.user.js)12
-rw-r--r--tests/qunit/suites/resources/mediawiki/mediawiki.util.test.js (renamed from tests/qunit/suites/resources/mediawiki/mediawiki.util.js)56
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(), '&lt;goodbye&gt;', 'Message.toString returns properly escaped &lt;key&gt; 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() {