rdbms: Rename "memCache" to "memStash" in LBFactory
[mediawiki.git] / tests / phpunit / includes / resourceloader / ResourceLoaderTest.php
blobe9d022f62e60f94ee0351f12b7351fcdd1427fc6
1 <?php
3 use Wikimedia\TestingAccessWrapper;
5 class ResourceLoaderTest extends ResourceLoaderTestCase {
7 protected function setUp() {
8 parent::setUp();
10 $this->setMwGlobals( [
11 'wgResourceLoaderLESSImportPaths' => [
12 dirname( dirname( __DIR__ ) ) . '/data/less/common',
14 'wgResourceLoaderLESSVars' => [
15 'foo' => '2px',
16 'Foo' => '#eeeeee',
17 'bar' => 5,
19 // Clear ResourceLoaderGetConfigVars hooks (called by StartupModule)
20 // to avoid notices during testMakeModuleResponse for missing
21 // wgResourceLoaderLESSVars keys in extension hooks.
22 'wgHooks' => [],
23 'wgShowExceptionDetails' => true,
24 ] );
27 /**
28 * Ensure the ResourceLoaderRegisterModules hook is called.
30 * @covers ResourceLoader::__construct
32 public function testConstructRegistrationHook() {
33 $resourceLoaderRegisterModulesHook = false;
35 $this->setMwGlobals( 'wgHooks', [
36 'ResourceLoaderRegisterModules' => [
37 function ( &$resourceLoader ) use ( &$resourceLoaderRegisterModulesHook ) {
38 $resourceLoaderRegisterModulesHook = true;
41 ] );
43 $unused = new ResourceLoader();
44 $this->assertTrue(
45 $resourceLoaderRegisterModulesHook,
46 'Hook ResourceLoaderRegisterModules called'
50 /**
51 * @covers ResourceLoader::register
52 * @covers ResourceLoader::getModule
54 public function testRegisterValidObject() {
55 $module = new ResourceLoaderTestModule();
56 $resourceLoader = new EmptyResourceLoader();
57 $resourceLoader->register( 'test', $module );
58 $this->assertEquals( $module, $resourceLoader->getModule( 'test' ) );
61 /**
62 * @covers ResourceLoader::register
63 * @covers ResourceLoader::getModule
65 public function testRegisterValidArray() {
66 $module = new ResourceLoaderTestModule();
67 $resourceLoader = new EmptyResourceLoader();
68 // Covers case of register() setting $rl->moduleInfos,
69 // but $rl->modules lazy-populated by getModule()
70 $resourceLoader->register( 'test', [ 'object' => $module ] );
71 $this->assertEquals( $module, $resourceLoader->getModule( 'test' ) );
74 /**
75 * @covers ResourceLoader::register
77 public function testRegisterEmptyString() {
78 $module = new ResourceLoaderTestModule();
79 $resourceLoader = new EmptyResourceLoader();
80 $resourceLoader->register( '', $module );
81 $this->assertEquals( $module, $resourceLoader->getModule( '' ) );
84 /**
85 * @covers ResourceLoader::register
87 public function testRegisterInvalidName() {
88 $resourceLoader = new EmptyResourceLoader();
89 $this->setExpectedException( 'MWException', "name 'test!invalid' is invalid" );
90 $resourceLoader->register( 'test!invalid', new ResourceLoaderTestModule() );
93 /**
94 * @covers ResourceLoader::register
96 public function testRegisterInvalidType() {
97 $resourceLoader = new EmptyResourceLoader();
98 $this->setExpectedException( 'MWException', 'ResourceLoader module info type error' );
99 $resourceLoader->register( 'test', new stdClass() );
103 * @covers ResourceLoader::getModuleNames
105 public function testGetModuleNames() {
106 // Use an empty one so that core and extension modules don't get in.
107 $resourceLoader = new EmptyResourceLoader();
108 $resourceLoader->register( 'test.foo', new ResourceLoaderTestModule() );
109 $resourceLoader->register( 'test.bar', new ResourceLoaderTestModule() );
110 $this->assertEquals(
111 [ 'test.foo', 'test.bar' ],
112 $resourceLoader->getModuleNames()
116 public function provideTestIsFileModule() {
117 $fileModuleObj = $this->getMockBuilder( ResourceLoaderFileModule::class )
118 ->disableOriginalConstructor()
119 ->getMock();
120 return [
121 'object' => [ false,
122 new ResourceLoaderTestModule()
124 'FileModule object' => [ false,
125 $fileModuleObj
127 'simple empty' => [ true,
130 'simple scripts' => [ true,
131 [ 'scripts' => 'example.js' ]
133 'simple scripts, raw and targets' => [ true, [
134 'scripts' => [ 'a.js', 'b.js' ],
135 'raw' => true,
136 'targets' => [ 'desktop', 'mobile' ],
137 ] ],
138 'FileModule' => [ true,
139 [ 'class' => ResourceLoaderFileModule::class, 'scripts' => 'example.js' ]
141 'TestModule' => [ false,
142 [ 'class' => ResourceLoaderTestModule::class, 'scripts' => 'example.js' ]
144 'SkinModule (FileModule subclass)' => [ true,
145 [ 'class' => ResourceLoaderSkinModule::class, 'scripts' => 'example.js' ]
147 'JqueryMsgModule (FileModule subclass)' => [ true, [
148 'class' => ResourceLoaderJqueryMsgModule::class,
149 'scripts' => 'example.js',
150 ] ],
151 'WikiModule' => [ false, [
152 'class' => ResourceLoaderWikiModule::class,
153 'scripts' => [ 'MediaWiki:Example.js' ],
154 ] ],
159 * @dataProvider provideTestIsFileModule
160 * @covers ResourceLoader::isFileModule
162 public function testIsFileModule( $expected, $module ) {
163 $rl = TestingAccessWrapper::newFromObject( new EmptyResourceLoader() );
164 $rl->register( 'test', $module );
165 $this->assertSame( $expected, $rl->isFileModule( 'test' ) );
169 * @covers ResourceLoader::isFileModule
171 public function testIsFileModuleUnknown() {
172 $rl = TestingAccessWrapper::newFromObject( new EmptyResourceLoader() );
173 $this->assertSame( false, $rl->isFileModule( 'unknown' ) );
177 * @covers ResourceLoader::isModuleRegistered
179 public function testIsModuleRegistered() {
180 $rl = new EmptyResourceLoader();
181 $rl->register( 'test', new ResourceLoaderTestModule() );
182 $this->assertTrue( $rl->isModuleRegistered( 'test' ) );
183 $this->assertFalse( $rl->isModuleRegistered( 'test.unknown' ) );
187 * @covers ResourceLoader::getModule
189 public function testGetModuleUnknown() {
190 $rl = new EmptyResourceLoader();
191 $this->assertSame( null, $rl->getModule( 'test' ) );
195 * @covers ResourceLoader::getModule
197 public function testGetModuleClass() {
198 $rl = new EmptyResourceLoader();
199 $rl->register( 'test', [ 'class' => ResourceLoaderTestModule::class ] );
200 $this->assertInstanceOf(
201 ResourceLoaderTestModule::class,
202 $rl->getModule( 'test' )
207 * @covers ResourceLoader::getModule
209 public function testGetModuleFactory() {
210 $factory = function ( array $info ) {
211 $this->assertArrayHasKey( 'kitten', $info );
212 return new ResourceLoaderTestModule( $info );
215 $rl = new EmptyResourceLoader();
216 $rl->register( 'test', [ 'factory' => $factory, 'kitten' => 'little ball of fur' ] );
217 $this->assertInstanceOf(
218 ResourceLoaderTestModule::class,
219 $rl->getModule( 'test' )
224 * @covers ResourceLoader::getModule
226 public function testGetModuleClassDefault() {
227 $rl = new EmptyResourceLoader();
228 $rl->register( 'test', [] );
229 $this->assertInstanceOf(
230 ResourceLoaderFileModule::class,
231 $rl->getModule( 'test' ),
232 'Array-style module registrations default to FileModule'
237 * @covers ResourceLoaderFileModule::compileLessFile
239 public function testLessFileCompilation() {
240 $context = $this->getResourceLoaderContext();
241 $basePath = __DIR__ . '/../../data/less/module';
242 $module = new ResourceLoaderFileModule( [
243 'localBasePath' => $basePath,
244 'styles' => [ 'styles.less' ],
245 ] );
246 $module->setName( 'test.less' );
247 $styles = $module->getStyles( $context );
248 $this->assertStringEqualsFile( $basePath . '/styles.css', $styles['all'] );
251 public static function providePackedModules() {
252 return [
254 'Example from makePackedModulesString doc comment',
255 [ 'foo.bar', 'foo.baz', 'bar.baz', 'bar.quux' ],
256 'foo.bar,baz|bar.baz,quux',
259 'Example from expandModuleNames doc comment',
260 [ 'jquery.foo', 'jquery.bar', 'jquery.ui.baz', 'jquery.ui.quux' ],
261 'jquery.foo,bar|jquery.ui.baz,quux',
264 'Regression fixed in r88706 with dotless names',
265 [ 'foo', 'bar', 'baz' ],
266 'foo,bar,baz',
269 'Prefixless modules after a prefixed module',
270 [ 'single.module', 'foobar', 'foobaz' ],
271 'single.module|foobar,foobaz',
274 'Ordering',
275 [ 'foo', 'foo.baz', 'baz.quux', 'foo.bar' ],
276 'foo|foo.baz,bar|baz.quux',
277 [ 'foo', 'foo.baz', 'foo.bar', 'baz.quux' ],
283 * @dataProvider providePackedModules
284 * @covers ResourceLoader::makePackedModulesString
286 public function testMakePackedModulesString( $desc, $modules, $packed ) {
287 $this->assertEquals( $packed, ResourceLoader::makePackedModulesString( $modules ), $desc );
291 * @dataProvider providePackedModules
292 * @covers ResourceLoaderContext::expandModuleNames
294 public function testExpandModuleNames( $desc, $modules, $packed, $unpacked = null ) {
295 $this->assertEquals(
296 $unpacked ?: $modules,
297 ResourceLoaderContext::expandModuleNames( $packed ),
298 $desc
302 public static function provideAddSource() {
303 return [
304 [ 'foowiki', 'https://example.org/w/load.php', 'foowiki' ],
305 [ 'foowiki', [ 'loadScript' => 'https://example.org/w/load.php' ], 'foowiki' ],
308 'foowiki' => 'https://example.org/w/load.php',
309 'bazwiki' => 'https://example.com/w/load.php',
311 null,
312 [ 'foowiki', 'bazwiki' ]
318 * @dataProvider provideAddSource
319 * @covers ResourceLoader::addSource
320 * @covers ResourceLoader::getSources
322 public function testAddSource( $name, $info, $expected ) {
323 $rl = new ResourceLoader;
324 $rl->addSource( $name, $info );
325 if ( is_array( $expected ) ) {
326 foreach ( $expected as $source ) {
327 $this->assertArrayHasKey( $source, $rl->getSources() );
329 } else {
330 $this->assertArrayHasKey( $expected, $rl->getSources() );
335 * @covers ResourceLoader::addSource
337 public function testAddSourceDupe() {
338 $rl = new ResourceLoader;
339 $this->setExpectedException( 'MWException', 'ResourceLoader duplicate source addition error' );
340 $rl->addSource( 'foo', 'https://example.org/w/load.php' );
341 $rl->addSource( 'foo', 'https://example.com/w/load.php' );
345 * @covers ResourceLoader::addSource
347 public function testAddSourceInvalid() {
348 $rl = new ResourceLoader;
349 $this->setExpectedException( 'MWException', 'with no "loadScript" key' );
350 $rl->addSource( 'foo', [ 'x' => 'https://example.org/w/load.php' ] );
353 public static function provideLoaderImplement() {
354 return [
356 'title' => 'Implement scripts, styles and messages',
358 'name' => 'test.example',
359 'scripts' => 'mw.example();',
360 'styles' => [ 'css' => [ '.mw-example {}' ] ],
361 'messages' => [ 'example' => '' ],
362 'templates' => [],
364 'expected' => 'mw.loader.implement( "test.example", function ( $, jQuery, require, module ) {
365 mw.example();
366 }, {
367 "css": [
368 ".mw-example {}"
370 }, {
371 "example": ""
372 } );',
373 ] ],
375 'title' => 'Implement scripts',
377 'name' => 'test.example',
378 'scripts' => 'mw.example();',
379 'styles' => [],
381 'expected' => 'mw.loader.implement( "test.example", function ( $, jQuery, require, module ) {
382 mw.example();
383 } );',
384 ] ],
386 'title' => 'Implement styles',
388 'name' => 'test.example',
389 'scripts' => [],
390 'styles' => [ 'css' => [ '.mw-example {}' ] ],
392 'expected' => 'mw.loader.implement( "test.example", [], {
393 "css": [
394 ".mw-example {}"
396 } );',
397 ] ],
399 'title' => 'Implement scripts and messages',
401 'name' => 'test.example',
402 'scripts' => 'mw.example();',
403 'messages' => [ 'example' => '' ],
405 'expected' => 'mw.loader.implement( "test.example", function ( $, jQuery, require, module ) {
406 mw.example();
407 }, {}, {
408 "example": ""
409 } );',
410 ] ],
412 'title' => 'Implement scripts and templates',
414 'name' => 'test.example',
415 'scripts' => 'mw.example();',
416 'templates' => [ 'example.html' => '' ],
418 'expected' => 'mw.loader.implement( "test.example", function ( $, jQuery, require, module ) {
419 mw.example();
420 }, {}, {}, {
421 "example.html": ""
422 } );',
423 ] ],
425 'title' => 'Implement unwrapped user script',
427 'name' => 'user',
428 'scripts' => 'mw.example( 1 );',
429 'wrap' => false,
431 'expected' => 'mw.loader.implement( "user", "mw.example( 1 );" );',
432 ] ],
437 * @dataProvider provideLoaderImplement
438 * @covers ResourceLoader::makeLoaderImplementScript
439 * @covers ResourceLoader::trimArray
441 public function testMakeLoaderImplementScript( $case ) {
442 $case += [
443 'wrap' => true,
444 'styles' => [], 'templates' => [], 'messages' => new XmlJsCode( '{}' )
446 ResourceLoader::clearCache();
447 $this->setMwGlobals( 'wgResourceLoaderDebug', true );
449 $rl = TestingAccessWrapper::newFromClass( 'ResourceLoader' );
450 $this->assertEquals(
451 $case['expected'],
452 $rl->makeLoaderImplementScript(
453 $case['name'],
454 ( $case['wrap'] && is_string( $case['scripts'] ) )
455 ? new XmlJsCode( $case['scripts'] )
456 : $case['scripts'],
457 $case['styles'],
458 $case['messages'],
459 $case['templates']
465 * @covers ResourceLoader::makeLoaderImplementScript
467 public function testMakeLoaderImplementScriptInvalid() {
468 $this->setExpectedException( 'MWException', 'Invalid scripts error' );
469 $rl = TestingAccessWrapper::newFromClass( 'ResourceLoader' );
470 $rl->makeLoaderImplementScript(
471 'test', // name
472 123, // scripts
473 null, // styles
474 null, // messages
475 null // templates
480 * @covers ResourceLoader::makeLoaderRegisterScript
482 public function testMakeLoaderRegisterScript() {
483 $this->assertEquals(
484 'mw.loader.register( [
486 "test.name",
487 "1234567"
489 ] );',
490 ResourceLoader::makeLoaderRegisterScript( [
491 [ 'test.name', '1234567' ],
492 ] ),
493 'Nested array parameter'
496 $this->assertEquals(
497 'mw.loader.register( "test.name", "1234567" );',
498 ResourceLoader::makeLoaderRegisterScript(
499 'test.name',
500 '1234567'
502 'Variadic parameters'
507 * @covers ResourceLoader::makeLoaderSourcesScript
509 public function testMakeLoaderSourcesScript() {
510 $this->assertEquals(
511 'mw.loader.addSource( "local", "/w/load.php" );',
512 ResourceLoader::makeLoaderSourcesScript( 'local', '/w/load.php' )
514 $this->assertEquals(
515 'mw.loader.addSource( {
516 "local": "/w/load.php"
517 } );',
518 ResourceLoader::makeLoaderSourcesScript( [ 'local' => '/w/load.php' ] )
520 $this->assertEquals(
521 'mw.loader.addSource( {
522 "local": "/w/load.php",
523 "example": "https://example.org/w/load.php"
524 } );',
525 ResourceLoader::makeLoaderSourcesScript( [
526 'local' => '/w/load.php',
527 'example' => 'https://example.org/w/load.php'
530 $this->assertEquals(
531 'mw.loader.addSource( [] );',
532 ResourceLoader::makeLoaderSourcesScript( [] )
536 private static function fakeSources() {
537 return [
538 'examplewiki' => [
539 'loadScript' => '//example.org/w/load.php',
540 'apiScript' => '//example.org/w/api.php',
542 'example2wiki' => [
543 'loadScript' => '//example.com/w/load.php',
544 'apiScript' => '//example.com/w/api.php',
550 * @covers ResourceLoader::getLoadScript
552 public function testGetLoadScript() {
553 $this->setMwGlobals( 'wgResourceLoaderSources', [] );
554 $rl = new ResourceLoader();
555 $sources = self::fakeSources();
556 $rl->addSource( $sources );
557 foreach ( [ 'examplewiki', 'example2wiki' ] as $name ) {
558 $this->assertEquals( $rl->getLoadScript( $name ), $sources[$name]['loadScript'] );
561 try {
562 $rl->getLoadScript( 'thiswasneverreigstered' );
563 $this->assertTrue( false, 'ResourceLoader::getLoadScript should have thrown an exception' );
564 } catch ( MWException $e ) {
565 $this->assertTrue( true );
569 protected function getFailFerryMock() {
570 $mock = $this->getMockBuilder( ResourceLoaderTestModule::class )
571 ->setMethods( [ 'getScript' ] )
572 ->getMock();
573 $mock->method( 'getScript' )->will( $this->throwException(
574 new Exception( 'Ferry not found' )
575 ) );
576 return $mock;
579 protected function getSimpleModuleMock( $script = '' ) {
580 $mock = $this->getMockBuilder( ResourceLoaderTestModule::class )
581 ->setMethods( [ 'getScript' ] )
582 ->getMock();
583 $mock->method( 'getScript' )->willReturn( $script );
584 return $mock;
588 * @covers ResourceLoader::getCombinedVersion
590 public function testGetCombinedVersion() {
591 $rl = $this->getMockBuilder( EmptyResourceLoader::class )
592 // Disable log from outputErrorAndLog
593 ->setMethods( [ 'outputErrorAndLog' ] )->getMock();
594 $rl->register( [
595 'foo' => self::getSimpleModuleMock(),
596 'ferry' => self::getFailFerryMock(),
597 'bar' => self::getSimpleModuleMock(),
598 ] );
599 $context = $this->getResourceLoaderContext( [], $rl );
601 $this->assertEquals(
603 $rl->getCombinedVersion( $context, [] ),
604 'empty list'
607 $this->assertEquals(
608 ResourceLoader::makeHash( self::BLANK_VERSION ),
609 $rl->getCombinedVersion( $context, [ 'foo' ] ),
610 'compute foo'
613 // Verify that getCombinedVersion() does not throw when ferry fails.
614 // Instead it gracefully continues to combine the remaining modules.
615 $this->assertEquals(
616 ResourceLoader::makeHash( self::BLANK_VERSION . self::BLANK_VERSION ),
617 $rl->getCombinedVersion( $context, [ 'foo', 'ferry', 'bar' ] ),
618 'compute foo+ferry+bar (T152266)'
622 public static function provideMakeModuleResponseConcat() {
623 $testcases = [
625 'modules' => [
626 'foo' => 'foo()',
628 'expected' => "foo()\n" . 'mw.loader.state( {
629 "foo": "ready"
630 } );',
631 'minified' => "foo()\n" . 'mw.loader.state({"foo":"ready"});',
632 'message' => 'Script without semi-colon',
635 'modules' => [
636 'foo' => 'foo()',
637 'bar' => 'bar()',
639 'expected' => "foo()\nbar()\n" . 'mw.loader.state( {
640 "foo": "ready",
641 "bar": "ready"
642 } );',
643 'minified' => "foo()\nbar()\n" . 'mw.loader.state({"foo":"ready","bar":"ready"});',
644 'message' => 'Two scripts without semi-colon',
647 'modules' => [
648 'foo' => "foo()\n// bar();"
650 'expected' => "foo()\n// bar();\n" . 'mw.loader.state( {
651 "foo": "ready"
652 } );',
653 'minified' => "foo()\n" . 'mw.loader.state({"foo":"ready"});',
654 'message' => 'Script with semi-colon in comment (T162719)',
657 $ret = [];
658 foreach ( $testcases as $i => $case ) {
659 $ret["#$i"] = [
660 $case['modules'],
661 $case['expected'],
662 true, // debug
663 $case['message'],
665 $ret["#$i (minified)"] = [
666 $case['modules'],
667 $case['minified'],
668 false, // debug
669 $case['message'],
672 return $ret;
676 * Verify how multiple scripts and mw.loader.state() calls are concatenated.
678 * @dataProvider provideMakeModuleResponseConcat
679 * @covers ResourceLoader::makeModuleResponse
681 public function testMakeModuleResponseConcat( $scripts, $expected, $debug, $message = null ) {
682 $rl = new EmptyResourceLoader();
683 $modules = array_map( function ( $script ) {
684 return self::getSimpleModuleMock( $script );
685 }, $scripts );
686 $rl->register( $modules );
688 $this->setMwGlobals( 'wgResourceLoaderDebug', $debug );
689 $context = $this->getResourceLoaderContext(
691 'modules' => implode( '|', array_keys( $modules ) ),
692 'only' => 'scripts',
697 $response = $rl->makeModuleResponse( $context, $modules );
698 $this->assertSame( [], $rl->getErrors(), 'Errors' );
699 $this->assertEquals( $expected, $response, $message ?: 'Response' );
703 * Verify that when building module content in a load.php response,
704 * an exception from one module will not break script output from
705 * other modules.
707 * @covers ResourceLoader::makeModuleResponse
709 public function testMakeModuleResponseError() {
710 $modules = [
711 'foo' => self::getSimpleModuleMock( 'foo();' ),
712 'ferry' => self::getFailFerryMock(),
713 'bar' => self::getSimpleModuleMock( 'bar();' ),
715 $rl = new EmptyResourceLoader();
716 $rl->register( $modules );
717 $context = $this->getResourceLoaderContext(
719 'modules' => 'foo|ferry|bar',
720 'only' => 'scripts',
725 // Disable log from makeModuleResponse via outputErrorAndLog
726 $this->setLogger( 'exception', new Psr\Log\NullLogger() );
728 $response = $rl->makeModuleResponse( $context, $modules );
729 $errors = $rl->getErrors();
731 $this->assertCount( 1, $errors );
732 $this->assertRegExp( '/Ferry not found/', $errors[0] );
733 $this->assertEquals(
734 "foo();\nbar();\n" . 'mw.loader.state( {
735 "ferry": "error",
736 "foo": "ready",
737 "bar": "ready"
738 } );',
739 $response
744 * Verify that when building the startup module response,
745 * an exception from one module class will not break the entire
746 * startup module response. See T152266.
748 * @covers ResourceLoader::makeModuleResponse
750 public function testMakeModuleResponseStartupError() {
751 $rl = new EmptyResourceLoader();
752 $rl->register( [
753 'foo' => self::getSimpleModuleMock( 'foo();' ),
754 'ferry' => self::getFailFerryMock(),
755 'bar' => self::getSimpleModuleMock( 'bar();' ),
756 'startup' => [ 'class' => 'ResourceLoaderStartUpModule' ],
757 ] );
758 $context = $this->getResourceLoaderContext(
760 'modules' => 'startup',
761 'only' => 'scripts',
766 $this->assertEquals(
767 [ 'foo', 'ferry', 'bar', 'startup' ],
768 $rl->getModuleNames(),
769 'getModuleNames'
772 // Disable log from makeModuleResponse via outputErrorAndLog
773 $this->setLogger( 'exception', new Psr\Log\NullLogger() );
775 $modules = [ 'startup' => $rl->getModule( 'startup' ) ];
776 $response = $rl->makeModuleResponse( $context, $modules );
777 $errors = $rl->getErrors();
779 $this->assertRegExp( '/Ferry not found/', $errors[0] );
780 $this->assertCount( 1, $errors );
781 $this->assertRegExp(
782 '/isCompatible.*function startUp/s',
783 $response,
784 'startup response undisrupted (T152266)'
786 $this->assertRegExp(
787 '/register\([^)]+"ferry",\s*""/s',
788 $response,
789 'startup response registers broken module'
791 $this->assertRegExp(
792 '/state\([^)]+"ferry":\s*"error"/s',
793 $response,
794 'startup response sets state to error'
799 * Integration test for modules sending extra HTTP response headers.
801 * @covers ResourceLoaderModule::getHeaders
802 * @covers ResourceLoaderModule::buildContent
803 * @covers ResourceLoader::makeModuleResponse
805 public function testMakeModuleResponseExtraHeaders() {
806 $module = $this->getMockBuilder( ResourceLoaderTestModule::class )
807 ->setMethods( [ 'getPreloadLinks' ] )->getMock();
808 $module->method( 'getPreloadLinks' )->willReturn( [
809 'https://example.org/script.js' => [ 'as' => 'script' ],
810 ] );
812 $rl = new EmptyResourceLoader();
813 $rl->register( [
814 'foo' => $module,
815 ] );
816 $context = $this->getResourceLoaderContext(
817 [ 'modules' => 'foo', 'only' => 'scripts' ],
821 $modules = [ 'foo' => $rl->getModule( 'foo' ) ];
822 $response = $rl->makeModuleResponse( $context, $modules );
823 $extraHeaders = TestingAccessWrapper::newFromObject( $rl )->extraHeaders;
825 $this->assertEquals(
827 'Link: <https://example.org/script.js>;rel=preload;as=script'
829 $extraHeaders,
830 'Extra headers'
835 * @covers ResourceLoaderModule::getHeaders
836 * @covers ResourceLoaderModule::buildContent
837 * @covers ResourceLoader::makeModuleResponse
839 public function testMakeModuleResponseExtraHeadersMulti() {
840 $foo = $this->getMockBuilder( ResourceLoaderTestModule::class )
841 ->setMethods( [ 'getPreloadLinks' ] )->getMock();
842 $foo->method( 'getPreloadLinks' )->willReturn( [
843 'https://example.org/script.js' => [ 'as' => 'script' ],
844 ] );
846 $bar = $this->getMockBuilder( ResourceLoaderTestModule::class )
847 ->setMethods( [ 'getPreloadLinks' ] )->getMock();
848 $bar->method( 'getPreloadLinks' )->willReturn( [
849 '/example.png' => [ 'as' => 'image' ],
850 '/example.jpg' => [ 'as' => 'image' ],
851 ] );
853 $rl = new EmptyResourceLoader();
854 $rl->register( [ 'foo' => $foo, 'bar' => $bar ] );
855 $context = $this->getResourceLoaderContext(
856 [ 'modules' => 'foo|bar', 'only' => 'scripts' ],
860 $modules = [ 'foo' => $rl->getModule( 'foo' ), 'bar' => $rl->getModule( 'bar' ) ];
861 $response = $rl->makeModuleResponse( $context, $modules );
862 $extraHeaders = TestingAccessWrapper::newFromObject( $rl )->extraHeaders;
863 $this->assertEquals(
865 'Link: <https://example.org/script.js>;rel=preload;as=script',
866 'Link: </example.png>;rel=preload;as=image,</example.jpg>;rel=preload;as=image'
868 $extraHeaders,
869 'Extra headers'