summaryrefslogtreecommitdiff
path: root/tests/qunit
diff options
context:
space:
mode:
authorPierre Schmitz <pierre@archlinux.de>2015-06-04 07:31:04 +0200
committerPierre Schmitz <pierre@archlinux.de>2015-06-04 07:58:39 +0200
commitf6d65e533c62f6deb21342d4901ece24497b433e (patch)
treef28adf0362d14bcd448f7b65a7aaf38650f923aa /tests/qunit
parentc27b2e832fe25651ef2410fae85b41072aae7519 (diff)
Update to MediaWiki 1.25.1
Diffstat (limited to 'tests/qunit')
-rw-r--r--tests/qunit/QUnitTestResources.php11
-rw-r--r--tests/qunit/data/testrunner.js40
-rw-r--r--tests/qunit/suites/resources/jquery/jquery.accessKeyLabel.test.js14
-rw-r--r--tests/qunit/suites/resources/jquery/jquery.byteLimit.test.js2
-rw-r--r--tests/qunit/suites/resources/jquery/jquery.client.test.js638
-rw-r--r--tests/qunit/suites/resources/jquery/jquery.getAttrs.test.js7
-rw-r--r--tests/qunit/suites/resources/jquery/jquery.placeholder.test.js6
-rw-r--r--tests/qunit/suites/resources/jquery/jquery.tablesorter.parsers.test.js223
-rw-r--r--tests/qunit/suites/resources/jquery/jquery.tablesorter.test.js51
-rw-r--r--tests/qunit/suites/resources/mediawiki.api/mediawiki.api.options.test.js78
-rw-r--r--tests/qunit/suites/resources/mediawiki.api/mediawiki.api.test.js62
-rw-r--r--tests/qunit/suites/resources/mediawiki/mediawiki.Title.test.js59
-rw-r--r--tests/qunit/suites/resources/mediawiki/mediawiki.Uri.test.js61
-rw-r--r--tests/qunit/suites/resources/mediawiki/mediawiki.cookie.test.js19
-rw-r--r--tests/qunit/suites/resources/mediawiki/mediawiki.errorLogger.test.js42
-rw-r--r--tests/qunit/suites/resources/mediawiki/mediawiki.jqueryMsg.test.js575
-rw-r--r--tests/qunit/suites/resources/mediawiki/mediawiki.language.test.js17
-rw-r--r--tests/qunit/suites/resources/mediawiki/mediawiki.messagePoster.factory.test.js28
-rw-r--r--tests/qunit/suites/resources/mediawiki/mediawiki.template.test.js63
-rw-r--r--tests/qunit/suites/resources/mediawiki/mediawiki.test.js89
-rw-r--r--tests/qunit/suites/resources/mediawiki/mediawiki.track.test.js42
-rw-r--r--tests/qunit/suites/resources/mediawiki/mediawiki.user.test.js48
-rw-r--r--tests/qunit/suites/resources/mediawiki/mediawiki.util.test.js208
-rw-r--r--tests/qunit/suites/resources/startup.test.js1
24 files changed, 1232 insertions, 1152 deletions
diff --git a/tests/qunit/QUnitTestResources.php b/tests/qunit/QUnitTestResources.php
index 15c33f64..17b8b632 100644
--- a/tests/qunit/QUnitTestResources.php
+++ b/tests/qunit/QUnitTestResources.php
@@ -48,7 +48,6 @@ return 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.color.test.js',
'tests/qunit/suites/resources/jquery/jquery.colorUtil.test.js',
'tests/qunit/suites/resources/jquery/jquery.getAttrs.test.js',
@@ -60,18 +59,24 @@ return array(
'tests/qunit/suites/resources/jquery/jquery.placeholder.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.tablesorter.parsers.test.js',
'tests/qunit/suites/resources/jquery/jquery.textSelection.test.js',
'tests/qunit/data/mediawiki.jqueryMsg.data.js',
+ 'tests/qunit/suites/resources/mediawiki/mediawiki.errorLogger.test.js',
'tests/qunit/suites/resources/mediawiki/mediawiki.jqueryMsg.test.js',
'tests/qunit/suites/resources/mediawiki/mediawiki.jscompat.test.js',
+ 'tests/qunit/suites/resources/mediawiki/mediawiki.messagePoster.factory.test.js',
+ 'tests/qunit/suites/resources/mediawiki/mediawiki.template.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.toc.test.js',
+ 'tests/qunit/suites/resources/mediawiki/mediawiki.track.test.js',
'tests/qunit/suites/resources/mediawiki/mediawiki.Uri.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.api/mediawiki.api.test.js',
'tests/qunit/suites/resources/mediawiki.api/mediawiki.api.category.test.js',
+ 'tests/qunit/suites/resources/mediawiki.api/mediawiki.api.options.test.js',
'tests/qunit/suites/resources/mediawiki.api/mediawiki.api.parse.test.js',
'tests/qunit/suites/resources/mediawiki.api/mediawiki.api.watch.test.js',
'tests/qunit/suites/resources/mediawiki.special/mediawiki.special.recentchanges.test.js',
@@ -84,7 +89,6 @@ return array(
'jquery.autoEllipsis',
'jquery.byteLength',
'jquery.byteLimit',
- 'jquery.client',
'jquery.color',
'jquery.colorUtil',
'jquery.getAttrs',
@@ -99,13 +103,16 @@ return array(
'jquery.textSelection',
'mediawiki.api',
'mediawiki.api.category',
+ 'mediawiki.api.options',
'mediawiki.api.parse',
'mediawiki.api.watch',
'mediawiki.jqueryMsg',
+ 'mediawiki.messagePoster',
'mediawiki.Title',
'mediawiki.toc',
'mediawiki.Uri',
'mediawiki.user',
+ 'mediawiki.template',
'mediawiki.util',
'mediawiki.special.recentchanges',
'mediawiki.language',
diff --git a/tests/qunit/data/testrunner.js b/tests/qunit/data/testrunner.js
index db312b21..03aaf4af 100644
--- a/tests/qunit/data/testrunner.js
+++ b/tests/qunit/data/testrunner.js
@@ -26,32 +26,20 @@
*/
// When a test() indicates asynchronicity with stop(),
- // allow 10 seconds to pass before killing the test(),
+ // allow 30 seconds to pass before killing the test(),
// and assuming failure.
- QUnit.config.testTimeout = 10 * 1000;
+ QUnit.config.testTimeout = 30 * 1000;
+
+ QUnit.config.requireExpects = true;
// Add a checkbox to QUnit header to toggle MediaWiki ResourceLoader debug mode.
QUnit.config.urlConfig.push( {
id: 'debug',
label: 'Enable ResourceLoaderDebug',
- tooltip: 'Enable debug mode in ResourceLoader'
+ tooltip: 'Enable debug mode in ResourceLoader',
+ value: 'true'
} );
- QUnit.config.requireExpects = true;
-
- /**
- * Load TestSwarm agent
- */
- // Only if the current url indicates that there is a TestSwarm instance watching us
- // (TestSwarm appends swarmURL to the test suites url it loads in iframes).
- // Otherwise this is just a simple view of Special:JavaScriptTest/qunit directly,
- // no point in loading inject.js in that case. Also, make sure that this instance
- // of MediaWiki has actually been configured with the required url to that inject.js
- // script. By default it is false.
- if ( QUnit.urlParams.swarmURL && mw.config.get( 'QUnitTestSwarmInjectJSPath' ) ) {
- jQuery.getScript( QUnit.fixurl( mw.config.get( 'QUnitTestSwarmInjectJSPath' ) ) );
- }
-
/**
* CompletenessTest
*
@@ -256,6 +244,7 @@
},
teardown: function () {
+ var timers;
log( 'MwEnvironment> TEARDOWN for "' + QUnit.config.current.module
+ ': ' + QUnit.config.current.testName + '"' );
@@ -272,9 +261,18 @@
// Check for incomplete animations/requests/etc and throw
// error if there are any.
if ( $.timers && $.timers.length !== 0 ) {
- // Test may need to use fake timers, wait for animations or
- // call $.fx.stop().
- throw new Error( 'Unfinished animations: ' + $.timers.length );
+ timers = $.timers.length;
+ // Tests shoulld use fake timers or wait for animations to complete
+ $.each( $.timers, function ( i, timer ) {
+ var node = timer.elem;
+ mw.log.warn( 'Unfinished animation #' + i + ' in ' + timer.queue + ' queue on ' +
+ mw.html.element( node.nodeName.toLowerCase(), $(node).getAttrs() )
+ );
+ } );
+ // Force animations to stop to give the next test a clean start
+ $.fx.stop();
+
+ throw new Error( 'Unfinished animations: ' + timers );
}
if ( $.active !== undefined && $.active !== 0 ) {
// Test may need to use fake XHR, wait for requests or
diff --git a/tests/qunit/suites/resources/jquery/jquery.accessKeyLabel.test.js b/tests/qunit/suites/resources/jquery/jquery.accessKeyLabel.test.js
index f6ea1b48..4484467d 100644
--- a/tests/qunit/suites/resources/jquery/jquery.accessKeyLabel.test.js
+++ b/tests/qunit/suites/resources/jquery/jquery.accessKeyLabel.test.js
@@ -7,7 +7,7 @@
} ) );
var getAccessKeyPrefixTestData = [
- //ua string, platform string, expected prefix
+ // ua string, platform string, expected prefix
// Internet Explorer
['Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)', 'Win32', 'alt-'],
['Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)', 'Win32', 'alt-'],
@@ -27,11 +27,11 @@
['Mozilla/5.0 (Macintosh; Intel Mac OS X 10_5_8) AppleWebKit/534.30 (KHTML, like Gecko) Chrome/12.0.742.112 Safari/534.30', 'MacIntel', 'ctrl-option-'],
['Mozilla/5.0 (X11; Linux i686) AppleWebKit/534.30 (KHTML, like Gecko) Chrome/12.0.742.68 Safari/534.30', 'Linux i686', 'alt-shift-']
],
- //strings appended to title to make sure updateTooltipAccessKeys handles them correctly
+ // strings appended to title to make sure updateTooltipAccessKeys handles them correctly
updateTooltipAccessKeysTestData = [ '', ' [a]', ' [test-a]', ' [alt-b]' ];
function makeInput( title, accessKey ) {
- //The properties aren't escaped, so make sure you don't call this function with values that need to be escaped!
+ // The properties aren't escaped, so make sure you don't call this function with values that need to be escaped!
return '<input title="' + title + '" ' + ( accessKey ? 'accessKey="' + accessKey + '" ' : '' ) + ' />';
}
@@ -46,10 +46,10 @@
} );
QUnit.test( 'updateTooltipAccessKeys - current browser', 2, function ( assert ) {
- var title = $( makeInput ( 'Title', 'a' ) ).updateTooltipAccessKeys().prop( 'title' ),
- //The new title should be something like "Title [alt-a]", but the exact label will depend on the browser.
- //The "a" could be capitalized, and the prefix could be anything, e.g. a simple "^" for ctrl-
- //(no browser is known using such a short prefix, though) or "Alt+Umschalt+" in German Firefox.
+ var title = $( makeInput( 'Title', 'a' ) ).updateTooltipAccessKeys().prop( 'title' ),
+ // The new title should be something like "Title [alt-a]", but the exact label will depend on the browser.
+ // The "a" could be capitalized, and the prefix could be anything, e.g. a simple "^" for ctrl-
+ // (no browser is known using such a short prefix, though) or "Alt+Umschalt+" in German Firefox.
result = /^Title \[(.+)[aA]\]$/.exec( title );
assert.ok( result, 'title should match expected structure.' );
assert.notEqual( result[1], 'test-', 'Prefix used for testing shouldn\'t be used in production.' );
diff --git a/tests/qunit/suites/resources/jquery/jquery.byteLimit.test.js b/tests/qunit/suites/resources/jquery/jquery.byteLimit.test.js
index 22d2af19..0cb9cc81 100644
--- a/tests/qunit/suites/resources/jquery/jquery.byteLimit.test.js
+++ b/tests/qunit/suites/resources/jquery/jquery.byteLimit.test.js
@@ -60,7 +60,7 @@
);
QUnit.start();
- }, 10 );
+ } );
} );
}
diff --git a/tests/qunit/suites/resources/jquery/jquery.client.test.js b/tests/qunit/suites/resources/jquery/jquery.client.test.js
deleted file mode 100644
index c6dd91c4..00000000
--- a/tests/qunit/suites/resources/jquery/jquery.client.test.js
+++ /dev/null
@@ -1,638 +0,0 @@
-( function ( $ ) {
-
- QUnit.module( 'jquery.client', QUnit.newMwEnvironment() );
-
- var uacount = 0,
- // Object keyed by userAgent. Value is an array (human-readable name, client-profile object, navigator.platform value)
- uas = {
- // Internet Explorer 6
- // Internet Explorer 7
- 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)': {
- title: 'Internet Explorer 7',
- platform: 'Win32',
- profile: {
- name: 'msie',
- layout: 'trident',
- layoutVersion: 'unknown',
- platform: 'win',
- 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: 6,
- platform: 'win',
- version: '10.0',
- versionBase: '10',
- versionNumber: 10
- },
- wikiEditor: {
- ltr: true,
- rtl: true
- }
- },
- // Internet Explorer 11
- 'Mozilla/5.0 (Windows NT 6.3; Trident/7.0; rv 11.0) like Gecko': {
- title: 'Internet Explorer 11',
- platform: 'Win32',
- profile: {
- name: 'msie',
- layout: 'trident',
- layoutVersion: 7,
- platform: 'win',
- version: '11.0',
- versionBase: '11',
- versionNumber: 11
- },
- wikiEditor: {
- ltr: true,
- rtl: true
- }
- },
- // Internet Explorer 11 - Windows 8.1 x64 Modern UI
- 'Mozilla/5.0 (Windows NT 6.3; Win64; x64; Trident/7.0; rv:11.0) like Gecko': {
- title: 'Internet Explorer 11',
- platform: 'Win64',
- profile: {
- name: 'msie',
- layout: 'trident',
- layoutVersion: 7,
- platform: 'win',
- version: '11.0',
- versionBase: '11',
- versionNumber: 11
- },
- wikiEditor: {
- ltr: true,
- rtl: true
- }
- },
- // Internet Explorer 11 - Windows 8.1 x64 desktop UI
- 'Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko': {
- title: 'Internet Explorer 11',
- platform: 'WOW64',
- profile: {
- name: 'msie',
- layout: 'trident',
- layoutVersion: 7,
- platform: 'win',
- version: '11.0',
- versionBase: '11',
- versionNumber: 11
- },
- 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': {
- title: 'Firefox 3.5',
- platform: 'MacIntel',
- profile: {
- name: 'firefox',
- layout: 'gecko',
- layoutVersion: 20110420,
- platform: 'mac',
- version: '3.5.19',
- versionBase: '3',
- versionNumber: 3.5
- },
- wikiEditor: {
- ltr: true,
- rtl: true
- }
- },
- // Firefox 3.6
- 'Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.17) Gecko/20110422 Ubuntu/10.10 (maverick) Firefox/3.6.17': {
- title: 'Firefox 3.6',
- platform: 'Linux i686',
- profile: {
- name: 'firefox',
- layout: 'gecko',
- layoutVersion: 20110422,
- platform: 'linux',
- version: '3.6.17',
- versionBase: '3',
- versionNumber: 3.6
- },
- wikiEditor: {
- ltr: true,
- rtl: true
- }
- },
- // Firefox 4
- 'Mozilla/5.0 (Windows NT 6.0; rv:2.0.1) Gecko/20100101 Firefox/4.0.1': {
- title: 'Firefox 4',
- platform: 'Win32',
- profile: {
- name: 'firefox',
- layout: 'gecko',
- layoutVersion: 20100101,
- platform: 'win',
- 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
- }
- },
- // Iceweasel 10.0.6
- 'Mozilla/5.0 (X11; Linux i686; rv:10.0.6) Gecko/20100101 Iceweasel/10.0.6': {
- title: 'Iceweasel 10.0.6',
- platform: 'Linux',
- profile: {
- name: 'iceweasel',
- layout: 'gecko',
- layoutVersion: 20100101,
- platform: 'linux',
- version: '10.0.6',
- versionBase: '10',
- versionNumber: 10
- },
- wikiEditor: {
- ltr: true,
- rtl: true
- }
- },
- // Iceweasel 15.0.1
- 'Mozilla/5.0 (X11; Linux x86_64; rv:15.0) Gecko/20100101 Firefox/15.0.1 Iceweasel/15.0.1': {
- title: 'Iceweasel 15.0.1',
- platform: 'Linux',
- profile: {
- name: 'iceweasel',
- layout: 'gecko',
- layoutVersion: 20100101,
- platform: 'linux',
- version: '15.0.1',
- versionBase: '15',
- versionNumber: 15
- },
- wikiEditor: {
- ltr: true,
- rtl: true
- }
- },
- // Firefox 5
- // Safari 3
- // Safari 4
- 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_7; nl-nl) AppleWebKit/531.22.7 (KHTML, like Gecko) Version/4.0.5 Safari/531.22.7': {
- title: 'Safari 4',
- platform: 'MacIntel',
- profile: {
- name: 'safari',
- layout: 'webkit',
- layoutVersion: 531,
- platform: 'mac',
- 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',
- platform: 'Win32',
- profile: {
- name: 'safari',
- layout: 'webkit',
- layoutVersion: 533,
- platform: 'win',
- version: '4.0.5',
- versionBase: '4',
- versionNumber: 4
- },
- wikiEditor: {
- ltr: true,
- rtl: true
- }
- },
- // Safari 5
- // Safari 6
- 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_3) AppleWebKit/536.29.13 (KHTML, like Gecko) Version/6.0.4 Safari/536.29.13': {
- title: 'Safari 6',
- platform: 'MacIntel',
- profile: {
- name: 'safari',
- layout: 'webkit',
- layoutVersion: 536,
- platform: 'mac',
- version: '6.0.4',
- versionBase: '6',
- versionNumber: 6
- },
- wikiEditor: {
- ltr: true,
- rtl: true
- }
- },
- // Safari 6.0.5+ (doesn't have the comma in "KHTML, like Gecko")
- 'Mozilla/5.0 (Macintosh; Intel Mac OS X 1084) AppleWebKit/536.30.1 (KHTML like Gecko) Version/6.0.5 Safari/536.30.1': {
- title: 'Safari 6',
- platform: 'MacIntel',
- profile: {
- name: 'safari',
- layout: 'webkit',
- layoutVersion: 536,
- platform: 'mac',
- version: '6.0.5',
- versionBase: '6',
- versionNumber: 6
- },
- wikiEditor: {
- ltr: true,
- rtl: true
- }
- },
- // Opera 10+
- 'Opera/9.80 (Windows NT 5.1)': {
- title: 'Opera 10+ (exact version unspecified)',
- platform: 'Win32',
- profile: {
- name: 'opera',
- layout: 'presto',
- layoutVersion: 'unknown',
- platform: 'win',
- version: '10',
- versionBase: '10',
- versionNumber: 10
- },
- wikiEditor: {
- ltr: true,
- rtl: true
- }
- },
- // Opera 12
- 'Opera/9.80 (Windows NT 5.1) Presto/2.12.388 Version/12.11': {
- title: 'Opera 12',
- platform: 'Win32',
- profile: {
- name: 'opera',
- layout: 'presto',
- layoutVersion: 'unknown',
- platform: 'win',
- version: '12.11',
- versionBase: '12',
- versionNumber: 12.11
- },
- wikiEditor: {
- ltr: true,
- rtl: true
- }
- },
- // Opera 15 (WebKit-based)
- 'Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.52 Safari/537.36 OPR/15.0.1147.130': {
- title: 'Opera 15',
- platform: 'Win32',
- profile: {
- name: 'opera',
- layout: 'webkit',
- layoutVersion: 537,
- platform: 'win',
- version: '15.0.1147.130',
- versionBase: '15',
- versionNumber: 15
- },
- wikiEditor: {
- ltr: true,
- rtl: true
- }
- },
- // Chrome 5
- // Chrome 6
- // Chrome 7
- // Chrome 8
- // Chrome 9
- // Chrome 10
- // Chrome 11
- // Chrome 12
- 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_5_8) AppleWebKit/534.30 (KHTML, like Gecko) Chrome/12.0.742.112 Safari/534.30': {
- title: 'Chrome 12',
- platform: 'MacIntel',
- profile: {
- name: 'chrome',
- layout: 'webkit',
- layoutVersion: 534,
- platform: 'mac',
- 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': {
- title: 'Chrome 12',
- platform: 'Linux i686',
- profile: {
- name: 'chrome',
- layout: 'webkit',
- layoutVersion: 534,
- platform: 'linux',
- version: '12.0.742.68',
- versionBase: '12',
- versionNumber: 12
- },
- wikiEditor: {
- ltr: true,
- rtl: true
- }
- },
- // Android WebKit Browser 2.3
- 'Mozilla/5.0 (Linux; U; Android 2.3.5; en-us; HTC Vision Build/GRI40) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1': {
- title: 'Android WebKit Browser 2.3',
- platform: 'Linux armv7l',
- profile: {
- name: 'android',
- layout: 'webkit',
- layoutVersion: 533,
- platform: 'linux',
- version: '2.3.5',
- versionBase: '2',
- versionNumber: 2.3
- },
- wikiEditor: {
- ltr: true,
- rtl: true
- }
- },
- // Rekonq (bug 34924)
- 'Mozilla/5.0 (X11; Linux i686) AppleWebKit/534.34 (KHTML, like Gecko) rekonq Safari/534.34': {
- title: 'Rekonq',
- platform: 'Linux i686',
- profile: {
- name: 'rekonq',
- layout: 'webkit',
- layoutVersion: 534,
- platform: 'linux',
- version: '534.34',
- versionBase: '534',
- versionNumber: 534.34
- },
- wikiEditor: {
- ltr: true,
- rtl: true
- }
- },
- // Konqueror
- 'Mozilla/5.0 (X11; Linux i686) KHTML/4.9.1 (like Gecko) Konqueror/4.9': {
- title: 'Konqueror',
- platform: 'Linux i686',
- profile: {
- name: 'konqueror',
- layout: 'khtml',
- layoutVersion: 'unknown',
- platform: 'linux',
- version: '4.9.1',
- versionBase: '4',
- versionNumber: 4.9
- },
- wikiEditor: {
- // '4.9' is less than '4.11'.
- ltr: false,
- rtl: false
- },
- wikiEditorLegacy: {
- // The check is missing in legacyTestMap
- ltr: true,
- rtl: true
- }
- },
- // Amazon Silk
- 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_3; en-us; Silk/1.0.13.81_10003810) AppleWebKit/533.16 (KHTML, like Gecko) Version/5.0 Safari/533.16 Silk-Accelerated=true': {
- title: 'Silk',
- platform: 'Desktop',
- profile: {
- name: 'silk',
- layout: 'webkit',
- layoutVersion: 533,
- platform: 'unknown',
- version: '1.0.13.81_10003810',
- versionBase: '1',
- versionNumber: 1
- },
- wikiEditor: {
- ltr: true,
- rtl: true
- }
- },
- 'Mozilla/5.0 (Linux; U; Android 4.0.3; en-us; KFTT Build/IML74K) AppleWebKit/535.19 (KHTML, like Gecko) Silk/2.1 Mobile Safari/535.19 Silk-Accelerated=true': {
- title: 'Silk',
- platform: 'Mobile',
- profile: {
- name: 'silk',
- layout: 'webkit',
- layoutVersion: 535,
- platform: 'unknown',
- version: '2.1',
- versionBase: '2',
- versionNumber: 2.1
- },
- wikiEditor: {
- ltr: true,
- rtl: true
- }
- }
- },
- testMap = {
- // Example from WikiEditor, modified to provide version identifiers as strings and with
- // Konqueror 4.11 check added.
- 'ltr': {
- 'msie': [['>=', '7.0']],
- 'firefox': [['>=', '2']],
- 'opera': [['>=', '9.6']],
- 'safari': [['>=', '3']],
- 'chrome': [['>=', '3']],
- 'netscape': [['>=', '9']],
- 'konqueror': [['>=', '4.11']],
- 'blackberry': false,
- 'ipod': false,
- 'iphone': false
- },
- 'rtl': {
- 'msie': [['>=', '8']],
- 'firefox': [['>=', '2']],
- 'opera': [['>=', '9.6']],
- 'safari': [['>=', '3']],
- 'chrome': [['>=', '3']],
- 'netscape': [['>=', '9']],
- 'konqueror': [['>=', '4.11']],
- 'blackberry': false,
- 'ipod': false,
- 'iphone': false
- }
- },
- legacyTestMap = {
- // Original example from WikiEditor.
- // This is using the old, but still supported way of providing version identifiers as numbers
- // instead of strings; with this method, 4.9 would be considered larger than 4.11.
- '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
- }
- }
- ;
-
- // Count test cases
- $.each( uas, function () {
- uacount++;
- } );
-
- QUnit.test( 'profile( navObject )', 7, function ( assert ) {
- var p = $.client.profile();
-
- function unknownOrType( val, type, summary ) {
- assert.ok( typeof val === type || val === 'unknown', summary );
- }
-
- assert.equal( typeof p, 'object', 'profile returns an object' );
- unknownOrType( p.layout, 'string', 'p.layout is a string (or "unknown")' );
- unknownOrType( p.layoutVersion, 'number', 'p.layoutVersion is a number (or "unknown")' );
- unknownOrType( p.platform, 'string', 'p.platform is a string (or "unknown")' );
- unknownOrType( p.version, 'string', 'p.version is a string (or "unknown")' );
- unknownOrType( p.versionBase, 'string', 'p.versionBase is a string (or "unknown")' );
- assert.equal( typeof p.versionNumber, 'number', 'p.versionNumber is a number' );
- } );
-
- QUnit.test( 'profile( navObject ) - samples', uacount, function ( assert ) {
- // Loop through and run tests
- $.each( uas, function ( rawUserAgent, data ) {
- // Generate a client profile object and compare recursively
- var ret = $.client.profile( {
- userAgent: rawUserAgent,
- platform: data.platform
- } );
- assert.deepEqual( ret, data.profile, 'Client profile support check for ' + data.title + ' (' + data.platform + '): ' + rawUserAgent );
- } );
- } );
-
- QUnit.test( 'test( testMap )', 4, function ( assert ) {
- // .test() uses eval, make sure no exceptions are thrown
- // then do a basic return value type check
- var testMatch = $.client.test( testMap ),
- ie7Profile = $.client.profile( {
- 'userAgent': 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)',
- 'platform': ''
- } );
-
- assert.equal( typeof testMatch, 'boolean', 'map with ltr/rtl split returns a boolean value' );
-
- testMatch = $.client.test( testMap.ltr );
-
- assert.equal( typeof testMatch, 'boolean', 'simple map (without ltr/rtl split) returns a boolean value' );
-
- assert.equal( $.client.test( {
- 'msie': null
- }, ie7Profile ), true, 'returns true if any version of a browser are allowed (null)' );
-
- assert.equal( $.client.test( {
- 'msie': false
- }, ie7Profile ), false, 'returns false if all versions of a browser are not allowed (false)' );
- } );
-
- QUnit.test( 'test( testMap, exactMatchOnly )', 2, function ( assert ) {
- var ie7Profile = $.client.profile( {
- 'userAgent': 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)',
- 'platform': ''
- } );
-
- assert.equal( $.client.test( {
- 'firefox': [['>=', 2]]
- }, ie7Profile, false ), true, 'returns true if browser not found and exactMatchOnly not set' );
-
- assert.equal( $.client.test( {
- 'firefox': [['>=', 2]]
- }, ie7Profile, true ), false, 'returns false if browser not found and exactMatchOnly is set' );
- } );
-
- QUnit.test( 'test( testMap ), test( legacyTestMap ) - WikiEditor sample', uacount * 2 * 2, function ( assert ) {
- var $body = $( 'body' ),
- bodyClasses = $body.attr( 'class' );
-
- // Loop through and run tests
- $.each( uas, function ( agent, data ) {
- $.each( ['ltr', 'rtl'], function ( i, dir ) {
- var profile, testMatch, legacyTestMatch;
- $body.removeClass( 'ltr rtl' ).addClass( dir );
- profile = $.client.profile( {
- userAgent: agent,
- platform: data.platform
- } );
- testMatch = $.client.test( testMap, profile );
- legacyTestMatch = $.client.test( legacyTestMap, profile );
- $body.removeClass( dir );
-
- assert.equal(
- testMatch,
- data.wikiEditor[dir],
- 'testing comparison based on ' + dir + ', ' + agent
- );
- assert.equal(
- legacyTestMatch,
- data.wikiEditorLegacy ? data.wikiEditorLegacy[dir] : data.wikiEditor[dir],
- 'testing comparison based on ' + dir + ', ' + agent + ' (legacyTestMap)'
- );
- } );
- } );
-
- // Restore body classes
- $body.attr( 'class', bodyClasses );
- } );
-}( jQuery ) );
diff --git a/tests/qunit/suites/resources/jquery/jquery.getAttrs.test.js b/tests/qunit/suites/resources/jquery/jquery.getAttrs.test.js
index 0b7e87ee..ca3f418c 100644
--- a/tests/qunit/suites/resources/jquery/jquery.getAttrs.test.js
+++ b/tests/qunit/suites/resources/jquery/jquery.getAttrs.test.js
@@ -1,13 +1,14 @@
( function ( $ ) {
QUnit.module( 'jquery.getAttrs', QUnit.newMwEnvironment() );
- QUnit.test( 'Check', 1, function ( assert ) {
+ QUnit.test( 'getAttrs()', 1, function ( assert ) {
var attrs = {
foo: 'bar',
- 'class': 'lorem'
+ 'class': 'lorem',
+ 'data-foo': 'data value'
},
$el = $( '<div>' ).attr( attrs );
- assert.deepEqual( $el.getAttrs(), attrs, 'getAttrs() return object should match the attributes set, no more, no less' );
+ assert.propEqual( $el.getAttrs(), attrs, 'keys and values match' );
} );
}( jQuery ) );
diff --git a/tests/qunit/suites/resources/jquery/jquery.placeholder.test.js b/tests/qunit/suites/resources/jquery/jquery.placeholder.test.js
index bbea8297..78c185f1 100644
--- a/tests/qunit/suites/resources/jquery/jquery.placeholder.test.js
+++ b/tests/qunit/suites/resources/jquery/jquery.placeholder.test.js
@@ -1,10 +1,10 @@
-(function ($) {
+( function ($) {
QUnit.module('jquery.placeholder', QUnit.newMwEnvironment());
QUnit.test('caches results of feature tests', 2, function (assert) {
- assert.strictEqual(typeof $.fn.placeholder.input, 'boolean', '$.fn.placeholder.input');
- assert.strictEqual(typeof $.fn.placeholder.textarea, 'boolean', '$.fn.placeholder.textarea');
+ assert.strictEqual( typeof $.fn.placeholder.input, 'boolean', '$.fn.placeholder.input');
+ assert.strictEqual( typeof $.fn.placeholder.textarea, 'boolean', '$.fn.placeholder.textarea');
});
if ($.fn.placeholder.input && $.fn.placeholder.textarea) {
diff --git a/tests/qunit/suites/resources/jquery/jquery.tablesorter.parsers.test.js b/tests/qunit/suites/resources/jquery/jquery.tablesorter.parsers.test.js
new file mode 100644
index 00000000..97a3ae12
--- /dev/null
+++ b/tests/qunit/suites/resources/jquery/jquery.tablesorter.parsers.test.js
@@ -0,0 +1,223 @@
+( function ( $, mw ) {
+ /**
+ * This module tests the input/output capabilities of the parsers of tablesorter.
+ * It does not test actual sorting.
+ */
+
+ var text, ipv4,
+ simpleMDYDatesInMDY, simpleMDYDatesInDMY, oldMDYDates, complexMDYDates, clobberedDates, MYDates, YDates,
+ currencyData, transformedCurrencyData;
+
+ QUnit.module( 'jquery.tablesorter.parsers', QUnit.newMwEnvironment( {
+ setup: function () {
+ this.liveMonths = mw.language.months;
+ mw.language.months = {
+ 'keys': {
+ 'names': ['january', 'february', 'march', 'april', 'may_long', 'june',
+ 'july', 'august', 'september', 'october', 'november', 'december'],
+ 'genitive': ['january-gen', 'february-gen', 'march-gen', 'april-gen', 'may-gen', 'june-gen',
+ 'july-gen', 'august-gen', 'september-gen', 'october-gen', 'november-gen', 'december-gen'],
+ 'abbrev': ['jan', 'feb', 'mar', 'apr', 'may', 'jun',
+ 'jul', 'aug', 'sep', 'oct', 'nov', 'dec']
+ },
+ 'names': ['January', 'February', 'March', 'April', 'May', 'June',
+ 'July', 'August', 'September', 'October', 'November', 'December'],
+ 'genitive': ['January', 'February', 'March', 'April', 'May', 'June',
+ 'July', 'August', 'September', 'October', 'November', 'December'],
+ 'abbrev': ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
+ 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
+ };
+ },
+ teardown: function () {
+ mw.language.months = this.liveMonths;
+ },
+ config: {
+ wgContentLanguage: 'en',
+ /* default date format of the content language */
+ wgDefaultDateFormat: 'dmy',
+ /* These two are important for numeric interpretations */
+ wgSeparatorTransformTable: ['', ''],
+ wgDigitTransformTable: ['', '']
+ }
+ } ) );
+
+ /**
+ * For a value, check if the parser recognizes it and how it transforms it
+ *
+ * @param {String} msg text to pass on to qunit describing the test case
+ * @param {String[]} parserId of the parser that will be tested
+ * @param {String[][]} data Array of testcases. Each testcase, array of
+ * inputValue: The string value that we want to test the parser for
+ * recognized: If we expect that this value's type is detectable by the parser
+ * outputValue: The value the parser has converted the input to
+ * msg: describing the testcase
+ * @param {function($table)} callback something to do before we start the testcase
+ */
+ function parserTest( msg, parserId, data, callback ) {
+ QUnit.test( msg, data.length * 2, function ( assert ) {
+ var extractedR, extractedF, parser;
+
+ if (callback !== undefined ) {
+ callback();
+ }
+
+ parser = $.tablesorter.getParser( parserId );
+ $.each( data, function ( index, testcase ) {
+ extractedR = parser.is( testcase[0] );
+ extractedF = parser.format( testcase[0] );
+
+ assert.strictEqual( extractedR, testcase[1], 'Detect: ' + testcase[3] );
+ assert.strictEqual( extractedF, testcase[2], 'Sortkey: ' + testcase[3] );
+ } );
+
+ } );
+ }
+
+ text = [
+ [ 'Mars', true, 'mars', 'Simple text' ],
+ [ 'Mẘas', true, 'mẘas', 'Non ascii character' ],
+ [ 'A sentence', true, 'a sentence', 'A sentence with space chars' ]
+ ];
+ parserTest( 'Textual keys', 'text', text );
+
+ ipv4 = [
+ // Some randomly generated fake IPs
+ ['0.0.0.0', true, 0, 'An IP address' ],
+ ['255.255.255.255', true, 255255255255, 'An IP address' ],
+ ['45.238.27.109', true, 45238027109, 'An IP address' ],
+ ['1.238.27.1', true, 1238027001, 'An IP address with small numbers' ],
+ ['238.27.1', false, 238027001, 'A malformed IP Address' ],
+ ['1', false, 1, 'A super malformed IP Address' ],
+ ['Just text', false, 0, 'A line with just text' ],
+ ['45.238.27.109Postfix', false, 45238027109, 'An IP address with a connected postfix' ],
+ ['45.238.27.109 postfix', false, 45238027109, 'An IP address with a seperated postfix' ]
+ ];
+ parserTest( 'IPv4', 'IPAddress', ipv4 );
+
+ simpleMDYDatesInMDY = [
+ ['January 17, 2010', true, 20100117, 'Long middle endian date'],
+ ['Jan 17, 2010', true, 20100117, 'Short middle endian date'],
+ ['1/17/2010', true, 20100117, 'Numeric middle endian date'],
+ ['01/17/2010', true, 20100117, 'Numeric middle endian date with padding on month'],
+ ['01/07/2010', true, 20100107, 'Numeric middle endian date with padding on day'],
+ ['01/07/0010', true, 20100107, 'Numeric middle endian date with padding on year'],
+ ['5.12.1990', true, 19900512, 'Numeric middle endian date with . separator']
+ ];
+ parserTest( 'MDY Dates using mdy content language', 'date', simpleMDYDatesInMDY );
+
+ simpleMDYDatesInDMY = [
+ ['January 17, 2010', true, 20100117, 'Long middle endian date'],
+ ['Jan 17, 2010', true, 20100117, 'Short middle endian date'],
+ ['1/17/2010', true, 20101701, 'Numeric middle endian date'],
+ ['01/17/2010', true, 20101701, 'Numeric middle endian date with padding on month'],
+ ['01/07/2010', true, 20100701, 'Numeric middle endian date with padding on day'],
+ ['01/07/0010', true, 20100701, 'Numeric middle endian date with padding on year'],
+ ['5.12.1990', true, 19901205, 'Numeric middle endian date with . separator']
+ ];
+ parserTest( 'MDY Dates using dmy content language', 'date', simpleMDYDatesInDMY, function () {
+ mw.config.set( {
+ 'wgDefaultDateFormat': 'dmy',
+ 'wgContentLanguage': 'de'
+ } );
+ } );
+
+ oldMDYDates = [
+ ['January 19, 1400 BC', false, '99999999', 'BC'],
+ ['January 19, 1400BC', false, '99999999', 'Connected BC'],
+ ['January, 19 1400 B.C.', false, '99999999', 'B.C.'],
+ ['January 19, 1400 AD', false, '99999999', 'AD'],
+ ['January, 19 10', true, 20100119, 'AD'],
+ ['January, 19 1', false, '99999999', 'AD']
+ ];
+ parserTest( 'Very old MDY dates', 'date', oldMDYDates );
+
+ complexMDYDates = [
+ ['January, 19 2010', true, 20100119, 'Comma after month'],
+ ['January 19, 2010', true, 20100119, 'Comma after day'],
+ ['January/19/2010', true, 20100119, 'Forward slash separator'],
+ ['04 22 1991', true, 19910422, 'Month with 0 padding'],
+ ['April 21 1991', true, 19910421, 'Space separation'],
+ ['04 22 1991', true, 19910422, 'Month with 0 padding'],
+ ['December 12 \'10', true, 20101212, ''],
+ ['Dec 12 \'10', true, 20101212, ''],
+ ['Dec. 12 \'10', true, 20101212, '']
+ ];
+ parserTest( 'MDY Dates', 'date', complexMDYDates );
+
+ clobberedDates = [
+ ['January, 19 2010 - January, 20 2010', false, '99999999', 'Date range with hyphen'],
+ ['January, 19 2010 — January, 20 2010', false, '99999999', 'Date range with mdash'],
+ ['prefixJanuary, 19 2010', false, '99999999', 'Connected prefix'],
+ ['prefix January, 19 2010', false, '99999999', 'Prefix'],
+ ['December 12 2010postfix', false, '99999999', 'ConnectedPostfix'],
+ ['December 12 2010 postfix', false, '99999999', 'Postfix'],
+ ['A simple text', false, '99999999', 'Plain text in date sort'],
+ ['04l22l1991', false, '99999999', 'l char as separator'],
+ ['January\\19\\2010', false, '99999999', 'backslash as date separator']
+ ];
+ parserTest( 'Clobbered Dates', 'date', clobberedDates );
+
+ MYDates = [
+ ['December 2010', false, '99999999', 'Plain month year'],
+ ['Dec 2010', false, '99999999', 'Abreviated month year'],
+ ['12 2010', false, '99999999', 'Numeric month year']
+ ];
+ parserTest( 'MY Dates', 'date', MYDates );
+
+ YDates = [
+ ['2010', false, '99999999', 'Plain 4-digit year'],
+ ['876', false, '99999999', '3-digit year'],
+ ['76', false, '99999999', '2-digit year'],
+ ['\'76', false, '99999999', '2-digit millenium bug year'],
+ ['2010 BC', false, '99999999', '4-digit year BC']
+ ];
+ parserTest( 'Y Dates', 'date', YDates );
+
+ currencyData = [
+ ['1.02 $', true, 1.02, ''],
+ ['$ 3.00', true, 3, ''],
+ ['€ 2,99', true, 299, ''],
+ ['$ 1.00', true, 1, ''],
+ ['$3.50', true, 3.50, ''],
+ ['$ 1.50', true, 1.50, ''],
+ ['€ 0.99', true, 0.99, ''],
+ ['$ 299.99', true, 299.99, ''],
+ ['$ 2,299.99', true, 2299.99, ''],
+ ['$ 2,989', true, 2989, ''],
+ ['$ 2 299.99', true, 2299.99, ''],
+ ['$ 2 989', true, 2989, ''],
+ ['$ 2.989', true, 2.989, '']
+ ];
+ parserTest( 'Currency', 'currency', currencyData );
+
+ transformedCurrencyData = [
+ ['1.02 $', true, 102, ''],
+ ['$ 3.00', true, 300, ''],
+ ['€ 2,99', true, 2.99, ''],
+ ['$ 1.00', true, 100, ''],
+ ['$3.50', true, 350, ''],
+ ['$ 1.50', true, 150, ''],
+ ['€ 0.99', true, 99, ''],
+ ['$ 299.99', true, 29999, ''],
+ ['$ 2\'299,99', true, 2299.99, ''],
+ ['$ 2,989', true, 2.989, ''],
+ ['$ 2 299.99', true, 229999, ''],
+ ['2 989 $', true, 2989, ''],
+ ['299.99 $', true, 29999, ''],
+ ['2\'299,99 $', true, 2299.99, ''],
+ ['2,989 $', true, 2.989, ''],
+ ['2 299.99 $', true, 229999, ''],
+ ['2 989 $', true, 2989, '']
+ ];
+ parserTest( 'Currency with european separators', 'currency', transformedCurrencyData, function () {
+ mw.config.set( {
+ // We expect 22'234.444,22
+ // Map from ascii separators => localized separators
+ wgSeparatorTransformTable: [', . ,', '\' , .'],
+ wgDigitTransformTable: ['', '']
+ } );
+ } );
+
+ // TODO add numbers sorting tests for bug 8115 with a different language
+
+}( jQuery, mediaWiki ) );
diff --git a/tests/qunit/suites/resources/jquery/jquery.tablesorter.test.js b/tests/qunit/suites/resources/jquery/jquery.tablesorter.test.js
index 92dad9ff..f63aa27a 100644
--- a/tests/qunit/suites/resources/jquery/jquery.tablesorter.test.js
+++ b/tests/qunit/suites/resources/jquery/jquery.tablesorter.test.js
@@ -156,9 +156,29 @@
];
QUnit.module( 'jquery.tablesorter', QUnit.newMwEnvironment( {
+ setup: function () {
+ this.liveMonths = mw.language.months;
+ mw.language.months = {
+ 'keys': {
+ 'names': ['january', 'february', 'march', 'april', 'may_long', 'june',
+ 'july', 'august', 'september', 'october', 'november', 'december'],
+ 'genitive': ['january-gen', 'february-gen', 'march-gen', 'april-gen', 'may-gen', 'june-gen',
+ 'july-gen', 'august-gen', 'september-gen', 'october-gen', 'november-gen', 'december-gen'],
+ 'abbrev': ['jan', 'feb', 'mar', 'apr', 'may', 'jun',
+ 'jul', 'aug', 'sep', 'oct', 'nov', 'dec']
+ },
+ 'names': ['January', 'February', 'March', 'April', 'May', 'June',
+ 'July', 'August', 'September', 'October', 'November', 'December'],
+ 'genitive': ['January', 'February', 'March', 'April', 'May', 'June',
+ 'July', 'August', 'September', 'October', 'November', 'December'],
+ 'abbrev': ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
+ 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
+ };
+ },
+ teardown: function () {
+ mw.language.months = this.liveMonths;
+ },
config: {
- wgMonthNames: ['', 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
- wgMonthNamesShort: ['', 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
wgDefaultDateFormat: 'dmy',
wgSeparatorTransformTable: ['', ''],
wgDigitTransformTable: ['', ''],
@@ -1160,11 +1180,11 @@
'</table>'
);
$table.tablesorter();
- assert.equal( $table.find( '#A2' ).prop( 'headerIndex' ),
+ assert.equal( $table.find( '#A2' ).data( 'headerIndex' ),
undefined,
'A2 should not be a sort header'
);
- assert.equal( $table.find( '#C1' ).prop( 'headerIndex' ),
+ assert.equal( $table.find( '#C1' ).data( 'headerIndex' ),
2,
'C1 should be a sort header'
);
@@ -1181,11 +1201,11 @@
'</table>'
);
$table.tablesorter();
- assert.equal( $table.find( '#C2' ).prop( 'headerIndex' ),
+ assert.equal( $table.find( '#C2' ).data( 'headerIndex' ),
2,
'C2 should be a sort header'
);
- assert.equal( $table.find( '#C1' ).prop( 'headerIndex' ),
+ assert.equal( $table.find( '#C1' ).data( 'headerIndex' ),
undefined,
'C1 should not be a sort header'
);
@@ -1209,18 +1229,19 @@
// bug 53211 - exploding rowspans in more complex cases
QUnit.test(
'Rowspan exploding with row headers and colspans', 1, function ( assert ) {
- var $table = $( '<table class="sortable">' +
- '<thead><tr><th rowspan="2">n</th><th colspan="2">foo</th><th rowspan="2">baz</th></tr>' +
- '<tr><th>foo</th><th>bar</th></tr></thead>' +
- '<tbody>' +
- '<tr><td>1</td><td>foo</td><td>bar</td><td>baz</td></tr>' +
- '<tr><td>2</td><td>foo</td><td>bar</td><td>baz</td></tr>' +
- '</tbody></table>' );
+ var $table = $( '<table class="sortable">' +
+ '<thead><tr><th rowspan="2">n</th><th colspan="2">foo</th><th rowspan="2">baz</th></tr>' +
+ '<tr><th>foo</th><th>bar</th></tr></thead>' +
+ '<tbody>' +
+ '<tr><td>1</td><td>foo</td><td>bar</td><td>baz</td></tr>' +
+ '<tr><td>2</td><td>foo</td><td>bar</td><td>baz</td></tr>' +
+ '</tbody></table>' );
$table.tablesorter();
- assert.equal( $table.find( 'tr:eq(1) th:eq(1)').prop('headerIndex'),
+ assert.equal( $table.find( 'tr:eq(1) th:eq(1)').data('headerIndex'),
2,
- 'Incorrect index of sort header' );
+ 'Incorrect index of sort header'
+ );
}
);
diff --git a/tests/qunit/suites/resources/mediawiki.api/mediawiki.api.options.test.js b/tests/qunit/suites/resources/mediawiki.api/mediawiki.api.options.test.js
new file mode 100644
index 00000000..c0a6585f
--- /dev/null
+++ b/tests/qunit/suites/resources/mediawiki.api/mediawiki.api.options.test.js
@@ -0,0 +1,78 @@
+( function ( mw ) {
+ QUnit.module( 'mediawiki.api.options', QUnit.newMwEnvironment( {
+ setup: function () {
+ this.server = this.sandbox.useFakeServer();
+ }
+ } ) );
+
+ QUnit.test( 'saveOption', function ( assert ) {
+ QUnit.expect( 2 );
+
+ var
+ api = new mw.Api(),
+ stub = this.sandbox.stub( mw.Api.prototype, 'saveOptions' );
+
+ api.saveOption( 'foo', 'bar' );
+
+ assert.ok( stub.calledOnce, '#saveOptions called once' );
+ assert.deepEqual( stub.getCall( 0 ).args, [ { foo: 'bar' } ], '#saveOptions called correctly' );
+ } );
+
+ QUnit.test( 'saveOptions', function ( assert ) {
+ QUnit.expect( 13 );
+
+ var api = new mw.Api();
+
+ // We need to respond to the request for token first, otherwise the other requests won't be sent
+ // until after the server.respond call, which confuses sinon terribly. This sucks a lot.
+ api.getToken( 'options' );
+ this.server.respond(
+ /action=tokens.*&type=options/,
+ [ 200, { 'Content-Type': 'application/json' },
+ '{ "tokens": { "optionstoken": "+\\\\" } }' ]
+ );
+
+ api.saveOptions( {} ).done( function () {
+ assert.ok( true, 'Request completed: empty case' );
+ } );
+ api.saveOptions( { foo: 'bar' } ).done( function () {
+ assert.ok( true, 'Request completed: simple' );
+ } );
+ api.saveOptions( { foo: 'bar', baz: 'quux' } ).done( function () {
+ assert.ok( true, 'Request completed: two options' );
+ } );
+ api.saveOptions( { foo: 'bar|quux', bar: 'a|b|c', baz: 'quux' } ).done( function () {
+ assert.ok( true, 'Request completed: not bundleable' );
+ } );
+ api.saveOptions( { foo: null } ).done( function () {
+ assert.ok( true, 'Request completed: reset an option' );
+ } );
+ api.saveOptions( { 'foo|bar=quux': null } ).done( function () {
+ assert.ok( true, 'Request completed: reset an option, not bundleable' );
+ } );
+
+ // Requests are POST, match requestBody instead of url
+ this.server.respond( function ( request ) {
+ switch ( request.requestBody ) {
+ // simple
+ case 'action=options&format=json&change=foo%3Dbar&token=%2B%5C':
+ // two options
+ case 'action=options&format=json&change=foo%3Dbar%7Cbaz%3Dquux&token=%2B%5C':
+ // not bundleable
+ case 'action=options&format=json&optionname=foo&optionvalue=bar%7Cquux&token=%2B%5C':
+ case 'action=options&format=json&optionname=bar&optionvalue=a%7Cb%7Cc&token=%2B%5C':
+ case 'action=options&format=json&change=baz%3Dquux&token=%2B%5C':
+ // reset an option
+ case 'action=options&format=json&change=foo&token=%2B%5C':
+ // reset an option, not bundleable
+ case 'action=options&format=json&optionname=foo%7Cbar%3Dquux&token=%2B%5C':
+ assert.ok( true, 'Repond to ' + request.requestBody );
+ request.respond( 200, { 'Content-Type': 'application/json' },
+ '{ "options": "success" }' );
+ break;
+ default:
+ assert.ok( false, 'Unexpected request:' + request.requestBody );
+ }
+ } );
+ } );
+}( mediaWiki ) );
diff --git a/tests/qunit/suites/resources/mediawiki.api/mediawiki.api.test.js b/tests/qunit/suites/resources/mediawiki.api/mediawiki.api.test.js
index f156c728..b89526fb 100644
--- a/tests/qunit/suites/resources/mediawiki.api/mediawiki.api.test.js
+++ b/tests/qunit/suites/resources/mediawiki.api/mediawiki.api.test.js
@@ -51,52 +51,26 @@
this.server.respond( function ( request ) {
if ( window.FormData ) {
- assert.ok( !request.url.match( /action=/), 'Request has no query string' );
+ assert.ok( !request.url.match( /action=/ ), 'Request has no query string' );
assert.ok( request.requestBody instanceof FormData, 'Request uses FormData body' );
} else {
- assert.ok( !request.url.match( /action=test/), 'Request has no query string' );
+ assert.ok( !request.url.match( /action=test/ ), 'Request has no query string' );
assert.equal( request.requestBody, 'action=test&format=json', 'Request uses query string body' );
}
request.respond( 200, { 'Content-Type': 'application/json' }, '[]' );
} );
} );
- QUnit.test( 'Deprecated callback methods', function ( assert ) {
- QUnit.expect( 3 );
+ QUnit.test( 'Converting arrays to pipe-separated', function ( assert ) {
+ QUnit.expect( 1 );
var api = new mw.Api();
+ api.get( { test: [ 'foo', 'bar', 'baz' ] } );
- this.suppressWarnings();
-
- api.get( {}, function () {
- assert.ok( true, 'Function argument treated as success callback.' );
- } );
-
- api.get( {}, {
- ok: function () {
- assert.ok( true, '"ok" property treated as success callback.' );
- }
- } );
-
- api.get( { action: 'doesntexist' }, {
- err: function () {
- assert.ok( true, '"err" property treated as error callback.' );
- }
- } );
-
- this.restoreWarnings();
-
- this.server.respondWith( /action=query/, function ( request ) {
+ this.server.respond( function ( request ) {
+ assert.ok( request.url.match( /test=foo%7Cbar%7Cbaz/ ), 'Pipe-separated value was submitted' );
request.respond( 200, { 'Content-Type': 'application/json' }, '[]' );
} );
-
- this.server.respondWith( /action=doesntexist/, function ( request ) {
- request.respond( 200, { 'Content-Type': 'application/json' },
- '{ "error": { "code": "unknown_action" } }'
- );
- } );
-
- this.server.respond();
} );
QUnit.test( 'getToken( pre-populated )', function ( assert ) {
@@ -196,6 +170,28 @@
);
} );
+ QUnit.test( 'postWithToken( tokenType, params with assert )', function ( assert ) {
+ QUnit.expect( 2 );
+
+ var api = new mw.Api( { ajax: { url: '/postWithToken/api.php' } } );
+
+ api.postWithToken( 'testasserttoken', { action: 'example', key: 'foo', assert: 'user' } )
+ .fail( function ( errorCode ) {
+ assert.equal( errorCode, 'assertuserfailed', 'getToken fails assert' );
+ } );
+
+ assert.equal( this.server.requests.length, 1, 'Request for token made' );
+ this.server.respondWith( /assert=user/, function ( request ) {
+ request.respond(
+ 200,
+ { 'Content-Type': 'application/json' },
+ '{ "error": { "code": "assertuserfailed", "info": "Assertion failed" } }'
+ );
+ } );
+
+ this.server.respond();
+ } );
+
QUnit.test( 'postWithToken( tokenType, params, ajaxOptions )', function ( assert ) {
QUnit.expect( 3 );
diff --git a/tests/qunit/suites/resources/mediawiki/mediawiki.Title.test.js b/tests/qunit/suites/resources/mediawiki/mediawiki.Title.test.js
index 7ab309aa..c0afe07c 100644
--- a/tests/qunit/suites/resources/mediawiki/mediawiki.Title.test.js
+++ b/tests/qunit/suites/resources/mediawiki/mediawiki.Title.test.js
@@ -46,8 +46,8 @@
// Note: The ones with # are commented out as those are interpreted as fragment and
// as such end up being valid.
'A &eacute; B',
- //'A &#233; B',
- //'A &#x00E9; B',
+ // 'A &#233; B',
+ // 'A &#x00E9; B',
// Subject of NS_TALK does not roundtrip to NS_MAIN
'Talk:File:Example.svg',
// Directory navigation
@@ -437,33 +437,34 @@
} );
QUnit.test( 'getRelativeText', 5, function ( assert ) {
- var cases = [
- {
- text: 'asd',
- relativeTo: 123,
- expectedResult: ':Asd'
- },
- {
- text: 'dfg',
- relativeTo: 0,
- expectedResult: 'Dfg'
- },
- {
- text: 'Template:Ghj',
- relativeTo: 0,
- expectedResult: 'Template:Ghj'
- },
- {
- text: 'Template:1',
- relativeTo: 10,
- expectedResult: '1'
- },
- {
- text: 'User:Hi',
- relativeTo: 10,
- expectedResult: 'User:Hi'
- }
- ], i, thisCase, title;
+ var i, thisCase, title,
+ cases = [
+ {
+ text: 'asd',
+ relativeTo: 123,
+ expectedResult: ':Asd'
+ },
+ {
+ text: 'dfg',
+ relativeTo: 0,
+ expectedResult: 'Dfg'
+ },
+ {
+ text: 'Template:Ghj',
+ relativeTo: 0,
+ expectedResult: 'Template:Ghj'
+ },
+ {
+ text: 'Template:1',
+ relativeTo: 10,
+ expectedResult: '1'
+ },
+ {
+ text: 'User:Hi',
+ relativeTo: 10,
+ expectedResult: 'User:Hi'
+ }
+ ];
for ( i = 0; i < cases.length; i++ ) {
thisCase = cases[i];
diff --git a/tests/qunit/suites/resources/mediawiki/mediawiki.Uri.test.js b/tests/qunit/suites/resources/mediawiki/mediawiki.Uri.test.js
index 7a58d38d..ba366553 100644
--- a/tests/qunit/suites/resources/mediawiki/mediawiki.Uri.test.js
+++ b/tests/qunit/suites/resources/mediawiki/mediawiki.Uri.test.js
@@ -314,6 +314,66 @@
assert.equal( uri.toString(), 'http://www.example.com/dir/', 'empty array value is ommitted' );
} );
+ QUnit.test( 'Variable defaultUri', 2, function ( assert ) {
+ var uri,
+ href = 'http://example.org/w/index.php#here',
+ UriClass = mw.UriRelative( function () {
+ return href;
+ } );
+
+ uri = new UriClass();
+ assert.deepEqual(
+ {
+ protocol: uri.protocol,
+ user: uri.user,
+ password: uri.password,
+ host: uri.host,
+ port: uri.port,
+ path: uri.path,
+ query: uri.query,
+ fragment: uri.fragment
+ },
+ {
+ protocol: 'http',
+ user: undefined,
+ password: undefined,
+ host: 'example.org',
+ port: undefined,
+ path: '/w/index.php',
+ query: {},
+ fragment: 'here'
+ },
+ 'basic object properties'
+ );
+
+ // Default URI may change, e.g. via history.replaceState, pushState or location.hash (T74334)
+ href = 'https://example.com/wiki/Foo?v=2';
+ uri = new UriClass();
+ assert.deepEqual(
+ {
+ protocol: uri.protocol,
+ user: uri.user,
+ password: uri.password,
+ host: uri.host,
+ port: uri.port,
+ path: uri.path,
+ query: uri.query,
+ fragment: uri.fragment
+ },
+ {
+ protocol: 'https',
+ user: undefined,
+ password: undefined,
+ host: 'example.com',
+ port: undefined,
+ path: '/wiki/Foo',
+ query: { 'v': '2' },
+ fragment: undefined
+ },
+ 'basic object properties'
+ );
+ } );
+
QUnit.test( 'Advanced URL', 11, function ( assert ) {
var uri, queryString, relativePath;
@@ -429,6 +489,5 @@
uri = new UriClass( testPath );
href = uri.toString();
assert.equal( href, testProtocol + testServer + ':' + testPort + testPath, 'Root-relative URL gets host, protocol, and port supplied' );
-
} );
}( mediaWiki, jQuery ) );
diff --git a/tests/qunit/suites/resources/mediawiki/mediawiki.cookie.test.js b/tests/qunit/suites/resources/mediawiki/mediawiki.cookie.test.js
index c9653dab..f5f199ea 100644
--- a/tests/qunit/suites/resources/mediawiki/mediawiki.cookie.test.js
+++ b/tests/qunit/suites/resources/mediawiki/mediawiki.cookie.test.js
@@ -53,7 +53,7 @@
assert.strictEqual( call[ 1 ], '0', '0 is value' );
} );
- QUnit.test( 'set( key, value, expires )', 5, function ( assert ) {
+ QUnit.test( 'set( key, value, expires )', 6, function ( assert ) {
var date, options;
date = new Date();
@@ -61,15 +61,22 @@
mw.cookie.set( 'foo', 'bar' );
options = $.cookie.lastCall.args[ 2 ];
- assert.deepEqual( options.expires, expiryDate, 'Default cookie expiration is used' );
+ assert.deepEqual( options.expires, expiryDate, 'default expiration' );
mw.cookie.set( 'foo', 'bar', date );
options = $.cookie.lastCall.args[ 2 ];
- assert.strictEqual( options.expires, date, 'Custom expiration date' );
+ assert.strictEqual( options.expires, date, 'custom expiration as Date' );
+
+ date = new Date();
+ date.setDate( date.getDate() + 1 );
+
+ mw.cookie.set( 'foo', 'bar', 86400 );
+ options = $.cookie.lastCall.args[ 2 ];
+ assert.deepEqual( options.expires, date, 'custom expiration as lifetime in seconds' );
mw.cookie.set( 'foo', 'bar', null );
options = $.cookie.lastCall.args[ 2 ];
- assert.strictEqual( options.expires, undefined, 'Expiry null forces session cookie' );
+ assert.strictEqual( options.expires, undefined, 'null forces session cookie' );
// Per DefaultSettings.php, when wgCookieExpiration is 0, the default should
// be session cookies
@@ -81,7 +88,7 @@
mw.cookie.set( 'foo', 'bar', date );
options = $.cookie.lastCall.args[ 2 ];
- assert.strictEqual( options.expires, date, 'Custom expiration when default is session cookies' );
+ assert.strictEqual( options.expires, date, 'custom expiration (with wgCookieExpiration=0)' );
} );
QUnit.test( 'set( key, value, options )', 4, function ( assert ) {
@@ -169,4 +176,4 @@
assert.strictEqual( key, 'barfoo' );
} );
-} ( mediaWiki, jQuery ) );
+}( mediaWiki, jQuery ) );
diff --git a/tests/qunit/suites/resources/mediawiki/mediawiki.errorLogger.test.js b/tests/qunit/suites/resources/mediawiki/mediawiki.errorLogger.test.js
new file mode 100644
index 00000000..7c3f1ecb
--- /dev/null
+++ b/tests/qunit/suites/resources/mediawiki/mediawiki.errorLogger.test.js
@@ -0,0 +1,42 @@
+( function ( $, mw ) {
+ QUnit.module( 'mediawiki.errorLogger', QUnit.newMwEnvironment() );
+
+ QUnit.test( 'installGlobalHandler', 7, function ( assert ) {
+ var w = {},
+ errorMessage = 'Foo',
+ errorUrl = 'http://example.com',
+ errorLine = '123',
+ errorColumn = '45',
+ errorObject = new Error( 'Foo'),
+ oldHandler = this.sandbox.stub();
+
+ this.sandbox.stub( mw, 'track' );
+
+ mw.errorLogger.installGlobalHandler( w );
+
+ assert.ok( w.onerror, 'Global handler has been installed' );
+ assert.strictEqual( w.onerror( errorMessage, errorUrl, errorLine ), false,
+ 'Global handler returns false when there is no previous handler' );
+ sinon.assert.calledWithExactly( mw.track, 'global.error',
+ sinon.match( { errorMessage: errorMessage, url: errorUrl, lineNumber: errorLine } ) );
+
+ mw.track.reset();
+ w.onerror( errorMessage, errorUrl, errorLine, errorColumn, errorObject );
+ sinon.assert.calledWithExactly( mw.track, 'global.error',
+ sinon.match( { errorMessage: errorMessage, url: errorUrl, lineNumber: errorLine,
+ columnNumber: errorColumn, errorObject: errorObject } ) );
+
+ w = { onerror: oldHandler };
+
+ mw.errorLogger.installGlobalHandler( w );
+ w.onerror( errorMessage, errorUrl, errorLine );
+ sinon.assert.calledWithExactly( oldHandler, errorMessage, errorUrl, errorLine );
+
+ oldHandler.returns( false );
+ assert.strictEqual( w.onerror( errorMessage, errorUrl, errorLine ), false,
+ 'Global handler preserves false return from previous handler' );
+ oldHandler.returns( true );
+ assert.strictEqual( w.onerror( errorMessage, errorUrl, errorLine ), true,
+ 'Global handler preserves true return from previous handler' );
+ } );
+}( jQuery, mediaWiki ) );
diff --git a/tests/qunit/suites/resources/mediawiki/mediawiki.jqueryMsg.test.js b/tests/qunit/suites/resources/mediawiki/mediawiki.jqueryMsg.test.js
index 6b3be43b..7e23e2ff 100644
--- a/tests/qunit/suites/resources/mediawiki/mediawiki.jqueryMsg.test.js
+++ b/tests/qunit/suites/resources/mediawiki/mediawiki.jqueryMsg.test.js
@@ -55,7 +55,9 @@
'jquerymsg-test-version-entrypoints-index-php': '[https://www.mediawiki.org/wiki/Manual:index.php index.php]',
- 'external-link-replace': 'Foo [$1 bar]'
+ 'external-link-replace': 'Foo [$1 bar]',
+ 'external-link-plural': 'Foo {{PLURAL:$1|is [$2 one]|are [$2 some]|2=[$2 two]|3=three|4=a=b|5=}} things.',
+ 'plural-only-explicit-forms': 'It is a {{PLURAL:$1|1=single|2=double}} room.'
}
} ) );
@@ -107,7 +109,7 @@
run();
}
- QUnit.test( 'Replace', 9, function ( assert ) {
+ QUnit.test( 'Replace', 16, function ( assert ) {
mw.messages.set( 'simple', 'Foo $1 baz $2' );
assert.equal( formatParse( 'simple' ), 'Foo $1 baz $2', 'Replacements with no substitutes' );
@@ -155,6 +157,41 @@
'Foo <a href="http://example.org/?x=y&amp;z">bar</a>',
'Href is not double-escaped in wikilink function'
);
+ assert.equal(
+ formatParse( 'external-link-plural', 1, 'http://example.org' ),
+ 'Foo is <a href="http://example.org">one</a> things.',
+ 'Link is expanded inside plural and is not escaped html'
+ );
+ assert.equal(
+ formatParse( 'external-link-plural', 2, 'http://example.org' ),
+ 'Foo <a href=\"http://example.org\">two</a> things.',
+ 'Link is expanded inside an explicit plural form and is not escaped html'
+ );
+ assert.equal(
+ formatParse( 'external-link-plural', 3 ),
+ 'Foo three things.',
+ 'A simple explicit plural form co-existing with complex explicit plural forms'
+ );
+ assert.equal(
+ formatParse( 'external-link-plural', 4, 'http://example.org' ),
+ 'Foo a=b things.',
+ 'Only first equal sign is used as delimiter for explicit plural form. Repeated equal signs does not create issue'
+ );
+ assert.equal(
+ formatParse( 'external-link-plural', 5, 'http://example.org' ),
+ 'Foo are <a href="http://example.org">some</a> things.',
+ 'Invalid explicit plural form. Plural fallback to the "other" plural form'
+ );
+ assert.equal(
+ formatParse( 'external-link-plural', 6, 'http://example.org' ),
+ 'Foo are <a href="http://example.org">some</a> things.',
+ 'Plural fallback to the "other" plural form'
+ );
+ assert.equal(
+ formatParse( 'plural-only-explicit-forms', 2 ),
+ 'It is a double room.',
+ 'Plural with explicit forms alone.'
+ );
} );
QUnit.test( 'Plural', 6, function ( assert ) {
@@ -505,274 +542,274 @@
mw.jqueryMsg.getMessageFunction = oldGMF;
} );
-formatnumTests = [
- {
- lang: 'en',
- number: 987654321.654321,
- result: '987,654,321.654',
- description: 'formatnum test for English, decimal seperator'
- },
- {
- lang: 'ar',
- number: 987654321.654321,
- result: '٩٨٧٬٦٥٤٬٣٢١٫٦٥٤',
- description: 'formatnum test for Arabic, with decimal seperator'
- },
- {
- lang: 'ar',
- number: '٩٨٧٦٥٤٣٢١٫٦٥٤٣٢١',
- result: 987654321,
- integer: true,
- description: 'formatnum test for Arabic, with decimal seperator, reverse'
- },
- {
- lang: 'ar',
- number: -12.89,
- result: '-١٢٫٨٩',
- description: 'formatnum test for Arabic, negative number'
- },
- {
- lang: 'ar',
- number: '-١٢٫٨٩',
- result: -12,
- integer: true,
- description: 'formatnum test for Arabic, negative number, reverse'
- },
- {
- lang: 'nl',
- number: 987654321.654321,
- result: '987.654.321,654',
- description: 'formatnum test for Nederlands, decimal seperator'
- },
- {
- lang: 'nl',
- number: -12.89,
- result: '-12,89',
- description: 'formatnum test for Nederlands, negative number'
- },
- {
- lang: 'nl',
- number: '.89',
- result: '0,89',
- description: 'formatnum test for Nederlands'
- },
- {
- lang: 'nl',
- number: 'invalidnumber',
- result: 'invalidnumber',
- description: 'formatnum test for Nederlands, invalid number'
- },
- {
- lang: 'ml',
- number: '1000000000',
- result: '1,00,00,00,000',
- description: 'formatnum test for Malayalam'
- },
- {
- lang: 'ml',
- number: '-1000000000',
- result: '-1,00,00,00,000',
- description: 'formatnum test for Malayalam, negative number'
- },
- /*
- * This will fail because of wrong pattern for ml in MW(different from CLDR)
- {
- lang: 'ml',
- number: '1000000000.000',
- result: '1,00,00,00,000.000',
- description: 'formatnum test for Malayalam with decimal place'
- },
- */
- {
- lang: 'hi',
- number: '123456789.123456789',
- result: '१२,३४,५६,७८९',
- description: 'formatnum test for Hindi'
- },
- {
- lang: 'hi',
- number: '१२,३४,५६,७८९',
- result: '१२,३४,५६,७८९',
- description: 'formatnum test for Hindi, Devanagari digits passed'
- },
- {
- lang: 'hi',
- number: '१२३४५६,७८९',
- result: '123456',
- integer: true,
- description: 'formatnum test for Hindi, Devanagari digits passed to get integer value'
- }
-];
-
-QUnit.test( 'formatnum', formatnumTests.length, function ( assert ) {
- mw.messages.set( 'formatnum-msg', '{{formatnum:$1}}' );
- mw.messages.set( 'formatnum-msg-int', '{{formatnum:$1|R}}' );
- var queue = $.map( formatnumTests, function ( test ) {
- return function ( next ) {
- getMwLanguage( test.lang )
- .done( function ( langClass ) {
- mw.config.set( 'wgUserLanguage', test.lang );
- var parser = new mw.jqueryMsg.parser( { language: langClass } );
- assert.equal(
- parser.parse( test.integer ? 'formatnum-msg-int' : 'formatnum-msg',
- [ test.number ] ).html(),
- test.result,
- test.description
- );
- } )
- .fail( function () {
- assert.ok( false, 'Language "' + test.lang + '" failed to load' );
- } )
- .always( next );
- };
+ formatnumTests = [
+ {
+ lang: 'en',
+ number: 987654321.654321,
+ result: '987,654,321.654',
+ description: 'formatnum test for English, decimal separator'
+ },
+ {
+ lang: 'ar',
+ number: 987654321.654321,
+ result: '٩٨٧٬٦٥٤٬٣٢١٫٦٥٤',
+ description: 'formatnum test for Arabic, with decimal separator'
+ },
+ {
+ lang: 'ar',
+ number: '٩٨٧٦٥٤٣٢١٫٦٥٤٣٢١',
+ result: 987654321,
+ integer: true,
+ description: 'formatnum test for Arabic, with decimal separator, reverse'
+ },
+ {
+ lang: 'ar',
+ number: -12.89,
+ result: '-١٢٫٨٩',
+ description: 'formatnum test for Arabic, negative number'
+ },
+ {
+ lang: 'ar',
+ number: '-١٢٫٨٩',
+ result: -12,
+ integer: true,
+ description: 'formatnum test for Arabic, negative number, reverse'
+ },
+ {
+ lang: 'nl',
+ number: 987654321.654321,
+ result: '987.654.321,654',
+ description: 'formatnum test for Nederlands, decimal separator'
+ },
+ {
+ lang: 'nl',
+ number: -12.89,
+ result: '-12,89',
+ description: 'formatnum test for Nederlands, negative number'
+ },
+ {
+ lang: 'nl',
+ number: '.89',
+ result: '0,89',
+ description: 'formatnum test for Nederlands'
+ },
+ {
+ lang: 'nl',
+ number: 'invalidnumber',
+ result: 'invalidnumber',
+ description: 'formatnum test for Nederlands, invalid number'
+ },
+ {
+ lang: 'ml',
+ number: '1000000000',
+ result: '1,00,00,00,000',
+ description: 'formatnum test for Malayalam'
+ },
+ {
+ lang: 'ml',
+ number: '-1000000000',
+ result: '-1,00,00,00,000',
+ description: 'formatnum test for Malayalam, negative number'
+ },
+ /*
+ * This will fail because of wrong pattern for ml in MW(different from CLDR)
+ {
+ lang: 'ml',
+ number: '1000000000.000',
+ result: '1,00,00,00,000.000',
+ description: 'formatnum test for Malayalam with decimal place'
+ },
+ */
+ {
+ lang: 'hi',
+ number: '123456789.123456789',
+ result: '१२,३४,५६,७८९',
+ description: 'formatnum test for Hindi'
+ },
+ {
+ lang: 'hi',
+ number: '१२,३४,५६,७८९',
+ result: '१२,३४,५६,७८९',
+ description: 'formatnum test for Hindi, Devanagari digits passed'
+ },
+ {
+ lang: 'hi',
+ number: '१२३४५६,७८९',
+ result: '123456',
+ integer: true,
+ description: 'formatnum test for Hindi, Devanagari digits passed to get integer value'
+ }
+ ];
+
+ QUnit.test( 'formatnum', formatnumTests.length, function ( assert ) {
+ mw.messages.set( 'formatnum-msg', '{{formatnum:$1}}' );
+ mw.messages.set( 'formatnum-msg-int', '{{formatnum:$1|R}}' );
+ var queue = $.map( formatnumTests, function ( test ) {
+ return function ( next ) {
+ getMwLanguage( test.lang )
+ .done( function ( langClass ) {
+ mw.config.set( 'wgUserLanguage', test.lang );
+ var parser = new mw.jqueryMsg.parser( { language: langClass } );
+ assert.equal(
+ parser.parse( test.integer ? 'formatnum-msg-int' : 'formatnum-msg',
+ [ test.number ] ).html(),
+ test.result,
+ test.description
+ );
+ } )
+ .fail( function () {
+ assert.ok( false, 'Language "' + test.lang + '" failed to load' );
+ } )
+ .always( next );
+ };
+ } );
+ QUnit.stop();
+ process( queue, QUnit.start );
+ } );
+
+ // HTML in wikitext
+ QUnit.test( 'HTML', 26, function ( assert ) {
+ mw.messages.set( 'jquerymsg-italics-msg', '<i>Very</i> important' );
+
+ assertBothModes( assert, ['jquerymsg-italics-msg'], mw.messages.get( 'jquerymsg-italics-msg' ), 'Simple italics unchanged' );
+
+ mw.messages.set( 'jquerymsg-bold-msg', '<b>Strong</b> speaker' );
+ assertBothModes( assert, ['jquerymsg-bold-msg'], mw.messages.get( 'jquerymsg-bold-msg' ), 'Simple bold unchanged' );
+
+ mw.messages.set( 'jquerymsg-bold-italics-msg', 'It is <b><i>key</i></b>' );
+ assertBothModes( assert, ['jquerymsg-bold-italics-msg'], mw.messages.get( 'jquerymsg-bold-italics-msg' ), 'Bold and italics nesting order preserved' );
+
+ mw.messages.set( 'jquerymsg-italics-bold-msg', 'It is <i><b>vital</b></i>' );
+ assertBothModes( assert, ['jquerymsg-italics-bold-msg'], mw.messages.get( 'jquerymsg-italics-bold-msg' ), 'Italics and bold nesting order preserved' );
+
+ mw.messages.set( 'jquerymsg-italics-with-link', 'An <i>italicized [[link|wiki-link]]</i>' );
+
+ assert.htmlEqual(
+ formatParse( 'jquerymsg-italics-with-link' ),
+ 'An <i>italicized <a title="link" href="' + mw.html.escape( mw.util.getUrl( 'link' ) ) + '">wiki-link</i>',
+ 'Italics with link inside in parse mode'
+ );
+
+ assert.equal(
+ formatText( 'jquerymsg-italics-with-link' ),
+ mw.messages.get( 'jquerymsg-italics-with-link' ),
+ 'Italics with link unchanged in text mode'
+ );
+
+ mw.messages.set( 'jquerymsg-italics-id-class', '<i id="foo" class="bar">Foo</i>' );
+ assert.htmlEqual(
+ formatParse( 'jquerymsg-italics-id-class' ),
+ mw.messages.get( 'jquerymsg-italics-id-class' ),
+ 'ID and class are allowed'
+ );
+
+ mw.messages.set( 'jquerymsg-italics-onclick', '<i onclick="alert(\'foo\')">Foo</i>' );
+ assert.htmlEqual(
+ formatParse( 'jquerymsg-italics-onclick' ),
+ '&lt;i onclick=&quot;alert(\'foo\')&quot;&gt;Foo&lt;/i&gt;',
+ 'element with onclick is escaped because it is not allowed'
+ );
+
+ mw.messages.set( 'jquerymsg-script-msg', '<script >alert( "Who put this tag here?" );</script>' );
+ assert.htmlEqual(
+ formatParse( 'jquerymsg-script-msg' ),
+ '&lt;script &gt;alert( &quot;Who put this tag here?&quot; );&lt;/script&gt;',
+ 'Tag outside whitelist escaped in parse mode'
+ );
+
+ assert.equal(
+ formatText( 'jquerymsg-script-msg' ),
+ mw.messages.get( 'jquerymsg-script-msg' ),
+ 'Tag outside whitelist unchanged in text mode'
+ );
+
+ mw.messages.set( 'jquerymsg-script-link-msg', '<script>[[Foo|bar]]</script>' );
+ assert.htmlEqual(
+ formatParse( 'jquerymsg-script-link-msg' ),
+ '&lt;script&gt;<a title="Foo" href="' + mw.html.escape( mw.util.getUrl( 'Foo' ) ) + '">bar</a>&lt;/script&gt;',
+ 'Script tag text is escaped because that element is not allowed, but link inside is still HTML'
+ );
+
+ mw.messages.set( 'jquerymsg-mismatched-html', '<i class="important">test</b>' );
+ assert.htmlEqual(
+ formatParse( 'jquerymsg-mismatched-html' ),
+ '&lt;i class=&quot;important&quot;&gt;test&lt;/b&gt;',
+ 'Mismatched HTML start and end tag treated as text'
+ );
+
+ // TODO (mattflaschen, 2013-03-18): It's not a security issue, but there's no real
+ // reason the htmlEmitter span needs to be here. It's an artifact of how emitting works.
+ mw.messages.set( 'jquerymsg-script-and-external-link', '<script>alert( "jquerymsg-script-and-external-link test" );</script> [http://example.com <i>Foo</i> bar]' );
+ assert.htmlEqual(
+ formatParse( 'jquerymsg-script-and-external-link' ),
+ '&lt;script&gt;alert( "jquerymsg-script-and-external-link test" );&lt;/script&gt; <a href="http://example.com"><span class="mediaWiki_htmlEmitter"><i>Foo</i> bar</span></a>',
+ 'HTML tags in external links not interfering with escaping of other tags'
+ );
+
+ mw.messages.set( 'jquerymsg-link-script', '[http://example.com <script>alert( "jquerymsg-link-script test" );</script>]' );
+ assert.htmlEqual(
+ formatParse( 'jquerymsg-link-script' ),
+ '<a href="http://example.com"><span class="mediaWiki_htmlEmitter">&lt;script&gt;alert( "jquerymsg-link-script test" );&lt;/script&gt;</span></a>',
+ 'Non-whitelisted HTML tag in external link anchor treated as text'
+ );
+
+ // Intentionally not using htmlEqual for the quote tests
+ mw.messages.set( 'jquerymsg-double-quotes-preserved', '<i id="double">Double</i>' );
+ assert.equal(
+ formatParse( 'jquerymsg-double-quotes-preserved' ),
+ mw.messages.get( 'jquerymsg-double-quotes-preserved' ),
+ 'Attributes with double quotes are preserved as such'
+ );
+
+ mw.messages.set( 'jquerymsg-single-quotes-normalized-to-double', '<i id=\'single\'>Single</i>' );
+ assert.equal(
+ formatParse( 'jquerymsg-single-quotes-normalized-to-double' ),
+ '<i id="single">Single</i>',
+ 'Attributes with single quotes are normalized to double'
+ );
+
+ mw.messages.set( 'jquerymsg-escaped-double-quotes-attribute', '<i style="font-family:&quot;Arial&quot;">Styled</i>' );
+ assert.htmlEqual(
+ formatParse( 'jquerymsg-escaped-double-quotes-attribute' ),
+ mw.messages.get( 'jquerymsg-escaped-double-quotes-attribute' ),
+ 'Escaped attributes are parsed correctly'
+ );
+
+ mw.messages.set( 'jquerymsg-escaped-single-quotes-attribute', '<i style=\'font-family:&#039;Arial&#039;\'>Styled</i>' );
+ assert.htmlEqual(
+ formatParse( 'jquerymsg-escaped-single-quotes-attribute' ),
+ mw.messages.get( 'jquerymsg-escaped-single-quotes-attribute' ),
+ 'Escaped attributes are parsed correctly'
+ );
+
+ mw.messages.set( 'jquerymsg-wikitext-contents-parsed', '<i>[http://example.com Example]</i>' );
+ assert.htmlEqual(
+ formatParse( 'jquerymsg-wikitext-contents-parsed' ),
+ '<i><a href="http://example.com">Example</a></i>',
+ 'Contents of valid tag are treated as wikitext, so external link is parsed'
+ );
+
+ mw.messages.set( 'jquerymsg-wikitext-contents-script', '<i><script>Script inside</script></i>' );
+ assert.htmlEqual(
+ formatParse( 'jquerymsg-wikitext-contents-script' ),
+ '<i><span class="mediaWiki_htmlEmitter">&lt;script&gt;Script inside&lt;/script&gt;</span></i>',
+ 'Contents of valid tag are treated as wikitext, so invalid HTML element is treated as text'
+ );
+
+ mw.messages.set( 'jquerymsg-unclosed-tag', 'Foo<tag>bar' );
+ assert.htmlEqual(
+ formatParse( 'jquerymsg-unclosed-tag' ),
+ 'Foo&lt;tag&gt;bar',
+ 'Nonsupported unclosed tags are escaped'
+ );
+
+ mw.messages.set( 'jquerymsg-self-closing-tag', 'Foo<tag/>bar' );
+ assert.htmlEqual(
+ formatParse( 'jquerymsg-self-closing-tag' ),
+ 'Foo&lt;tag/&gt;bar',
+ 'Self-closing tags don\'t cause a parse error'
+ );
} );
- QUnit.stop();
- process( queue, QUnit.start );
-} );
-
-// HTML in wikitext
-QUnit.test( 'HTML', 26, function ( assert ) {
- mw.messages.set( 'jquerymsg-italics-msg', '<i>Very</i> important' );
-
- assertBothModes( assert, ['jquerymsg-italics-msg'], mw.messages.get( 'jquerymsg-italics-msg' ), 'Simple italics unchanged' );
-
- mw.messages.set( 'jquerymsg-bold-msg', '<b>Strong</b> speaker' );
- assertBothModes( assert, ['jquerymsg-bold-msg'], mw.messages.get( 'jquerymsg-bold-msg' ), 'Simple bold unchanged' );
-
- mw.messages.set( 'jquerymsg-bold-italics-msg', 'It is <b><i>key</i></b>' );
- assertBothModes( assert, ['jquerymsg-bold-italics-msg'], mw.messages.get( 'jquerymsg-bold-italics-msg' ), 'Bold and italics nesting order preserved' );
-
- mw.messages.set( 'jquerymsg-italics-bold-msg', 'It is <i><b>vital</b></i>' );
- assertBothModes( assert, ['jquerymsg-italics-bold-msg'], mw.messages.get( 'jquerymsg-italics-bold-msg' ), 'Italics and bold nesting order preserved' );
-
- mw.messages.set( 'jquerymsg-italics-with-link', 'An <i>italicized [[link|wiki-link]]</i>' );
-
- assert.htmlEqual(
- formatParse( 'jquerymsg-italics-with-link' ),
- 'An <i>italicized <a title="link" href="' + mw.html.escape( mw.util.getUrl( 'link' ) ) + '">wiki-link</i>',
- 'Italics with link inside in parse mode'
- );
-
- assert.equal(
- formatText( 'jquerymsg-italics-with-link' ),
- mw.messages.get( 'jquerymsg-italics-with-link' ),
- 'Italics with link unchanged in text mode'
- );
-
- mw.messages.set( 'jquerymsg-italics-id-class', '<i id="foo" class="bar">Foo</i>' );
- assert.htmlEqual(
- formatParse( 'jquerymsg-italics-id-class' ),
- mw.messages.get( 'jquerymsg-italics-id-class' ),
- 'ID and class are allowed'
- );
-
- mw.messages.set( 'jquerymsg-italics-onclick', '<i onclick="alert(\'foo\')">Foo</i>' );
- assert.htmlEqual(
- formatParse( 'jquerymsg-italics-onclick' ),
- '&lt;i onclick=&quot;alert(\'foo\')&quot;&gt;Foo&lt;/i&gt;',
- 'element with onclick is escaped because it is not allowed'
- );
-
- mw.messages.set( 'jquerymsg-script-msg', '<script >alert( "Who put this tag here?" );</script>' );
- assert.htmlEqual(
- formatParse( 'jquerymsg-script-msg' ),
- '&lt;script &gt;alert( &quot;Who put this tag here?&quot; );&lt;/script&gt;',
- 'Tag outside whitelist escaped in parse mode'
- );
-
- assert.equal(
- formatText( 'jquerymsg-script-msg' ),
- mw.messages.get( 'jquerymsg-script-msg' ),
- 'Tag outside whitelist unchanged in text mode'
- );
-
- mw.messages.set( 'jquerymsg-script-link-msg', '<script>[[Foo|bar]]</script>' );
- assert.htmlEqual(
- formatParse( 'jquerymsg-script-link-msg' ),
- '&lt;script&gt;<a title="Foo" href="' + mw.html.escape( mw.util.getUrl( 'Foo' ) ) + '">bar</a>&lt;/script&gt;',
- 'Script tag text is escaped because that element is not allowed, but link inside is still HTML'
- );
-
- mw.messages.set( 'jquerymsg-mismatched-html', '<i class="important">test</b>' );
- assert.htmlEqual(
- formatParse( 'jquerymsg-mismatched-html' ),
- '&lt;i class=&quot;important&quot;&gt;test&lt;/b&gt;',
- 'Mismatched HTML start and end tag treated as text'
- );
-
- // TODO (mattflaschen, 2013-03-18): It's not a security issue, but there's no real
- // reason the htmlEmitter span needs to be here. It's an artifact of how emitting works.
- mw.messages.set( 'jquerymsg-script-and-external-link', '<script>alert( "jquerymsg-script-and-external-link test" );</script> [http://example.com <i>Foo</i> bar]' );
- assert.htmlEqual(
- formatParse( 'jquerymsg-script-and-external-link' ),
- '&lt;script&gt;alert( "jquerymsg-script-and-external-link test" );&lt;/script&gt; <a href="http://example.com"><span class="mediaWiki_htmlEmitter"><i>Foo</i> bar</span></a>',
- 'HTML tags in external links not interfering with escaping of other tags'
- );
-
- mw.messages.set( 'jquerymsg-link-script', '[http://example.com <script>alert( "jquerymsg-link-script test" );</script>]' );
- assert.htmlEqual(
- formatParse( 'jquerymsg-link-script' ),
- '<a href="http://example.com"><span class="mediaWiki_htmlEmitter">&lt;script&gt;alert( "jquerymsg-link-script test" );&lt;/script&gt;</span></a>',
- 'Non-whitelisted HTML tag in external link anchor treated as text'
- );
-
- // Intentionally not using htmlEqual for the quote tests
- mw.messages.set( 'jquerymsg-double-quotes-preserved', '<i id="double">Double</i>' );
- assert.equal(
- formatParse( 'jquerymsg-double-quotes-preserved' ),
- mw.messages.get( 'jquerymsg-double-quotes-preserved' ),
- 'Attributes with double quotes are preserved as such'
- );
-
- mw.messages.set( 'jquerymsg-single-quotes-normalized-to-double', '<i id=\'single\'>Single</i>' );
- assert.equal(
- formatParse( 'jquerymsg-single-quotes-normalized-to-double' ),
- '<i id="single">Single</i>',
- 'Attributes with single quotes are normalized to double'
- );
-
- mw.messages.set( 'jquerymsg-escaped-double-quotes-attribute', '<i style="font-family:&quot;Arial&quot;">Styled</i>' );
- assert.htmlEqual(
- formatParse( 'jquerymsg-escaped-double-quotes-attribute' ),
- mw.messages.get( 'jquerymsg-escaped-double-quotes-attribute' ),
- 'Escaped attributes are parsed correctly'
- );
-
- mw.messages.set( 'jquerymsg-escaped-single-quotes-attribute', '<i style=\'font-family:&#039;Arial&#039;\'>Styled</i>' );
- assert.htmlEqual(
- formatParse( 'jquerymsg-escaped-single-quotes-attribute' ),
- mw.messages.get( 'jquerymsg-escaped-single-quotes-attribute' ),
- 'Escaped attributes are parsed correctly'
- );
-
- mw.messages.set( 'jquerymsg-wikitext-contents-parsed', '<i>[http://example.com Example]</i>' );
- assert.htmlEqual(
- formatParse( 'jquerymsg-wikitext-contents-parsed' ),
- '<i><a href="http://example.com">Example</a></i>',
- 'Contents of valid tag are treated as wikitext, so external link is parsed'
- );
-
- mw.messages.set( 'jquerymsg-wikitext-contents-script', '<i><script>Script inside</script></i>' );
- assert.htmlEqual(
- formatParse( 'jquerymsg-wikitext-contents-script' ),
- '<i><span class="mediaWiki_htmlEmitter">&lt;script&gt;Script inside&lt;/script&gt;</span></i>',
- 'Contents of valid tag are treated as wikitext, so invalid HTML element is treated as text'
- );
-
- mw.messages.set( 'jquerymsg-unclosed-tag', 'Foo<tag>bar' );
- assert.htmlEqual(
- formatParse( 'jquerymsg-unclosed-tag' ),
- 'Foo&lt;tag&gt;bar',
- 'Nonsupported unclosed tags are escaped'
- );
-
- mw.messages.set( 'jquerymsg-self-closing-tag', 'Foo<tag/>bar' );
- assert.htmlEqual(
- formatParse( 'jquerymsg-self-closing-tag' ),
- 'Foo&lt;tag/&gt;bar',
- 'Self-closing tags don\'t cause a parse error'
- );
-} );
QUnit.test( 'Behavior in case of invalid wikitext', 3, function ( assert ) {
mw.messages.set( 'invalid-wikitext', '<b>{{FAIL}}</b>' );
diff --git a/tests/qunit/suites/resources/mediawiki/mediawiki.language.test.js b/tests/qunit/suites/resources/mediawiki/mediawiki.language.test.js
index 16f90df8..670914eb 100644
--- a/tests/qunit/suites/resources/mediawiki/mediawiki.language.test.js
+++ b/tests/qunit/suites/resources/mediawiki/mediawiki.language.test.js
@@ -8,19 +8,32 @@
},
teardown: function () {
mw.language.data.values = this.liveLangData;
+ },
+ messages: {
+ // mw.language.listToText test
+ 'and': ' and',
+ 'comma-separator': ', ',
+ 'word-separator': ' '
}
} ) );
- QUnit.test( 'mw.language getData and setData', 2, function ( assert ) {
+ QUnit.test( 'mw.language getData and setData', 3, function ( assert ) {
mw.language.setData( 'en', 'testkey', 'testvalue' );
assert.equal( mw.language.getData( 'en', 'testkey' ), 'testvalue', 'Getter setter test for mw.language' );
assert.equal( mw.language.getData( 'en', 'invalidkey' ), undefined, 'Getter setter test for mw.language with invalid key' );
+ mw.language.setData( 'en-us', 'testkey', 'testvalue' );
+ assert.equal( mw.language.getData( 'en-US', 'testkey' ), 'testvalue', 'Case insensitive test for mw.language' );
} );
QUnit.test( 'mw.language.commafy test', 9, function ( assert ) {
+ mw.language.setData( 'en', 'digitGroupingPattern', null );
+ mw.language.setData( 'en', 'digitTransformTable', null );
+ mw.language.setData( 'en', 'separatorTransformTable', null );
+
+ mw.config.set( 'wgUserLanguage', 'en' );
// Number grouping patterns are as per http://cldr.unicode.org/translation/number-patterns
assert.equal( mw.language.commafy( 1234.567, '###0.#####' ), '1234.567', 'Pattern with no digit grouping separator defined' );
- assert.equal( mw.language.commafy( 123456789.567, '###0.#####' ), '123456789.567', 'Pattern with no digit grouping seperator defined, bigger decimal part' );
+ assert.equal( mw.language.commafy( 123456789.567, '###0.#####' ), '123456789.567', 'Pattern with no digit grouping separator defined, bigger decimal part' );
assert.equal( mw.language.commafy( 0.567, '###0.#####' ), '0.567', 'Decimal part 0' );
assert.equal( mw.language.commafy( '.567', '###0.#####' ), '0.567', 'Decimal part missing. replace with zero' );
assert.equal( mw.language.commafy( 1234, '##,#0.#####' ), '12,34', 'Pattern with no fractional part' );
diff --git a/tests/qunit/suites/resources/mediawiki/mediawiki.messagePoster.factory.test.js b/tests/qunit/suites/resources/mediawiki/mediawiki.messagePoster.factory.test.js
new file mode 100644
index 00000000..61bab03f
--- /dev/null
+++ b/tests/qunit/suites/resources/mediawiki/mediawiki.messagePoster.factory.test.js
@@ -0,0 +1,28 @@
+( function ( mw ) {
+ var TEST_MODEL = 'test-content-model';
+
+ QUnit.module( 'mediawiki.messagePoster', QUnit.newMwEnvironment( {
+ teardown: function () {
+ mw.messagePoster.factory.unregister( TEST_MODEL );
+ }
+ } ) );
+
+ QUnit.test( 'register', 2, function ( assert ) {
+ var testMessagePosterConstructor = function () {};
+
+ mw.messagePoster.factory.register( TEST_MODEL, testMessagePosterConstructor );
+ assert.strictEqual(
+ mw.messagePoster.factory.contentModelToClass[TEST_MODEL],
+ testMessagePosterConstructor,
+ 'Constructor is registered'
+ );
+
+ assert.throws(
+ function () {
+ mw.messagePoster.factory.register( TEST_MODEL, testMessagePosterConstructor );
+ },
+ new RegExp( 'The content model \'' + TEST_MODEL + '\' is already registered.' ),
+ 'Throws exception is same model is registered a second time'
+ );
+ } );
+}( mediaWiki ) );
diff --git a/tests/qunit/suites/resources/mediawiki/mediawiki.template.test.js b/tests/qunit/suites/resources/mediawiki/mediawiki.template.test.js
new file mode 100644
index 00000000..86fd828a
--- /dev/null
+++ b/tests/qunit/suites/resources/mediawiki/mediawiki.template.test.js
@@ -0,0 +1,63 @@
+( function ( mw ) {
+
+ QUnit.module( 'mediawiki.template', {
+ setup: function () {
+ var abcCompiler = {
+ compile: function () {
+ return 'abc default compiler';
+ }
+ };
+
+ // Register some template compiler languages
+ mw.template.registerCompiler( 'abc', abcCompiler );
+ mw.template.registerCompiler( 'xyz', {
+ compile: function () {
+ return 'xyz compiler';
+ }
+ } );
+
+ // Stub register some templates
+ this.sandbox.stub( mw.templates, 'get' ).returns( {
+ 'test_templates_foo.xyz': 'goodbye',
+ 'test_templates_foo.abc': 'thankyou'
+ } );
+ }
+ } );
+
+ QUnit.test( 'add', 1, function ( assert ) {
+ assert.throws(
+ function () {
+ mw.template.add( 'module', 'test_templates_foo', 'hello' );
+ },
+ 'When no prefix throw exception'
+ );
+ } );
+
+ QUnit.test( 'compile', 1, function ( assert ) {
+ assert.throws(
+ function () {
+ mw.template.compile( '{{foo}}', 'rainbow' );
+ },
+ 'Unknown compiler names throw exceptions'
+ );
+ } );
+
+ QUnit.test( 'get', 4, function ( assert ) {
+ assert.strictEqual( mw.template.get( 'test.mediawiki.template', 'test_templates_foo.xyz' ), 'xyz compiler' );
+ assert.strictEqual( mw.template.get( 'test.mediawiki.template', 'test_templates_foo.abc' ), 'abc default compiler' );
+ assert.throws(
+ function () {
+ mw.template.get( 'this.should.not.exist', 'hello' );
+ },
+ 'When bad module name given throw error.'
+ );
+
+ assert.throws(
+ function () {
+ mw.template.get( 'mediawiki.template', 'hello' );
+ },
+ 'The template hello should not exist in the mediawiki.templates module and should throw an exception.'
+ );
+ } );
+
+}( mediaWiki ) );
diff --git a/tests/qunit/suites/resources/mediawiki/mediawiki.test.js b/tests/qunit/suites/resources/mediawiki/mediawiki.test.js
index 7e0ee917..cf36ea82 100644
--- a/tests/qunit/suites/resources/mediawiki/mediawiki.test.js
+++ b/tests/qunit/suites/resources/mediawiki/mediawiki.test.js
@@ -1,6 +1,8 @@
/*jshint -W024 */
( function ( mw, $ ) {
- var specialCharactersPageName;
+ var specialCharactersPageName,
+ // Can't mock SITENAME since jqueryMsg caches it at load
+ siteName = mw.config.get( 'wgSiteName' );
// Since QUnitTestResources.php loads both mediawiki and mediawiki.jqueryMsg as
// dependencies, this only tests the monkey-patched behavior with the two of them combined.
@@ -55,7 +57,7 @@
this.restoreWarnings();
} );
- QUnit.test( 'mw.Map', 28, function ( assert ) {
+ QUnit.test( 'mw.Map', 35, function ( assert ) {
var arry, conf, funky, globalConf, nummy, someValues;
conf = new mw.Map();
@@ -86,8 +88,10 @@
assert.strictEqual( conf.set( 'constructor', 42 ), true, 'Map.set for key "constructor"' );
assert.strictEqual( conf.get( 'constructor' ), 42, 'Map.get for key "constructor"' );
- assert.strictEqual( conf.set( 'ImUndefined', undefined ), true, 'Map.set allows setting value to `undefined`' );
- assert.equal( conf.get( 'ImUndefined', 'fallback' ), undefined, 'Map.get supports retreiving value of `undefined`' );
+ assert.strictEqual( conf.set( 'undef' ), false, 'Map.set requires explicit value (no undefined default)' );
+
+ assert.strictEqual( conf.set( 'undef', undefined ), true, 'Map.set allows setting value to `undefined`' );
+ assert.equal( conf.get( 'undef', 'fallback' ), undefined, 'Map.get supports retreiving value of `undefined`' );
assert.strictEqual( conf.set( funky, 'Funky' ), false, 'Map.set returns boolean false if key was invalid (Function)' );
assert.strictEqual( conf.set( arry, 'Arry' ), false, 'Map.set returns boolean false if key was invalid (Array)' );
@@ -99,7 +103,7 @@
conf.set( String( nummy ), 'I used to be a number' );
assert.strictEqual( conf.exists( 'doesNotExist' ), false, 'Map.exists where property does not exist' );
- assert.strictEqual( conf.exists( 'ImUndefined' ), true, 'Map.exists where value is `undefined`' );
+ assert.strictEqual( conf.exists( 'undef' ), true, 'Map.exists where value is `undefined`' );
assert.strictEqual( conf.exists( nummy ), false, 'Map.exists where key is invalid but looks like an existing key' );
// Multiple values at once
@@ -126,12 +130,31 @@
conf.set( 'globalMapChecker', 'Hi' );
- assert.ok( 'globalMapChecker' in window === false, 'new mw.Map did not store its values in the global window object by default' );
+ assert.ok( ( 'globalMapChecker' in window ) === false, 'Map does not its store values in the window object by default' );
globalConf = new mw.Map( true );
globalConf.set( 'anotherGlobalMapChecker', 'Hello' );
- assert.ok( 'anotherGlobalMapChecker' in window, 'new mw.Map( true ) did store its values in the global window object' );
+ assert.ok( 'anotherGlobalMapChecker' in window, 'global Map stores its values in the window object' );
+
+ assert.equal( globalConf.get( 'anotherGlobalMapChecker' ), 'Hello', 'get value from global Map via get()' );
+ this.suppressWarnings();
+ assert.equal( window.anotherGlobalMapChecker, 'Hello', 'get value from global Map via window object' );
+ this.restoreWarnings();
+
+ // Change value via global Map
+ globalConf.set('anotherGlobalMapChecker', 'Again');
+ assert.equal( globalConf.get( 'anotherGlobalMapChecker' ), 'Again', 'Change in global Map reflected via get()' );
+ this.suppressWarnings();
+ assert.equal( window.anotherGlobalMapChecker, 'Again', 'Change in global Map reflected window object' );
+ this.restoreWarnings();
+
+ // Change value via window object
+ this.suppressWarnings();
+ window.anotherGlobalMapChecker = 'World';
+ assert.equal( window.anotherGlobalMapChecker, 'World', 'Change in window object works' );
+ this.restoreWarnings();
+ assert.equal( globalConf.get( 'anotherGlobalMapChecker' ), 'Again', 'Change in window object not reflected in global Map' );
// Whitelist this global variable for QUnit's 'noglobal' mode
if ( QUnit.config.noglobals ) {
@@ -148,7 +171,9 @@
// Convenience method for asserting the same result for multiple formats
function assertMultipleFormats( messageArguments, formats, expectedResult, assertMessage ) {
- var len = formats.length, format, i;
+ var format, i,
+ len = formats.length;
+
for ( i = 0; i < len; i++ ) {
format = formats[i];
assert.equal( mw.message.apply( null, messageArguments )[format](), expectedResult, assertMessage + ' when format is ' + format );
@@ -178,9 +203,9 @@
assert.equal( hello.format, 'escaped', 'Message.escaped correctly updated the "format" property' );
assert.ok( mw.messages.set( 'multiple-curly-brace', '"{{SITENAME}}" is the home of {{int:other-message}}' ), 'mw.messages.set: Register' );
- assertMultipleFormats( ['multiple-curly-brace'], ['text', 'parse'], '"' + mw.config.get( 'wgSiteName') + '" is the home of Other Message', 'Curly brace format works correctly' );
+ assertMultipleFormats( ['multiple-curly-brace'], ['text', 'parse'], '"' + siteName + '" is the home of Other Message', 'Curly brace format works correctly' );
assert.equal( mw.message( 'multiple-curly-brace' ).plain(), mw.messages.get( 'multiple-curly-brace' ), 'Plain format works correctly for curly brace message' );
- assert.equal( mw.message( 'multiple-curly-brace' ).escaped(), mw.html.escape( '"' + mw.config.get( 'wgSiteName') + '" is the home of Other Message' ), 'Escaped format works correctly for curly brace message' );
+ assert.equal( mw.message( 'multiple-curly-brace' ).escaped(), mw.html.escape( '"' + siteName + '" is the home of Other Message' ), 'Escaped format works correctly for curly brace message' );
assert.ok( mw.messages.set( 'multiple-square-brackets-and-ampersand', 'Visit the [[Project:Community portal|community portal]] & [[Project:Help desk|help desk]]' ), 'mw.messages.set: Register' );
assertMultipleFormats( ['multiple-square-brackets-and-ampersand'], ['plain', 'text'], mw.messages.get( 'multiple-square-brackets-and-ampersand' ), 'Square bracket message is not processed' );
@@ -243,8 +268,8 @@
assert.equal( mw.message( 'gender-plural-msg', 'male', 1 ).plain(), '{{GENDER:male|he|she|they}} {{PLURAL:1|is|are}} awesome', 'Parameters are substituted, but gender and plural are not resolved in plain mode' );
assert.equal( mw.message( 'grammar-msg' ).plain(), mw.messages.get( 'grammar-msg' ), 'Grammar is not resolved in plain mode' );
- assertMultipleFormats( ['grammar-msg'], ['text', 'parse'], 'Przeszukaj ' + mw.config.get( 'wgSiteName' ), 'Grammar is resolved' );
- assert.equal( mw.message( 'grammar-msg' ).escaped(), 'Przeszukaj ' + mw.html.escape( mw.config.get( 'wgSiteName' ) ), 'Grammar is resolved in escaped mode' );
+ assertMultipleFormats( ['grammar-msg'], ['text', 'parse'], 'Przeszukaj ' + siteName, 'Grammar is resolved' );
+ assert.equal( mw.message( 'grammar-msg' ).escaped(), 'Przeszukaj ' + siteName, 'Grammar is resolved in escaped mode' );
assertMultipleFormats( ['formatnum-msg', '987654321.654321'], ['text', 'parse', 'escaped'], '987,654,321.654', 'formatnum is resolved' );
assert.equal( mw.message( 'formatnum-msg' ).plain(), mw.messages.get( 'formatnum-msg' ), 'formatnum is not resolved in plain mode' );
@@ -304,7 +329,7 @@
assert.equal( mw.msg( 'gender-plural-msg', 'female', '1' ), 'she is awesome', 'Gender test for female, plural count 1' );
assert.equal( mw.msg( 'gender-plural-msg', 'unknown', 10 ), 'they are awesome', 'Gender test for neutral, plural count 10' );
- assert.equal( mw.msg( 'grammar-msg' ), 'Przeszukaj ' + mw.config.get( 'wgSiteName' ), 'Grammar is resolved' );
+ assert.equal( mw.msg( 'grammar-msg' ), 'Przeszukaj ' + siteName, 'Grammar is resolved' );
assert.equal( mw.msg( 'formatnum-msg', '987654321.654321' ), '987,654,321.654', 'formatnum is resolved' );
@@ -352,7 +377,7 @@
return;
}
// Otherwise, keep polling
- setTimeout( styleTestLoop, 150 );
+ setTimeout( styleTestLoop );
}
// Start the loop
@@ -396,6 +421,30 @@
} );
} );
+ QUnit.asyncTest( 'mw.loader with Object method as module name', 2, function ( assert ) {
+ var isAwesomeDone;
+
+ mw.loader.testCallback = function () {
+ QUnit.start();
+ assert.strictEqual( isAwesomeDone, undefined, 'Implementing module hasOwnProperty: isAwesomeDone should still be undefined' );
+ isAwesomeDone = true;
+ };
+
+ mw.loader.implement( 'hasOwnProperty', [QUnit.fixurl( mw.config.get( 'wgScriptPath' ) + '/tests/qunit/data/callMwLoaderTestCallback.js' )], {}, {} );
+
+ mw.loader.using( 'hasOwnProperty', function () {
+
+ // /sample/awesome.js declares the "mw.loader.testCallback" function
+ // which contains a call to start() and ok()
+ assert.strictEqual( isAwesomeDone, true, 'hasOwnProperty module should\'ve caused isAwesomeDone to be true' );
+ delete mw.loader.testCallback;
+
+ }, function () {
+ QUnit.start();
+ assert.ok( false, 'Error callback fired while loader.using "hasOwnProperty" module' );
+ } );
+ } );
+
QUnit.asyncTest( 'mw.loader.using( .. ).promise', 2, function ( assert ) {
var isAwesomeDone;
@@ -625,6 +674,11 @@
} );
+ QUnit.test( 'mw.loader.implement( only scripts )', 1, function ( assert ) {
+ mw.loader.implement( 'test.onlyscripts', function () {} );
+ assert.strictEqual( mw.loader.getState( 'test.onlyscripts' ), 'ready' );
+ } );
+
QUnit.asyncTest( 'mw.loader.implement( only messages )', 2, function ( assert ) {
assert.assertFalse( mw.messages.exists( 'bug_29107' ), 'Verify that the test message doesn\'t exist yet' );
@@ -638,7 +692,10 @@
} );
} );
- QUnit.test( 'mw.loader erroneous indirect dependency', 3, function ( assert ) {
+ QUnit.test( 'mw.loader erroneous indirect dependency', 4, function ( assert ) {
+ // don't emit an error event
+ this.sandbox.stub( mw, 'track' );
+
mw.loader.register( [
['test.module1', '0'],
['test.module2', '0', ['test.module1']],
@@ -650,6 +707,8 @@
assert.strictEqual( mw.loader.getState( 'test.module1' ), 'error', 'Expected "error" state for test.module1' );
assert.strictEqual( mw.loader.getState( 'test.module2' ), 'error', 'Expected "error" state for test.module2' );
assert.strictEqual( mw.loader.getState( 'test.module3' ), 'error', 'Expected "error" state for test.module3' );
+
+ assert.strictEqual( mw.track.callCount, 1 );
} );
QUnit.test( 'mw.loader out-of-order implementation', 9, function ( assert ) {
diff --git a/tests/qunit/suites/resources/mediawiki/mediawiki.track.test.js b/tests/qunit/suites/resources/mediawiki/mediawiki.track.test.js
new file mode 100644
index 00000000..cdb26244
--- /dev/null
+++ b/tests/qunit/suites/resources/mediawiki/mediawiki.track.test.js
@@ -0,0 +1,42 @@
+( function ( mw ) {
+ QUnit.module( 'mediawiki.track' );
+
+ QUnit.test( 'track', 1, function ( assert ) {
+ var sequence = [];
+ mw.trackSubscribe( 'simple', function ( topic, data ) {
+ sequence.push( [ topic, data ] );
+ } );
+ mw.track( 'simple', { key: 1 } );
+ mw.track( 'simple', { key: 2 } );
+
+ assert.deepEqual( sequence, [
+ [ 'simple', { key: 1 } ],
+ [ 'simple', { key: 2 } ]
+ ], 'Events after subscribing' );
+ } );
+
+ QUnit.test( 'trackSubscribe', 4, function ( assert ) {
+ var now,
+ sequence = [];
+ mw.track( 'before', { key: 1 } );
+ mw.track( 'before', { key: 2 } );
+ mw.trackSubscribe( 'before', function ( topic, data ) {
+ sequence.push( [ topic, data ] );
+ } );
+ mw.track( 'before', { key: 3 } );
+
+ assert.deepEqual( sequence, [
+ [ 'before', { key: 1 } ],
+ [ 'before', { key: 2 } ],
+ [ 'before', { key: 3 } ]
+ ], 'Replay events from before subscribing' );
+
+ now = mw.now();
+ mw.track( 'context', { key: 0 } );
+ mw.trackSubscribe( 'context', function ( topic, data ) {
+ assert.strictEqual( this.topic, topic, 'thisValue has topic' );
+ assert.strictEqual( this.data, data, 'thisValue has data' );
+ assert.assertTrue( this.timeStamp >= now, 'thisValue has sane timestamp' );
+ } );
+ } );
+}( mediaWiki ) );
diff --git a/tests/qunit/suites/resources/mediawiki/mediawiki.user.test.js b/tests/qunit/suites/resources/mediawiki/mediawiki.user.test.js
index 91321a2f..04e002dd 100644
--- a/tests/qunit/suites/resources/mediawiki/mediawiki.user.test.js
+++ b/tests/qunit/suites/resources/mediawiki/mediawiki.user.test.js
@@ -1,7 +1,17 @@
-( function ( mw ) {
+( function ( mw, $ ) {
QUnit.module( 'mediawiki.user', QUnit.newMwEnvironment( {
setup: function () {
this.server = this.sandbox.useFakeServer();
+ this.crypto = window.crypto;
+ this.msCrypto = window.msCrypto;
+ },
+ teardown: function () {
+ if ( this.crypto ) {
+ window.crypto = this.crypto;
+ }
+ if ( this.msCrypto ) {
+ window.msCrypto = this.msCrypto;
+ }
}
} ) );
@@ -51,4 +61,38 @@
this.server.respond();
} );
-}( mediaWiki ) );
+
+ QUnit.test( 'generateRandomSessionId', 4, function ( assert ) {
+ var result, result2;
+
+ result = mw.user.generateRandomSessionId();
+ assert.equal( typeof result, 'string', 'type' );
+ assert.equal( $.trim( result ), result, 'no whitespace at beginning or end' );
+ assert.equal( result.length, 16, 'size' );
+
+ result2 = mw.user.generateRandomSessionId();
+ assert.notEqual( result, result2, 'different when called multiple times' );
+
+ } );
+
+ QUnit.test( 'generateRandomSessionId (fallback)', 4, function ( assert ) {
+ var result, result2;
+
+ // Pretend crypto API is not there to test the Math.random fallback
+ if ( window.crypto ) {
+ window.crypto = undefined;
+ }
+ if ( window.msCrypto ) {
+ window.msCrypto = undefined;
+ }
+
+ result = mw.user.generateRandomSessionId();
+ assert.equal( typeof result, 'string', 'type' );
+ assert.equal( $.trim( result ), result, 'no whitespace at beginning or end' );
+ assert.equal( result.length, 16, 'size' );
+
+ result2 = mw.user.generateRandomSessionId();
+ assert.notEqual( result, result2, 'different when called multiple times' );
+
+ } );
+}( mediaWiki, jQuery ) );
diff --git a/tests/qunit/suites/resources/mediawiki/mediawiki.util.test.js b/tests/qunit/suites/resources/mediawiki/mediawiki.util.test.js
index 4401eadb..0b42af4b 100644
--- a/tests/qunit/suites/resources/mediawiki/mediawiki.util.test.js
+++ b/tests/qunit/suites/resources/mediawiki/mediawiki.util.test.js
@@ -1,4 +1,79 @@
( function ( mw, $ ) {
+ var
+ // Based on IPTest.php > testisIPv4
+ IPV4_CASES = [
+ [false, false, 'Boolean false is not an IP'],
+ [false, true, 'Boolean true is not an IP'],
+ [false, '', 'Empty string is not an IP'],
+ [false, 'abc', '"abc" is not an IP'],
+ [false, ':', 'Colon is not an IP'],
+ [false, '124.24.52', 'IPv4 not enough quads'],
+ [false, '24.324.52.13', 'IPv4 out of range'],
+ [false, '.24.52.13', 'IPv4 starts with period'],
+
+ [true, '124.24.52.13', '124.24.52.134 is a valid IP'],
+ [true, '1.24.52.13', '1.24.52.13 is a valid IP'],
+ [false, '74.24.52.13/20', 'IPv4 ranges are not recognized as valid IPs']
+ ],
+
+ // Based on IPTest.php > testisIPv6
+ IPV6_CASES = [
+ [false, ':fc:100::', 'IPv6 starting with lone ":"'],
+ [false, 'fc:100:::', 'IPv6 ending with a ":::"'],
+ [false, 'fc:300', 'IPv6 with only 2 words'],
+ [false, 'fc:100:300', 'IPv6 with only 3 words'],
+
+ [false, 'fc:100:a:d:1:e:ac:0::', 'IPv6 with 8 words ending with "::"'],
+ [false, 'fc:100:a:d:1:e:ac:0:1::', 'IPv6 with 9 words ending with "::"'],
+
+ [false, ':::'],
+ [false, '::0:', 'IPv6 ending in a lone ":"'],
+
+ [true, '::', 'IPv6 zero address'],
+
+ [false, '::fc:100:a:d:1:e:ac:0', 'IPv6 with "::" and 8 words'],
+ [false, '::fc:100:a:d:1:e:ac:0:1', 'IPv6 with 9 words'],
+
+ [false, ':fc::100', 'IPv6 starting with lone ":"'],
+ [false, 'fc::100:', 'IPv6 ending with lone ":"'],
+ [false, 'fc:::100', 'IPv6 with ":::" in the middle'],
+
+ [true, 'fc::100', 'IPv6 with "::" and 2 words'],
+ [true, 'fc::100:a', 'IPv6 with "::" and 3 words'],
+ [true, 'fc::100:a:d', 'IPv6 with "::" and 4 words'],
+ [true, 'fc::100:a:d:1', 'IPv6 with "::" and 5 words'],
+ [true, 'fc::100:a:d:1:e', 'IPv6 with "::" and 6 words'],
+ [true, 'fc::100:a:d:1:e:ac', 'IPv6 with "::" and 7 words'],
+ [true, '2001::df', 'IPv6 with "::" and 2 words'],
+ [true, '2001:5c0:1400:a::df', 'IPv6 with "::" and 5 words'],
+ [true, '2001:5c0:1400:a::df:2', 'IPv6 with "::" and 6 words'],
+
+ [false, 'fc::100:a:d:1:e:ac:0', 'IPv6 with "::" and 8 words'],
+ [false, 'fc::100:a:d:1:e:ac:0:1', 'IPv6 with 9 words']
+ ];
+
+ Array.prototype.push.apply( IPV6_CASES,
+ $.map( [
+ 'fc:100::',
+ 'fc:100:a::',
+ 'fc:100:a:d::',
+ 'fc:100:a:d:1::',
+ 'fc:100:a:d:1:e::',
+ 'fc:100:a:d:1:e:ac::',
+ '::0',
+ '::fc',
+ '::fc:100',
+ '::fc:100:a',
+ '::fc:100:a:d',
+ '::fc:100:a:d:1',
+ '::fc:100:a:d:1:e',
+ '::fc:100:a:d:1:e:ac',
+ 'fc:100:a:d:1:e:ac:0'
+ ], function ( el ) {
+ return [[ true, el, el + ' is a valid IP' ]];
+ } )
+ );
+
QUnit.module( 'mediawiki.util', QUnit.newMwEnvironment( {
setup: function () {
$.fn.updateTooltipAccessKeys.setTestMode( true );
@@ -35,24 +110,25 @@
} );
} );
- QUnit.test( 'getUrl', 4, function ( assert ) {
+ QUnit.test( 'getUrl', 5, function ( assert ) {
// Not part of startUp module
mw.config.set( 'wgArticlePath', '/wiki/$1' );
mw.config.set( 'wgPageName', 'Foobar' );
var href = mw.util.getUrl( 'Sandbox' );
- assert.equal( href, '/wiki/Sandbox', 'Simple title; Get link for "Sandbox"' );
+ assert.equal( href, '/wiki/Sandbox', 'simple title' );
- href = mw.util.getUrl( 'Foo:Sandbox ? 5+5=10 ! (test)/subpage' );
- assert.equal( href, '/wiki/Foo:Sandbox_%3F_5%2B5%3D10_!_(test)/subpage',
- 'Advanced title; Get link for "Foo:Sandbox ? 5+5=10 ! (test)/subpage"' );
+ href = mw.util.getUrl( 'Foo:Sandbox? 5+5=10! (test)/sub ' );
+ assert.equal( href, '/wiki/Foo:Sandbox%3F_5%2B5%3D10!_(test)/sub_', 'advanced title' );
href = mw.util.getUrl();
- assert.equal( href, '/wiki/Foobar', 'Default title; Get link for current page ("Foobar")' );
+ assert.equal( href, '/wiki/Foobar', 'default title' );
+
+ href = mw.util.getUrl( null, { action: 'edit' } );
+ assert.equal( href, '/wiki/Foobar?action=edit', 'default title with query string' );
href = mw.util.getUrl( 'Sandbox', { action: 'edit' } );
- assert.equal( href, '/wiki/Sandbox?action=edit',
- 'Simple title with query string; Get link for "Sandbox" with action=edit' );
+ assert.equal( href, '/wiki/Sandbox?action=edit', 'simple title with query string' );
} );
QUnit.test( 'wikiScript', 4, function ( assert ) {
@@ -189,7 +265,7 @@
);
assert.equal( $tbMW.closest( '.portlet' ).attr( 'id' ), 'p-test-tb', 'Link was inserted within correct portlet' );
- assert.strictEqual( $tbMW.next()[0], tbRL, 'Link is in the correct position (by passing nextnode)' );
+ assert.strictEqual( $tbMW.next()[0], tbRL, 'Link is in the correct position (nextnode as Node object)' );
cuQuux = mw.util.addPortletLink( 'p-test-custom', '#', 'Quux', null, 'Example [shift-x]', 'q' );
$cuQuux = $( cuQuux );
@@ -205,7 +281,7 @@
tbRLDM = mw.util.addPortletLink( 'p-test-tb', '//mediawiki.org/wiki/RL/DM',
'Default modules', 't-rldm', 'List of all default modules ', 'd', '#t-rl' );
- assert.equal( $( tbRLDM ).next().attr( 'id' ), 't-rl', 'Link is in the correct position (by passing CSS selector)' );
+ assert.strictEqual( $( tbRLDM ).next()[0], tbRL, 'Link is in the correct position (CSS selector as nextnode)' );
caFoo = mw.util.addPortletLink( 'p-test-views', '#', 'Foo' );
@@ -213,26 +289,19 @@
assert.strictEqual( $( caFoo ).find( 'span' ).length, 1, 'A <span> element should be added for porlets with vectorTabs class.' );
addedAfter = mw.util.addPortletLink( 'p-test-tb', '#', 'After foo', 'post-foo', 'After foo', null, $( tbRL ) );
- assert.strictEqual( $( addedAfter ).next()[0], tbRL, 'Link is in the correct position (by passing a jQuery object as nextnode)' );
+ assert.strictEqual( $( addedAfter ).next()[0], tbRL, 'Link is in the correct position (jQuery object as nextnode)' );
// test case - nonexistent id as next node
tbRLDMnonexistentid = mw.util.addPortletLink( 'p-test-tb', '//mediawiki.org/wiki/RL/DM',
'Default modules', 't-rldm-nonexistent', 'List of all default modules ', 'd', '#t-rl-nonexistent' );
- assert.equal( tbRLDMnonexistentid, $( '#p-test-tb li:last' )[0], 'Nonexistent id as nextnode adds the portlet at end' );
+ assert.equal( tbRLDMnonexistentid, $( '#p-test-tb li:last' )[0], 'Fallback to adding at the end (nextnode non-matching CSS selector)' );
// test case - empty jquery object as next node
tbRLDMemptyjquery = mw.util.addPortletLink( 'p-test-tb', '//mediawiki.org/wiki/RL/DM',
'Default modules', 't-rldm-empty-jquery', 'List of all default modules ', 'd', $( '#t-rl-nonexistent' ) );
- assert.equal( tbRLDMemptyjquery, $( '#p-test-tb li:last' )[0], 'Empty jquery as nextnode adds the portlet at end' );
- } );
-
- QUnit.test( 'jsMessage', 1, function ( assert ) {
- this.suppressWarnings();
- var a = mw.util.jsMessage( 'MediaWiki is <b>Awesome</b>.' );
- this.restoreWarnings();
- assert.ok( a, 'Basic checking of return value' );
+ assert.equal( tbRLDMemptyjquery, $( '#p-test-tb li:last' )[0], 'Fallback to adding at the end (nextnode as empty jQuery object)' );
} );
QUnit.test( 'validateEmail', 6, function ( assert ) {
@@ -249,95 +318,24 @@
} );
QUnit.test( 'isIPv6Address', 40, function ( assert ) {
- // Shortcuts
- function assertFalseIPv6( addy, summary ) {
- return assert.strictEqual( mw.util.isIPv6Address( addy ), false, summary );
- }
-
- function assertTrueIPv6( addy, summary ) {
- return assert.strictEqual( mw.util.isIPv6Address( addy ), true, summary );
- }
-
- // Based on IPTest.php > testisIPv6
- assertFalseIPv6( ':fc:100::', 'IPv6 starting with lone ":"' );
- assertFalseIPv6( 'fc:100:::', 'IPv6 ending with a ":::"' );
- assertFalseIPv6( 'fc:300', 'IPv6 with only 2 words' );
- assertFalseIPv6( 'fc:100:300', 'IPv6 with only 3 words' );
-
- $.each(
- ['fc:100::',
- 'fc:100:a::',
- 'fc:100:a:d::',
- 'fc:100:a:d:1::',
- 'fc:100:a:d:1:e::',
- 'fc:100:a:d:1:e:ac::'], function ( i, addy ) {
- assertTrueIPv6( addy, addy + ' is a valid IP' );
- } );
-
- assertFalseIPv6( 'fc:100:a:d:1:e:ac:0::', 'IPv6 with 8 words ending with "::"' );
- assertFalseIPv6( 'fc:100:a:d:1:e:ac:0:1::', 'IPv6 with 9 words ending with "::"' );
-
- assertFalseIPv6( ':::' );
- assertFalseIPv6( '::0:', 'IPv6 ending in a lone ":"' );
-
- assertTrueIPv6( '::', 'IPv6 zero address' );
- $.each(
- ['::0',
- '::fc',
- '::fc:100',
- '::fc:100:a',
- '::fc:100:a:d',
- '::fc:100:a:d:1',
- '::fc:100:a:d:1:e',
- '::fc:100:a:d:1:e:ac',
-
- 'fc:100:a:d:1:e:ac:0'], function ( i, addy ) {
- assertTrueIPv6( addy, addy + ' is a valid IP' );
- } );
-
- assertFalseIPv6( '::fc:100:a:d:1:e:ac:0', 'IPv6 with "::" and 8 words' );
- assertFalseIPv6( '::fc:100:a:d:1:e:ac:0:1', 'IPv6 with 9 words' );
-
- assertFalseIPv6( ':fc::100', 'IPv6 starting with lone ":"' );
- assertFalseIPv6( 'fc::100:', 'IPv6 ending with lone ":"' );
- assertFalseIPv6( 'fc:::100', 'IPv6 with ":::" in the middle' );
-
- assertTrueIPv6( 'fc::100', 'IPv6 with "::" and 2 words' );
- assertTrueIPv6( 'fc::100:a', 'IPv6 with "::" and 3 words' );
- assertTrueIPv6( 'fc::100:a:d', 'IPv6 with "::" and 4 words' );
- assertTrueIPv6( 'fc::100:a:d:1', 'IPv6 with "::" and 5 words' );
- assertTrueIPv6( 'fc::100:a:d:1:e', 'IPv6 with "::" and 6 words' );
- assertTrueIPv6( 'fc::100:a:d:1:e:ac', 'IPv6 with "::" and 7 words' );
- assertTrueIPv6( '2001::df', 'IPv6 with "::" and 2 words' );
- assertTrueIPv6( '2001:5c0:1400:a::df', 'IPv6 with "::" and 5 words' );
- assertTrueIPv6( '2001:5c0:1400:a::df:2', 'IPv6 with "::" and 6 words' );
-
- assertFalseIPv6( 'fc::100:a:d:1:e:ac:0', 'IPv6 with "::" and 8 words' );
- assertFalseIPv6( 'fc::100:a:d:1:e:ac:0:1', 'IPv6 with 9 words' );
+ $.each( IPV6_CASES, function ( i, ipCase ) {
+ assert.strictEqual( mw.util.isIPv6Address( ipCase[1] ), ipCase[0], ipCase[2] );
+ } );
} );
QUnit.test( 'isIPv4Address', 11, function ( assert ) {
- // Shortcuts
- function assertFalseIPv4( addy, summary ) {
- assert.strictEqual( mw.util.isIPv4Address( addy ), false, summary );
- }
+ $.each( IPV4_CASES, function ( i, ipCase ) {
+ assert.strictEqual( mw.util.isIPv4Address( ipCase[1] ), ipCase[0], ipCase[2] );
+ } );
+ } );
- function assertTrueIPv4( addy, summary ) {
- assert.strictEqual( mw.util.isIPv4Address( addy ), true, summary );
- }
+ QUnit.test( 'isIPAddress', 51, function ( assert ) {
+ $.each( IPV4_CASES, function ( i, ipCase ) {
+ assert.strictEqual( mw.util.isIPv4Address( ipCase[1] ), ipCase[0], ipCase[2] );
+ } );
- // Based on IPTest.php > testisIPv4
- assertFalseIPv4( false, 'Boolean false is not an IP' );
- assertFalseIPv4( true, 'Boolean true is not an IP' );
- assertFalseIPv4( '', 'Empty string is not an IP' );
- assertFalseIPv4( 'abc', '"abc" is not an IP' );
- assertFalseIPv4( ':', 'Colon is not an IP' );
- assertFalseIPv4( '124.24.52', 'IPv4 not enough quads' );
- assertFalseIPv4( '24.324.52.13', 'IPv4 out of range' );
- assertFalseIPv4( '.24.52.13', 'IPv4 starts with period' );
-
- assertTrueIPv4( '124.24.52.13', '124.24.52.134 is a valid IP' );
- assertTrueIPv4( '1.24.52.13', '1.24.52.13 is a valid IP' );
- assertFalseIPv4( '74.24.52.13/20', 'IPv4 ranges are not recogzized as valid IPs' );
+ $.each( IPV6_CASES, function ( i, ipCase ) {
+ assert.strictEqual( mw.util.isIPv6Address( ipCase[1] ), ipCase[0], ipCase[2] );
+ } );
} );
}( mediaWiki, jQuery ) );
diff --git a/tests/qunit/suites/resources/startup.test.js b/tests/qunit/suites/resources/startup.test.js
index ed03418a..6011961a 100644
--- a/tests/qunit/suites/resources/startup.test.js
+++ b/tests/qunit/suites/resources/startup.test.js
@@ -96,6 +96,7 @@
'Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060928 (Debian|Debian-1.8.0.7-1) Epiphany/2.14',
'Mozilla/5.0 (X11; U; Linux i686 (x86_64); en-US; rv:1.8.1.6) Gecko/20070817 IceWeasel/2.0.0.6-g2',
// KHTML
+ 'Mozilla/5.0 (compatible; Konqueror/3.5; Linux) KHTML/3.5.4 (like Gecko)',
'Mozilla/5.0 (compatible; Konqueror/4.3; Linux) KHTML/4.3.5 (like Gecko)',
// Text browsers
'Links (2.1pre33; Darwin 8.11.0 Power Macintosh; x)',