r100340 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r100339‎ | r100340 | r100341 >
Date:12:53, 20 October 2011
Author:hashar
Status:reverted
Tags:
Comment:
(bug 30339) SpecialPage for running JS tests

This commit apply Krinkle's patch attached to bug 30339 and implements
a new special page dedicated to running our QUnit tests.
Modified paths:
  • /branches/JSTesting/includes/AutoLoader.php (modified) (history)
  • /branches/JSTesting/includes/DefaultSettings.php (modified) (history)
  • /branches/JSTesting/includes/Skin.php (modified) (history)
  • /branches/JSTesting/includes/SpecialPageFactory.php (modified) (history)
  • /branches/JSTesting/includes/resourceloader/ResourceLoader.php (modified) (history)
  • /branches/JSTesting/includes/specials/SpecialJavaScriptTest.php (added) (history)
  • /branches/JSTesting/languages/messages/MessagesEn.php (modified) (history)
  • /branches/JSTesting/languages/messages/MessagesQqq.php (modified) (history)
  • /branches/JSTesting/resources/Resources.php (modified) (history)
  • /branches/JSTesting/resources/mediawiki.special/mediawiki.special.javaScriptTest.js (added) (history)
  • /branches/JSTesting/tests/qunit/QUnitTestResources.php (added) (history)

Diff [purge]

Index: branches/JSTesting/languages/messages/MessagesEn.php
@@ -392,6 +392,7 @@
393393 'Filepath' => array( 'FilePath' ),
394394 'Import' => array( 'Import' ),
395395 'Invalidateemail' => array( 'InvalidateEmail' ),
 396+ 'JavaScriptTest' => array( 'JavaScriptTest' ),
396397 'BlockList' => array( 'BlockList', 'ListBlocks', 'IPBlockList' ),
397398 'LinkSearch' => array( 'LinkSearch' ),
398399 'Listadmins' => array( 'ListAdmins' ),
@@ -2957,6 +2958,18 @@
29582959 'undelete-show-file-confirm' => 'Are you sure you want to view the deleted revision of the file "<nowiki>$1</nowiki>" from $2 at $3?',
29592960 'undelete-show-file-submit' => 'Yes',
29602961
 2962+# JavaScriptTest
 2963+'javascripttest' => 'JavaScript Test',
 2964+'javascripttest-disabled' => 'This function is disabled.',
 2965+'javascripttest-title' => 'Running $1 tests',
 2966+'javascripttest-pagetext-noframework' => 'This page is reserved for running javascript tests.',
 2967+'javascripttest-pagetext-unknownframework' => 'Unknown framework "$1".',
 2968+'javascripttest-pagetext-frameworks' => 'Please choose one of the following frameworks: $1',
 2969+'javascripttest-pagetext-skins' => 'Available skins: ',
 2970+'javascripttest-qunit-name' => 'QUnit', // Ignore, do not translate
 2971+'javascripttest-qunit-intro' => 'See [$1 testing documentation] on mediawiki.org.',
 2972+'javascripttest-qunit-heading' => 'MediaWiki JavaScript Test Suite', // Optional, translate if needed
 2973+
29612974 # Namespace form on various pages
29622975 'namespace' => 'Namespace:',
29632976 'invert' => 'Invert selection',
Index: branches/JSTesting/languages/messages/MessagesQqq.php
@@ -2613,6 +2613,16 @@
26142614 {{identical|Are you sure you want to view the deleted revision of the file...}}',
26152615 'undelete-show-file-submit' => '{{Identical|Yes}}',
26162616
 2617+# JavaScriptTest
 2618+'javascripttest' => 'Title of the special page',
 2619+'javascripttest-disabled' => '{{Identical|Function disabled}}.',
 2620+'javascripttest-title' => 'Title of the special page when running a test suite. $1 is the name of the framework.',
 2621+'javascripttest-pagetext-unknownframework' => 'Error message when given framework id is not found. $1 is the if of the framework.',
 2622+'javascripttest-pagetext-frameworks' => '$1 is the if of the framework.',
 2623+'javascripttest-qunit-name' => '{{Ignore}}',
 2624+'javascripttest-qunit-intro' => '$1 is the configured url to the documentation.',
 2625+'javascripttest-qunit-heading' => '{{Optional}}',
 2626+
