From 067d81a4b88f8aa057e5fc9373b7f49d5da470da Mon Sep 17 00:00:00 2001 From: Jonas Eberle <flightvision@googlemail.com> Date: Tue, 31 Mar 2020 00:16:07 +0200 Subject: [PATCH] [FEATURE] Introduce AssetRenderer BeforeRendering events AssetRenderer gets events for manipulating AssetCollector assets. This enables asset post-processing extensions like EXT:min or EXT:ws_scss to fully support AssetCollector assets. The AssetCollector::add*() methods get an optional parameter that allows filtering for (non-)priority assets. It became apararent during testing that any listener would otherwise have to implement that itself in order to avoid double-processing assets (as there are two rendering passes, priority and non-priority). Resolves: #90899 Releases: master Change-Id: I2526328d3ee4ab269fa654347abd6156ee516a84 Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/64021 Tested-by: TYPO3com <noreply@typo3.com> Tested-by: Benni Mack <benni@typo3.org> Tested-by: Georg Ringer <georg.ringer@gmail.com> Reviewed-by: Benni Mack <benni@typo3.org> Reviewed-by: Georg Ringer <georg.ringer@gmail.com> --- .../core/Classes/Page/AssetCollector.php | 35 ++++- .../core/Classes/Page/AssetRenderer.php | 50 +++++-- .../AbstractBeforeAssetRenderingEvent.php | 52 +++++++ .../Event/BeforeJavaScriptsRenderingEvent.php | 32 ++++ .../Event/BeforeStylesheetsRenderingEvent.php | 32 ++++ typo3/sysext/core/Configuration/Services.yaml | 5 + .../Feature-90522-IntroduceAssetCollector.rst | 17 ++- ...90899-IntroduceAssetPreRenderingEvents.rst | 139 ++++++++++++++++++ .../Tests/Unit/Page/AssetDataProvider.php | 21 +++ .../Tests/Unit/Page/AssetRendererTest.php | 41 +++++- .../TypoScriptFrontendControllerTest.php | 15 ++ 11 files changed, 411 insertions(+), 28 deletions(-) create mode 100644 typo3/sysext/core/Classes/Page/Event/AbstractBeforeAssetRenderingEvent.php create mode 100644 typo3/sysext/core/Classes/Page/Event/BeforeJavaScriptsRenderingEvent.php create mode 100644 typo3/sysext/core/Classes/Page/Event/BeforeStylesheetsRenderingEvent.php create mode 100644 typo3/sysext/core/Documentation/Changelog/master/Feature-90899-IntroduceAssetPreRenderingEvents.rst diff --git a/typo3/sysext/core/Classes/Page/AssetCollector.php b/typo3/sysext/core/Classes/Page/AssetCollector.php index 6886d75316c0..e108477df5c1 100644 --- a/typo3/sysext/core/Classes/Page/AssetCollector.php +++ b/typo3/sysext/core/Classes/Page/AssetCollector.php @@ -204,23 +204,42 @@ class AssetCollector implements SingletonInterface return $this->media; } - public function getJavaScripts(): array + public function getJavaScripts(?bool $priority = null): array { - return $this->javaScripts; + return $this->filterAssetsPriority($this->javaScripts, $priority); } - public function getInlineJavaScripts(): array + public function getInlineJavaScripts(?bool $priority = null): array { - return $this->inlineJavaScripts; + return $this->filterAssetsPriority($this->inlineJavaScripts, $priority); } - public function getStyleSheets(): array + public function getStyleSheets(?bool $priority = null): array { - return $this->styleSheets; + return $this->filterAssetsPriority($this->styleSheets, $priority); } - public function getInlineStyleSheets(): array + public function getInlineStyleSheets(?bool $priority = null): array { - return $this->inlineStyleSheets; + return $this->filterAssetsPriority($this->inlineStyleSheets, $priority); + } + + /** + * @param array $assets Takes a reference to prevent a deep copy. The variable is not changed (const). + * @param bool|null $priority null: no filter; else filters for the given priority + * @return array + */ + protected function filterAssetsPriority(array &$assets, ?bool $priority): array + { + if ($priority === null) { + return $assets; + } + $currentPriorityAssets = []; + foreach ($assets as $identifier => $asset) { + if ($priority === ($asset['options']['priority'] ?? false)) { + $currentPriorityAssets[$identifier] = $asset; + } + } + return $currentPriorityAssets; } } diff --git a/typo3/sysext/core/Classes/Page/AssetRenderer.php b/typo3/sysext/core/Classes/Page/AssetRenderer.php index a34927c7d7d9..2ed399cb867c 100644 --- a/typo3/sysext/core/Classes/Page/AssetRenderer.php +++ b/typo3/sysext/core/Classes/Page/AssetRenderer.php @@ -16,11 +16,14 @@ namespace TYPO3\CMS\Core\Page; * The TYPO3 project - inspiring people to share! */ +use Psr\EventDispatcher\EventDispatcherInterface; +use TYPO3\CMS\Core\EventDispatcher\EventDispatcher; +use TYPO3\CMS\Core\Page\Event\BeforeJavaScriptsRenderingEvent; +use TYPO3\CMS\Core\Page\Event\BeforeStylesheetsRenderingEvent; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Core\Utility\PathUtility; /** - * Class AssetRenderer * @internal The AssetRenderer is used for the asset rendering and is not public API */ class AssetRenderer @@ -30,54 +33,73 @@ class AssetRenderer */ protected $assetCollector; - public function __construct(AssetCollector $assetCollector = null) + /** + * @var EventDispatcherInterface + */ + protected $eventDispatcher; + + public function __construct(AssetCollector $assetCollector = null, EventDispatcherInterface $eventDispatcher = null) { $this->assetCollector = $assetCollector ?? GeneralUtility::makeInstance(AssetCollector::class); + $this->eventDispatcher = $eventDispatcher ?? GeneralUtility::makeInstance(EventDispatcher::class); } public function renderInlineJavaScript($priority = false): string { + $this->eventDispatcher->dispatch( + new BeforeJavaScriptsRenderingEvent($this->assetCollector, true, $priority) + ); + $template = '<script%attributes%>%source%</script>'; - $assets = $this->assetCollector->getInlineJavaScripts(); - return $this->render($assets, $template, $priority); + $assets = $this->assetCollector->getInlineJavaScripts($priority); + return $this->render($assets, $template); } public function renderJavaScript($priority = false): string { + $this->eventDispatcher->dispatch( + new BeforeJavaScriptsRenderingEvent($this->assetCollector, false, $priority) + ); + $template = '<script%attributes%></script>'; - $assets = $this->assetCollector->getJavaScripts(); + $assets = $this->assetCollector->getJavaScripts($priority); foreach ($assets as &$assetData) { $assetData['attributes']['src'] = $this->getAbsoluteWebPath($assetData['source']); } - return $this->render($assets, $template, $priority); + return $this->render($assets, $template); } public function renderInlineStyleSheets($priority = false): string { + $this->eventDispatcher->dispatch( + new BeforeStylesheetsRenderingEvent($this->assetCollector, true, $priority) + ); + $template = '<style%attributes%>%source%</style>'; - $assets = $this->assetCollector->getInlineStyleSheets(); - return $this->render($assets, $template, $priority); + $assets = $this->assetCollector->getInlineStyleSheets($priority); + return $this->render($assets, $template); } public function renderStyleSheets(bool $priority = false, string $endingSlash = ''): string { + $this->eventDispatcher->dispatch( + new BeforeStylesheetsRenderingEvent($this->assetCollector, false, $priority) + ); + $template = '<link%attributes% ' . $endingSlash . '>'; - $assets = $this->assetCollector->getStyleSheets(); + $assets = $this->assetCollector->getStyleSheets($priority); foreach ($assets as &$assetData) { $assetData['attributes']['href'] = $this->getAbsoluteWebPath($assetData['source']); $assetData['attributes']['rel'] = $assetData['attributes']['rel'] ?? 'stylesheet'; $assetData['attributes']['type'] = $assetData['attributes']['type'] ?? 'text/css'; } - return $this->render($assets, $template, $priority); + return $this->render($assets, $template); } - protected function render(array $assets, string $template, bool $priority = false): string + protected function render(array $assets, string $template): string { $results = []; foreach ($assets as $assetData) { - if (($assetData['options']['priority'] ?? false) !== $priority) { - continue; - } $attributes = $assetData['attributes']; $attributesString = count($attributes) ? ' ' . GeneralUtility::implodeAttributes($attributes, true) : ''; $results[] = str_replace( diff --git a/typo3/sysext/core/Classes/Page/Event/AbstractBeforeAssetRenderingEvent.php b/typo3/sysext/core/Classes/Page/Event/AbstractBeforeAssetRenderingEvent.php new file mode 100644 index 000000000000..1990e146ed21 --- /dev/null +++ b/typo3/sysext/core/Classes/Page/Event/AbstractBeforeAssetRenderingEvent.php @@ -0,0 +1,52 @@ +<?php +declare(strict_types = 1); + +namespace TYPO3\CMS\Core\Page\Event; + +/* + * This file is part of the TYPO3 CMS project. + * + * It is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, either version 2 + * of the License, or any later version. + * + * For the full copyright and license information, please read the + * LICENSE.txt file that was distributed with this source code. + * + * The TYPO3 project - inspiring people to share! + */ + +use TYPO3\CMS\Core\Page\AssetCollector; + +abstract class AbstractBeforeAssetRenderingEvent +{ + /** + * @var AssetCollector + */ + protected $assetCollector; + + /** + * @var bool + */ + protected $inline; + + /** + * @var bool + */ + protected $priority; + + public function getAssetCollector(): AssetCollector + { + return $this->assetCollector; + } + + public function isInline(): bool + { + return $this->inline; + } + + public function isPriority(): bool + { + return $this->priority; + } +} diff --git a/typo3/sysext/core/Classes/Page/Event/BeforeJavaScriptsRenderingEvent.php b/typo3/sysext/core/Classes/Page/Event/BeforeJavaScriptsRenderingEvent.php new file mode 100644 index 000000000000..8d5c461c6a9f --- /dev/null +++ b/typo3/sysext/core/Classes/Page/Event/BeforeJavaScriptsRenderingEvent.php @@ -0,0 +1,32 @@ +<?php +declare(strict_types = 1); + +namespace TYPO3\CMS\Core\Page\Event; + +/* + * This file is part of the TYPO3 CMS project. + * + * It is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, either version 2 + * of the License, or any later version. + * + * For the full copyright and license information, please read the + * LICENSE.txt file that was distributed with this source code. + * + * The TYPO3 project - inspiring people to share! + */ + +use TYPO3\CMS\Core\Page\AssetCollector; + +/** + * This event is fired once before \TYPO3\CMS\Core\Page\AssetRenderer::render[Inline]JavaScript renders the output. + */ +final class BeforeJavaScriptsRenderingEvent extends AbstractBeforeAssetRenderingEvent +{ + public function __construct(AssetCollector $assetCollector, bool $isInline, bool $priority) + { + $this->assetCollector = $assetCollector; + $this->inline = $isInline; + $this->priority = $priority; + } +} diff --git a/typo3/sysext/core/Classes/Page/Event/BeforeStylesheetsRenderingEvent.php b/typo3/sysext/core/Classes/Page/Event/BeforeStylesheetsRenderingEvent.php new file mode 100644 index 000000000000..6a56e14295bc --- /dev/null +++ b/typo3/sysext/core/Classes/Page/Event/BeforeStylesheetsRenderingEvent.php @@ -0,0 +1,32 @@ +<?php +declare(strict_types = 1); + +namespace TYPO3\CMS\Core\Page\Event; + +/* + * This file is part of the TYPO3 CMS project. + * + * It is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, either version 2 + * of the License, or any later version. + * + * For the full copyright and license information, please read the + * LICENSE.txt file that was distributed with this source code. + * + * The TYPO3 project - inspiring people to share! + */ + +use TYPO3\CMS\Core\Page\AssetCollector; + +/** + * This event is fired once before \TYPO3\CMS\Core\Page\AssetRenderer::render[Inline]Stylesheets renders the output. + */ +final class BeforeStylesheetsRenderingEvent extends AbstractBeforeAssetRenderingEvent +{ + public function __construct(AssetCollector $assetCollector, bool $isInline, bool $priority) + { + $this->assetCollector = $assetCollector; + $this->inline = $isInline; + $this->priority = $priority; + } +} diff --git a/typo3/sysext/core/Configuration/Services.yaml b/typo3/sysext/core/Configuration/Services.yaml index 850feacf6dda..4a5291ca131b 100644 --- a/typo3/sysext/core/Configuration/Services.yaml +++ b/typo3/sysext/core/Configuration/Services.yaml @@ -329,6 +329,11 @@ services: method: 'addCategoryDatabaseSchema' event: TYPO3\CMS\Core\Database\Event\AlterTableDefinitionStatementsEvent + TYPO3\CMS\Core\Page\AssetRenderer: + public: true + arguments: + $eventDispatcher: '@Psr\EventDispatcher\EventDispatcherInterface' + # clean up files TYPO3\CMS\Core\Resource\Processing\FileDeletionAspect: tags: diff --git a/typo3/sysext/core/Documentation/Changelog/10.3/Feature-90522-IntroduceAssetCollector.rst b/typo3/sysext/core/Documentation/Changelog/10.3/Feature-90522-IntroduceAssetCollector.rst index 950dc6ce4d5e..7fb74c8e4e2f 100644 --- a/typo3/sysext/core/Documentation/Changelog/10.3/Feature-90522-IntroduceAssetCollector.rst +++ b/typo3/sysext/core/Documentation/Changelog/10.3/Feature-90522-IntroduceAssetCollector.rst @@ -1,5 +1,7 @@ .. include:: ../../Includes.txt +.. _changelog-Feature-90522-IntroduceAssetCollector: + ========================================== Feature: #90522 - Introduce AssetCollector ========================================== @@ -41,10 +43,10 @@ The new API - :php:`\TYPO3\CMS\Core\Page\AssetCollector::removeStyleSheet(string $identifier): self` - :php:`\TYPO3\CMS\Core\Page\AssetCollector::removeInlineStyleSheet(string $identifier): self` - :php:`\TYPO3\CMS\Core\Page\AssetCollector::removeMedia(string $identifier): self` -- :php:`\TYPO3\CMS\Core\Page\AssetCollector::getJavaScripts(): array` -- :php:`\TYPO3\CMS\Core\Page\AssetCollector::getInlineJavaScripts(): array` -- :php:`\TYPO3\CMS\Core\Page\AssetCollector::getStyleSheets(): array` -- :php:`\TYPO3\CMS\Core\Page\AssetCollector::getInlineStyleSheets(): array` +- :php:`\TYPO3\CMS\Core\Page\AssetCollector::getJavaScripts(?bool $priority = null): array` +- :php:`\TYPO3\CMS\Core\Page\AssetCollector::getInlineJavaScripts(?bool $priority = null): array` +- :php:`\TYPO3\CMS\Core\Page\AssetCollector::getStyleSheets(?bool $priority = null): array` +- :php:`\TYPO3\CMS\Core\Page\AssetCollector::getInlineStyleSheets(?bool $priority = null): array` - :php:`\TYPO3\CMS\Core\Page\AssetCollector::getMedia(): array` New ViewHelpers @@ -74,7 +76,12 @@ Currently, assets registered with the AssetCollector are not included in callbac - :php:`$GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['cssConcatenateHandler']` - :php:`$GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['jsConcatenateHandler']` -Currently, CSS and JavaScript registered with the AssetCollector will always be rendered after their +.. versionadded:: 10.4 + + Events for the new API have been introduced in + :ref:`changelog-Feature-90899-IntroduceAssetPreRenderingEvents` + +Currently, CSS and JavaScript registered with the AssetCollector will be rendered after their PageRenderer counterparts. The order is: - :html:`<head>` diff --git a/typo3/sysext/core/Documentation/Changelog/master/Feature-90899-IntroduceAssetPreRenderingEvents.rst b/typo3/sysext/core/Documentation/Changelog/master/Feature-90899-IntroduceAssetPreRenderingEvents.rst new file mode 100644 index 000000000000..b27cbae324ef --- /dev/null +++ b/typo3/sysext/core/Documentation/Changelog/master/Feature-90899-IntroduceAssetPreRenderingEvents.rst @@ -0,0 +1,139 @@ +.. include:: ../../Includes.txt + +.. _changelog-Feature-90899-IntroduceAssetPreRenderingEvents: + +============================================================== +Feature: #90899 - Introduce AssetRenderer pre-rendering events +============================================================== + +See :issue:`90899` + +Description +=========== + +AssetRenderer is amended by two events which allow post-processing of +AssetCollector assets. + +These new PSR-14 events are introduced: + +.. code-block:: php + + \TYPO3\CMS\Core\Page\Event\BeforeJavaScriptsRenderingEvent + \TYPO3\CMS\Core\Page\Event\BeforeStylesheetsRenderingEvent + +Both stem fom the abstract base class +`\TYPO3\CMS\Core\Page\Event\AbstractBeforeAssetRenderingEvent` and provide +these public methods: + +.. code-block:: php + + getAssetCollector(): AssetCollector + isInline(): bool + isPriority(): bool + +:php:`inline` and :php:`priority` refer to how the asset was registered with +:ref:`AssetCollector <changelog-Feature-90522-IntroduceAssetCollector>`. + +The events are fired exactly once for every combination of +:php:`inline`/:php:`priority` before the corresponding section of JS/CSS assets +are rendered by the AssetRenderer. + +To make the events easier to use, the :php:`AssetCollector::get*()` methods +have gotten an optional parameter `?bool $priority = null` which when given a +boolean only returns assets of the given priority. + + +.. note:: + Note: post-processing functionality for assets registered via + TypoScript :ts:`page.include...` or the :php:`PageRenderer::add*()` + functions are still provided by these hooks: + + .. code-block:: php + + $GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['cssCompressHandler'] + $GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['jsCompressHandler'] + $GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['cssConcatenateHandler'] + $GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['jsConcatenateHandler'] + $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS'] + ['t3lib/class.t3lib_pagerenderer.php']['render-preProcess'] + + Assets registered with the AssetCollector (and output through the + AssetRenderer) are not included in those. + + +Example +======= + +As an example let's make sure jQuery is included in a specific version and +from a CDN. + +.. rst-class:: bignums + +1. Register our listeners + + :file:`Configuration/Services.yaml` + + .. code-block:: yaml + + services: + MyVendor\MyExt\EventListener\AssetRenderer\LibraryVersion: + tags: + - name: event.listener + identifier: 'myExt/LibraryVersion' + event: TYPO3\CMS\Core\Page\Event\AssetRendererBeforeRenderingEvent + + +2. Implement Listener to enforce a library version or CDN URI + + .. code-block:: php + + namespace MyVendor\MyExt\EventListener\AssetRenderer; + + use TYPO3\CMS\Core\Page\Event\BeforeJavaScriptsRenderingEvent; + + /** + * If a library has been registered, it is made sure that it is loaded + * from the given URI + */ + class LibraryVersion + { + protected $libraries = [ + 'jquery' => 'https://code.jquery.com/jquery-3.4.1.min.js', + ]; + + public function __invoke(BeforeJavaScriptsRenderingEvent $event): void + { + if ($event->isInline()) { + return; + } + + foreach ($this->libraries as $library => $source) { + $asset = $event->getAssetCollector()->getJavaScripts($event->isPriority()) + // if it was already registered + if ($asset[$library] ?? false) { + // we set our authoritative version + $event->getAssetCollector()->addJavaScript($library, $source); + } + } + } + } + + + + +Impact +====== + +Existing installations are not affected. + +If using the AssetCollector API, these new events should be used for asset +postprocessing. + +Related +======= + +- :ref:`changelog-Feature-90522-IntroduceAssetCollector` + +.. _examples: https://typo3.org + +.. index:: PHP-API, ext:core diff --git a/typo3/sysext/core/Tests/Unit/Page/AssetDataProvider.php b/typo3/sysext/core/Tests/Unit/Page/AssetDataProvider.php index 5e57a45409c1..8b85a64c196a 100644 --- a/typo3/sysext/core/Tests/Unit/Page/AssetDataProvider.php +++ b/typo3/sysext/core/Tests/Unit/Page/AssetDataProvider.php @@ -4,6 +4,9 @@ declare(strict_types=1); namespace TYPO3\CMS\Core\Tests\Unit\Page; +use TYPO3\CMS\Core\Page\Event\BeforeJavaScriptsRenderingEvent; +use TYPO3\CMS\Core\Page\Event\BeforeStylesheetsRenderingEvent; + class AssetDataProvider { public static function filesDataProvider(): array @@ -439,4 +442,22 @@ class AssetDataProvider ], ]; } + + /** + * cross-product of all combinations of AssetRenderer::render*() methods and priorities + * @return array[] [render method name, isInline, isPriority, event class] + */ + public static function renderMethodsAndEventsDataProvider(): array + { + return [ + ['renderInlineJavaScript', true, true, BeforeJavaScriptsRenderingEvent::class], + ['renderInlineJavaScript', true, false, BeforeJavaScriptsRenderingEvent::class], + ['renderJavaScript', false, true, BeforeJavaScriptsRenderingEvent::class], + ['renderJavaScript', false, false, BeforeJavaScriptsRenderingEvent::class], + ['renderInlineStylesheets', true, true, BeforeStylesheetsRenderingEvent::class], + ['renderInlineStylesheets', true, false, BeforeStylesheetsRenderingEvent::class], + ['renderStylesheets', false, true, BeforeStylesheetsRenderingEvent::class], + ['renderStylesheets', false, false, BeforeStylesheetsRenderingEvent::class], + ]; + } } diff --git a/typo3/sysext/core/Tests/Unit/Page/AssetRendererTest.php b/typo3/sysext/core/Tests/Unit/Page/AssetRendererTest.php index 5652bd5d7f4c..eabde26acccf 100644 --- a/typo3/sysext/core/Tests/Unit/Page/AssetRendererTest.php +++ b/typo3/sysext/core/Tests/Unit/Page/AssetRendererTest.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace TYPO3\CMS\Core\Tests\Unit\Page; +use Psr\EventDispatcher\EventDispatcherInterface; use TYPO3\CMS\Core\Page\AssetCollector; use TYPO3\CMS\Core\Page\AssetRenderer; use TYPO3\CMS\Core\Utility\GeneralUtility; @@ -16,11 +17,21 @@ class AssetRendererTest extends UnitTestCase */ protected $assetRenderer; + /** + * @var EventDispatcherInterface + */ + protected $eventDispatcher; + public function setUp(): void { parent::setUp(); $this->resetSingletonInstances = true; - $this->assetRenderer = GeneralUtility::makeInstance(AssetRenderer::class); + $this->eventDispatcher = $this->createMock(EventDispatcherInterface::class); + $this->assetRenderer = GeneralUtility::makeInstance( + AssetRenderer::class, + null, + $this->eventDispatcher + ); } /** @@ -90,4 +101,32 @@ class AssetRendererTest extends UnitTestCase self::assertSame($expectedMarkup['css_no_prio'], $this->assetRenderer->renderInlineStyleSheets()); self::assertSame($expectedMarkup['css_prio'], $this->assetRenderer->renderInlineStyleSheets(true)); } + + /** + * @param string $renderMethodName + * @param bool $isInline + * @param bool $priority + * @param string $eventClassName + * @dataProvider \TYPO3\CMS\Core\Tests\Unit\Page\AssetDataProvider::renderMethodsAndEventsDataProvider + */ + public function testBeforeRenderingEvent( + string $renderMethodName, + bool $isInline, + bool $priority, + string $eventClassName + ): void { + $assetCollector = GeneralUtility::makeInstance(AssetCollector::class); + $event = new $eventClassName( + $assetCollector, + $isInline, + $priority + ); + + $this->eventDispatcher + ->expects(self::once()) + ->method('dispatch') + ->with($event); + + $this->assetRenderer->$renderMethodName($priority); + } } diff --git a/typo3/sysext/frontend/Tests/Unit/Controller/TypoScriptFrontendControllerTest.php b/typo3/sysext/frontend/Tests/Unit/Controller/TypoScriptFrontendControllerTest.php index 7585efed441f..ab6ddec86ac8 100644 --- a/typo3/sysext/frontend/Tests/Unit/Controller/TypoScriptFrontendControllerTest.php +++ b/typo3/sysext/frontend/Tests/Unit/Controller/TypoScriptFrontendControllerTest.php @@ -16,10 +16,13 @@ namespace TYPO3\CMS\Frontend\Tests\Unit\Controller; * The TYPO3 project - inspiring people to share! */ +use Psr\Container\ContainerInterface; use TYPO3\CMS\Core\Cache\Backend\NullBackend; use TYPO3\CMS\Core\Cache\CacheManager; use TYPO3\CMS\Core\Context\Context; use TYPO3\CMS\Core\Domain\Repository\PageRepository; +use TYPO3\CMS\Core\EventDispatcher\EventDispatcher; +use TYPO3\CMS\Core\EventDispatcher\ListenerProvider; use TYPO3\CMS\Core\Http\ServerRequest; use TYPO3\CMS\Core\Http\ServerRequestFactory; use TYPO3\CMS\Core\Page\PageRenderer; @@ -102,6 +105,18 @@ class TypoScriptFrontendControllerTest extends UnitTestCase ])->disableOriginalConstructor() ->getMock(); $tsfe->expects(self::exactly(2))->method('processNonCacheableContentPartsAndSubstituteContentMarkers')->willReturnCallback([$this, 'processNonCacheableContentPartsAndSubstituteContentMarkers']); + + /** + * prepare an EventDispatcher for ::makeInstance(AssetRenderer) + * @see \TYPO3\CMS\Core\Page\PageRenderer::renderJavaScriptAndCss + */ + GeneralUtility::setSingletonInstance( + EventDispatcher::class, + new EventDispatcher( + new ListenerProvider($this->createMock(ContainerInterface::class)) + ) + ); + $tsfe->content = file_get_contents(__DIR__ . '/Fixtures/renderedPage.html'); $config = [ 'INTincScript_ext' => [ -- GitLab