3 use MediaWiki\MediaWikiServices
;
4 use Wikimedia\Rdbms\IDatabase
;
5 use Wikimedia\TestingAccessWrapper
;
8 * @covers ResourceLoaderWikiModule
10 class ResourceLoaderWikiModuleTest
extends ResourceLoaderTestCase
{
13 * @dataProvider provideConstructor
15 public function testConstructor( $params ) {
16 $module = new ResourceLoaderWikiModule( $params );
17 $this->assertInstanceOf( ResourceLoaderWikiModule
::class, $module );
20 public static function provideConstructor() {
21 yield
'null' => [ null ];
22 yield
'empty' => [ [] ];
23 yield
'unknown settings' => [ [ 'foo' => 'baz' ] ];
24 yield
'real settings' => [ [ 'MediaWiki:Common.js' ] ];
27 private function prepareTitleInfo( array $mockInfo ) {
28 $module = TestingAccessWrapper
::newFromClass( ResourceLoaderWikiModule
::class );
30 foreach ( $mockInfo as $key => $val ) {
31 $info[ $module->makeTitleKey( Title
::newFromText( $key ) ) ] = $val;
37 * @dataProvider provideGetPages
39 public function testGetPages( $params, Config
$config, $expected ) {
40 $module = new ResourceLoaderWikiModule( $params );
41 $module->setConfig( $config );
43 // Because getPages is protected..
44 $getPages = new ReflectionMethod( $module, 'getPages' );
45 $getPages->setAccessible( true );
46 $out = $getPages->invoke( $module, ResourceLoaderContext
::newDummyContext() );
47 $this->assertSame( $expected, $out );
50 public static function provideGetPages() {
51 $settings = self
::getSettings() +
[
57 'styles' => [ 'MediaWiki:Common.css' ],
58 'scripts' => [ 'MediaWiki:Common.js' ],
62 [ [], new HashConfig( $settings ), [] ],
63 [ $params, new HashConfig( $settings ), [
64 'MediaWiki:Common.js' => [ 'type' => 'script' ],
65 'MediaWiki:Common.css' => [ 'type' => 'style' ]
67 [ $params, new HashConfig( [ 'UseSiteCss' => false ] +
$settings ), [
68 'MediaWiki:Common.js' => [ 'type' => 'script' ],
70 [ $params, new HashConfig( [ 'UseSiteJs' => false ] +
$settings ), [
71 'MediaWiki:Common.css' => [ 'type' => 'style' ],
75 [ 'UseSiteJs' => false, 'UseSiteCss' => false ]
83 * @dataProvider provideGetGroup
85 public function testGetGroup( $params, $expected ) {
86 $module = new ResourceLoaderWikiModule( $params );
87 $this->assertSame( $expected, $module->getGroup() );
90 public static function provideGetGroup() {
91 yield
'no group' => [ [], null ];
92 yield
'some group' => [ [ 'group' => 'foobar' ], 'foobar' ];
96 * @dataProvider provideGetType
98 public function testGetType( $params, $expected ) {
99 $module = new ResourceLoaderWikiModule( $params );
100 $this->assertSame( $expected, $module->getType() );
103 public static function provideGetType() {
106 ResourceLoaderWikiModule
::LOAD_GENERAL
,
109 [ 'scripts' => [ 'Example.js' ] ],
110 ResourceLoaderWikiModule
::LOAD_GENERAL
,
113 [ 'styles' => [ 'Example.css' ] ],
114 ResourceLoaderWikiModule
::LOAD_STYLES
,
116 yield
'styles and scripts' => [
117 [ 'styles' => [ 'Example.css' ], 'scripts' => [ 'Example.js' ] ],
118 ResourceLoaderWikiModule
::LOAD_GENERAL
,
123 * @dataProvider provideIsKnownEmpty
125 public function testIsKnownEmpty( $titleInfo, $group, $dependencies, $expected ) {
126 $module = $this->getMockBuilder( ResourceLoaderWikiModule
::class )
127 ->disableOriginalConstructor()
128 ->setMethods( [ 'getTitleInfo', 'getGroup', 'getDependencies' ] )
130 $module->method( 'getTitleInfo' )
131 ->willReturn( $this->prepareTitleInfo( $titleInfo ) );
132 $module->method( 'getGroup' )
133 ->willReturn( $group );
134 $module->method( 'getDependencies' )
135 ->willReturn( $dependencies );
136 $context = $this->createMock( ResourceLoaderContext
::class );
137 $this->assertSame( $expected, $module->isKnownEmpty( $context ) );
140 public static function provideIsKnownEmpty() {
145 // No pages exist, considered empty.
149 yield
'an empty page exists (no group)' => [
150 [ 'Project:Example/foo.js' => [ 'page_len' => 0 ] ],
153 // There is an existing page, so we should let the module be queued.
154 // Its emptiness might be temporary, hence considered non-empty (T70488).
157 yield
'an empty page exists (site group)' => [
158 [ 'MediaWiki:Foo.js' => [ 'page_len' => 0 ] ],
161 // There is an existing page, hence considered non-empty.
164 yield
'an empty page exists (user group)' => [
165 [ 'User:Example/foo.js' => [ 'page_len' => 0 ] ],
168 // There is an existing page, but it is empty.
169 // For user-specific modules, don't bother loading a known-empty module.
170 // Given user-specific HTML output, this will vary and re-appear if/when
171 // the page becomes non-empty again.
175 yield
'no pages but having dependencies (no group)' => [
178 [ 'another-module' ],
181 yield
'no pages but having dependencies (site group)' => [
184 [ 'another-module' ],
187 yield
'no pages but having dependencies (user group)' => [
190 [ 'another-module' ],
194 yield
'a non-empty page exists (user group)' => [
195 [ 'User:Example/foo.js' => [ 'page_len' => 25 ] ],
200 yield
'a non-empty page exists (site group)' => [
201 [ 'MediaWiki:Foo.js' => [ 'page_len' => 25 ] ],
208 public function testGetTitleInfo() {
210 'MediaWiki:Common.css' => [ 'type' => 'styles' ],
211 'mediawiki: fallback.css' => [ 'type' => 'styles' ],
213 $titleInfo = $this->prepareTitleInfo( [
214 'MediaWiki:Common.css' => [ 'page_len' => 1234 ],
215 'MediaWiki:Fallback.css' => [ 'page_len' => 0 ],
217 $expected = $titleInfo;
219 $module = $this->getMockBuilder( ResourceLoaderWikiModule
::class )
220 ->setMethods( [ 'getPages', 'getTitleInfo' ] )
222 $module->method( 'getPages' )->willReturn( $pages );
223 $module->method( 'getTitleInfo' )->willReturn( $titleInfo );
225 $context = $this->getMockBuilder( ResourceLoaderContext
::class )
226 ->disableOriginalConstructor()
229 $module = TestingAccessWrapper
::newFromObject( $module );
230 $this->assertSame( $expected, $module->getTitleInfo( $context ), 'Title info' );
233 public function testGetPreloadedTitleInfo() {
235 'MediaWiki:Common.css' => [ 'type' => 'styles' ],
236 // Regression against T145673. It's impossible to statically declare page names in
237 // a canonical way since the canonical prefix is localised. As such, the preload
238 // cache computed the right cache key, but failed to find the results when
239 // doing an intersect on the canonical result, producing an empty array.
240 'mediawiki: fallback.css' => [ 'type' => 'styles' ],
242 $titleInfo = $this->prepareTitleInfo( [
243 'MediaWiki:Common.css' => [ 'page_len' => 1234 ],
244 'MediaWiki:Fallback.css' => [ 'page_len' => 0 ],
246 $expected = $titleInfo;
248 $module = $this->getMockBuilder( TestResourceLoaderWikiModule
::class )
249 ->setMethods( [ 'getPages' ] )
251 $module->method( 'getPages' )->willReturn( $pages );
252 // Can't mock static methods
253 $module::$returnFetchTitleInfo = $titleInfo;
255 $rl = new EmptyResourceLoader();
256 $context = new ResourceLoaderContext( $rl, new FauxRequest() );
258 TestResourceLoaderWikiModule
::invalidateModuleCache(
259 Title
::newFromText( 'MediaWiki:Common.css' ),
264 TestResourceLoaderWikiModule
::preloadTitleInfo(
266 $this->createMock( IDatabase
::class ),
270 $module = TestingAccessWrapper
::newFromObject( $module );
271 $this->assertSame( $expected, $module->getTitleInfo( $context ), 'Title info' );
274 public function testGetPreloadedBadTitle() {
276 TestResourceLoaderWikiModule
::$returnFetchTitleInfo = [];
277 $rl = new EmptyResourceLoader();
278 $rl->getConfig()->set( 'UseSiteJs', true );
279 $rl->getConfig()->set( 'UseSiteCss', true );
280 $rl->register( 'testmodule', [
281 'class' => TestResourceLoaderWikiModule
::class,
282 // Covers preloadTitleInfo branch for invalid page name
283 'styles' => [ '[x]' ],
285 $context = new ResourceLoaderContext( $rl, new FauxRequest() );
288 TestResourceLoaderWikiModule
::preloadTitleInfo(
290 $this->createMock( IDatabase
::class ),
295 $module = TestingAccessWrapper
::newFromObject( $rl->getModule( 'testmodule' ) );
296 $this->assertSame( [], $module->getTitleInfo( $context ), 'Title info' );
299 public function testGetPreloadedTitleInfoEmpty() {
300 $context = new ResourceLoaderContext( new EmptyResourceLoader(), new FauxRequest() );
301 // This covers the early return case
304 ResourceLoaderWikiModule
::preloadTitleInfo(
306 $this->createMock( IDatabase
::class ),
312 public static function provideGetContent() {
313 yield
'Bad title' => [ null, '[x]' ];
314 yield
'Dead redirect' => [ null, [
315 'text' => 'Dead redirect',
316 'title' => 'Dead_redirect',
319 yield
'Bad content model' => [ null, [
320 'text' => 'MediaWiki:Wikitext',
321 'ns' => NS_MEDIAWIKI
,
322 'title' => 'Wikitext',
324 yield
'No JS content found' => [ null, [
325 'text' => 'MediaWiki:Script.js',
326 'ns' => NS_MEDIAWIKI
,
327 'title' => 'Script.js',
329 yield
'No CSS content found' => [ null, [
330 'text' => 'MediaWiki:Styles.css',
331 'ns' => NS_MEDIAWIKI
,
332 'title' => 'Script.css',
337 * @dataProvider provideGetContent
339 public function testGetContent( $expected, $title ) {
340 $context = $this->getResourceLoaderContext( [], new EmptyResourceLoader
);
341 $module = $this->getMockBuilder( ResourceLoaderWikiModule
::class )
342 ->setMethods( [ 'getContentObj' ] )->getMock();
343 $module->method( 'getContentObj' )
344 ->willReturn( null );
346 if ( is_array( $title ) ) {
347 $title +
= [ 'ns' => NS_MAIN
, 'id' => 1, 'len' => 1, 'redirect' => 0 ];
348 $titleText = $title['text'];
349 // Mock Title db access via LinkCache
350 MediaWikiServices
::getInstance()->getLinkCache()->addGoodLinkObj(
352 new TitleValue( $title['ns'], $title['title'] ),
360 $module = TestingAccessWrapper
::newFromObject( $module );
363 $module->getContent( $titleText, $context )
367 public function testContentOverrides() {
369 'MediaWiki:Common.css' => [ 'type' => 'style' ],
372 $module = $this->getMockBuilder( ResourceLoaderWikiModule
::class )
373 ->setMethods( [ 'getPages' ] )
375 $module->method( 'getPages' )->willReturn( $pages );
377 $rl = new EmptyResourceLoader();
378 $context = new DerivativeResourceLoaderContext(
379 new ResourceLoaderContext( $rl, new FauxRequest() )
381 $context->setContentOverrideCallback( function ( Title
$t ) {
382 if ( $t->getPrefixedText() === 'MediaWiki:Common.css' ) {
383 return new CssContent( '.override{}' );
388 $this->assertTrue( $module->shouldEmbedModule( $context ) );
391 "/*\nMediaWiki:Common.css\n*/\n.override{}"
393 ], $module->getStyles( $context ) );
395 $context->setContentOverrideCallback( function ( Title
$t ) {
396 if ( $t->getPrefixedText() === 'MediaWiki:Skin.css' ) {
397 return new CssContent( '.override{}' );
401 $this->assertFalse( $module->shouldEmbedModule( $context ) );
404 public function testGetContentForRedirects() {
405 // Set up context and module object
406 $context = new DerivativeResourceLoaderContext(
407 $this->getResourceLoaderContext( [], new EmptyResourceLoader
)
409 $module = $this->getMockBuilder( ResourceLoaderWikiModule
::class )
410 ->setMethods( [ 'getPages' ] )
412 $module->method( 'getPages' )
414 'MediaWiki:Redirect.js' => [ 'type' => 'script' ]
416 $context->setContentOverrideCallback( function ( Title
$title ) {
417 if ( $title->getPrefixedText() === 'MediaWiki:Redirect.js' ) {
418 $handler = new JavaScriptContentHandler();
419 return $handler->makeRedirectContent(
420 Title
::makeTitle( NS_MEDIAWIKI
, 'Target.js' )
422 } elseif ( $title->getPrefixedText() === 'MediaWiki:Target.js' ) {
423 return new JavaScriptContent( 'target;' );
429 // Mock away Title's db queries with LinkCache
430 MediaWikiServices
::getInstance()->getLinkCache()->addGoodLinkObj(
432 new TitleValue( NS_MEDIAWIKI
, 'Redirect.js' ),
438 "/*\nMediaWiki:Redirect.js\n*/\ntarget;\n",
439 $module->getScript( $context ),
440 'Redirect resolved by getContent'
444 public function tearDown() {
445 Title
::clearCaches();
450 class TestResourceLoaderWikiModule
extends ResourceLoaderWikiModule
{
451 public static $returnFetchTitleInfo = null;
453 protected static function fetchTitleInfo( IDatabase
$db, array $pages, $fname = null ) {
454 $ret = self
::$returnFetchTitleInfo;
455 self
::$returnFetchTitleInfo = null;