Merge "Add test for Message::params"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Thu, 6 Mar 2014 20:12:18 +0000 (20:12 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Thu, 6 Mar 2014 20:12:18 +0000 (20:12 +0000)
13 files changed:
RELEASE-NOTES-1.23
includes/diff/DifferenceEngine.php
includes/specials/SpecialContributions.php
includes/specials/SpecialDeletedContributions.php
includes/specials/SpecialUserlogin.php
includes/templates/Usercreate.php
languages/messages/MessagesEn.php
languages/messages/MessagesQqq.php
maintenance/language/messages.inc
resources/Resources.php
resources/mediawiki.special/mediawiki.special.userlogin.signup.js
tests/phpunit/includes/MessageTest.php
tests/phpunit/languages/SpecialPageAliasTest.php [new file with mode: 0644]

index 798d59c..2b91f88 100644 (file)
@@ -116,10 +116,6 @@ production.
   creations, similar to the topOnly option.
 * Add mediawiki.ui.button styling to all pages so wiki content can use styled
   buttons.
-* Special:UserLogin/signup now does AJAX checks for invalid and taken usernames,
-  displaying the error live.
-* Special:UserLogin/signup now warns the user if their chosen username has to be
-  normalized.
 
 === Bug fixes in 1.23 ===
 * (bug 41759) The "updated since last visit" markers (on history pages, recent
index 52eb003..414b9f8 100644 (file)
@@ -105,8 +105,9 @@ class DifferenceEngine extends ContextSource {
         * @param bool $refreshCache If set, refreshes the diff cache
         * @param bool $unhide If set, allow viewing deleted revs
         */
-       function __construct( $context = null, $old = 0, $new = 0, $rcid = 0,
-               $refreshCache = false, $unhide = false ) {
+       public function __construct( $context = null, $old = 0, $new = 0, $rcid = 0,
+               $refreshCache = false, $unhide = false
+       ) {
                if ( $context instanceof IContextSource ) {
                        $this->setContext( $context );
                }
@@ -122,14 +123,14 @@ class DifferenceEngine extends ContextSource {
        /**
         * @param bool $value
         */
-       function setReducedLineNumbers( $value = true ) {
+       public function setReducedLineNumbers( $value = true ) {
                $this->mReducedLineNumbers = $value;
        }
 
        /**
         * @return Language
         */
-       function getDiffLang() {
+       public function getDiffLang() {
                if ( $this->mDiffLang === null ) {
                        # Default language in which the diff text is written.
                        $this->mDiffLang = $this->getTitle()->getPageLanguage();
@@ -141,14 +142,14 @@ class DifferenceEngine extends ContextSource {
        /**
         * @return bool
         */
-       function wasCacheHit() {
+       public function wasCacheHit() {
                return $this->mCacheHit;
        }
 
        /**
         * @return int
         */
-       function getOldid() {
+       public function getOldid() {
                $this->loadRevisionIds();
 
                return $this->mOldid;
@@ -157,7 +158,7 @@ class DifferenceEngine extends ContextSource {
        /**
         * @return bool|int
         */
-       function getNewid() {
+       public function getNewid() {
                $this->loadRevisionIds();
 
                return $this->mNewid;
@@ -171,7 +172,7 @@ class DifferenceEngine extends ContextSource {
         *
         * @return mixed URL or false
         */
-       function deletedLink( $id ) {
+       public function deletedLink( $id ) {
                if ( $this->getUser()->isAllowed( 'deletedhistory' ) ) {
                        $dbr = wfGetDB( DB_SLAVE );
                        $row = $dbr->selectRow( 'archive', '*',
@@ -198,7 +199,7 @@ class DifferenceEngine extends ContextSource {
         *
         * @return string Wikitext fragment
         */
-       function deletedIdMarker( $id ) {
+       public function deletedIdMarker( $id ) {
                $link = $this->deletedLink( $id );
                if ( $link ) {
                        return "[$link $id]";
@@ -227,7 +228,7 @@ class DifferenceEngine extends ContextSource {
                        $this->getLanguage()->listToText( $missing ), count( $missing ) );
        }
 
-       function showDiffPage( $diffOnly = false ) {
+       public function showDiffPage( $diffOnly = false ) {
                wfProfileIn( __METHOD__ );
 
                # Allow frames except in certain special cases
@@ -536,7 +537,7 @@ class DifferenceEngine extends ContextSource {
        /**
         * Show the new revision of the page.
         */
-       function renderNewRevision() {
+       public function renderNewRevision() {
                wfProfileIn( __METHOD__ );
                $out = $this->getOutput();
                $revHeader = $this->getRevisionHeader( $this->mNewRev );
@@ -619,7 +620,7 @@ class DifferenceEngine extends ContextSource {
         *
         * @return bool
         */
-       function showDiff( $otitle, $ntitle, $notice = '' ) {
+       public function showDiff( $otitle, $ntitle, $notice = '' ) {
                $diff = $this->getDiff( $otitle, $ntitle, $notice );
                if ( $diff === false ) {
                        $this->showMissingRevision();
@@ -636,7 +637,7 @@ class DifferenceEngine extends ContextSource {
        /**
         * Add style sheets and supporting JS for diff display.
         */
-       function showDiffStyle() {
+       public function showDiffStyle() {
                $this->getOutput()->addModuleStyles( 'mediawiki.action.history.diff' );
        }
 
@@ -649,7 +650,7 @@ class DifferenceEngine extends ContextSource {
         *
         * @return mixed
         */
-       function getDiff( $otitle, $ntitle, $notice = '' ) {
+       public function getDiff( $otitle, $ntitle, $notice = '' ) {
                $body = $this->getDiffBody();
                if ( $body === false ) {
                        return false;
@@ -784,7 +785,7 @@ class DifferenceEngine extends ContextSource {
         * @throws MWException If old or new content is not an instance of TextContent.
         * @return bool|string
         */
-       function generateContentDiffBody( Content $old, Content $new ) {
+       public function generateContentDiffBody( Content $old, Content $new ) {
                if ( !( $old instanceof TextContent ) ) {
                        throw new MWException( "Diff not implemented for " . get_class( $old ) . "; " .
                                "override generateContentDiffBody to fix this." );
@@ -810,7 +811,7 @@ class DifferenceEngine extends ContextSource {
         * @return bool|string
         * @deprecated since 1.21, use generateContentDiffBody() instead!
         */
-       function generateDiffBody( $otext, $ntext ) {
+       public function generateDiffBody( $otext, $ntext ) {
                ContentHandler::deprecated( __METHOD__, "1.21" );
 
                return $this->generateTextDiffBody( $otext, $ntext );
@@ -826,7 +827,7 @@ class DifferenceEngine extends ContextSource {
         *
         * @return bool|string
         */
-       function generateTextDiffBody( $otext, $ntext ) {
+       public function generateTextDiffBody( $otext, $ntext ) {
                global $wgExternalDiffEngine, $wgContLang;
 
                wfProfileIn( __METHOD__ );
@@ -932,7 +933,7 @@ class DifferenceEngine extends ContextSource {
         *
         * @return mixed
         */
-       function localiseLineNumbers( $text ) {
+       public function localiseLineNumbers( $text ) {
                return preg_replace_callback(
                        '/<!--LINE (\d+)-->/',
                        array( &$this, 'localiseLineNumbersCb' ),
@@ -940,7 +941,7 @@ class DifferenceEngine extends ContextSource {
                );
        }
 
-       function localiseLineNumbersCb( $matches ) {
+       public function localiseLineNumbersCb( $matches ) {
                if ( $matches[1] === '1' && $this->mReducedLineNumbers ) {
                        return '';
                }
@@ -953,7 +954,7 @@ class DifferenceEngine extends ContextSource {
         *
         * @return string
         */
-       function getMultiNotice() {
+       public function getMultiNotice() {
                if ( !is_object( $this->mOldRev ) || !is_object( $this->mNewRev ) ) {
                        return '';
                } elseif ( !$this->mOldPage->equals( $this->mNewPage ) ) {
@@ -1076,7 +1077,7 @@ class DifferenceEngine extends ContextSource {
         *
         * @return string
         */
-       function addHeader( $diff, $otitle, $ntitle, $multi = '', $notice = '' ) {
+       public function addHeader( $diff, $otitle, $ntitle, $multi = '', $notice = '' ) {
                // shared.css sets diff in interface language/dir, but the actual content
                // is often in a different language, mostly the page content language/dir
                $tableClass = 'diff diff-contentalign-' . htmlspecialchars( $this->getDiffLang()->alignStart() );
@@ -1125,7 +1126,7 @@ class DifferenceEngine extends ContextSource {
         * Use specified text instead of loading from the database
         * @deprecated since 1.21, use setContent() instead.
         */
-       function setText( $oldText, $newText ) {
+       public function setText( $oldText, $newText ) {
                ContentHandler::deprecated( __METHOD__, "1.21" );
 
                $oldContent = ContentHandler::makeContent( $oldText, $this->getTitle() );
@@ -1138,7 +1139,7 @@ class DifferenceEngine extends ContextSource {
         * Use specified text instead of loading from the database
         * @since 1.21
         */
-       function setContent( Content $oldContent, Content $newContent ) {
+       public function setContent( Content $oldContent, Content $newContent ) {
                $this->mOldContent = $oldContent;
                $this->mNewContent = $newContent;
 
@@ -1151,7 +1152,7 @@ class DifferenceEngine extends ContextSource {
         * (Defaults to page content language).
         * @since 1.19
         */
-       function setTextLanguage( $lang ) {
+       public function setTextLanguage( $lang ) {
                $this->mDiffLang = wfGetLangObj( $lang );
        }
 
@@ -1221,7 +1222,7 @@ class DifferenceEngine extends ContextSource {
         *
         * @return bool
         */
-       function loadRevisionData() {
+       public function loadRevisionData() {
                if ( $this->mRevisionsLoaded ) {
                        return true;
                }
@@ -1301,7 +1302,7 @@ class DifferenceEngine extends ContextSource {
         *
         * @return bool
         */
-       function loadText() {
+       public function loadText() {
                if ( $this->mTextLoaded == 2 ) {
                        return true;
                }
@@ -1335,7 +1336,7 @@ class DifferenceEngine extends ContextSource {
         *
         * @return bool
         */
-       function loadNewText() {
+       public function loadNewText() {
                if ( $this->mTextLoaded >= 1 ) {
                        return true;
                }
index fdb781b..3642750 100644 (file)
@@ -323,6 +323,16 @@ class SpecialContributions extends IncludableSpecialPage {
                                array(),
                                array( 'page' => $userpage->getPrefixedText() )
                        );
+
+                       # Suppression log link (bug 59120)
+                       if ( $this->getUser()->isAllowed( 'suppressionlog' ) ) {
+                               $tools[] = Linker::linkKnown(
+                                       SpecialPage::getTitleFor( 'Log', 'suppress' ),
+                                       $this->msg( 'sp-contributions-suppresslog' )->escaped(),
+                                       array(),
+                                       array( 'offender' => $username )
+                               );
+                       }
                }
                # Uploads
                $tools[] = Linker::linkKnown(
index d148a50..6c3cb95 100644 (file)
@@ -427,6 +427,15 @@ class DeletedContributionsPage extends SpecialPage {
                                                'page' => $nt->getPrefixedText()
                                        )
                                );
+                               # Suppression log link (bug 59120)
+                               if ( $this->getUser()->isAllowed( 'suppressionlog' ) ) {
+                                       $tools[] = Linker::linkKnown(
+                                               SpecialPage::getTitleFor( 'Log', 'suppress' ),
+                                               $this->msg( 'sp-contributions-suppresslog' )->escaped(),
+                                               array(),
+                                               array( 'offender' => $userObj->getName() )
+                                       );
+                               }
                        }
 
                        # Uploads
index 67e33b3..bbe56ec 100644 (file)
@@ -223,7 +223,7 @@ class LoginForm extends SpecialPage {
                $status = $this->addNewAccountInternal();
                if ( !$status->isGood() ) {
                        $error = $status->getMessage();
-                       $this->mainLoginForm( $error->toString(), $status->isOK() ? 'warning' : 'error' );
+                       $this->mainLoginForm( $error->toString() );
                        return;
                }
 
@@ -259,7 +259,7 @@ class LoginForm extends SpecialPage {
                $status = $this->addNewAccountInternal();
                if ( !$status->isGood() ) {
                        $error = $status->getMessage();
-                       $this->mainLoginForm( $error->toString(), $status->isOK() ? 'warning' : 'error' );
+                       $this->mainLoginForm( $error->toString() );
                        return false;
                }
 
@@ -401,10 +401,6 @@ class LoginForm extends SpecialPage {
                        return Status::newFatal( 'sorbs_create_account_reason' );
                }
 
-               // Leading/trailing/multiple whitespace characters are never accepted in usernames and users
-               // know that, don't warn if someone accidentally types it. We do warn about underscores.
-               $name = trim( preg_replace( '/\s+/', ' ', $this->mUsername ) );
-
                // Normalize the name so that silly things don't cause "invalid username" errors.
                // User::newFromName does some rather strict checking, rejecting e.g. leading/trailing/multiple spaces.
                $title = Title::makeTitleSafe( NS_USER, $this->mUsername );
@@ -412,23 +408,12 @@ class LoginForm extends SpecialPage {
                        return Status::newFatal( 'noname' );
                }
 
-               // Now create a dummy user ($u) and check if it is valid.
+               # Now create a dummy user ($u) and check if it is valid
                $u = User::newFromName( $title->getText(), 'creatable' );
-
                if ( !is_object( $u ) ) {
                        return Status::newFatal( 'noname' );
                } elseif ( 0 != $u->idForName() ) {
                        return Status::newFatal( 'userexists' );
-               } elseif ( $name !== $u->getName() ) {
-                       // User name was adjusted due to technical restrictions (e.g. first letter capitalized).
-                       // This is normally handled by a client-side check, but users with JavaScript disabled get here.
-                       $status = Status::newGood();
-                       $status->warning( 'createacct-normalization', $name, $u->getName() );
-
-                       // Set the form field to the correct name, so the user can just hit the button again.
-                       $this->mUsername = $u->getName();
-
-                       return $status;
                }
 
                if ( $this->mCreateaccountMail ) {
index aba0d27..0cb83d5 100644 (file)
@@ -58,23 +58,15 @@ class UsercreateTemplate extends BaseTemplate {
                        <section class="mw-form-header">
                                <?php $this->html( 'header' ); /* extensions such as ConfirmEdit add form HTML here */ ?>
                        </section>
-                       <!-- This element is used by the mediawiki.special.userlogin.signup.js module. -->
-                       <div
-                               id="mw-createacct-status-area"
-                               <?php if ( $this->data['message'] ) { ?>
-                                       class="<?php echo $this->data['messagetype']; ?>box"
-                               <?php } else { ?>
-                                       style="display: none;"
-                               <?php } ?>
-                       >
                        <?php if ( $this->data['message'] ) { ?>
+                               <div class="<?php $this->text( 'messagetype' ); ?>box">
                                        <?php if ( $this->data['messagetype'] == 'error' ) { ?>
                                                <strong><?php $this->msg( 'createacct-error' ); ?></strong>
                                                <br />
                                        <?php } ?>
                                        <?php $this->html( 'message' ); ?>
+                               </div>
                        <?php } ?>
-                       </div>
 
                        <div>
                                <label for='wpName2'>
index f16784a..b58e9df 100644 (file)
@@ -1161,7 +1161,6 @@ Use the form below to log in as another user.',
 'badretype'                       => 'The passwords you entered do not match.',
 'userexists'                      => 'Username entered already in use.
 Please choose a different name.',
-'createacct-normalization'        => 'Your username will be adjusted to "$2" due to technical restrictions.',
 'loginerror'                      => 'Login error',
 'createacct-error'                => 'Account creation error',
 'createaccounterror'              => 'Could not create account: $1',
@@ -3266,6 +3265,7 @@ $1',
 'sp-contributions-newbies-sub'         => 'For new accounts',
 'sp-contributions-newbies-title'       => 'User contributions for new accounts',
 'sp-contributions-blocklog'            => 'block log',
+'sp-contributions-suppresslog'         => 'suppressed user contributions',
 'sp-contributions-deleted'             => 'deleted user contributions',
 'sp-contributions-uploads'             => 'uploads',
 'sp-contributions-logs'                => 'logs',
index 56fafd0..a213e5e 100644 (file)
@@ -1436,9 +1436,6 @@ Parameters:
 * $1 - number of contributors (users)',
 'badretype' => 'Used as error message when the new password and its retype do not match.',
 'userexists' => 'Used as error message in creating a user account.',
-'createacct-normalization' => 'Used as warning message on account creation when user name is adjusted silently due to technical restrictions (e.g. first letter capitalized, underscores converted to spaces).
-* $1 - the old username
-* $2 - the new username',
 'loginerror' => 'Used as title of error message.
 {{Identical|Login error}}',
 'createacct-error' => 'Used as heading for the error message.',
@@ -6289,6 +6286,9 @@ See also:
 * {{msg-mw|Sp-contributions-deleted}}
 * {{msg-mw|Sp-contributions-userrights}}
 {{Identical|Block log}}',
+'sp-contributions-suppresslog' => 'Used as a display name for a link to log entries of suppressed edits made by that user.
+
+Used as link title in [[Special:Contributions]] and in [[Special:DeletedContributions]].',
 'sp-contributions-deleted' => "This is a link anchor used in [[Special:Contributions]]/''name'', when user viewing the page has the right to delete pages, or to restore deleted pages.
 
 Used as link title in [[Special:Contributions]].
index dda68a8..074d440 100644 (file)
@@ -503,7 +503,6 @@ $wgMessageStructure = array(
                'createacct-benefit-body3',
                'badretype',
                'userexists',
-               'createacct-normalization',
                'loginerror',
                'createacct-error',
                'createaccounterror',
@@ -2255,6 +2254,7 @@ $wgMessageStructure = array(
                'sp-contributions-blocked-notice',
                'sp-contributions-blocked-notice-anon',
                'sp-contributions-search',
+               'sp-contributions-suppresslog',
                'sp-contributions-username',
                'sp-contributions-toponly',
                'sp-contributions-newonly',
index a2958eb..5ceda32 100644 (file)
@@ -1249,17 +1249,9 @@ return array(
        'mediawiki.special.userlogin.signup.js' => array(
                'scripts' => 'resources/mediawiki.special/mediawiki.special.userlogin.signup.js',
                'messages' => array(
-                       'createacct-error',
                        'createacct-emailrequired',
-                       'createacct-normalization',
-                       'noname',
-                       'userexists',
-               ),
-               'dependencies' => array(
-                       'mediawiki.api',
-                       'mediawiki.jqueryMsg',
-                       'jquery.throttle-debounce',
                ),
+               'dependencies' => 'mediawiki.jqueryMsg',
        ),
        'mediawiki.special.javaScriptTest' => array(
                'scripts' => 'resources/mediawiki.special/mediawiki.special.javaScriptTest.js',
index 4fafd6d..c293f65 100644 (file)
@@ -3,7 +3,7 @@
  */
 ( function ( mw, $ ) {
        // When sending password by email, hide the password input fields.
-       $( function () {
+       function hidePasswordOnEmail() {
                // Always required if checked, otherwise it depends, so we use the original
                var $emailLabel = $( 'label[for="wpEmail"]' ),
                        originalText = $emailLabel.text(),
 
                $createByMailCheckbox.on( 'change', updateForCheckbox );
                updateForCheckbox();
-       } );
+       }
 
-       // Show username normalisation warning
-       $( function () {
-               var
-                       // All of these are apparently required to be sure we detect any changes.
-                       events = 'keyup keydown change mouseup cut paste focus blur',
-                       $input = $( '#wpName2' ),
-                       $warningContainer = $( '#mw-createacct-status-area' ),
-                       api = new mw.Api(),
-                       currentRequest,
-                       tweakedUsername;
-
-               // Hide any warnings / errors.
-               function cleanup() {
-                       $warningContainer.slideUp( function () {
-                               $warningContainer
-                                       .removeAttr( 'class' )
-                                       .empty();
-                       } );
-               }
-
-               function updateUsernameStatus() {
-                       var
-                               // Leading/trailing/multiple whitespace characters are never accepted in usernames and users
-                               // know that, don't warn if someone accidentally types it. We do warn about underscores.
-                               username = $.trim( $input.val().replace( /\s+/g, ' ' ) ),
-                               currentRequestInternal;
-
-                       // Abort any pending requests.
-                       if ( currentRequest ) {
-                               currentRequest.abort();
-                       }
-
-                       if ( username === '' ) {
-                               cleanup();
-                               return;
-                       }
-
-                       currentRequest = currentRequestInternal = api.get( {
-                               action: 'query',
-                               list: 'users',
-                               ususers: username // '|' in usernames is handled below
-                       } ).done( function ( resp ) {
-                               var userinfo, state;
-
-                               // Another request was fired in the meantime, the result we got here is no longer current.
-                               // This shouldn't happen as we abort pending requests, but you never know.
-                               if ( currentRequest !== currentRequestInternal ) {
-                                       return;
-                               }
-
-                               tweakedUsername = undefined;
-
-                               userinfo = resp.query.users[0];
-
-                               if ( resp.query.users.length !== 1 ) {
-                                       // Happens if the user types '|' into the field
-                                       state = 'invalid';
-                               } else if ( userinfo.invalid !== undefined ) {
-                                       state = 'invalid';
-                               } else if ( userinfo.userid !== undefined ) {
-                                       state = 'taken';
-                               } else if ( username !== userinfo.name ) {
-                                       state = 'tweaked';
-                               } else {
-                                       state = 'ok';
-                               }
-
-                               if ( state === 'ok' ) {
-                                       cleanup();
-                               } else if ( state === 'tweaked' ) {
-                                       $warningContainer
-                                               .attr( 'class', 'warningbox' )
-                                               .text( mw.message( 'createacct-normalization', username, userinfo.name ).text() )
-                                               .slideDown();
-
-                                       tweakedUsername = userinfo.name;
-                               } else {
-                                       $warningContainer
-                                               .attr( 'class', 'errorbox' )
-                                               .empty()
-                                               .append(
-                                                       $( '<strong>' ).text( mw.message( 'createacct-error' ).text() ),
-                                                       $( '<br>' ) // Ugh
-                                               );
-
-                                       if ( state === 'invalid' ) {
-                                               $warningContainer
-                                                       .attr( 'class', 'errorbox' )
-                                                       .append( document.createTextNode( mw.message( 'noname' ).text() ) )
-                                                       .slideDown();
-                                       } else if ( state === 'taken' ) {
-                                               $warningContainer
-                                                       .attr( 'class', 'errorbox' )
-                                                       .append( document.createTextNode( mw.message( 'userexists' ).text() ) )
-                                                       .slideDown();
-                                       }
-
-                                       $warningContainer.slideDown();
-                               }
-                       } ).fail( function () {
-                               cleanup();
-                       } );
-               }
-
-               $input.on( events, $.debounce( 250, updateUsernameStatus ) );
-
-               $input.closest( 'form' ).on( 'submit', function () {
-                       // If the username has to be adjusted before it's accepted, server-side check will force the
-                       // form to be resubmitted. Let's prevent that.
-                       if ( tweakedUsername !== undefined ) {
-                               $input.val( tweakedUsername );
-                       }
-               } );
-       } );
+       $( hidePasswordOnEmail );
 }( mediaWiki, jQuery ) );
index ab5c3f6..7c99352 100644 (file)
@@ -104,55 +104,84 @@ class MessageTest extends MediaWikiLangTestCase {
        }
 
        /**
-        * FIXME: This should not need database, but Language#formatExpiry does (bug 55912)
-        * @group Database
-        * @todo this should be split up into multiple test methods
         * @covers Message::numParams
-        * @covers Message::durationParams
-        * @covers Message::expiryParams
-        * @covers Message::timeperiodParams
-        * @covers Message::sizeParams
-        * @covers Message::bitrateParams
         */
-       public function testMessageParamTypes() {
+       public function testMessageNumParams() {
                $lang = Language::factory( 'en' );
-
                $msg = new RawMessage( '$1' );
+
                $this->assertEquals(
                        $lang->formatNum( 123456.789 ),
                        $msg->inLanguage( $lang )->numParams( 123456.789 )->plain(),
                        'numParams is handled correctly'
                );
+       }
 
+       /**
+        * @covers Message::durationParams
+        */
+       public function testMessageDurationParams() {
+               $lang = Language::factory( 'en' );
                $msg = new RawMessage( '$1' );
+
                $this->assertEquals(
                        $lang->formatDuration( 1234 ),
                        $msg->inLanguage( $lang )->durationParams( 1234 )->plain(),
                        'durationParams is handled correctly'
                );
+       }
 
+       /**
+        * FIXME: This should not need database, but Language#formatExpiry does (bug 55912)
+        * @group Database
+        * @covers Message::expiryParams
+        */
+       public function testMessageExpiryParams() {
+               $lang = Language::factory( 'en' );
                $msg = new RawMessage( '$1' );
+
                $this->assertEquals(
                        $lang->formatExpiry( wfTimestampNow() ),
                        $msg->inLanguage( $lang )->expiryParams( wfTimestampNow() )->plain(),
                        'expiryParams is handled correctly'
                );
+       }
 
+       /**
+        * @covers Message::timeperiodParams
+        */
+       public function testMessageTimeperiodParams() {
+               $lang = Language::factory( 'en' );
                $msg = new RawMessage( '$1' );
+
                $this->assertEquals(
                        $lang->formatTimePeriod( 1234 ),
                        $msg->inLanguage( $lang )->timeperiodParams( 1234 )->plain(),
                        'timeperiodParams is handled correctly'
                );
+       }
 
+       /**
+        * @covers Message::sizeParams
+        */
+       public function testMessageSizeParams() {
+               $lang = Language::factory( 'en' );
                $msg = new RawMessage( '$1' );
+
                $this->assertEquals(
                        $lang->formatSize( 123456 ),
                        $msg->inLanguage( $lang )->sizeParams( 123456 )->plain(),
                        'sizeParams is handled correctly'
                );
+       }
 
+       /**
+        * @covers Message::bitrateParams
+        */
+       public function testMessageBitrateParams() {
+               $lang = Language::factory( 'en' );
                $msg = new RawMessage( '$1' );
+
                $this->assertEquals(
                        $lang->formatBitrate( 123456 ),
                        $msg->inLanguage( $lang )->bitrateParams( 123456 )->plain(),
diff --git a/tests/phpunit/languages/SpecialPageAliasTest.php b/tests/phpunit/languages/SpecialPageAliasTest.php
new file mode 100644 (file)
index 0000000..8865f68
--- /dev/null
@@ -0,0 +1,63 @@
+<?php
+
+/**
+ * Verifies that special page aliases are valid, with no slashes.
+ *
+ * @group Language
+ * @group SpecialPageAliases
+ * @group SystemTest
+ * @group medium
+ *
+ * @licence GNU GPL v2+
+ * @author Katie Filbert < aude.wiki@gmail.com >
+ */
+class SpecialPageAliasTest extends MediaWikiTestCase {
+
+       /**
+        * @dataProvider validSpecialPageAliasesProvider
+        */
+       public function testValidSpecialPageAliases( $code, $specialPageAliases ) {
+               foreach( $specialPageAliases as $specialPage => $aliases ) {
+                       foreach( $aliases as $alias ) {
+                               $msg = "$specialPage alias '$alias' in $code is valid with no slashes";
+                               $this->assertRegExp( '/^[^\/]*$/', $msg );
+                       }
+               }
+       }
+
+       public function validSpecialPageAliasesProvider() {
+               $codes = array_keys( Language::fetchLanguageNames( 'mwfile' ) );
+
+               $data = array();
+
+               foreach( $codes as $code ) {
+                       $specialPageAliases = $this->getSpecialPageAliases( $code );
+
+                       if ( $specialPageAliases !== array() ) {
+                               $data[] = array( $code, $specialPageAliases );
+                       }
+               }
+
+               return $data;
+       }
+
+       /**
+        * @param string $code
+        *
+        * @return array
+        */
+       protected function getSpecialPageAliases( $code ) {
+               $file = Language::getMessagesFileName( $code );
+
+               if ( is_readable( $file ) ) {
+                       include $file;
+
+                       if ( isset( $specialPageAliases ) && $specialPageAliases !== null ) {
+                               return $specialPageAliases;
+                       }
+               }
+
+               return array();
+       }
+
+}