if ( isset( $prop['sections'] ) ) {
$result_array['sections'] = $p_result->getSections();
}
+ if ( isset( $prop['parsewarnings'] ) ) {
+ $result_array['parsewarnings'] = $p_result->getWarnings();
+ }
if ( isset( $prop['displaytitle'] ) ) {
$result_array['displaytitle'] = $p_result->getDisplayTitle() ?:
'modulestyles' => 'm',
'properties' => 'pp',
'limitreportdata' => 'lr',
+ 'parsewarnings' => 'pw'
];
$this->setIndexedTagNames( $result_array, $result_mapping );
$result->addValue( null, $this->getModuleName(), $result_array );
],
'prop' => [
ApiBase::PARAM_DFLT => 'text|langlinks|categories|links|templates|' .
- 'images|externallinks|sections|revid|displaytitle|iwlinks|properties',
+ 'images|externallinks|sections|revid|displaytitle|iwlinks|' .
+ 'properties|parsewarnings',
ApiBase::PARAM_ISMULTI => true,
ApiBase::PARAM_TYPE => [
'text',
'limitreportdata',
'limitreporthtml',
'parsetree',
+ 'parsewarnings'
],
ApiBase::PARAM_HELP_MSG_PER_VALUE => [
'parsetree' => [ 'apihelp-parse-paramvalue-prop-parsetree', CONTENT_MODEL_WIKITEXT ],
"apihelp-parse-paramvalue-prop-limitreportdata": "Gives the limit report in a structured way. Gives no data, when <var>$1disablelimitreport</var> is set.",
"apihelp-parse-paramvalue-prop-limitreporthtml": "Gives the HTML version of the limit report. Gives no data, when <var>$1disablelimitreport</var> is set.",
"apihelp-parse-paramvalue-prop-parsetree": "The XML parse tree of revision content (requires content model <code>$1</code>)",
+ "apihelp-parse-paramvalue-prop-parsewarnings": "Gives the warnings that occurred while parsing content.",
"apihelp-parse-param-pst": "Do a pre-save transform on the input before parsing it. Only valid when used with text.",
"apihelp-parse-param-onlypst": "Do a pre-save transform (PST) on the input, but don't parse it. Returns the same wikitext, after a PST has been applied. Only valid when used with <var>$1text</var>.",
"apihelp-parse-param-effectivelanglinks": "Includes language links supplied by extensions (for use with <kbd>$1prop=langlinks</kbd>).",
"apihelp-parse-paramvalue-prop-limitreportdata": "{{doc-apihelp-paramvalue|parse|prop|limitreportdata}}",
"apihelp-parse-paramvalue-prop-limitreporthtml": "{{doc-apihelp-paramvalue|parse|prop|limitreporthtml}}",
"apihelp-parse-paramvalue-prop-parsetree": "{{doc-apihelp-paramvalue|parse|prop|parsetree|params=* $1 - Value of the constant CONTENT_MODEL_WIKITEXT|paramstart=2}}",
+ "apihelp-parse-paramvalue-prop-parsewarnings": "{{doc-apihelp-paramvalue|parse|prop|parsewarnings}}",
"apihelp-parse-param-pst": "{{doc-apihelp-param|parse|pst}}",
"apihelp-parse-param-onlypst": "{{doc-apihelp-param|parse|onlypst}}",
"apihelp-parse-param-effectivelanglinks": "{{doc-apihelp-param|parse|effectivelanglinks}}",
}
$json = FormatJson::encode( (object)$messages );
+ // @codeCoverageIgnoreStart
if ( $json === false ) {
$this->logger->warning( 'Failed to encode message blob for {module} ({lang})', [
'module' => $module->getName(),
] );
$json = '{}';
}
+ // codeCoverageIgnoreEnd
return $json;
}
}
global $wgMainCacheType, $wgMessageCacheType, $wgParserCacheType;
$candidates = [ $wgMainCacheType, $wgMessageCacheType, $wgParserCacheType ];
foreach ( $candidates as $candidate ) {
+ $cache = false;
if ( $candidate !== CACHE_NONE && $candidate !== CACHE_ANYTHING ) {
- return self::getInstance( $candidate );
+ $cache = self::getInstance( $candidate );
+ // CACHE_ACCEL might default to nothing if no APCu
+ // See includes/ServiceWiring.php
+ if ( !( $cache instanceof EmptyBagOStuff ) ) {
+ return $cache;
+ }
}
}
protected function getContent( $titleText ) {
$title = Title::newFromText( $titleText );
if ( !$title ) {
- return null;
+ return null; // Bad title
}
// If the page is a redirect, follow the redirect.
$content = $this->getContentObj( $title );
$title = $content ? $content->getUltimateRedirectTarget() : null;
if ( !$title ) {
- return null;
+ return null; // Dead redirect
}
}
} elseif ( $handler->isSupportedFormat( CONTENT_FORMAT_JAVASCRIPT ) ) {
$format = CONTENT_FORMAT_JAVASCRIPT;
} else {
- return null;
+ return null; // Bad content model
}
$content = $this->getContentObj( $title );
if ( !$content ) {
- return null;
+ return null; // No content found
}
return $content->serialize( $format );
* - Add mw.libs.jpegmeta wrapper.
*/
-( function () {
+( function ( mw ) {
/*
Copyright (c) 2009 Ben Leslie
return new JpegMeta.JpegFile( fileReaderResult, fileName );
};
-}() );
+}( mediaWiki ) );
$this->assertEmpty( $errors, implode( "\n", $errors ) );
}
- /**
- * @param array $matcher
- * @param string $actual
- * @param bool $isHtml
- *
- * @return bool
- */
- private static function tagMatch( $matcher, $actual, $isHtml = true ) {
- $dom = PHPUnit_Util_XML::load( $actual, $isHtml );
- $tags = PHPUnit_Util_XML::findNodes( $dom, $matcher, $isHtml );
- return count( $tags ) > 0 && $tags[0] instanceof DOMNode;
- }
-
- /**
- * Note: we are overriding this method to remove the deprecated error
- * @see https://phabricator.wikimedia.org/T71505
- * @see https://github.com/sebastianbergmann/phpunit/issues/1292
- * @deprecated
- *
- * @param array $matcher
- * @param string $actual
- * @param string $message
- * @param bool $isHtml
- */
- public static function assertTag( $matcher, $actual, $message = '', $isHtml = true ) {
- // trigger_error(__METHOD__ . ' is deprecated', E_USER_DEPRECATED);
-
- self::assertTrue( self::tagMatch( $matcher, $actual, $isHtml ), $message );
- }
-
- /**
- * @see MediaWikiTestCase::assertTag
- * @deprecated
- *
- * @param array $matcher
- * @param string $actual
- * @param string $message
- * @param bool $isHtml
- */
- public static function assertNotTag( $matcher, $actual, $message = '', $isHtml = true ) {
- // trigger_error(__METHOD__ . ' is deprecated', E_USER_DEPRECATED);
-
- self::assertFalse( self::tagMatch( $matcher, $actual, $isHtml ), $message );
- }
-
/**
* Used as a marker to prevent wfResetOutputBuffers from breaking PHPUnit.
* @return string
<?php
+use MediaWiki\Session\SessionManager;
+
class FauxRequestTest extends PHPUnit_Framework_TestCase {
/**
* @covers FauxRequest::__construct
$req = new FauxRequest( [], false, 'x' );
}
+ /**
+ * @covers FauxRequest::__construct
+ */
+ public function testConstructWithSession() {
+ $session = SessionManager::singleton()->getEmptySession( new FauxRequest( [] ) );
+ $this->assertInstanceOf(
+ FauxRequest::class,
+ new FauxRequest( [], false, $session )
+ );
+ }
+
/**
* @covers FauxRequest::getText
*/
/**
* @covers Html::element
+ * @covers Html::rawElement
+ * @covers Html::openElement
+ * @covers Html::closeElement
*/
public function testElementBasics() {
$this->assertEquals(
/**
* @covers Html::namespaceSelector
+ * @covers Html::namespaceSelectorOptions
*/
public function testNamespaceSelector() {
$this->assertEquals(
$this->assertEquals( 'prev', $cacheEntry->lastlink, 'pref link for delete log or rev' );
}
+ private function assertValidHTML( $actual ) {
+ // Throws if invalid
+ $doc = PHPUnit_Util_XML::load( $actual, /* isHtml */ true );
+ }
+
private function assertUserLinks( $user, $cacheEntry ) {
- $this->assertTag(
- [
- 'tag' => 'a',
- 'attributes' => [
- 'class' => 'new mw-userlink'
- ],
- 'content' => $user
- ],
+ $this->assertValidHTML( $cacheEntry->userlink );
+ $this->assertRegExp(
+ '#^<a .*class="new mw-userlink".*><bdi>' . $user . '</bdi></a>#',
$cacheEntry->userlink,
'verify user link'
);
- $this->assertTag(
- [
- 'tag' => 'span',
- 'attributes' => [
- 'class' => 'mw-usertoollinks'
- ],
- 'child' => [
- 'tag' => 'a',
- 'content' => 'talk',
- ]
- ],
+ $this->assertValidHTML( $cacheEntry->usertalklink );
+ $this->assertRegExp(
+ '#^ <span class="mw-usertoollinks">\(.*<a .+>talk</a>.*\)</span>#',
$cacheEntry->usertalklink,
'verify user talk link'
);
- $this->assertTag(
- [
- 'tag' => 'span',
- 'attributes' => [
- 'class' => 'mw-usertoollinks'
- ],
- 'child' => [
- 'tag' => 'a',
- 'content' => 'contribs',
- ]
- ],
+ $this->assertValidHTML( $cacheEntry->usertalklink );
+ $this->assertRegExp(
+ '#^ <span class="mw-usertoollinks">\(.*<a .+>contribs</a>.*\)</span>$#',
$cacheEntry->usertalklink,
'verify user tool links'
);
}
private function assertDeleteLogLink( $cacheEntry ) {
- $this->assertTag(
- [
- 'tag' => 'a',
- 'attributes' => [
- 'href' => '/wiki/Special:Log/delete',
- 'title' => 'Special:Log/delete'
- ],
- 'content' => 'Deletion log'
- ],
+ $this->assertEquals(
+ '(<a href="/wiki/Special:Log/delete" title="Special:Log/delete">Deletion log</a>)',
$cacheEntry->link,
'verify deletion log link'
);
+
+ $this->assertValidHTML( $cacheEntry->link );
}
private function assertRevDel( $cacheEntry ) {
- $this->assertTag(
- [
- 'tag' => 'span',
- 'attributes' => [
- 'class' => 'history-deleted'
- ],
- 'content' => '(username removed)'
- ],
+ $this->assertEquals(
+ ' <span class="history-deleted">(username removed)</span>',
$cacheEntry->userlink,
'verify user link for change with deleted revision and user'
);
+ $this->assertValidHTML( $cacheEntry->userlink );
}
private function assertTitleLink( $title, $cacheEntry ) {
- $this->assertTag(
- [
- 'tag' => 'a',
- 'attributes' => [
- 'href' => '/wiki/' . $title,
- 'title' => $title
- ],
- 'content' => $title
- ],
+ $this->assertEquals(
+ '<a href="/wiki/' . $title . '" title="' . $title . '">' . $title . '</a>',
$cacheEntry->link,
'verify title link'
);
+ $this->assertValidHTML( $cacheEntry->link );
}
private function assertQueryLink( $content, $params, $link ) {
- $this->assertTag(
- [
- 'tag' => 'a',
- 'content' => $content
- ],
+ $this->assertRegExp(
+ "#^<a .+>$content</a>$#",
$link,
- 'assert query link element'
+ 'verify query link element'
);
+ $this->assertValidHTML( $link );
foreach ( $params as $key => $value ) {
$this->assertRegExp( '/' . $key . '=' . $value . '/', $link, "verify $key link params" );
public function testClosureExpansionDisabled() {
$obj = ObjectFactory::getObjectFromSpec( [
'class' => 'ObjectFactoryTestFixture',
- 'args' => [ function() {
- return 'unwrapped';
- }, ],
+ 'args' => [
+ function() {
+ return 'wrapped';
+ },
+ 'unwrapped',
+ ],
'calls' => [
'setter' => [ function() {
- return 'unwrapped';
+ return 'wrapped';
}, ],
],
'closure_expansion' => false,
] );
$this->assertInstanceOf( 'Closure', $obj->args[0] );
- $this->assertSame( 'unwrapped', $obj->args[0]() );
+ $this->assertSame( 'wrapped', $obj->args[0]() );
+ $this->assertSame( 'unwrapped', $obj->args[1] );
$this->assertInstanceOf( 'Closure', $obj->setterArgs[0] );
- $this->assertSame( 'unwrapped', $obj->setterArgs[0]() );
+ $this->assertSame( 'wrapped', $obj->setterArgs[0]() );
}
/**
public function testClosureExpansionEnabled() {
$obj = ObjectFactory::getObjectFromSpec( [
'class' => 'ObjectFactoryTestFixture',
- 'args' => [ function() {
- return 'unwrapped';
- }, ],
+ 'args' => [
+ function() {
+ return 'wrapped';
+ },
+ 'unwrapped',
+ ],
'calls' => [
'setter' => [ function() {
- return 'unwrapped';
+ return 'wrapped';
}, ],
],
'closure_expansion' => true,
] );
$this->assertInternalType( 'string', $obj->args[0] );
- $this->assertSame( 'unwrapped', $obj->args[0] );
+ $this->assertSame( 'wrapped', $obj->args[0] );
+ $this->assertSame( 'unwrapped', $obj->args[1] );
$this->assertInternalType( 'string', $obj->setterArgs[0] );
- $this->assertSame( 'unwrapped', $obj->setterArgs[0] );
+ $this->assertSame( 'wrapped', $obj->setterArgs[0] );
$obj = ObjectFactory::getObjectFromSpec( [
'class' => 'ObjectFactoryTestFixture',
*/
class HashBagOStuffTest extends PHPUnit_Framework_TestCase {
+ /**
+ * @covers HashBagOStuff::__construct
+ */
+ public function testConstruct() {
+ $this->assertInstanceOf(
+ HashBagOStuff::class,
+ new HashBagOStuff()
+ );
+ }
+
+ /**
+ * @covers HashBagOStuff::__construct
+ * @expectedException InvalidArgumentException
+ */
+ public function testConstructBadZero() {
+ $cache = new HashBagOStuff( [ 'maxKeys' => 0 ] );
+ }
+
+ /**
+ * @covers HashBagOStuff::__construct
+ * @expectedException InvalidArgumentException
+ */
+ public function testConstructBadNeg() {
+ $cache = new HashBagOStuff( [ 'maxKeys' => -1 ] );
+ }
+
+ /**
+ * @covers HashBagOStuff::__construct
+ * @expectedException InvalidArgumentException
+ */
+ public function testConstructBadType() {
+ $cache = new HashBagOStuff( [ 'maxKeys' => 'x' ] );
+ }
+
/**
* @covers HashBagOStuff::delete
*/
/**
* Ensure maxKeys eviction prefers keeping new keys.
*
- * @covers HashBagOStuff::__construct
* @covers HashBagOStuff::set
*/
public function testEvictionAdd() {
* Ensure maxKeys eviction prefers recently set keys
* even if the keys pre-exist.
*
- * @covers HashBagOStuff::__construct
* @covers HashBagOStuff::set
*/
public function testEvictionSet() {
/**
* Ensure maxKeys eviction prefers recently retrieved keys (LRU).
*
- * @covers HashBagOStuff::__construct
* @covers HashBagOStuff::doGet
* @covers HashBagOStuff::hasKey
*/
--- /dev/null
+<?php
+
+class ObjectCacheTest extends MediaWikiTestCase {
+
+ protected function setUp() {
+ // Parent calls ObjectCache::clear() among other things
+ parent::setUp();
+
+ $this->setCacheConfig();
+ $this->setMwGlobals( [
+ 'wgMainCacheType' => CACHE_NONE,
+ 'wgMessageCacheType' => CACHE_NONE,
+ 'wgParserCacheType' => CACHE_NONE,
+ ] );
+ }
+
+ private function setCacheConfig( $arr = [] ) {
+ $defaults = [
+ CACHE_NONE => [ 'class' => 'EmptyBagOStuff' ],
+ CACHE_DB => [ 'class' => 'SqlBagOStuff' ],
+ CACHE_ANYTHING => [ 'factory' => 'ObjectCache::newAnything' ],
+ // Mock ACCEL with 'hash' as being installed.
+ // This makes tests deterministic regardless of APC.
+ CACHE_ACCEL => [ 'class' => 'HashBagOStuff' ],
+ 'hash' => [ 'class' => 'HashBagOStuff' ],
+ ];
+ $this->setMwGlobals( 'wgObjectCaches', $arr + $defaults );
+ }
+
+ /** @covers ObjectCache::newAnything */
+ public function testNewAnythingNothing() {
+ $this->assertInstanceOf(
+ SqlBagOStuff::class,
+ ObjectCache::newAnything( [] ),
+ 'No available types. Fallback to DB'
+ );
+ }
+
+ /** @covers ObjectCache::newAnything */
+ public function testNewAnythingHash() {
+ $this->setMwGlobals( [
+ 'wgMainCacheType' => 'hash'
+ ] );
+
+ $this->assertInstanceOf(
+ HashBagOStuff::class,
+ ObjectCache::newAnything( [] ),
+ 'Use an available type (hash)'
+ );
+ }
+
+ /** @covers ObjectCache::newAnything */
+ public function testNewAnythingAccel() {
+ $this->setMwGlobals( [
+ 'wgMainCacheType' => CACHE_ACCEL
+ ] );
+
+ $this->assertInstanceOf(
+ HashBagOStuff::class,
+ ObjectCache::newAnything( [] ),
+ 'Use an available type (CACHE_ACCEL)'
+ );
+ }
+
+ /** @covers ObjectCache::newAnything */
+ public function txestNewAnythingNoAccel() {
+ $this->setMwGlobals( [
+ 'wgMainCacheType' => CACHE_ACCEL
+ ] );
+
+ $this->setCacheConfig( [
+ // Mock APC not being installed (T160519, T147161)
+ CACHE_ACCEL => [ 'class' => 'EmptyBagOStuff' ]
+ ] );
+
+ $this->assertInstanceOf(
+ SqlBagOStuff::class,
+ ObjectCache::newAnything( [] ),
+ 'Fallback to DB if available types fall back to Empty'
+ );
+ }
+}
return $module;
}
+ /** @covers MessageBlobStore::setLogger */
+ public function testSetLogger() {
+ $blobStore = $this->makeBlobStore();
+ $this->assertSame( null, $blobStore->setLogger( new Psr\Log\NullLogger() ) );
+ }
+
+ /** @covers MessageBlobStore::getResourceLoader */
+ public function testGetResourceLoader() {
+ // Call protected method
+ $blobStore = TestingAccessWrapper::newFromObject( $this->makeBlobStore() );
+ $this->assertInstanceOf(
+ ResourceLoader::class,
+ $blobStore->getResourceLoader()
+ );
+ }
+
+ /** @covers MessageBlobStore::fetchMessage */
+ public function testFetchMessage() {
+ $module = $this->makeModule( [ 'mainpage' ] );
+ $rl = new ResourceLoader();
+ $rl->register( $module->getName(), $module );
+
+ $blobStore = $this->makeBlobStore( null, $rl );
+ $blob = $blobStore->getBlob( $module, 'en' );
+
+ $this->assertEquals( '{"mainpage":"Main Page"}', $blob, 'Generated blob' );
+ }
+
+ /** @covers MessageBlobStore::fetchMessage */
+ public function testFetchMessageFail() {
+ $module = $this->makeModule( [ 'i-dont-exist' ] );
+ $rl = new ResourceLoader();
+ $rl->register( $module->getName(), $module );
+
+ $blobStore = $this->makeBlobStore( null, $rl );
+ $blob = $blobStore->getBlob( $module, 'en' );
+
+ $this->assertEquals( '{"i-dont-exist":"\u29fci-dont-exist\u29fd"}', $blob, 'Generated blob' );
+ }
+
public function testGetBlob() {
$module = $this->makeModule( [ 'foo' ] );
$rl = new ResourceLoader();
'script' => "var a = 'this is';\n {\ninvalid"
] );
$this->assertEquals(
- $module->getScript( $context ),
'mw.log.error(' .
'"JavaScript parse error: Parse error: Unexpected token; ' .
'token } expected in file \'input\' on line 3"' .
');',
+ $module->getScript( $context ),
'Replace invalid syntax with error logging'
);
'script' => "\n'valid';"
] );
$this->assertEquals(
- $module->getScript( $context ),
"\n'valid';",
+ $module->getScript( $context ),
'Leave valid scripts as-is'
);
}
'../skins/Example/images/quux.png',
];
$this->assertEquals(
- $getRelativePaths->invoke( null, $raw ),
$canonical,
+ $getRelativePaths->invoke( null, $raw ),
'Insert placeholders'
);
$this->assertEquals(
- $expandRelativePaths->invoke( null, $canonical ),
$raw,
+ $expandRelativePaths->invoke( null, $canonical ),
'Substitute placeholders'
);
}
$this->assertEquals( $expected, $module->getTitleInfo( $context ), 'Title info' );
}
+ /**
+ * @covers ResourceLoaderWikiModule::preloadTitleInfo
+ */
+ public function testGetPreloadedBadTitle() {
+ // Mock values
+ $pages = [
+ // Covers else branch for invalid page name
+ '[x]' => [ 'type' => 'styles' ],
+ ];
+ $titleInfo = [];
+
+ // Set up objects
+ $module = $this->getMockBuilder( 'TestResourceLoaderWikiModule' )
+ ->setMethods( [ 'getPages' ] ) ->getMock();
+ $module->method( 'getPages' )->willReturn( $pages );
+ $module::$returnFetchTitleInfo = $titleInfo;
+ $rl = new EmptyResourceLoader();
+ $rl->register( 'testmodule', $module );
+ $context = new ResourceLoaderContext( $rl, new FauxRequest() );
+
+ // Act
+ TestResourceLoaderWikiModule::preloadTitleInfo(
+ $context,
+ wfGetDB( DB_REPLICA ),
+ [ 'testmodule' ]
+ );
+
+ // Assert
+ $module = TestingAccessWrapper::newFromObject( $module );
+ $this->assertEquals( $titleInfo, $module->getTitleInfo( $context ), 'Title info' );
+ }
+
+ /**
+ * @covers ResourceLoaderWikiModule::preloadTitleInfo
+ */
+ public function testGetPreloadedTitleInfoEmpty() {
+ $context = new ResourceLoaderContext( new EmptyResourceLoader(), new FauxRequest() );
+ // Covers early return
+ $this->assertSame(
+ null,
+ ResourceLoaderWikiModule::preloadTitleInfo(
+ $context,
+ wfGetDB( DB_REPLICA ),
+ []
+ )
+ );
+ }
+
+ public static function provideGetContent() {
+ return [
+ 'Bad title' => [ null, '[x]' ],
+ 'Dead redirect' => [ null, [
+ 'text' => 'Dead redirect',
+ 'title' => 'Dead_redirect',
+ 'redirect' => 1,
+ ] ],
+ 'Bad content model' => [ null, [
+ 'text' => 'MediaWiki:Wikitext',
+ 'ns' => NS_MEDIAWIKI,
+ 'title' => 'Wikitext',
+ ] ],
+ 'No JS content found' => [ null, [
+ 'text' => 'MediaWiki:Script.js',
+ 'ns' => NS_MEDIAWIKI,
+ 'title' => 'Script.js',
+ ] ],
+ 'No CSS content found' => [ null, [
+ 'text' => 'MediaWiki:Styles.css',
+ 'ns' => NS_MEDIAWIKI,
+ 'title' => 'Script.css',
+ ] ],
+ ];
+ }
+
+ /**
+ * @covers ResourceLoaderWikiModule::getContent
+ * @dataProvider provideGetContent
+ */
+ public function testGetContent( $expected, $title ) {
+ $context = $this->getResourceLoaderContext( [], new EmptyResourceLoader );
+ $module = $this->getMockBuilder( 'ResourceLoaderWikiModule' )
+ ->setMethods( [ 'getContentObj' ] ) ->getMock();
+ $module->expects( $this->any() )
+ ->method( 'getContentObj' )->willReturn( null );
+
+ if ( is_array( $title ) ) {
+ $title += [ 'ns' => NS_MAIN, 'id' => 1, 'len' => 1, 'redirect' => 0 ];
+ $titleText = $title['text'];
+ // Mock Title db access via LinkCache
+ MediaWikiServices::getInstance()->getLinkCache()->addGoodLinkObj(
+ $title['id'],
+ new TitleValue( $title['ns'], $title['title'] ),
+ $title['len'],
+ $title['redirect']
+ );
+ } else {
+ $titleText = $title;
+ }
+
+ $module = TestingAccessWrapper::newFromObject( $module );
+ $this->assertEquals(
+ $expected,
+ $module->getContent( $titleText )
+ );
+
+ }
+
/**
* @covers ResourceLoaderWikiModule::getContent
*/