26172627 # Namespace form on various pages
26182628 'namespace' => 'This message is located at [[Special:Contributions]].',
26192629 'invert' => 'Displayed in [[Special:RecentChanges|RecentChanges]], [[Special:RecentChangesLinked|RecentChangesLinked]] and [[Special:Watchlist|Watchlist]]
Index: branches/JSTesting/resources/Resources.php
@@ -666,6 +666,13 @@
667667 'mediawiki.special.undelete' => array(
668668 'scripts' => 'resources/mediawiki.special/mediawiki.special.undelete.js',
669669 ),
 670+ 'mediawiki.special.javaScriptTest' => array(
 671+ 'scripts' => 'resources/mediawiki.special/mediawiki.special.javaScriptTest.js',
 672+ 'messages' => array_merge( Skin::getSkinNameMessages(), array(
 673+ 'javascripttest-pagetext-skins'
 674+ ) ),
 675+ 'dependencies' => array( 'jquery.qunit' ),
 676+ ),
670677 'mediawiki.special.movePage' => array(
671678 'scripts' => 'resources/mediawiki.special/mediawiki.special.movePage.js',
672679 'dependencies' => 'jquery.byteLimit',
@@ -689,6 +696,17 @@
690697 'dependencies' => array( 'mediawiki.libs.jpegmeta' ),
691698 ),
692699
 700+ /* Testing */
 701+
 702+ 'mediawiki.tests.qunit.testrunner' => array(
 703+ 'scripts' => 'tests/qunit/data/testrunner.js',
 704+ 'dependencies' => array(
 705+ 'jquery.qunit',
 706+ 'jquery.qunit.completenessTest',
 707+ ),
 708+ 'position' => 'top',
 709+ ),
 710+
