diff --git a/typo3/sysext/core/Classes/Page/AssetCollector.php b/typo3/sysext/core/Classes/Page/AssetCollector.php
index 6886d75316c0a875d8129f868b3d39258eb27841..e108477df5c13cc77def853bf103667a8e4f6774 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 a34927c7d7d96bf77d0bda740ca1f733d5969f53..2ed399cb867c05b599b2be08d521b578db25d16a 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 0000000000000000000000000000000000000000..1990e146ed21aa298bcd72d864e4d6d1f54da86c
--- /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 0000000000000000000000000000000000000000..8d5c461c6a9fde11db22f36ddc82c8c9d3ec1468
--- /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 0000000000000000000000000000000000000000..6a56e14295bc55ea6b114ec6245946f7c4484602
--- /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 850feacf6dda22b3d94b7b4424601463c51f01d0..4a5291ca131b39b118ba8ff1cf9eeba063e07d9b 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 950dc6ce4d5e1725cd8cedf6a6c8aa860cfb3414..7fb74c8e4e2ff0d284b72a2b3952cdaec3d696e5 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 0000000000000000000000000000000000000000..b27cbae324ef64bbdb3c9efaf5c68a4cbb59f81a
--- /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 5e57a45409c1f3d197eef71960883656b70dd852..8b85a64c196ae1a7ebcb88dd83d6204bcb835d7d 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 5652bd5d7f4cc1b564ffb7d672b129a79012840e..eabde26acccf1336bb6b61fe777b9a8dc12c5d41 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 7585efed441f753b772cd822131f2383421a04c4..ab6ddec86ac81836fdfca6dbfdc30210bbd09c64 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' => [