diff --git a/src/AbstractComponent.php b/src/AbstractComponent.php index e3ae555..8e9dd2e 100644 --- a/src/AbstractComponent.php +++ b/src/AbstractComponent.php @@ -26,6 +26,7 @@ namespace MediaWiki\Extension\BootstrapComponents; +use MediaWiki\MediaWikiServices; use \MWException; /** @@ -153,6 +154,9 @@ public function parseComponent( $parserRequest ) { throw new MWException( 'Invalid ParserRequest supplied to component ' . $this->getComponentName() . '!' ); } $this->getNestingController()->open( $this ); + MediaWikiServices::getInstance() + ->getService( 'BootstrapComponentsService' ) + ->registerComponentAsActive( $this->getComponentName() ); $this->initComponentData( $parserRequest ); $input = $this->prepareInput( $parserRequest ); diff --git a/src/HooksHandler.php b/src/HooksHandler.php index fa01e95..7ff5bac 100644 --- a/src/HooksHandler.php +++ b/src/HooksHandler.php @@ -228,6 +228,7 @@ public function onParserAfterParse( $parser, &$text, $stripState ): bool { } foreach ( $this->getComponentLibrary()->getModulesFor( $activeComponent ) as $module ) { $parser->getOutput()->addModuleStyles( [ $module ] ); + $parser->getOutput()->addModules( [ $module ] ); } } return true; diff --git a/tests/phpunit/Unit/HooksHandlerTest.php b/tests/phpunit/Unit/HooksHandlerTest.php index 827d40d..3d9867e 100644 --- a/tests/phpunit/Unit/HooksHandlerTest.php +++ b/tests/phpunit/Unit/HooksHandlerTest.php @@ -6,6 +6,8 @@ use MediaWiki\Extension\BootstrapComponents\ComponentLibrary; use MediaWiki\Extension\BootstrapComponents\HooksHandler; use MediaWiki\Extension\BootstrapComponents\NestingController; +use Parser; +use ParserOutput; use PHPUnit\Framework\TestCase; /** @@ -72,4 +74,50 @@ public function testOnGalleryGetModes() { public function testOnOutputPageParserOutput() { $this->assertTrue( true ); } + + public function testOnParserAfterParseLoadsActiveComponentScriptsAndStyles() { + $loadedStyles = []; + $loadedModules = []; + + $parserOutput = $this->createMock( ParserOutput::class ); + $parserOutput->method( 'addModuleStyles' ) + ->willReturnCallback( function ( $m ) use ( &$loadedStyles ) { + $loadedStyles = array_merge( $loadedStyles, (array)$m ); + } ); + $parserOutput->method( 'addModules' ) + ->willReturnCallback( function ( $m ) use ( &$loadedModules ) { + $loadedModules = array_merge( $loadedModules, (array)$m ); + } ); + + $parser = $this->createMock( Parser::class ); + $parser->method( 'getOutput' )->willReturn( $parserOutput ); + + $service = $this->createMock( BootstrapComponentsService::class ); + $service->method( 'getNameOfActiveSkin' )->willReturn( 'vector' ); + $service->method( 'getActiveComponents' )->willReturn( [ 'tooltip', 'popover' ] ); + + $library = $this->createMock( ComponentLibrary::class ); + $library->method( 'isRegistered' )->willReturn( true ); + $library->method( 'getModulesFor' ) + ->willReturnCallback( function ( $name ) { + return [ 'ext.bootstrapComponents.' . $name . '.fix' ]; + } ); + + $handler = new HooksHandler( + $service, + $library, + $this->createMock( NestingController::class ) + ); + + $text = ''; + $handler->onParserAfterParse( $parser, $text, null ); + + // Per-component fix modules must reach BOTH addModuleStyles and addModules + // (style-only modules treat addModules as a no-op; modules with a scripts + // entry get their JS init via that call). + $this->assertContains( 'ext.bootstrapComponents.tooltip.fix', $loadedStyles ); + $this->assertContains( 'ext.bootstrapComponents.popover.fix', $loadedStyles ); + $this->assertContains( 'ext.bootstrapComponents.tooltip.fix', $loadedModules ); + $this->assertContains( 'ext.bootstrapComponents.popover.fix', $loadedModules ); + } }