693711 /* MediaWiki Legacy */
694712
695713 'mediawiki.legacy.ajax' => array(
Index: branches/JSTesting/resources/mediawiki.special/mediawiki.special.javaScriptTest.js
@@ -0,0 +1,31 @@
 2+/*
 3+ * JavaScript for Special:JavaScriptTest
 4+ */
 5+jQuery( document ).ready( function( $ ) {
 6+
 7+ // Create useskin dropdown menu and reload onchange to the selected skin
 8+ // (only if a framework was found, not on error pages).
 9+ $( '#mw-javascripttest-summary.mw-javascripttest-frameworkfound' ).append( function() {
 10+
 11+ var $html = $( '<p><label for="useskin">'
 12+ + mw.message( 'javascripttest-pagetext-skins' ).escaped() + '</label></p>' ),
 13+ select = '<select name="useskin" id="useskin">';
 14+
 15+ // Build <select> further
 16+ $.each( mw.config.get( 'wgAvailableSkins' ), function( id ) {
 17+ select += '<option value="' + id + '"'
 18+ + ( mw.config.get( 'skin' ) === id ? ' selected="selected"' : '' )
 19+ + '>' + mw.message( 'skinname-' + id ).escaped() + '</option>';
 20+ } );
 21+ select += '</select>';
 22+
 23+ // Bind onchange event handler and append to form
 24+ $html.append(
 25+ $( select ).change( function() {
 26+ window.location = QUnit.url( { useskin: $(this).val() } );
 27+ } )
 28+ );
 29+
 30+ return $html;
 31+ } );
 32+} );
Index: branches/JSTesting/tests/qunit/QUnitTestResources.php
@@ -0,0 +1,49 @@
 2+<?php
 3+
 4+return array(
 5+
 6+ /* Resources of QUnit test suite for MediaWiki core */
 7+
 8+ 'mediawiki.tests.qunit.suites' => array(
 9+ 'scripts' => array(
 10+ 'tests/qunit/suites/resources/jquery/jquery.autoEllipsis.test.js',
 11+ 'tests/qunit/suites/resources/jquery/jquery.byteLength.test.js',
 12+ 'tests/qunit/suites/resources/jquery/jquery.byteLimit.test.js', // mw-config def
 13+ 'tests/qunit/suites/resources/jquery/jquery.client.test.js',
 14+ 'tests/qunit/suites/resources/jquery/jquery.colorUtil.test.js',
 15+ 'tests/qunit/suites/resources/jquery/jquery.getAttrs.test.js',
 16+ 'tests/qunit/suites/resources/jquery/jquery.localize.test.js',
 17+ 'tests/qunit/suites/resources/jquery/jquery.mwExtension.test.js',
 18+ 'tests/qunit/suites/resources/jquery/jquery.tabIndex.test.js',
 19+ # jquery.tablesorter.test.js: Broken
 20+ #'tests/qunit/suites/resources/jquery/jquery.tablesorter.test.js', // mw-config def
 21+ 'tests/qunit/suites/resources/jquery/jquery.textSelection.test.js',
 22+ # mediawiki.test.js: Broken due to relative path to /data/defineTestCallback.js
 23+ #'tests/qunit/suites/resources/mediawiki/mediawiki.test.js',
 24+ 'tests/qunit/suites/resources/mediawiki/mediawiki.title.test.js', // mw-config def
 25+ 'tests/qunit/suites/resources/mediawiki/mediawiki.user.test.js',
 26+ 'tests/qunit/suites/resources/mediawiki/mediawiki.util.test.js',
 27+ 'tests/qunit/suites/resources/mediawiki.special/mediawiki.special.recentchanges.test.js',
 28+
 29+ // mw-config def: Contains mw.config defaults, fix when /qunit/index.html is removed
 30+ ),
 31+ 'dependencies' => array(
 32+ 'jquery.autoEllipsis',
 33+ 'jquery.byteLength',
 34+ 'jquery.byteLimit',
 35+ 'jquery.client',
 36+ 'jquery.colorUtil',
 37+ 'jquery.getAttrs',
 38+ 'jquery.localize',
 39+ 'jquery.mwExtension',
 40+ 'jquery.tabIndex',
 41+ 'jquery.tablesorter',
 42+ 'jquery.textSelection',
 43+ 'mediawiki',
 44+ 'mediawiki.Title',
 45+ 'mediawiki.user',
 46+ 'mediawiki.util',
 47+ 'mediawiki.special.recentchanges',
 48+ ),
 49+ )
 50+);
Index: branches/JSTesting/includes/resourceloader/ResourceLoader.php
@@ -38,6 +38,10 @@
3939 /** Associative array mapping module name to info associative array */
4040 protected $moduleInfos = array();
4141
 42+ /** Associative array mapping framework ids to list of test suite module(s) */
 43+ /** like array( 'qunit' => array( 'mediawiki.tests.qunit.suites', 'ext.foo.tests', .. ), .. ) */
 44+ protected $testModuleNames = array();
 45+
4246 /** array( 'source-id' => array( 'loadScript' => 'http://.../load.php' ) ) **/
4347 protected $sources = array();
4448
@@ -183,7 +187,7 @@
184188 * Registers core modules and runs registration hooks.
185189 */
186190 public function __construct() {
187 - global $IP, $wgResourceModules, $wgResourceLoaderSources, $wgLoadScript;
 191+ global $IP, $wgResourceModules, $wgResourceLoaderSources, $wgLoadScript, $wgEnableJavaScriptTest;
188192
189193 wfProfileIn( __METHOD__ );
190194
@@ -199,6 +203,29 @@
200204 wfRunHooks( 'ResourceLoaderRegisterModules', array( &$this ) );
201205 $this->register( $wgResourceModules );
202206
 207+ if ( $wgEnableJavaScriptTest === true ) {
 208+
 209+ // Get core test suites
 210+ $testModules = array();
 211+ $testModules['qunit'] = include( "$IP/tests/qunit/QUnitTestResources.php" );
 212+ // Allow extensions to add test suites
 213+ wfRunHooks( 'ResourceLoaderTestModules', array( &$testModules, &$this ) );
 214+
 215+ foreach( $testModules as $id => $names ) {
 216+
 217+ // Add the testrunner to the dependencies to make sure it's loaded first
 218+ foreach( $names as $name ) {
 219+ $testModules[$id][$name]['dependencies'][] = 'mediawiki.tests.qunit.testrunner';
 220+ }
 221+
 222+ // Register test modules
 223+ $this->register( $testModules[$id] );
 224+
 225+ // Keep track of their names so that they can be loaded together
 226+ $this->testModuleNames[$id] = array_keys( $testModules[$id] );
 227+ }
 228+ }
 229+
203230 wfProfileOut( __METHOD__ );
204231 }
205232
@@ -307,6 +334,21 @@
308335 }
309336
310337 /**
 338+ * Get a list of test module names for one (or all) frameworks.
 339+ *
 340+ * @return Array
 341+ */
 342+ public function getTestModuleNames( $framework = 'all' ) {
 343+ if ( $framework == 'all' ) {
 344+ return $this->testModuleNames;
 345+ } elseif ( isset( $this->testModuleNames[$framework] ) && is_array( $this->testModuleNames[$framework] ) ) {
 346+ return $this->testModuleNames[$framework];
 347+ } else {
 348+ return array();
 349+ }
 350+ }
 351+
 352+ /**
311353 * Get the ResourceLoaderModule object for a given module name.
312354 *
313355 * @param $name String: Module name
Index: branches/JSTesting/includes/AutoLoader.php
@@ -777,6 +777,7 @@
778778 'SpecialExport' => 'includes/specials/SpecialExport.php',
779779 'SpecialFilepath' => 'includes/specials/SpecialFilepath.php',
780780 'SpecialImport' => 'includes/specials/SpecialImport.php',
 781+ 'SpecialJavaScriptTest' => 'includes/specials/SpecialJavaScriptTest.php',
781782 'SpecialListFiles' => 'includes/specials/SpecialListfiles.php',
782783 'SpecialListGroupRights' => 'includes/specials/SpecialListgrouprights.php',
783784 'SpecialListUsers' => 'includes/specials/SpecialListusers.php',
Index: branches/JSTesting/includes/Skin.php
@@ -22,7 +22,7 @@
2323
2424 /**
2525 * Fetch the set of available skins.
26 - * @return array of strings
 26+ * @return associative array of strings
2727 */
2828 static function getSkinNames() {
2929 global $wgValidSkinNames;
@@ -57,6 +57,18 @@
5858 }
5959
6060 /**
 61+ * Fetch the skinname messages for available skins.
 62+ * @return array of strings
 63+ */
 64+ static function getSkinNameMessages() {
 65+ $messages = array();
 66+ foreach( self::getSkinNames() as $skinKey => $skinName ) {
 67+ $messages[] = "skinname-$skinKey";
 68+ }
 69+ return $messages;
 70+ }
 71+
 72+ /**
6173 * Fetch the list of usable skins in regards to $wgSkipSkins.
6274 * Useful for Special:Preferences and other places where you
6375 * only want to show skins users _can_ use.
Index: branches/JSTesting/includes/DefaultSettings.php
@@ -4160,6 +4160,18 @@
41614161 */
41624162 $wgWikiID = false;
41634163
 4164+/**
 4165+ * Allow running of javascript test suites via [[Special:JavaScriptTest]] (such as QUnit).
 4166+ */
 4167+$wgEnableJavaScriptTest = false;
 4168+
 4169+/**
 4170+ * Configuration for QUnit testing (ie. through [[Special:JavaScriptTest/qunit]]).
 4171+ */
 4172+$wgQUnitConfig = array(
 4173+ 'documentation' => 'http://www.mediawiki.org/wiki/Manual:JavaScript_unit_testing',
 4174+);
 4175+
