$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
*/
* @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(),
] );
$context = $this->getResourceLoaderContext( [], $rl );
+ $this->assertEquals(
+ '',
+ $rl->getCombinedVersion( $context, [] ),
+ 'empty list'
+ );
+
$this->assertEquals(
ResourceLoader::makeHash( self::BLANK_VERSION ),
$rl->getCombinedVersion( $context, [ 'foo' ] ),
);
}
+ 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
* other modules.
+ *
+ * @covers ResourceLoader::makeModuleResponse
*/
public function testMakeModuleResponseError() {
$modules = [
$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"
* Verify that when building the startup module response,
* an exception from one module class will not break the entire
* startup module response. See T152266.
+ *
+ * @covers ResourceLoader::makeModuleResponse
*/
public function testMakeModuleResponseStartupError() {
$rl = new EmptyResourceLoader();
'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();
'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: <https://example.org/script.js>;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: <https://example.org/script.js>;rel=preload;as=script',
+ 'Link: </example.png>;rel=preload;as=image,</example.jpg>;rel=preload;as=image'
+ ],
+ $extraHeaders,
+ 'Extra headers'
+ );
+ }
}