X-Git-Url: https://git.heureux-cyclage.org/?a=blobdiff_plain;f=tests%2Fphpunit%2Fincludes%2Fresourceloader%2FResourceLoaderTest.php;h=e9d022f62e60f94ee0351f12b7351fcdd1427fc6;hb=a0947c9507065a83afe52b078f0f6d1c6163875e;hp=f00253b662436fef9a780d66db4cdaf922aa1dd6;hpb=d56b46598d58f16eb8d7c8252083bbf3844e5430;p=lhc%2Fweb%2Fwiklou.git diff --git a/tests/phpunit/includes/resourceloader/ResourceLoaderTest.php b/tests/phpunit/includes/resourceloader/ResourceLoaderTest.php index f00253b662..e9d022f62e 100644 --- a/tests/phpunit/includes/resourceloader/ResourceLoaderTest.php +++ b/tests/phpunit/includes/resourceloader/ResourceLoaderTest.php @@ -165,6 +165,14 @@ class ResourceLoaderTest extends ResourceLoaderTestCase { $this->assertSame( $expected, $rl->isFileModule( 'test' ) ); } + /** + * @covers ResourceLoader::isFileModule + */ + public function testIsFileModuleUnknown() { + $rl = TestingAccessWrapper::newFromObject( new EmptyResourceLoader() ); + $this->assertSame( false, $rl->isFileModule( 'unknown' ) ); + } + /** * @covers ResourceLoader::isModuleRegistered */ @@ -580,7 +588,9 @@ mw.example(); * @covers ResourceLoader::getCombinedVersion */ public function testGetCombinedVersion() { - $rl = new EmptyResourceLoader(); + $rl = $this->getMockBuilder( EmptyResourceLoader::class ) + // Disable log from outputErrorAndLog + ->setMethods( [ 'outputErrorAndLog' ] )->getMock(); $rl->register( [ 'foo' => self::getSimpleModuleMock(), 'ferry' => self::getFailFerryMock(), @@ -588,6 +598,12 @@ mw.example(); ] ); $context = $this->getResourceLoaderContext( [], $rl ); + $this->assertEquals( + '', + $rl->getCombinedVersion( $context, [] ), + 'empty list' + ); + $this->assertEquals( ResourceLoader::makeHash( self::BLANK_VERSION ), $rl->getCombinedVersion( $context, [ 'foo' ] ), @@ -603,6 +619,86 @@ mw.example(); ); } + public static function provideMakeModuleResponseConcat() { + $testcases = [ + [ + 'modules' => [ + 'foo' => 'foo()', + ], + 'expected' => "foo()\n" . 'mw.loader.state( { + "foo": "ready" +} );', + 'minified' => "foo()\n" . 'mw.loader.state({"foo":"ready"});', + 'message' => 'Script without semi-colon', + ], + [ + 'modules' => [ + 'foo' => 'foo()', + 'bar' => 'bar()', + ], + 'expected' => "foo()\nbar()\n" . 'mw.loader.state( { + "foo": "ready", + "bar": "ready" +} );', + 'minified' => "foo()\nbar()\n" . 'mw.loader.state({"foo":"ready","bar":"ready"});', + 'message' => 'Two scripts without semi-colon', + ], + [ + 'modules' => [ + 'foo' => "foo()\n// bar();" + ], + 'expected' => "foo()\n// bar();\n" . 'mw.loader.state( { + "foo": "ready" +} );', + 'minified' => "foo()\n" . 'mw.loader.state({"foo":"ready"});', + 'message' => 'Script with semi-colon in comment (T162719)', + ], + ]; + $ret = []; + foreach ( $testcases as $i => $case ) { + $ret["#$i"] = [ + $case['modules'], + $case['expected'], + true, // debug + $case['message'], + ]; + $ret["#$i (minified)"] = [ + $case['modules'], + $case['minified'], + false, // debug + $case['message'], + ]; + } + return $ret; + } + + /** + * Verify how multiple scripts and mw.loader.state() calls are concatenated. + * + * @dataProvider provideMakeModuleResponseConcat + * @covers ResourceLoader::makeModuleResponse + */ + public function testMakeModuleResponseConcat( $scripts, $expected, $debug, $message = null ) { + $rl = new EmptyResourceLoader(); + $modules = array_map( function ( $script ) { + return self::getSimpleModuleMock( $script ); + }, $scripts ); + $rl->register( $modules ); + + $this->setMwGlobals( 'wgResourceLoaderDebug', $debug ); + $context = $this->getResourceLoaderContext( + [ + 'modules' => implode( '|', array_keys( $modules ) ), + 'only' => 'scripts', + ], + $rl + ); + + $response = $rl->makeModuleResponse( $context, $modules ); + $this->assertSame( [], $rl->getErrors(), 'Errors' ); + $this->assertEquals( $expected, $response, $message ?: 'Response' ); + } + /** * Verify that when building module content in a load.php response, * an exception from one module will not break script output from @@ -626,13 +722,16 @@ mw.example(); $rl ); + // Disable log from makeModuleResponse via outputErrorAndLog + $this->setLogger( 'exception', new Psr\Log\NullLogger() ); + $response = $rl->makeModuleResponse( $context, $modules ); $errors = $rl->getErrors(); $this->assertCount( 1, $errors ); $this->assertRegExp( '/Ferry not found/', $errors[0] ); $this->assertEquals( - 'foo();bar();mw.loader.state( { + "foo();\nbar();\n" . 'mw.loader.state( { "ferry": "error", "foo": "ready", "bar": "ready" @@ -670,6 +769,9 @@ mw.example(); 'getModuleNames' ); + // Disable log from makeModuleResponse via outputErrorAndLog + $this->setLogger( 'exception', new Psr\Log\NullLogger() ); + $modules = [ 'startup' => $rl->getModule( 'startup' ) ]; $response = $rl->makeModuleResponse( $context, $modules ); $errors = $rl->getErrors(); @@ -692,4 +794,79 @@ mw.example(); 'startup response sets state to error' ); } + + /** + * Integration test for modules sending extra HTTP response headers. + * + * @covers ResourceLoaderModule::getHeaders + * @covers ResourceLoaderModule::buildContent + * @covers ResourceLoader::makeModuleResponse + */ + public function testMakeModuleResponseExtraHeaders() { + $module = $this->getMockBuilder( ResourceLoaderTestModule::class ) + ->setMethods( [ 'getPreloadLinks' ] )->getMock(); + $module->method( 'getPreloadLinks' )->willReturn( [ + 'https://example.org/script.js' => [ 'as' => 'script' ], + ] ); + + $rl = new EmptyResourceLoader(); + $rl->register( [ + 'foo' => $module, + ] ); + $context = $this->getResourceLoaderContext( + [ 'modules' => 'foo', 'only' => 'scripts' ], + $rl + ); + + $modules = [ 'foo' => $rl->getModule( 'foo' ) ]; + $response = $rl->makeModuleResponse( $context, $modules ); + $extraHeaders = TestingAccessWrapper::newFromObject( $rl )->extraHeaders; + + $this->assertEquals( + [ + 'Link: ;rel=preload;as=script' + ], + $extraHeaders, + 'Extra headers' + ); + } + + /** + * @covers ResourceLoaderModule::getHeaders + * @covers ResourceLoaderModule::buildContent + * @covers ResourceLoader::makeModuleResponse + */ + public function testMakeModuleResponseExtraHeadersMulti() { + $foo = $this->getMockBuilder( ResourceLoaderTestModule::class ) + ->setMethods( [ 'getPreloadLinks' ] )->getMock(); + $foo->method( 'getPreloadLinks' )->willReturn( [ + 'https://example.org/script.js' => [ 'as' => 'script' ], + ] ); + + $bar = $this->getMockBuilder( ResourceLoaderTestModule::class ) + ->setMethods( [ 'getPreloadLinks' ] )->getMock(); + $bar->method( 'getPreloadLinks' )->willReturn( [ + '/example.png' => [ 'as' => 'image' ], + '/example.jpg' => [ 'as' => 'image' ], + ] ); + + $rl = new EmptyResourceLoader(); + $rl->register( [ 'foo' => $foo, 'bar' => $bar ] ); + $context = $this->getResourceLoaderContext( + [ 'modules' => 'foo|bar', 'only' => 'scripts' ], + $rl + ); + + $modules = [ 'foo' => $rl->getModule( 'foo' ), 'bar' => $rl->getModule( 'bar' ) ]; + $response = $rl->makeModuleResponse( $context, $modules ); + $extraHeaders = TestingAccessWrapper::newFromObject( $rl )->extraHeaders; + $this->assertEquals( + [ + 'Link: ;rel=preload;as=script', + 'Link: ;rel=preload;as=image,;rel=preload;as=image' + ], + $extraHeaders, + 'Extra headers' + ); + } }