41644176 /** @} */ # end of profiling, testing and debugging }
41654177
41664178 /************************************************************************//**
@@ -5203,6 +5215,7 @@
52045216 'Specialpages' => 'other',
52055217 'Blockme' => 'other',
52065218 'Booksources' => 'other',
 5219+ 'JavaScriptTest' => 'other',
52075220 );
52085221
52095222 /** Whether or not to sort special pages in Special:Specialpages */
Index: branches/JSTesting/includes/SpecialPageFactory.php
@@ -138,6 +138,7 @@
139139 'Blankpage' => 'SpecialBlankpage',
140140 'Blockme' => 'SpecialBlockme',
141141 'Emailuser' => 'SpecialEmailUser',
 142+ 'JavaScriptTest' => 'SpecialJavaScriptTest',
142143 'Movepage' => 'MovePageForm',
143144 'Mycontributions' => 'SpecialMycontributions',
144145 'Mypage' => 'SpecialMypage',
Index: branches/JSTesting/includes/specials/SpecialJavaScriptTest.php
@@ -0,0 +1,122 @@
 2+<?php
 3+
 4+class SpecialJavaScriptTest extends SpecialPage {
 5+
 6+ /**
 7+ * @var $frameworks Array: Mapping of framework ids and their initilizer methods
 8+ * in this class. If a framework is requested but not in this array,
 9+ * the 'unknownframework' error is served.
 10+ */
 11+ static $frameworks = array(
 12+ 'qunit' => 'initQUnitTesting',
 13+ );
 14+
 15+ public function __construct() {
 16+ parent::__construct( 'JavaScriptTest' );
 17+ }
 18+
 19+ public function execute( $par ) {
 20+ global $wgEnableJavaScriptTest;
 21+
 22+ $out = $this->getOutput();
 23+
 24+ $this->setHeaders();
 25+ $out->disallowUserJs();
 26+
 27+ // Abort early if we're disabled
 28+ if ( $wgEnableJavaScriptTest !== true ) {
 29+ $out->addWikiMsg( 'javascripttest-disabled' );
 30+ return;
 31+ }
 32+
 33+ $out->addModules( 'mediawiki.special.javaScriptTest' );
 34+
 35+ // Determine framework
 36+ $pars = explode( '/', $par );
 37+ $framework = strtolower( $pars[0] );
 38+
 39+ // No framework specified
 40+ if ( $par == '' ) {
 41+ $out->setPagetitle( wfMsg( 'javascripttest' ) );
 42+ $summary = $this->wrapSummaryHtml(
 43+ wfMsg( 'javascripttest-pagetext-noframework' ) . $this->getFrameworkListHtml(),
 44+ 'noframework'
 45+ );
 46+ $out->addHtml( $summary );
 47+
 48+ // Matched! Display proper title and initialize the framework
 49+ } elseif ( isset( self::$frameworks[$framework] ) ) {
 50+ $out->setPagetitle( wfMsg( 'javascripttest-title', wfMsg( "javascripttest-$framework-name" ) ) );
 51+ $this->{self::$frameworks[$framework]}();
 52+
 53+ // Framework not found, display error
 54+ } else {
 55+ $out->setPagetitle( wfMsg( 'javascripttest' ) );
 56+ $summary = $this->wrapSummaryHtml( '<p class="error">'
 57+ . wfMsg( 'javascripttest-pagetext-unknownframework', $par )
 58+ . '</p>'
 59+ . $this->getFrameworkListHtml() );
 60+ $out->addHtml( $summary, 'unknownframework' );
 61+ }
 62+ }
 63+
 64+ /**
 65+ * Get a list of frameworks (including introduction paragraph and links to the framework run pages)
 66+ * @return String: HTML
 67+ */
 68+ private function getFrameworkListHtml() {
 69+ $list = '<ul>';
 70+ foreach( self::$frameworks as $framework => $initFn ) {
 71+ $list .= Html::rawElement(
 72+ 'li',
 73+ array(),
 74+ Linker::link( $this->getTitle( $framework ), wfMsg( "javascripttest-$framework-name" ) )
 75+ );
 76+ }
 77+ $list .= '</ul>';
 78+ $msg = wfMessage( 'javascripttest-pagetext-frameworks' )->rawParams( $list )->parseAsBlock();
 79+
 80+ return $msg;
 81+ }
 82+
 83+ /**
 84+ * Function to wrap the summary.
 85+ * @param $html String: The raw HTML.
 86+ * @param $state String: State, one of 'noframework', 'unknownframework' or 'frameworkfound'
 87+ */
 88+ private function wrapSummaryHtml( $html = '', $state ) {
 89+ return "<div id=\"mw-javascripttest-summary\" class=\"mw-javascripttest-$state\">$html</div>";
 90+ }
 91+
 92+ /**
 93+ * Initialize the page for QUnit.
 94+ */
 95+ private function initQUnitTesting() {
 96+ global $wgQUnitConfig;
 97+
 98+ $out = $this->getOutput();
 99+
 100+ $out->addModules( 'mediawiki.tests.qunit.testrunner' );
 101+ $qunitTestModules = $out->getResourceLoader()->getTestModuleNames( 'qunit' );
 102+ $out->addModules( $qunitTestModules );
 103+
 104+ $summary = wfMessage( 'javascripttest-qunit-intro' )->params( $wgQUnitConfig['documentation'] )->parseAsBlock();
 105+ $header = wfMessage( 'javascripttest-qunit-heading' )->escaped();
 106+
 107+ $baseHtml = <<<HTML
 108+<div id="qunit-header">$header</div>
 109+<div id="qunit-banner"></div>
 110+<div id="qunit-testrunner-toolbar"></div>
 111+<div id="qunit-userAgent"></div>
 112+<ol id="qunit-tests"></ol>
 113+HTML;
 114+ $out->addHtml( $this->wrapSummaryHtml( $summary, 'frameworkfound' ) . $baseHtml );
 115+
 116+ }
 117+
 118+ public function isListed(){
 119+ global $wgEnableJavaScriptTest;
 120+ return $wgEnableJavaScriptTest === true;
 121+ }
 122+
 123+}

Follow-up revisions

RevisionCommit summaryAuthorDate
r100341testrunner only needed for the qunit suite...hashar13:19, 20 October 2011
r100370[JSTesting] Reset branch. In preparation of re-branching....krinkle20:40, 20 October 2011
r100374[JSTesting] Re-branch from trunk....krinkle21:02, 20 October 2011

Past revisions this follows-up on

RevisionCommit summaryAuthorDate
r100339creating branch for JavaScript testing...hashar12:46, 20 October 2011

Status & tagging log