695 lines
18 KiB
PHP
695 lines
18 KiB
PHP
<?php
|
|
|
|
use Wikimedia\TestingAccessWrapper;
|
|
|
|
class ResourceLoaderTest extends ResourceLoaderTestCase {
|
|
|
|
protected function setUp() {
|
|
parent::setUp();
|
|
|
|
$this->setMwGlobals( [
|
|
'wgResourceLoaderLESSImportPaths' => [
|
|
dirname( dirname( __DIR__ ) ) . '/data/less/common',
|
|
],
|
|
'wgResourceLoaderLESSVars' => [
|
|
'foo' => '2px',
|
|
'Foo' => '#eeeeee',
|
|
'bar' => 5,
|
|
],
|
|
// Clear ResourceLoaderGetConfigVars hooks (called by StartupModule)
|
|
// to avoid notices during testMakeModuleResponse for missing
|
|
// wgResourceLoaderLESSVars keys in extension hooks.
|
|
'wgHooks' => [],
|
|
'wgShowExceptionDetails' => true,
|
|
] );
|
|
}
|
|
|
|
/**
|
|
* Ensure the ResourceLoaderRegisterModules hook is called.
|
|
*
|
|
* @covers ResourceLoader::__construct
|
|
*/
|
|
public function testConstructRegistrationHook() {
|
|
$resourceLoaderRegisterModulesHook = false;
|
|
|
|
$this->setMwGlobals( 'wgHooks', [
|
|
'ResourceLoaderRegisterModules' => [
|
|
function ( &$resourceLoader ) use ( &$resourceLoaderRegisterModulesHook ) {
|
|
$resourceLoaderRegisterModulesHook = true;
|
|
}
|
|
]
|
|
] );
|
|
|
|
$unused = new ResourceLoader();
|
|
$this->assertTrue(
|
|
$resourceLoaderRegisterModulesHook,
|
|
'Hook ResourceLoaderRegisterModules called'
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @covers ResourceLoader::register
|
|
* @covers ResourceLoader::getModule
|
|
*/
|
|
public function testRegisterValidObject() {
|
|
$module = new ResourceLoaderTestModule();
|
|
$resourceLoader = new EmptyResourceLoader();
|
|
$resourceLoader->register( 'test', $module );
|
|
$this->assertEquals( $module, $resourceLoader->getModule( 'test' ) );
|
|
}
|
|
|
|
/**
|
|
* @covers ResourceLoader::register
|
|
* @covers ResourceLoader::getModule
|
|
*/
|
|
public function testRegisterValidArray() {
|
|
$module = new ResourceLoaderTestModule();
|
|
$resourceLoader = new EmptyResourceLoader();
|
|
// Covers case of register() setting $rl->moduleInfos,
|
|
// but $rl->modules lazy-populated by getModule()
|
|
$resourceLoader->register( 'test', [ 'object' => $module ] );
|
|
$this->assertEquals( $module, $resourceLoader->getModule( 'test' ) );
|
|
}
|
|
|
|
/**
|
|
* @covers ResourceLoader::register
|
|
*/
|
|
public function testRegisterEmptyString() {
|
|
$module = new ResourceLoaderTestModule();
|
|
$resourceLoader = new EmptyResourceLoader();
|
|
$resourceLoader->register( '', $module );
|
|
$this->assertEquals( $module, $resourceLoader->getModule( '' ) );
|
|
}
|
|
|
|
/**
|
|
* @covers ResourceLoader::register
|
|
*/
|
|
public function testRegisterInvalidName() {
|
|
$resourceLoader = new EmptyResourceLoader();
|
|
$this->setExpectedException( 'MWException', "name 'test!invalid' is invalid" );
|
|
$resourceLoader->register( 'test!invalid', new ResourceLoaderTestModule() );
|
|
}
|
|
|
|
/**
|
|
* @covers ResourceLoader::register
|
|
*/
|
|
public function testRegisterInvalidType() {
|
|
$resourceLoader = new EmptyResourceLoader();
|
|
$this->setExpectedException( 'MWException', 'ResourceLoader module info type error' );
|
|
$resourceLoader->register( 'test', new stdClass() );
|
|
}
|
|
|
|
/**
|
|
* @covers ResourceLoader::getModuleNames
|
|
*/
|
|
public function testGetModuleNames() {
|
|
// Use an empty one so that core and extension modules don't get in.
|
|
$resourceLoader = new EmptyResourceLoader();
|
|
$resourceLoader->register( 'test.foo', new ResourceLoaderTestModule() );
|
|
$resourceLoader->register( 'test.bar', new ResourceLoaderTestModule() );
|
|
$this->assertEquals(
|
|
[ 'test.foo', 'test.bar' ],
|
|
$resourceLoader->getModuleNames()
|
|
);
|
|
}
|
|
|
|
public function provideTestIsFileModule() {
|
|
$fileModuleObj = $this->getMockBuilder( ResourceLoaderFileModule::class )
|
|
->disableOriginalConstructor()
|
|
->getMock();
|
|
return [
|
|
'object' => [ false,
|
|
new ResourceLoaderTestModule()
|
|
],
|
|
'FileModule object' => [ false,
|
|
$fileModuleObj
|
|
],
|
|
'simple empty' => [ true,
|
|
[]
|
|
],
|
|
'simple scripts' => [ true,
|
|
[ 'scripts' => 'example.js' ]
|
|
],
|
|
'simple scripts, raw and targets' => [ true, [
|
|
'scripts' => [ 'a.js', 'b.js' ],
|
|
'raw' => true,
|
|
'targets' => [ 'desktop', 'mobile' ],
|
|
] ],
|
|
'FileModule' => [ true,
|
|
[ 'class' => ResourceLoaderFileModule::class, 'scripts' => 'example.js' ]
|
|
],
|
|
'TestModule' => [ false,
|
|
[ 'class' => ResourceLoaderTestModule::class, 'scripts' => 'example.js' ]
|
|
],
|
|
'SkinModule (FileModule subclass)' => [ true,
|
|
[ 'class' => ResourceLoaderSkinModule::class, 'scripts' => 'example.js' ]
|
|
],
|
|
'JqueryMsgModule (FileModule subclass)' => [ true, [
|
|
'class' => ResourceLoaderJqueryMsgModule::class,
|
|
'scripts' => 'example.js',
|
|
] ],
|
|
'WikiModule' => [ false, [
|
|
'class' => ResourceLoaderWikiModule::class,
|
|
'scripts' => [ 'MediaWiki:Example.js' ],
|
|
] ],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @dataProvider provideTestIsFileModule
|
|
* @covers ResourceLoader::isFileModule
|
|
*/
|
|
public function testIsFileModule( $expected, $module ) {
|
|
$rl = TestingAccessWrapper::newFromObject( new EmptyResourceLoader() );
|
|
$rl->register( 'test', $module );
|
|
$this->assertSame( $expected, $rl->isFileModule( 'test' ) );
|
|
}
|
|
|
|
/**
|
|
* @covers ResourceLoader::isModuleRegistered
|
|
*/
|
|
public function testIsModuleRegistered() {
|
|
$rl = new EmptyResourceLoader();
|
|
$rl->register( 'test', new ResourceLoaderTestModule() );
|
|
$this->assertTrue( $rl->isModuleRegistered( 'test' ) );
|
|
$this->assertFalse( $rl->isModuleRegistered( 'test.unknown' ) );
|
|
}
|
|
|
|
/**
|
|
* @covers ResourceLoader::getModule
|
|
*/
|
|
public function testGetModuleUnknown() {
|
|
$rl = new EmptyResourceLoader();
|
|
$this->assertSame( null, $rl->getModule( 'test' ) );
|
|
}
|
|
|
|
/**
|
|
* @covers ResourceLoader::getModule
|
|
*/
|
|
public function testGetModuleClass() {
|
|
$rl = new EmptyResourceLoader();
|
|
$rl->register( 'test', [ 'class' => ResourceLoaderTestModule::class ] );
|
|
$this->assertInstanceOf(
|
|
ResourceLoaderTestModule::class,
|
|
$rl->getModule( 'test' )
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @covers ResourceLoader::getModule
|
|
*/
|
|
public function testGetModuleFactory() {
|
|
$factory = function ( array $info ) {
|
|
$this->assertArrayHasKey( 'kitten', $info );
|
|
return new ResourceLoaderTestModule( $info );
|
|
};
|
|
|
|
$rl = new EmptyResourceLoader();
|
|
$rl->register( 'test', [ 'factory' => $factory, 'kitten' => 'little ball of fur' ] );
|
|
$this->assertInstanceOf(
|
|
ResourceLoaderTestModule::class,
|
|
$rl->getModule( 'test' )
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @covers ResourceLoader::getModule
|
|
*/
|
|
public function testGetModuleClassDefault() {
|
|
$rl = new EmptyResourceLoader();
|
|
$rl->register( 'test', [] );
|
|
$this->assertInstanceOf(
|
|
ResourceLoaderFileModule::class,
|
|
$rl->getModule( 'test' ),
|
|
'Array-style module registrations default to FileModule'
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @covers ResourceLoaderFileModule::compileLessFile
|
|
*/
|
|
public function testLessFileCompilation() {
|
|
$context = $this->getResourceLoaderContext();
|
|
$basePath = __DIR__ . '/../../data/less/module';
|
|
$module = new ResourceLoaderFileModule( [
|
|
'localBasePath' => $basePath,
|
|
'styles' => [ 'styles.less' ],
|
|
] );
|
|
$module->setName( 'test.less' );
|
|
$styles = $module->getStyles( $context );
|
|
$this->assertStringEqualsFile( $basePath . '/styles.css', $styles['all'] );
|
|
}
|
|
|
|
public static function providePackedModules() {
|
|
return [
|
|
[
|
|
'Example from makePackedModulesString doc comment',
|
|
[ 'foo.bar', 'foo.baz', 'bar.baz', 'bar.quux' ],
|
|
'foo.bar,baz|bar.baz,quux',
|
|
],
|
|
[
|
|
'Example from expandModuleNames doc comment',
|
|
[ 'jquery.foo', 'jquery.bar', 'jquery.ui.baz', 'jquery.ui.quux' ],
|
|
'jquery.foo,bar|jquery.ui.baz,quux',
|
|
],
|
|
[
|
|
'Regression fixed in r88706 with dotless names',
|
|
[ 'foo', 'bar', 'baz' ],
|
|
'foo,bar,baz',
|
|
],
|
|
[
|
|
'Prefixless modules after a prefixed module',
|
|
[ 'single.module', 'foobar', 'foobaz' ],
|
|
'single.module|foobar,foobaz',
|
|
],
|
|
[
|
|
'Ordering',
|
|
[ 'foo', 'foo.baz', 'baz.quux', 'foo.bar' ],
|
|
'foo|foo.baz,bar|baz.quux',
|
|
[ 'foo', 'foo.baz', 'foo.bar', 'baz.quux' ],
|
|
]
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @dataProvider providePackedModules
|
|
* @covers ResourceLoader::makePackedModulesString
|
|
*/
|
|
public function testMakePackedModulesString( $desc, $modules, $packed ) {
|
|
$this->assertEquals( $packed, ResourceLoader::makePackedModulesString( $modules ), $desc );
|
|
}
|
|
|
|
/**
|
|
* @dataProvider providePackedModules
|
|
* @covers ResourceLoaderContext::expandModuleNames
|
|
*/
|
|
public function testExpandModuleNames( $desc, $modules, $packed, $unpacked = null ) {
|
|
$this->assertEquals(
|
|
$unpacked ?: $modules,
|
|
ResourceLoaderContext::expandModuleNames( $packed ),
|
|
$desc
|
|
);
|
|
}
|
|
|
|
public static function provideAddSource() {
|
|
return [
|
|
[ 'foowiki', 'https://example.org/w/load.php', 'foowiki' ],
|
|
[ 'foowiki', [ 'loadScript' => 'https://example.org/w/load.php' ], 'foowiki' ],
|
|
[
|
|
[
|
|
'foowiki' => 'https://example.org/w/load.php',
|
|
'bazwiki' => 'https://example.com/w/load.php',
|
|
],
|
|
null,
|
|
[ 'foowiki', 'bazwiki' ]
|
|
]
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @dataProvider provideAddSource
|
|
* @covers ResourceLoader::addSource
|
|
* @covers ResourceLoader::getSources
|
|
*/
|
|
public function testAddSource( $name, $info, $expected ) {
|
|
$rl = new ResourceLoader;
|
|
$rl->addSource( $name, $info );
|
|
if ( is_array( $expected ) ) {
|
|
foreach ( $expected as $source ) {
|
|
$this->assertArrayHasKey( $source, $rl->getSources() );
|
|
}
|
|
} else {
|
|
$this->assertArrayHasKey( $expected, $rl->getSources() );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @covers ResourceLoader::addSource
|
|
*/
|
|
public function testAddSourceDupe() {
|
|
$rl = new ResourceLoader;
|
|
$this->setExpectedException( 'MWException', 'ResourceLoader duplicate source addition error' );
|
|
$rl->addSource( 'foo', 'https://example.org/w/load.php' );
|
|
$rl->addSource( 'foo', 'https://example.com/w/load.php' );
|
|
}
|
|
|
|
/**
|
|
* @covers ResourceLoader::addSource
|
|
*/
|
|
public function testAddSourceInvalid() {
|
|
$rl = new ResourceLoader;
|
|
$this->setExpectedException( 'MWException', 'with no "loadScript" key' );
|
|
$rl->addSource( 'foo', [ 'x' => 'https://example.org/w/load.php' ] );
|
|
}
|
|
|
|
public static function provideLoaderImplement() {
|
|
return [
|
|
[ [
|
|
'title' => 'Implement scripts, styles and messages',
|
|
|
|
'name' => 'test.example',
|
|
'scripts' => 'mw.example();',
|
|
'styles' => [ 'css' => [ '.mw-example {}' ] ],
|
|
'messages' => [ 'example' => '' ],
|
|
'templates' => [],
|
|
|
|
'expected' => 'mw.loader.implement( "test.example", function ( $, jQuery, require, module ) {
|
|
mw.example();
|
|
}, {
|
|
"css": [
|
|
".mw-example {}"
|
|
]
|
|
}, {
|
|
"example": ""
|
|
} );',
|
|
] ],
|
|
[ [
|
|
'title' => 'Implement scripts',
|
|
|
|
'name' => 'test.example',
|
|
'scripts' => 'mw.example();',
|
|
'styles' => [],
|
|
|
|
'expected' => 'mw.loader.implement( "test.example", function ( $, jQuery, require, module ) {
|
|
mw.example();
|
|
} );',
|
|
] ],
|
|
[ [
|
|
'title' => 'Implement styles',
|
|
|
|
'name' => 'test.example',
|
|
'scripts' => [],
|
|
'styles' => [ 'css' => [ '.mw-example {}' ] ],
|
|
|
|
'expected' => 'mw.loader.implement( "test.example", [], {
|
|
"css": [
|
|
".mw-example {}"
|
|
]
|
|
} );',
|
|
] ],
|
|
[ [
|
|
'title' => 'Implement scripts and messages',
|
|
|
|
'name' => 'test.example',
|
|
'scripts' => 'mw.example();',
|
|
'messages' => [ 'example' => '' ],
|
|
|
|
'expected' => 'mw.loader.implement( "test.example", function ( $, jQuery, require, module ) {
|
|
mw.example();
|
|
}, {}, {
|
|
"example": ""
|
|
} );',
|
|
] ],
|
|
[ [
|
|
'title' => 'Implement scripts and templates',
|
|
|
|
'name' => 'test.example',
|
|
'scripts' => 'mw.example();',
|
|
'templates' => [ 'example.html' => '' ],
|
|
|
|
'expected' => 'mw.loader.implement( "test.example", function ( $, jQuery, require, module ) {
|
|
mw.example();
|
|
}, {}, {}, {
|
|
"example.html": ""
|
|
} );',
|
|
] ],
|
|
[ [
|
|
'title' => 'Implement unwrapped user script',
|
|
|
|
'name' => 'user',
|
|
'scripts' => 'mw.example( 1 );',
|
|
'wrap' => false,
|
|
|
|
'expected' => 'mw.loader.implement( "user", "mw.example( 1 );" );',
|
|
] ],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @dataProvider provideLoaderImplement
|
|
* @covers ResourceLoader::makeLoaderImplementScript
|
|
* @covers ResourceLoader::trimArray
|
|
*/
|
|
public function testMakeLoaderImplementScript( $case ) {
|
|
$case += [
|
|
'wrap' => true,
|
|
'styles' => [], 'templates' => [], 'messages' => new XmlJsCode( '{}' )
|
|
];
|
|
ResourceLoader::clearCache();
|
|
$this->setMwGlobals( 'wgResourceLoaderDebug', true );
|
|
|
|
$rl = TestingAccessWrapper::newFromClass( 'ResourceLoader' );
|
|
$this->assertEquals(
|
|
$case['expected'],
|
|
$rl->makeLoaderImplementScript(
|
|
$case['name'],
|
|
( $case['wrap'] && is_string( $case['scripts'] ) )
|
|
? new XmlJsCode( $case['scripts'] )
|
|
: $case['scripts'],
|
|
$case['styles'],
|
|
$case['messages'],
|
|
$case['templates']
|
|
)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @covers ResourceLoader::makeLoaderImplementScript
|
|
*/
|
|
public function testMakeLoaderImplementScriptInvalid() {
|
|
$this->setExpectedException( 'MWException', 'Invalid scripts error' );
|
|
$rl = TestingAccessWrapper::newFromClass( 'ResourceLoader' );
|
|
$rl->makeLoaderImplementScript(
|
|
'test', // name
|
|
123, // scripts
|
|
null, // styles
|
|
null, // messages
|
|
null // templates
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @covers ResourceLoader::makeLoaderRegisterScript
|
|
*/
|
|
public function testMakeLoaderRegisterScript() {
|
|
$this->assertEquals(
|
|
'mw.loader.register( [
|
|
[
|
|
"test.name",
|
|
"1234567"
|
|
]
|
|
] );',
|
|
ResourceLoader::makeLoaderRegisterScript( [
|
|
[ 'test.name', '1234567' ],
|
|
] ),
|
|
'Nested array parameter'
|
|
);
|
|
|
|
$this->assertEquals(
|
|
'mw.loader.register( "test.name", "1234567" );',
|
|
ResourceLoader::makeLoaderRegisterScript(
|
|
'test.name',
|
|
'1234567'
|
|
),
|
|
'Variadic parameters'
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @covers ResourceLoader::makeLoaderSourcesScript
|
|
*/
|
|
public function testMakeLoaderSourcesScript() {
|
|
$this->assertEquals(
|
|
'mw.loader.addSource( "local", "/w/load.php" );',
|
|
ResourceLoader::makeLoaderSourcesScript( 'local', '/w/load.php' )
|
|
);
|
|
$this->assertEquals(
|
|
'mw.loader.addSource( {
|
|
"local": "/w/load.php"
|
|
} );',
|
|
ResourceLoader::makeLoaderSourcesScript( [ 'local' => '/w/load.php' ] )
|
|
);
|
|
$this->assertEquals(
|
|
'mw.loader.addSource( {
|
|
"local": "/w/load.php",
|
|
"example": "https://example.org/w/load.php"
|
|
} );',
|
|
ResourceLoader::makeLoaderSourcesScript( [
|
|
'local' => '/w/load.php',
|
|
'example' => 'https://example.org/w/load.php'
|
|
] )
|
|
);
|
|
$this->assertEquals(
|
|
'mw.loader.addSource( [] );',
|
|
ResourceLoader::makeLoaderSourcesScript( [] )
|
|
);
|
|
}
|
|
|
|
private static function fakeSources() {
|
|
return [
|
|
'examplewiki' => [
|
|
'loadScript' => '//example.org/w/load.php',
|
|
'apiScript' => '//example.org/w/api.php',
|
|
],
|
|
'example2wiki' => [
|
|
'loadScript' => '//example.com/w/load.php',
|
|
'apiScript' => '//example.com/w/api.php',
|
|
],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @covers ResourceLoader::getLoadScript
|
|
*/
|
|
public function testGetLoadScript() {
|
|
$this->setMwGlobals( 'wgResourceLoaderSources', [] );
|
|
$rl = new ResourceLoader();
|
|
$sources = self::fakeSources();
|
|
$rl->addSource( $sources );
|
|
foreach ( [ 'examplewiki', 'example2wiki' ] as $name ) {
|
|
$this->assertEquals( $rl->getLoadScript( $name ), $sources[$name]['loadScript'] );
|
|
}
|
|
|
|
try {
|
|
$rl->getLoadScript( 'thiswasneverreigstered' );
|
|
$this->assertTrue( false, 'ResourceLoader::getLoadScript should have thrown an exception' );
|
|
} catch ( MWException $e ) {
|
|
$this->assertTrue( true );
|
|
}
|
|
}
|
|
|
|
protected function getFailFerryMock() {
|
|
$mock = $this->getMockBuilder( ResourceLoaderTestModule::class )
|
|
->setMethods( [ 'getScript' ] )
|
|
->getMock();
|
|
$mock->method( 'getScript' )->will( $this->throwException(
|
|
new Exception( 'Ferry not found' )
|
|
) );
|
|
return $mock;
|
|
}
|
|
|
|
protected function getSimpleModuleMock( $script = '' ) {
|
|
$mock = $this->getMockBuilder( ResourceLoaderTestModule::class )
|
|
->setMethods( [ 'getScript' ] )
|
|
->getMock();
|
|
$mock->method( 'getScript' )->willReturn( $script );
|
|
return $mock;
|
|
}
|
|
|
|
/**
|
|
* @covers ResourceLoader::getCombinedVersion
|
|
*/
|
|
public function testGetCombinedVersion() {
|
|
$rl = new EmptyResourceLoader();
|
|
$rl->register( [
|
|
'foo' => self::getSimpleModuleMock(),
|
|
'ferry' => self::getFailFerryMock(),
|
|
'bar' => self::getSimpleModuleMock(),
|
|
] );
|
|
$context = $this->getResourceLoaderContext( [], $rl );
|
|
|
|
$this->assertEquals(
|
|
ResourceLoader::makeHash( self::BLANK_VERSION ),
|
|
$rl->getCombinedVersion( $context, [ 'foo' ] ),
|
|
'compute foo'
|
|
);
|
|
|
|
// Verify that getCombinedVersion() does not throw when ferry fails.
|
|
// Instead it gracefully continues to combine the remaining modules.
|
|
$this->assertEquals(
|
|
ResourceLoader::makeHash( self::BLANK_VERSION . self::BLANK_VERSION ),
|
|
$rl->getCombinedVersion( $context, [ 'foo', 'ferry', 'bar' ] ),
|
|
'compute foo+ferry+bar (T152266)'
|
|
);
|
|
}
|
|
|
|
/**
|
|
* 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 = [
|
|
'foo' => self::getSimpleModuleMock( 'foo();' ),
|
|
'ferry' => self::getFailFerryMock(),
|
|
'bar' => self::getSimpleModuleMock( 'bar();' ),
|
|
];
|
|
$rl = new EmptyResourceLoader();
|
|
$rl->register( $modules );
|
|
$context = $this->getResourceLoaderContext(
|
|
[
|
|
'modules' => 'foo|ferry|bar',
|
|
'only' => 'scripts',
|
|
],
|
|
$rl
|
|
);
|
|
|
|
$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( {
|
|
"ferry": "error",
|
|
"foo": "ready",
|
|
"bar": "ready"
|
|
} );',
|
|
$response
|
|
);
|
|
}
|
|
|
|
/**
|
|
* 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();
|
|
$rl->register( [
|
|
'foo' => self::getSimpleModuleMock( 'foo();' ),
|
|
'ferry' => self::getFailFerryMock(),
|
|
'bar' => self::getSimpleModuleMock( 'bar();' ),
|
|
'startup' => [ 'class' => 'ResourceLoaderStartUpModule' ],
|
|
] );
|
|
$context = $this->getResourceLoaderContext(
|
|
[
|
|
'modules' => 'startup',
|
|
'only' => 'scripts',
|
|
],
|
|
$rl
|
|
);
|
|
|
|
$this->assertEquals(
|
|
[ 'foo', 'ferry', 'bar', 'startup' ],
|
|
$rl->getModuleNames(),
|
|
'getModuleNames'
|
|
);
|
|
|
|
$modules = [ 'startup' => $rl->getModule( 'startup' ) ];
|
|
$response = $rl->makeModuleResponse( $context, $modules );
|
|
$errors = $rl->getErrors();
|
|
|
|
$this->assertRegExp( '/Ferry not found/', $errors[0] );
|
|
$this->assertCount( 1, $errors );
|
|
$this->assertRegExp(
|
|
'/isCompatible.*function startUp/s',
|
|
$response,
|
|
'startup response undisrupted (T152266)'
|
|
);
|
|
$this->assertRegExp(
|
|
'/register\([^)]+"ferry",\s*""/s',
|
|
$response,
|
|
'startup response registers broken module'
|
|
);
|
|
$this->assertRegExp(
|
|
'/state\([^)]+"ferry":\s*"error"/s',
|
|
$response,
|
|
'startup response sets state to error'
|
|
);
|
|
}
|
|
}
|