getResourceLoader()->register( self::makeSampleModules() ); $client = new ResourceLoaderClientHtml( $context ); $client->setModules( [ 'test', 'test.private', 'test.shouldembed.empty', 'test.shouldembed', 'test.user', 'test.unregistered', ] ); $client->setModuleStyles( [ 'test.styles.mixed', 'test.styles.user.empty', 'test.styles.private', 'test.styles.pure', 'test.styles.shouldembed', 'test.styles.deprecated', 'test.unregistered.styles', ] ); $expected = [ 'states' => [ // The below are NOT queued for loading via `mw.loader.load(Array)`. // Instead we tell the client to set their state to "loading" so that // if they are needed as dependencies, the client will not try to // load them on-demand, because the server is taking care of them already. // Either: // - Embedded as inline scripts in the HTML (e.g. user-private code, and // previews). Once that script tag is reached, the state is "loaded". // - Loaded directly from the HTML with a dedicated HTTP request (e.g. // user scripts, which vary by a 'user' and 'version' parameter that // the static user-agnostic startup module won't have). 'test.private' => 'loading', 'test.shouldembed' => 'loading', 'test.user' => 'loading', // The below are known to the server to be empty scripts, or to be // synchronously loaded stylesheets. These start in the "ready" state. 'test.shouldembed.empty' => 'ready', 'test.styles.pure' => 'ready', 'test.styles.user.empty' => 'ready', 'test.styles.private' => 'ready', 'test.styles.shouldembed' => 'ready', 'test.styles.deprecated' => 'ready', ], 'general' => [ 'test', ], 'styles' => [ 'test.styles.pure', 'test.styles.deprecated', ], 'embed' => [ 'styles' => [ 'test.styles.private', 'test.styles.shouldembed' ], 'general' => [ 'test.private', 'test.shouldembed', 'test.user', ], ], 'styleDeprecations' => [ Xml::encodeJsCall( 'mw.log.warn', [ 'This page is using the deprecated ResourceLoader module "test.styles.deprecated". Deprecation message.' ] ) ], ]; $access = TestingAccessWrapper::newFromObject( $client ); $this->assertEquals( $expected, $access->getData() ); } public function testGetHeadHtml() { $context = self::makeContext(); $context->getResourceLoader()->register( self::makeSampleModules() ); $client = new ResourceLoaderClientHtml( $context, [ 'nonce' => false, ] ); $client->setConfig( [ 'key' => 'value' ] ); $client->setModules( [ 'test', 'test.private', ] ); $client->setModuleStyles( [ 'test.styles.pure', 'test.styles.private', 'test.styles.deprecated', ] ); $client->setExemptStates( [ 'test.exempt' => 'ready', ] ); // phpcs:disable Generic.Files.LineLength $expected = '' . "\n" . '' . "\n" . '' . "\n" . '' . "\n" . ''; // phpcs:enable $expected = self::expandVariables( $expected ); $this->assertSame( $expected, (string)$client->getHeadHtml() ); } /** * Confirm that 'target' is passed down to the startup module's load url. */ public function testGetHeadHtmlWithTarget() { $client = new ResourceLoaderClientHtml( self::makeContext(), [ 'target' => 'example' ] ); // phpcs:disable Generic.Files.LineLength $expected = '' . "\n" . ''; // phpcs:enable $this->assertSame( $expected, (string)$client->getHeadHtml() ); } /** * Confirm that 'safemode' is passed down to startup. */ public function testGetHeadHtmlWithSafemode() { $client = new ResourceLoaderClientHtml( self::makeContext(), [ 'safemode' => '1' ] ); // phpcs:disable Generic.Files.LineLength $expected = '' . "\n" . ''; // phpcs:enable $this->assertSame( $expected, (string)$client->getHeadHtml() ); } /** * Confirm that a null 'target' is the same as no target. */ public function testGetHeadHtmlWithNullTarget() { $client = new ResourceLoaderClientHtml( self::makeContext(), [ 'target' => null ] ); // phpcs:disable Generic.Files.LineLength $expected = '' . "\n" . ''; // phpcs:enable $this->assertSame( $expected, (string)$client->getHeadHtml() ); } public function testGetBodyHtml() { $context = self::makeContext(); $context->getResourceLoader()->register( self::makeSampleModules() ); $client = new ResourceLoaderClientHtml( $context, [ 'nonce' => false ] ); $client->setConfig( [ 'key' => 'value' ] ); $client->setModules( [ 'test', 'test.private.bottom', ] ); $client->setModuleStyles( [ 'test.styles.deprecated', ] ); // phpcs:disable Generic.Files.LineLength $expected = ''; // phpcs:enable $this->assertSame( $expected, (string)$client->getBodyHtml() ); } public static function provideMakeLoad() { // phpcs:disable Generic.Files.LineLength return [ [ 'context' => [], 'modules' => [ 'test.unknown' ], 'only' => ResourceLoaderModule::TYPE_STYLES, 'extra' => [], 'output' => '', ], [ 'context' => [], 'modules' => [ 'test.styles.private' ], 'only' => ResourceLoaderModule::TYPE_STYLES, 'extra' => [], 'output' => '', ], [ 'context' => [], 'modules' => [ 'test.private' ], 'only' => ResourceLoaderModule::TYPE_COMBINED, 'extra' => [], 'output' => '', ], [ 'context' => [], 'modules' => [ 'test.scripts' ], 'only' => ResourceLoaderModule::TYPE_SCRIPTS, // Eg. startup module 'extra' => [ 'raw' => '1' ], 'output' => '', ], [ 'context' => [], 'modules' => [ 'test.scripts' ], 'only' => ResourceLoaderModule::TYPE_SCRIPTS, 'extra' => [ 'raw' => '1', 'sync' => '1' ], 'output' => '', ], [ 'context' => [], 'modules' => [ 'test.scripts.user' ], 'only' => ResourceLoaderModule::TYPE_SCRIPTS, 'extra' => [], 'output' => '', ], [ 'context' => [], 'modules' => [ 'test.user' ], 'only' => ResourceLoaderModule::TYPE_COMBINED, 'extra' => [], 'output' => '', ], [ 'context' => [ 'debug' => 'true' ], 'modules' => [ 'test.styles.pure', 'test.styles.mixed' ], 'only' => ResourceLoaderModule::TYPE_STYLES, 'extra' => [], 'output' => '' . "\n" . '', ], [ 'context' => [ 'debug' => 'false' ], 'modules' => [ 'test.styles.pure', 'test.styles.mixed' ], 'only' => ResourceLoaderModule::TYPE_STYLES, 'extra' => [], 'output' => '', ], [ 'context' => [], 'modules' => [ 'test.styles.noscript' ], 'only' => ResourceLoaderModule::TYPE_STYLES, 'extra' => [], 'output' => '', ], [ 'context' => [], 'modules' => [ 'test.shouldembed' ], 'only' => ResourceLoaderModule::TYPE_COMBINED, 'extra' => [], 'output' => '', ], [ 'context' => [], 'modules' => [ 'test.styles.shouldembed' ], 'only' => ResourceLoaderModule::TYPE_STYLES, 'extra' => [], 'output' => '', ], [ 'context' => [], 'modules' => [ 'test.scripts.shouldembed' ], 'only' => ResourceLoaderModule::TYPE_SCRIPTS, 'extra' => [], 'output' => '', ], [ 'context' => [], 'modules' => [ 'test', 'test.shouldembed' ], 'only' => ResourceLoaderModule::TYPE_COMBINED, 'extra' => [], 'output' => '', ], [ 'context' => [], 'modules' => [ 'test.styles.pure', 'test.styles.shouldembed' ], 'only' => ResourceLoaderModule::TYPE_STYLES, 'extra' => [], 'output' => '' . "\n" . '' ], [ 'context' => [], 'modules' => [ 'test.ordering.a', 'test.ordering.e', 'test.ordering.b', 'test.ordering.d', 'test.ordering.c' ], 'only' => ResourceLoaderModule::TYPE_STYLES, 'extra' => [], 'output' => '' . "\n" . '' . "\n" . '' ], ]; // phpcs:enable } /** * @dataProvider provideMakeLoad * @covers ResourceLoaderClientHtml * @covers ResourceLoaderModule::getModuleContent * @covers ResourceLoader */ public function testMakeLoad( array $contextQuery, array $modules, $type, array $extraQuery, $expected ) { $context = self::makeContext( $contextQuery ); $context->getResourceLoader()->register( self::makeSampleModules() ); $actual = ResourceLoaderClientHtml::makeLoad( $context, $modules, $type, $extraQuery, false ); $expected = self::expandVariables( $expected ); $this->assertSame( $expected, (string)$actual ); } public function testGetDocumentAttributes() { $client = new ResourceLoaderClientHtml( self::makeContext() ); $this->assertInternalType( 'array', $client->getDocumentAttributes() ); } private static function expandVariables( $text ) { return strtr( $text, [ '{blankVer}' => ResourceLoaderTestCase::BLANK_VERSION ] ); } private static function makeContext( $extraQuery = [] ) { $conf = new HashConfig( [ 'ResourceModuleSkinStyles' => [], 'ResourceModules' => [], 'EnableJavaScriptTest' => false, 'LoadScript' => '/w/load.php', ] ); return new ResourceLoaderContext( new ResourceLoader( $conf ), new FauxRequest( array_merge( [ 'lang' => 'nl', 'skin' => 'fallback', 'user' => 'Example', 'target' => 'phpunit', ], $extraQuery ) ) ); } private static function makeModule( array $options = [] ) { return new ResourceLoaderTestModule( $options ); } private static function makeSampleModules() { $modules = [ 'test' => [], 'test.private' => [ 'group' => 'private' ], 'test.shouldembed.empty' => [ 'shouldEmbed' => true, 'isKnownEmpty' => true ], 'test.shouldembed' => [ 'shouldEmbed' => true ], 'test.user' => [ 'group' => 'user' ], 'test.styles.pure' => [ 'type' => ResourceLoaderModule::LOAD_STYLES ], 'test.styles.mixed' => [], 'test.styles.noscript' => [ 'type' => ResourceLoaderModule::LOAD_STYLES, 'group' => 'noscript', ], 'test.styles.user' => [ 'type' => ResourceLoaderModule::LOAD_STYLES, 'group' => 'user', ], 'test.styles.user.empty' => [ 'type' => ResourceLoaderModule::LOAD_STYLES, 'group' => 'user', 'isKnownEmpty' => true, ], 'test.styles.private' => [ 'type' => ResourceLoaderModule::LOAD_STYLES, 'group' => 'private', 'styles' => '.private{}', ], 'test.styles.shouldembed' => [ 'type' => ResourceLoaderModule::LOAD_STYLES, 'shouldEmbed' => true, 'styles' => '.shouldembed{}', ], 'test.styles.deprecated' => [ 'type' => ResourceLoaderModule::LOAD_STYLES, 'deprecated' => 'Deprecation message.', ], 'test.scripts' => [], 'test.scripts.user' => [ 'group' => 'user' ], 'test.scripts.user.empty' => [ 'group' => 'user', 'isKnownEmpty' => true ], 'test.scripts.shouldembed' => [ 'shouldEmbed' => true ], 'test.ordering.a' => [ 'shouldEmbed' => false ], 'test.ordering.b' => [ 'shouldEmbed' => false ], 'test.ordering.c' => [ 'shouldEmbed' => true, 'styles' => '.orderingC{}' ], 'test.ordering.d' => [ 'shouldEmbed' => true, 'styles' => '.orderingD{}' ], 'test.ordering.e' => [ 'shouldEmbed' => false ], ]; return array_map( function ( $options ) { return self::makeModule( $options ); }, $modules ); } }