Changed written by Timo and reviewed by Hashar. This should be harmless.
To enable the feature:
$wgEnableJavaScriptTest = true;
Then head to:
[[Special:JavaScriptTest/qunit]]
* (bug 23427) Introduced {{PAGEID}} variable to expose page.page_id.
* (bug 33447) Link to the broken image tracking category from Special:Wantedfiles.
* (bug 27724) Add timestamp to job queue.
+* (bug 30339) Implement SpecialPage for running javascript tests. Disabled by default, due to
+ tests potentially being harmful, not to be run on a production wiki.
+ Enable by setting $wgEnableJavaScriptTest to true.
=== Bug fixes in 1.19 ===
* $wgUploadNavigationUrl should be used for file redlinks if.
loader request or generating HTML output.
&$resourceLoader: ResourceLoader object
+'ResourceLoaderTestModules': let you add new javascript testing modules. This is called after the addition of 'qunit' and MediaWiki testing ressources.
+&testModules: array of javascript testing modules. 'qunit' is feed using tests/qunit/QUnitTestResources.php.
+&RessourceLoader object
+To add a new qunit module named 'myext.tests':
+testModules['qunit']['myext.tests'] = array(
+ 'script' => 'extension/myext/tests.js',
+ 'dependencies' => <any module dependency you might have>
+);
+For qunit framework, the mediawiki.tests.qunit.testrunner dependency will be added to any module.
+
'RevisionInsertComplete': called after a revision is inserted into the DB
&$revision: the Revision
$data: the data stored in old_text. The meaning depends on $flags: if external
'SpecialExport' => 'includes/specials/SpecialExport.php',
'SpecialFilepath' => 'includes/specials/SpecialFilepath.php',
'SpecialImport' => 'includes/specials/SpecialImport.php',
+ 'SpecialJavaScriptTest' => 'includes/specials/SpecialJavaScriptTest.php',
'SpecialListFiles' => 'includes/specials/SpecialListfiles.php',
'SpecialListGroupRights' => 'includes/specials/SpecialListgrouprights.php',
'SpecialListUsers' => 'includes/specials/SpecialListusers.php',
* );
*/
$wgParserTestRemote = false;
+
+/**
+ * Allow running of javascript test suites via [[Special:JavaScriptTest]] (such as QUnit).
+ */
+$wgEnableJavaScriptTest = false;
+
+/**
+ * Configuration for javascript testing.
+ */
+$wgJavaScriptTestConfig = array(
+ 'qunit' => array(
+ 'documentation' => '//www.mediawiki.org/wiki/Manual:JavaScript_unit_testing',
+ ),
+);
/**
'Specialpages' => 'other',
'Blockme' => 'other',
'Booksources' => 'other',
+ 'JavaScriptTest' => 'other',
);
/** Whether or not to sort special pages in Special:Specialpages */
/**
* Fetch the set of available skins.
- * @return array of strings
+ * @return associative array of strings
*/
static function getSkinNames() {
global $wgValidSkinNames;
}
return $wgValidSkinNames;
}
+
+ /**
+ * Fetch the skinname messages for available skins.
+ * @return array of strings
+ */
+ static function getSkinNameMessages() {
+ $messages = array();
+ foreach( self::getSkinNames() as $skinKey => $skinName ) {
+ $messages[] = "skinname-$skinKey";
+ }
+ return $messages;
+ }
/**
* Fetch the list of usable skins in regards to $wgSkipSkins.
'Blankpage' => 'SpecialBlankpage',
'Blockme' => 'SpecialBlockme',
'Emailuser' => 'SpecialEmailUser',
+ 'JavaScriptTest' => 'SpecialJavaScriptTest',
'Movepage' => 'MovePageForm',
'Mycontributions' => 'SpecialMycontributions',
'Mypage' => 'SpecialMypage',
/** Associative array mapping module name to info associative array */
protected $moduleInfos = array();
+
+ /** Associative array mapping framework ids to a list of names of test suite modules */
+ /** like array( 'qunit' => array( 'mediawiki.tests.qunit.suites', 'ext.foo.tests', .. ), .. ) */
+ protected $testModuleNames = array();
/** array( 'source-id' => array( 'loadScript' => 'http://.../load.php' ) ) **/
protected $sources = array();
* Registers core modules and runs registration hooks.
*/
public function __construct() {
- global $IP, $wgResourceModules, $wgResourceLoaderSources, $wgLoadScript;
+ global $IP, $wgResourceModules, $wgResourceLoaderSources, $wgLoadScript, $wgEnableJavaScriptTest;
wfProfileIn( __METHOD__ );
wfRunHooks( 'ResourceLoaderRegisterModules', array( &$this ) );
$this->register( $wgResourceModules );
+ if ( $wgEnableJavaScriptTest === true ) {
+ $this->registerTestModules();
+ }
+
+
wfProfileOut( __METHOD__ );
}
wfProfileOut( __METHOD__ );
}
+ /**
+ */
+ public function registerTestModules() {
+ global $IP, $wgEnableJavaScriptTest;
+
+ if ( $wgEnableJavaScriptTest !== true ) {
+ throw new MWException( 'Attempt to register JavaScript test modules but <tt>$wgEnableJavaScriptTest</tt> is false. Edit your <tt>LocalSettings.php</tt> to enable it.' );
+ }
+
+ wfProfileIn( __METHOD__ );
+
+ // Get core test suites
+ $testModules = array();
+ $testModules['qunit'] = include( "$IP/tests/qunit/QUnitTestResources.php" );
+ // Get other test suites (e.g. from extensions)
+ wfRunHooks( 'ResourceLoaderTestModules', array( &$testModules, &$this ) );
+
+ // Add the testrunner (which configures QUnit) to the dependencies.
+ // Since it must be ready before any of the test suites are executed.
+ foreach( $testModules['qunit'] as $moduleName => $moduleProps ) {
+ $testModules['qunit'][$moduleName]['dependencies'][] = 'mediawiki.tests.qunit.testrunner';
+ }
+
+ foreach( $testModules as $id => $names ) {
+ // Register test modules
+ $this->register( $testModules[$id] );
+
+ // Keep track of their names so that they can be loaded together
+ $this->testModuleNames[$id] = array_keys( $testModules[$id] );
+ }
+
+ wfProfileOut( __METHOD__ );
+ }
+
/**
* Add a foreign source of modules.
*
public function getModuleNames() {
return array_keys( $this->moduleInfos );
}
+
+ /**
+ * Get a list of test module names for one (or all) frameworks.
+ * If the given framework id is unknkown, or if the in-object variable is not an array,
+ * then it will return an empty array.
+ *
+ * @param $framework String: Optional. Get only the test module names for one
+ * particular framework.
+ * @return Array
+ */
+ public function getTestModuleNames( $framework = 'all' ) {
+ if ( $framework == 'all' ) {
+ return $this->testModuleNames;
+ } elseif ( isset( $this->testModuleNames[$framework] ) && is_array( $this->testModuleNames[$framework] ) ) {
+ return $this->testModuleNames[$framework];
+ } else {
+ return array();
+ }
+ }
/**
* Get the ResourceLoaderModule object for a given module name.
--- /dev/null
+<?php
+
+class SpecialJavaScriptTest extends SpecialPage {
+
+ /**
+ * @var $frameworks Array: Mapping of framework ids and their initilizer methods
+ * in this class. If a framework is requested but not in this array,
+ * the 'unknownframework' error is served.
+ */
+ static $frameworks = array(
+ 'qunit' => 'initQUnitTesting',
+ );
+
+ public function __construct() {
+ parent::__construct( 'JavaScriptTest' );
+ }
+
+ public function execute( $par ) {
+ global $wgEnableJavaScriptTest;
+
+ $out = $this->getOutput();
+
+ $this->setHeaders();
+ $out->disallowUserJs();
+
+ // Abort early if we're disabled
+ if ( $wgEnableJavaScriptTest !== true ) {
+ $out->addWikiMsg( 'javascripttest-disabled' );
+ return;
+ }
+
+ $out->addModules( 'mediawiki.special.javaScriptTest' );
+
+ // Determine framework
+ $pars = explode( '/', $par );
+ $framework = strtolower( $pars[0] );
+
+ // No framework specified
+ if ( $par == '' ) {
+ $out->setPagetitle( wfMsg( 'javascripttest' ) );
+ $summary = $this->wrapSummaryHtml(
+ wfMsg( 'javascripttest-pagetext-noframework' ) . $this->getFrameworkListHtml(),
+ 'noframework'
+ );
+ $out->addHtml( $summary );
+
+ // Matched! Display proper title and initialize the framework
+ } elseif ( isset( self::$frameworks[$framework] ) ) {
+ $out->setPagetitle( wfMsg( 'javascripttest-title', wfMsg( "javascripttest-$framework-name" ) ) );
+ $out->setSubtitle(
+ wfMessage( 'javascripttest-backlink' )->rawParams( Linker::linkKnown( $this->getTitle() ) )->escaped()
+ );
+ $this->{self::$frameworks[$framework]}();
+
+ // Framework not found, display error
+ } else {
+ $out->setPagetitle( wfMsg( 'javascripttest' ) );
+ $summary = $this->wrapSummaryHtml( '<p class="error">'
+ . wfMsg( 'javascripttest-pagetext-unknownframework', $par )
+ . '</p>'
+ . $this->getFrameworkListHtml() );
+ $out->addHtml( $summary, 'unknownframework' );
+ }
+ }
+
+ /**
+ * Get a list of frameworks (including introduction paragraph and links to the framework run pages)
+ * @return String: HTML
+ */
+ private function getFrameworkListHtml() {
+ $list = '<ul>';
+ foreach( self::$frameworks as $framework => $initFn ) {
+ $list .= Html::rawElement(
+ 'li',
+ array(),
+ Linker::link( $this->getTitle( $framework ), wfMsg( "javascripttest-$framework-name" ) )
+ );
+ }
+ $list .= '</ul>';
+ $msg = wfMessage( 'javascripttest-pagetext-frameworks' )->rawParams( $list )->parseAsBlock();
+
+ return $msg;
+ }
+
+ /**
+ * Function to wrap the summary.
+ * @param $html String: The raw HTML.
+ * @param $state String: State, one of 'noframework', 'unknownframework' or 'frameworkfound'
+ */
+ private function wrapSummaryHtml( $html = '', $state ) {
+ return "<div id=\"mw-javascripttest-summary\" class=\"mw-javascripttest-$state\">$html</div>";
+ }
+
+ /**
+ * Initialize the page for QUnit.
+ */
+ private function initQUnitTesting() {
+ global $wgJavaScriptTestConfig;
+
+ $out = $this->getOutput();
+
+ $out->addModules( 'mediawiki.tests.qunit.testrunner' );
+ $qunitTestModules = $out->getResourceLoader()->getTestModuleNames( 'qunit' );
+ $out->addModules( $qunitTestModules );
+
+ $summary = wfMessage( 'javascripttest-qunit-intro' )
+ ->params( $wgJavaScriptTestConfig['qunit']['documentation'] )
+ ->parseAsBlock();
+ $header = wfMessage( 'javascripttest-qunit-heading' )->escaped();
+
+ $baseHtml = <<<HTML
+<div id="qunit-header">$header</div>
+<div id="qunit-banner"></div>
+<div id="qunit-testrunner-toolbar"></div>
+<div id="qunit-userAgent"></div>
+<ol id="qunit-tests"></ol>
+HTML;
+ $out->addHtml( $this->wrapSummaryHtml( $summary, 'frameworkfound' ) . $baseHtml );
+
+ }
+
+ public function isListed(){
+ global $wgEnableJavaScriptTest;
+ return $wgEnableJavaScriptTest === true;
+ }
+
+}
'Filepath' => array( 'FilePath' ),
'Import' => array( 'Import' ),
'Invalidateemail' => array( 'InvalidateEmail' ),
+ 'JavaScriptTest' => array( 'JavaScriptTest' ),
'BlockList' => array( 'BlockList', 'ListBlocks', 'IPBlockList' ),
'LinkSearch' => array( 'LinkSearch' ),
'Listadmins' => array( 'ListAdmins' ),
'import-logentry-upload-detail' => '$1 {{PLURAL:$1|revision|revisions}}',
'import-logentry-interwiki' => 'transwikied $1',
'import-logentry-interwiki-detail' => '$1 {{PLURAL:$1|revision|revisions}} from $2',
+
+# JavaScriptTest
+'javascripttest' => 'JavaScript Test',
+'javascripttest-backlink' => '< $1',
+'javascripttest-disabled' => 'This function is disabled.',
+'javascripttest-title' => 'Running $1 tests',
+'javascripttest-pagetext-noframework' => 'This page is reserved for running javascript tests.',
+'javascripttest-pagetext-unknownframework' => 'Unknown framework "$1".',
+'javascripttest-pagetext-frameworks' => 'Please choose one of the following frameworks: $1',
+'javascripttest-pagetext-skins' => 'Available skins',
+'javascripttest-qunit-name' => 'QUnit', // Ignore, do not translate
+'javascripttest-qunit-intro' => 'See [$1 testing documentation] on mediawiki.org.',
+'javascripttest-qunit-heading' => 'MediaWiki JavaScript QUnit Test Suite', // Optional, only translate if needed
# Keyboard access keys for power users
'accesskey-pt-userpage' => '.', # do not translate or duplicate this message to other languages
'import-logentry-upload' => 'This is the text of an entry in the Import log (and Recent Changes), after hour (and date, only in the Import log) and sysop name:
* $1 is the name of the imported file',
+# JavaScriptTest
+'javascripttest' => 'Title of the special page',
+'javascripttest-backlink' => '{{optional}}',
+'javascripttest-disabled' => '{{Identical|Function disabled}}.',
+'javascripttest-title' => 'Title of the special page when running a test suite. $1 is the name of the framework.',
+'javascripttest-pagetext-unknownframework' => 'Error message when given framework id is not found. $1 is the if of the framework.',
+'javascripttest-pagetext-frameworks' => '$1 is the if of the framework.',
+'javascripttest-qunit-name' => '{{Ignore}}',
+'javascripttest-qunit-intro' => '$1 is the configured url to the documentation.',
+'javascripttest-qunit-heading' => '{{Optional}}',
+
# Tooltip help for the actions
'tooltip-pt-userpage' => 'Tooltip shown when hovering the mouse over the link to your own User page in the upper-side personal toolbox.',
'tooltip-pt-mytalk' => 'Tooltip shown when hovering over the "my talk" link in your personal toolbox (upper right side).',
'jquery.placeholder' => array(
'scripts' => 'resources/jquery/jquery.placeholder.js',
),
- 'jquery.qunit.completenessTest' => array(
- 'scripts' => 'resources/jquery/jquery.qunit.completenessTest.js',
- 'dependencies' => 'jquery.qunit',
- ),
'jquery.qunit' => array(
'scripts' => 'resources/jquery/jquery.qunit.js',
'styles' => 'resources/jquery/jquery.qunit.css',
'position' => 'top',
),
+ 'jquery.qunit.completenessTest' => array(
+ 'scripts' => 'resources/jquery/jquery.qunit.completenessTest.js',
+ 'dependencies' => 'jquery.qunit',
+ ),
'jquery.spinner' => array(
'scripts' => 'resources/jquery/jquery.spinner.js',
'styles' => 'resources/jquery/jquery.spinner.css',
),
'dependencies' => array( 'mediawiki.libs.jpegmeta', 'mediawiki.util' ),
),
+ 'mediawiki.special.javaScriptTest' => array(
+ 'scripts' => 'resources/mediawiki.special/mediawiki.special.javaScriptTest.js',
+ 'messages' => array_merge( Skin::getSkinNameMessages(), array(
+ 'colon-separator',
+ 'javascripttest-pagetext-skins',
+ ) ),
+ 'dependencies' => array( 'jquery.qunit' ),
+ 'position' => 'top',
+ ),
+
+ /* MediaWiki Tests */
+
+ 'mediawiki.tests.qunit.testrunner' => array(
+ 'scripts' => 'tests/qunit/data/testrunner.js',
+ 'dependencies' => array(
+ 'jquery.qunit',
+ 'jquery.qunit.completenessTest',
+ 'mediawiki.page.startup',
+ 'mediawiki.page.ready',
+ ),
+ 'position' => 'top',
+ ),
/* MediaWiki Legacy */
--- /dev/null
+/*
+ * JavaScript for Special:JavaScriptTest
+ */
+jQuery( document ).ready( function( $ ) {
+
+ // Create useskin dropdown menu and reload onchange to the selected skin
+ // (only if a framework was found, not on error pages).
+ $( '#mw-javascripttest-summary.mw-javascripttest-frameworkfound' ).append( function() {
+
+ var $html = $( '<p><label for="useskin">'
+ + mw.message( 'javascripttest-pagetext-skins' ).escaped()
+ + mw.message( 'colon-separator' ).plain()
+ + '</label></p>' ),
+ select = '<select name="useskin" id="useskin">';
+
+ // Build <select> further
+ $.each( mw.config.get( 'wgAvailableSkins' ), function( id ) {
+ select += '<option value="' + id + '"'
+ + ( mw.config.get( 'skin' ) === id ? ' selected="selected"' : '' )
+ + '>' + mw.message( 'skinname-' + id ).escaped() + '</option>';
+ } );
+ select += '</select>';
+
+ // Bind onchange event handler and append to form
+ $html.append(
+ $( select ).change( function() {
+ window.location = QUnit.url( { useskin: $(this).val() } );
+ } )
+ );
+
+ return $html;
+ } );
+} );
--- /dev/null
+<?php
+
+return array(
+
+ /* Test suites for MediaWiki core modules */
+
+ 'mediawiki.tests.qunit.suites' => array(
+ 'scripts' => array(
+ 'tests/qunit/suites/resources/jquery/jquery.autoEllipsis.test.js',
+ 'tests/qunit/suites/resources/jquery/jquery.byteLength.test.js',
+ 'tests/qunit/suites/resources/jquery/jquery.byteLimit.test.js',
+ 'tests/qunit/suites/resources/jquery/jquery.client.test.js',
+ 'tests/qunit/suites/resources/jquery/jquery.colorUtil.test.js',
+ 'tests/qunit/suites/resources/jquery/jquery.getAttrs.test.js',
+ 'tests/qunit/suites/resources/jquery/jquery.highlightText.test.js',
+ 'tests/qunit/suites/resources/jquery/jquery.localize.test.js',
+ 'tests/qunit/suites/resources/jquery/jquery.mwExtension.test.js',
+ 'tests/qunit/suites/resources/jquery/jquery.tabIndex.test.js',
+ 'tests/qunit/suites/resources/jquery/jquery.tablesorter.test.js',
+ 'tests/qunit/suites/resources/jquery/jquery.textSelection.test.js',
+ 'tests/qunit/suites/resources/mediawiki/mediawiki.jscompat.test.js',
+ 'tests/qunit/suites/resources/mediawiki/mediawiki.test.js',
+ 'tests/qunit/suites/resources/mediawiki/mediawiki.title.test.js',
+ 'tests/qunit/suites/resources/mediawiki/mediawiki.user.test.js',
+ 'tests/qunit/suites/resources/mediawiki/mediawiki.util.test.js',
+ 'tests/qunit/suites/resources/mediawiki.special/mediawiki.special.recentchanges.test.js',
+
+ // *has mw-config def:
+ // This means the module overwrites/sets mw.config variables, reason being that
+ // the static /qunit/index.html has an empty mw.config since it's static.
+ // Until /qunit/index.html is fully replaceable and WMF's TestSwarm is up and running
+ // with Special:JavaScriptTest - untill then, it is important that tests do not depend
+ // on anything being in mw.config (not even wgServer).
+ ),
+ 'dependencies' => array(
+ 'jquery.autoEllipsis',
+ 'jquery.byteLength',
+ 'jquery.byteLimit',
+ 'jquery.client',
+ 'jquery.colorUtil',
+ 'jquery.getAttrs',
+ 'jquery.highlightText',
+ 'jquery.localize',
+ 'jquery.mwExtension',
+ 'jquery.tabIndex',
+ 'jquery.tablesorter',
+ 'jquery.textSelection',
+ 'mediawiki',
+ 'mediawiki.Title',
+ 'mediawiki.user',
+ 'mediawiki.util',
+ 'mediawiki.special.recentchanges',
+ ),
+ )
+);
-( function( $ ) {
+( function ( $, mw, QUnit, undefined ) {
+"use strict";
+
+var mwTestIgnore, mwTester, addons;
/**
* Add bogus to url to prevent IE crazy caching
*
- * @param value {String} a relative path (eg. 'data/defineTestCallback.js' or 'data/test.php?foo=bar')
+ * @param value {String} a relative path (eg. 'data/defineTestCallback.js'
+ * or 'data/test.php?foo=bar').
* @return {String} Such as 'data/defineTestCallback.js?131031765087663960'
*/
-QUnit.fixurl = function(value) {
- return value + (/\?/.test(value) ? "&" : "?") + new Date().getTime() + "" + parseInt(Math.random()*100000);
+QUnit.fixurl = function (value) {
+ return value + (/\?/.test( value ) ? '&' : '?')
+ + String( new Date().getTime() )
+ + String( parseInt( Math.random()*100000, 10 ) );
};
/**
*/
QUnit.config.testTimeout = 5000;
+/**
+ * MediaWiki debug mode
+ */
+QUnit.config.urlConfig.push( 'debug' );
+
/**
* Load TestSwarm agent
*/
if ( QUnit.urlParams.swarmURL ) {
- document.write("<scr" + "ipt src='" + QUnit.fixurl( mw.config.get( 'wgScriptPath' ) + '/tests/qunit/data/testwarm.inject.js' ) + "'></scr" + "ipt>");
+ document.write( "<scr" + "ipt src='" + QUnit.fixurl( mw.config.get( 'wgScriptPath' )
+ + '/tests/qunit/data/testwarm.inject.js' ) + "'></scr" + "ipt>" );
}
/**
- * Load completenesstest
+ * CompletenessTest
*/
+// Adds toggle checkbox to header
+QUnit.config.urlConfig.push( 'completenesstest' );
+
+// Initiate when enabled
if ( QUnit.urlParams.completenesstest ) {
// Return true to ignore
- var mwTestIgnore = function( val, tester, funcPath ) {
+ mwTestIgnore = function ( val, tester, funcPath ) {
// Don't record methods of the properties of constructors,
// to avoid getting into a loop (prototype.constructor.prototype..).
// Since we're therefor skipping any injection for
// "new mw.Foo()", manually set it to true here.
if ( val instanceof mw.Map ) {
- tester.methodCallTracker['Map'] = true;
+ tester.methodCallTracker.Map = true;
return true;
}
if ( val instanceof mw.Title ) {
- tester.methodCallTracker['Title'] = true;
+ tester.methodCallTracker.Title = true;
return true;
}
return false;
};
- var mwTester = new CompletenessTest( mw, mwTestIgnore );
+ mwTester = new CompletenessTest( mw, mwTestIgnore );
}
+/**
+ * Test environment recommended for all QUnit test modules
+ */
+// Whether to log environment changes to the console
+QUnit.config.urlConfig.push( 'mwlogenv' );
+
+/**
+ * Reset mw.config to a fresh copy of the live config for each test();
+ * @param override {Object} [optional]
+ * @example:
+ * <code>
+ * module( .., newMwEnvironment() );
+ *
+ * test( .., function () {
+ * mw.config.set( 'foo', 'bar' ); // just for this test
+ * } );
+ *
+ * test( .., function () {
+ * mw.config.get( 'foo' ); // doesn't exist
+ * } );
+ *
+ *
+ * module( .., newMwEnvironment({ quux: 'corge' }) );
+ *
+ * test( .., function () {
+ * mw.config.get( 'quux' ); // "corge"
+ * mw.config.set( 'quux', "grault" );
+ * } );
+ *
+ * test( .., function () {
+ * mw.config.get( 'quux' ); // "corge"
+ * } );
+ * </code>
+ */
+QUnit.newMwEnvironment = ( function () {
+ var liveConfig, freshConfigCopy, log;
+
+ liveConfig = mw.config.values;
+
+ freshConfigCopy = function ( custom ) {
+ // "deep=true" is important here.
+ // Otherwise we just create a new object with values referring to live config.
+ // e.g. mw.config.set( 'wgFileExtensions', [] ) would not effect liveConfig,
+ // but mw.config.get( 'wgFileExtensions' ).push( 'png' ) would as the array
+ // was passed by reference in $.extend's loop.
+ return $.extend({}, liveConfig, custom, /*deep=*/true );
+ };
+
+ log = QUnit.urlParams.mwlogenv ? mw.log : function () {};
+
+ return function ( override ) {
+ override = override || {};
+
+ return {
+ setup: function () {
+ log( 'MwEnvironment> SETUP for "' + QUnit.config.current.module
+ + ': ' + QUnit.config.current.testName + '"' );
+ // Greetings, mock configuration!
+ mw.config.values = freshConfigCopy( override );
+ },
+
+ teardown: function () {
+ log( 'MwEnvironment> TEARDOWN for "' + QUnit.config.current.module
+ + ': ' + QUnit.config.current.testName + '"' );
+ // Farewell, mock configuration!
+ mw.config.values = liveConfig;
+ }
+ };
+ };
+}() );
+
/**
* Add-on assertion helpers
*/
// Define the add-ons
-var addons = {
+addons = {
// Expect boolean true
- assertTrue: function( actual, message ) {
+ assertTrue: function ( actual, message ) {
strictEqual( actual, true, message );
},
// Expect boolean false
- assertFalse: function( actual, message ) {
+ assertFalse: function ( actual, message ) {
strictEqual( actual, false, message );
},
// Expect numerical value less than X
- lt: function( actual, expected, message ) {
+ lt: function ( actual, expected, message ) {
QUnit.push( actual < expected, actual, 'less than ' + expected, message );
},
// Expect numerical value less than or equal to X
- ltOrEq: function( actual, expected, message ) {
+ ltOrEq: function ( actual, expected, message ) {
QUnit.push( actual <= expected, actual, 'less than or equal to ' + expected, message );
},
// Expect numerical value greater than X
- gt: function( actual, expected, message ) {
+ gt: function ( actual, expected, message ) {
QUnit.push( actual > expected, actual, 'greater than ' + expected, message );
},
// Expect numerical value greater than or equal to X
- gtOrEq: function( actual, expected, message ) {
+ gtOrEq: function ( actual, expected, message ) {
QUnit.push( actual >= expected, actual, 'greater than or equal to ' + expected, message );
},
$.extend( QUnit, addons );
$.extend( window, addons );
-})( jQuery );
+})( jQuery, mediaWiki, QUnit );
-module( 'jquery.autoEllipsis' );
+module( 'jquery.autoEllipsis', QUnit.newMwEnvironment() );
test( '-- Initial check', function() {
expect(1);
-module( 'jquery.byteLength' );
+module( 'jquery.byteLength', QUnit.newMwEnvironment() );
test( '-- Initial check', function() {
expect(1);
-module( 'jquery.byteLimit' );
+( function () {
+
+module( 'jquery.byteLimit', QUnit.newMwEnvironment() );
test( '-- Initial check', function() {
expect(1);
$input: $( '<input>' )
.attr( 'type', 'text' )
.byteLimit( 6, function( val ) {
- _titleConfig();
-
// Invalid title
if ( val == '' ) {
return '';
.attr( 'type', 'text' )
.prop( 'maxLength', '6' )
.byteLimit( function( val ) {
- _titleConfig();
-
// Invalid title
if ( val === '' ) {
return '';
limit: 6, // 'Sample' length
expected: 'User:Sample'
});
+
+}() );
\ No newline at end of file
-module( 'jquery.client' );
+module( 'jquery.client', QUnit.newMwEnvironment() );
test( '-- Initial check', function() {
expect(1);
-module( 'jquery.colorUtil' );
+module( 'jquery.colorUtil', QUnit.newMwEnvironment() );
test( '-- Initial check', function() {
expect(1);
-module( 'jquery.getAttrs' );
+module( 'jquery.getAttrs', QUnit.newMwEnvironment() );
test( '-- Initial check', function() {
expect(1);
-module( 'jquery.highlightText' );
+module( 'jquery.highlightText', QUnit.newMwEnvironment() );
test( '-- Initial check', function() {
expect(1);
-module( 'jquery.localize' );
+module( 'jquery.localize', QUnit.newMwEnvironment() );
test( '-- Initial check', function() {
expect(1);
-module( 'jquery.mwExtension' );
+module( 'jquery.mwExtension', QUnit.newMwEnvironment() );
test( 'String functions', function() {
-module( 'jquery.tabIndex' );
+module( 'jquery.tabIndex', QUnit.newMwEnvironment() );
test( '-- Initial check', function() {
expect(2);
-(function() {
+( function () {
-module( 'jquery.tablesorter' );
-
-// setup hack
-mw.config.set( 'wgMonthNames', [ '', 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December' ] );
-mw.config.set( 'wgMonthNamesShort', ['', 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'] );
-mw.config.set( 'wgDefaultDateFormat', 'dmy' );
+var config = {
+ wgMonthNames: ['', 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
+ wgMonthNamesShort: ['', 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
+ wgDefaultDateFormat: 'dmy',
+ wgContentLanguage: 'en'
+};
+module( 'jquery.tablesorter', QUnit.newMwEnvironment( config ) );
test( '-- Initial check', function() {
expect(1);
['11.11.2011']
],
function( $table ) {
- // @fixme reset it at end or change module to allow us to override it
mw.config.set( 'wgDefaultDateFormat', 'dmy' );
+ mw.config.set( 'wgContentLanguage', 'de' );
+
$table.tablesorter();
$table.find( '.headerSort:eq(0)' ).click();
}
['11.11.2011']
],
function( $table ) {
- // @fixme reset it at end or change module to allow us to override it
mw.config.set( 'wgDefaultDateFormat', 'mdy' );
+
$table.tablesorter();
$table.find( '.headerSort:eq(0)' ).click();
}
'ß': 'ss',
'ü':'ue'
} );
+
$table.tablesorter();
$table.find( '.headerSort:eq(0)' ).click();
- mw.config.set( 'tableSorterCollation', {} );
}
);
complexMDYSorted,
function( $table ) {
mw.config.set( 'wgDefaultDateFormat', 'mdy' );
+
$table.tablesorter();
$table.find( '.headerSort:eq(0)' ).click();
}
-module( 'jquery.textSelection' );
+module( 'jquery.textSelection', QUnit.newMwEnvironment() );
test( '-- Initial check', function() {
expect(1);
-module( 'mediawiki.special.recentchanges' );
+module( 'mediawiki.special.recentchanges', QUnit.newMwEnvironment() );
test( '-- Initial check', function() {
expect( 2 );
-module( 'mediawiki.Title' );
+( function () {
// mw.Title relies on these three config vars
// Restore them after each test run
-var _titleConfig = function() {
-
- mw.config.set({
- "wgFormattedNamespaces": {
- "-2": "Media",
- "-1": "Special",
- "0": "",
- "1": "Talk",
- "2": "User",
- "3": "User talk",
- "4": "Wikipedia",
- "5": "Wikipedia talk",
- "6": "File",
- "7": "File talk",
- "8": "MediaWiki",
- "9": "MediaWiki talk",
- "10": "Template",
- "11": "Template talk",
- "12": "Help",
- "13": "Help talk",
- "14": "Category",
- "15": "Category talk",
- /* testing custom / localized */
- "100": "Penguins"
- },
- "wgNamespaceIds": {
- "media": -2,
- "special": -1,
- "": 0,
- "talk": 1,
- "user": 2,
- "user_talk": 3,
- "wikipedia": 4,
- "wikipedia_talk": 5,
- "file": 6,
- "file_talk": 7,
- "mediawiki": 8,
- "mediawiki_talk": 9,
- "template": 10,
- "template_talk": 11,
- "help": 12,
- "help_talk": 13,
- "category": 14,
- "category_talk": 15,
- "image": 6,
- "image_talk": 7,
- "project": 4,
- "project_talk": 5,
- /* testing custom / alias */
- "penguins": 100,
- "antarctic_waterfowl": 100
- },
- "wgCaseSensitiveNamespaces": []
- });
+var config = {
+ "wgFormattedNamespaces": {
+ "-2": "Media",
+ "-1": "Special",
+ "0": "",
+ "1": "Talk",
+ "2": "User",
+ "3": "User talk",
+ "4": "Wikipedia",
+ "5": "Wikipedia talk",
+ "6": "File",
+ "7": "File talk",
+ "8": "MediaWiki",
+ "9": "MediaWiki talk",
+ "10": "Template",
+ "11": "Template talk",
+ "12": "Help",
+ "13": "Help talk",
+ "14": "Category",
+ "15": "Category talk",
+ // testing custom / localized namespace
+ "100": "Penguins"
+ },
+ "wgNamespaceIds": {
+ "media": -2,
+ "special": -1,
+ "": 0,
+ "talk": 1,
+ "user": 2,
+ "user_talk": 3,
+ "wikipedia": 4,
+ "wikipedia_talk": 5,
+ "file": 6,
+ "file_talk": 7,
+ "mediawiki": 8,
+ "mediawiki_talk": 9,
+ "template": 10,
+ "template_talk": 11,
+ "help": 12,
+ "help_talk": 13,
+ "category": 14,
+ "category_talk": 15,
+ "image": 6,
+ "image_talk": 7,
+ "project": 4,
+ "project_talk": 5,
+ /* testing custom / alias */
+ "penguins": 100,
+ "antarctic_waterfowl": 100
+ },
+ "wgCaseSensitiveNamespaces": []
};
-test( '-- Initial check', function() {
+module( 'mediawiki.Title', QUnit.newMwEnvironment( config ) );
+
+test( '-- Initial check', function () {
expect(1);
ok( mw.Title, 'mw.Title defined' );
});
-test( 'Transformation', function() {
+test( 'Transformation', function () {
expect(8);
- _titleConfig();
var title;
equal( title.getName(), 'Foo_bar_.js', "Merge multiple spaces to a single space." );
});
-test( 'Main text for filename', function() {
+test( 'Main text for filename', function () {
expect(8);
- _titleConfig();
var title = new mw.Title( 'File:foo_bar.JPG' );
equal( title.getDotExtension(), '.JPG' );
});
-test( 'Namespace detection and conversion', function() {
+test( 'Namespace detection and conversion', function () {
expect(6);
- _titleConfig();
var title;
equal( title.toString(), 'Penguins:Flightless_yet_cute.jpg' );
});
-test( 'Throw error on invalid title', function() {
+test( 'Throw error on invalid title', function () {
expect(1);
- _titleConfig();
- raises(function() {
+ raises(function () {
var title = new mw.Title( '' );
}, 'Throw error on empty string' );
});
-test( 'Case-sensivity', function() {
+test( 'Case-sensivity', function () {
expect(3);
- _titleConfig();
var title;
equal( title.toString(), 'User:John', '$wgCapitalLinks=false: User namespace is insensitive, first-letter becomes uppercase' );
});
-test( 'toString / toText', function() {
+test( 'toString / toText', function () {
expect(2);
- _titleConfig();
var title = new mw.Title( 'Some random page' );
equal( title.toText(), title.getPrefixedText() );
});
-test( 'Exists', function() {
+test( 'Exists', function () {
expect(3);
- _titleConfig();
var title;
});
-test( 'Url', function() {
+test( 'Url', function () {
expect(2);
- _titleConfig();
var title;
title = new mw.Title( 'John Doe', 3 );
equal( title.getUrl(), '/wiki/User_talk:John_Doe', 'Escaping in title and namespace for urls' );
});
+
+}() );
\ No newline at end of file
/* Some misc JavaScript compatibility tests, just to make sure the environments we run in are consistent */
-module( 'mediawiki.jscompat' );
+module( 'mediawiki.jscompat', QUnit.newMwEnvironment() );
test( 'Variable with Unicode letter in name', function() {
expect(3);
-module( 'mediawiki' );
+module( 'mediawiki', QUnit.newMwEnvironment() );
test( '-- Initial check', function() {
expect(8);
// This bug was actually already fixed in 1.18 and later when discovered in 1.17.
// Test is for regressions!
- expect(1);
+ expect(2);
+
+ var server = mw.config.get( 'wgServer' ),
+ basePath = mw.config.get( 'wgScriptPath' );
+
+ // From [[Special:JavaScriptTest]] we need to preprend the script path
+ // with the actual server (http://localhost/).
+ // Running from file tests/qunit/index.html, wgScriptPath is already
+ // including the wgServer part
+ if( server !== null ) {
+ basePath = server + basePath;
+ }
+ // Forge an URL to the test callback script
+ var target = QUnit.fixurl(
+ basePath + '/tests/qunit/data/qunitOkCall.js'
+ );
// Confirm that mw.loader.load() works with protocol-relative URLs
- var loc = window.location,
- base = ('//' + loc.hostname + loc.pathname).replace(/\/[^\/]*$/, ''),
- target = base + '/data/qunitOkCall.js?' + (new Date()).getTime();
+ target = target.replace( /https?:/, '' );
+
+ equal( target.substr( 0, 2 ), '//',
+ 'URL must be relative to test relative URLs!'
+ );
// Async!
stop();
-module( 'mediawiki.user' );
+module( 'mediawiki.user', QUnit.newMwEnvironment() );
test( '-- Initial check', function() {
expect(1);
test( 'User login status', function() {
expect(5);
+ /**
+ * Tests can be run under three different conditions:
+ * 1) From tests/qunit/index.html, user will be anonymous.
+ * 2) Logged in on [[Special:JavaScriptTest/qunit]]
+ * 3) Anonymously at the same special page.
+ */
+
+ // Forge an anonymous user:
+ mw.config.set( 'wgUserName', null);
+
strictEqual( mw.user.name(), null, 'user.name should return null when anonymous' );
ok( mw.user.anonymous(), 'user.anonymous should reutrn true when anonymous' );
-module( 'mediawiki.util' );
+module( 'mediawiki.util', QUnit.newMwEnvironment() );
test( '-- Initial check', function() {
expect(1);
test( 'wikiScript', function() {
expect(2);
- var prevConfig = mw.config.get([ 'wgScript', 'wgScriptPath', 'wgScriptExtension' ]);
mw.config.set({
'wgScript': '/w/index.php',
'wgScriptPath': '/w',
equal( mw.util.wikiScript(), mw.config.get( 'wgScript' ), 'Defaults to index.php and is equal to wgScript' );
equal( mw.util.wikiScript( 'api' ), '/w/api.php', 'API path' );
-
- // Restore mw.config
- mw.config.set( prevConfig );
});
test( 'addCSS', function() {