diff --git a/core/modules/layout_builder/layout_builder.services.yml b/core/modules/layout_builder/layout_builder.services.yml index 778a6d5fa60ce8ef3ec92e025bf5d1ae7d19e39c..a648eef6b1b867c2866e19ad2b272588c376b2d1 100644 --- a/core/modules/layout_builder/layout_builder.services.yml +++ b/core/modules/layout_builder/layout_builder.services.yml @@ -31,3 +31,8 @@ services: layout_builder.sample_entity_generator: class: Drupal\layout_builder\Entity\LayoutBuilderSampleEntityGenerator arguments: ['@tempstore.shared', '@entity_type.manager'] + layout_builder.render_block_component_subscriber: + class: Drupal\layout_builder\EventSubscriber\BlockComponentRenderArray + arguments: ['@current_user'] + tags: + - { name: event_subscriber } diff --git a/core/modules/layout_builder/src/Event/SectionComponentBuildRenderArrayEvent.php b/core/modules/layout_builder/src/Event/SectionComponentBuildRenderArrayEvent.php new file mode 100644 index 0000000000000000000000000000000000000000..9047e6e9dc52efc51f06ef8d03f92e9cb3d09ee2 --- /dev/null +++ b/core/modules/layout_builder/src/Event/SectionComponentBuildRenderArrayEvent.php @@ -0,0 +1,138 @@ +<?php + +namespace Drupal\layout_builder\Event; + +use Drupal\Core\Cache\CacheableResponseTrait; +use Drupal\layout_builder\SectionComponent; +use Symfony\Component\EventDispatcher\Event; + +/** + * Event fired when a section component's render array is being built. + * + * Subscribers to this event should manipulate the cacheability object and the + * build array in this event. + * + * @see \Drupal\layout_builder\LayoutBuilderEvents::SECTION_COMPONENT_BUILD_RENDER_ARRAY + * + * @internal + * Layout Builder is currently experimental and should only be leveraged by + * experimental modules and development releases of contributed modules. + * See https://www.drupal.org/core/experimental for more information. + */ +class SectionComponentBuildRenderArrayEvent extends Event { + + use CacheableResponseTrait; + + /** + * The section component whose render array is being built. + * + * @var \Drupal\layout_builder\SectionComponent + */ + protected $component; + + /** + * The available contexts. + * + * @var \Drupal\Core\Plugin\Context\ContextInterface[] + */ + protected $contexts; + + /** + * The plugin for the section component being built. + * + * @var \Drupal\Component\Plugin\PluginInspectionInterface + */ + protected $plugin; + + /** + * Whether the component is in preview mode or not. + * + * @var bool + */ + protected $inPreview; + + /** + * The render array built by the event subscribers. + * + * @var array + */ + protected $build = []; + + /** + * Creates a new SectionComponentBuildRenderArrayEvent object. + * + * @param \Drupal\layout_builder\SectionComponent $component + * The section component whose render array is being built. + * @param \Drupal\Core\Plugin\Context\ContextInterface[] $contexts + * The available contexts. + * @param bool $in_preview + * (optional) Whether the component is in preview mode or not. + */ + public function __construct(SectionComponent $component, array $contexts, $in_preview = FALSE) { + $this->component = $component; + $this->contexts = $contexts; + $this->plugin = $component->getPlugin($contexts); + $this->inPreview = $in_preview; + } + + /** + * Get the section component whose render array is being built. + * + * @return \Drupal\layout_builder\SectionComponent + * The section component whose render array is being built. + */ + public function getComponent() { + return $this->component; + } + + /** + * Get the available contexts. + * + * @return array|\Drupal\Core\Plugin\Context\ContextInterface[] + * The available contexts. + */ + public function getContexts() { + return $this->contexts; + } + + /** + * Get the plugin for the section component being built. + * + * @return \Drupal\Component\Plugin\PluginInspectionInterface + * The plugin for the section component being built. + */ + public function getPlugin() { + return $this->plugin; + } + + /** + * Determine if the component is in preview mode. + * + * @return bool + * Whether the component is in preview mode or not. + */ + public function inPreview() { + return $this->inPreview; + } + + /** + * Get the render array in its current state. + * + * @return array + * The render array built by the event subscribers. + */ + public function getBuild() { + return $this->build; + } + + /** + * Set the render array. + * + * @param array $build + * A render array. + */ + public function setBuild(array $build) { + $this->build = $build; + } + +} diff --git a/core/modules/layout_builder/src/EventSubscriber/BlockComponentRenderArray.php b/core/modules/layout_builder/src/EventSubscriber/BlockComponentRenderArray.php new file mode 100644 index 0000000000000000000000000000000000000000..181ed8229b6cc523f54c4b1b02451658986bb0da --- /dev/null +++ b/core/modules/layout_builder/src/EventSubscriber/BlockComponentRenderArray.php @@ -0,0 +1,85 @@ +<?php + +namespace Drupal\layout_builder\EventSubscriber; + +use Drupal\Core\Access\AccessResult; +use Drupal\Core\Block\BlockPluginInterface; +use Drupal\Core\Session\AccountInterface; +use Drupal\layout_builder\Event\SectionComponentBuildRenderArrayEvent; +use Drupal\layout_builder\LayoutBuilderEvents; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +/** + * Builds render arrays and handles access for all block components. + * + * @internal + * Layout Builder is currently experimental and should only be leveraged by + * experimental modules and development releases of contributed modules. + * See https://www.drupal.org/core/experimental for more information. + */ +class BlockComponentRenderArray implements EventSubscriberInterface { + + /** + * The current user. + * + * @var \Drupal\Core\Session\AccountInterface + */ + protected $currentUser; + + /** + * Creates a BlockComponentRenderArray object. + * + * @param \Drupal\Core\Session\AccountInterface $current_user + * The current user. + */ + public function __construct(AccountInterface $current_user) { + $this->currentUser = $current_user; + } + + /** + * {@inheritdoc} + */ + public static function getSubscribedEvents() { + $events[LayoutBuilderEvents::SECTION_COMPONENT_BUILD_RENDER_ARRAY] = ['onBuildRender', 100]; + return $events; + } + + /** + * Builds render arrays for block plugins and sets it on the event. + * + * @param \Drupal\layout_builder\Event\SectionComponentBuildRenderArrayEvent $event + * The section component render event. + */ + public function onBuildRender(SectionComponentBuildRenderArrayEvent $event) { + $block = $event->getPlugin(); + if (!$block instanceof BlockPluginInterface) { + return; + } + + // Only check access if the component is not being previewed. + if ($event->inPreview()) { + $access = AccessResult::allowed()->setCacheMaxAge(0); + } + else { + $access = $block->access($this->currentUser, TRUE); + } + + $event->addCacheableDependency($access); + if ($access->isAllowed()) { + $event->addCacheableDependency($block); + + $build = [ + // @todo Move this to BlockBase in https://www.drupal.org/node/2931040. + '#theme' => 'block', + '#configuration' => $block->getConfiguration(), + '#plugin_id' => $block->getPluginId(), + '#base_plugin_id' => $block->getBaseId(), + '#derivative_plugin_id' => $block->getDerivativeId(), + '#weight' => $event->getComponent()->getWeight(), + 'content' => $block->build(), + ]; + $event->setBuild($build); + } + } + +} diff --git a/core/modules/layout_builder/src/LayoutBuilderEvents.php b/core/modules/layout_builder/src/LayoutBuilderEvents.php new file mode 100644 index 0000000000000000000000000000000000000000..b680f98d341b45e9d73edcc82d2d2619dea3fdd4 --- /dev/null +++ b/core/modules/layout_builder/src/LayoutBuilderEvents.php @@ -0,0 +1,34 @@ +<?php + +namespace Drupal\layout_builder; + +/** + * Defines events for the layout_builder module. + * + * @see \Drupal\layout_builder\Event\SectionComponentBuildRenderArrayEvent + * + * @internal + * Layout Builder is currently experimental and should only be leveraged by + * experimental modules and development releases of contributed modules. + * See https://www.drupal.org/core/experimental for more information. + */ +final class LayoutBuilderEvents { + + /** + * Name of the event fired when a component's render array is built. + * + * This event allows modules to collaborate on creating the render array of + * the SectionComponent object. The event listener method receives a + * \Drupal\layout_builder\Event\SectionComponentBuildRenderArrayEvent + * instance. + * + * @Event + * + * @see \Drupal\layout_builder\Event\SectionComponentBuildRenderArrayEvent + * @see \Drupal\layout_builder\SectionComponent::toRenderArray() + * + * @var string + */ + const SECTION_COMPONENT_BUILD_RENDER_ARRAY = 'section_component.build.render_array'; + +} diff --git a/core/modules/layout_builder/src/SectionComponent.php b/core/modules/layout_builder/src/SectionComponent.php index 6c08fb3aeb7df5a1bd0fe4d1a96ec856056f2dfe..081d982f054b4dd50a12876a349acca5df6c0fa1 100644 --- a/core/modules/layout_builder/src/SectionComponent.php +++ b/core/modules/layout_builder/src/SectionComponent.php @@ -3,10 +3,8 @@ namespace Drupal\layout_builder; use Drupal\Component\Plugin\Exception\PluginException; -use Drupal\Core\Access\AccessResult; -use Drupal\Core\Block\BlockPluginInterface; -use Drupal\Core\Cache\CacheableMetadata; use Drupal\Core\Plugin\ContextAwarePluginInterface; +use Drupal\layout_builder\Event\SectionComponentBuildRenderArrayEvent; /** * Provides a value object for a section component. @@ -98,37 +96,10 @@ public function __construct($uuid, $region, array $configuration = [], array $ad * A renderable array representing the content of the component. */ public function toRenderArray(array $contexts = [], $in_preview = FALSE) { - $output = []; - - $plugin = $this->getPlugin($contexts); - // @todo Figure out the best way to unify fields and blocks and components - // in https://www.drupal.org/node/1875974. - if ($plugin instanceof BlockPluginInterface) { - $cacheability = CacheableMetadata::createFromObject($plugin); - - // Only check access if the component is not being previewed. - if ($in_preview) { - $access = AccessResult::allowed()->setCacheMaxAge(0); - } - else { - $access = $plugin->access($this->currentUser(), TRUE); - } - - $cacheability->addCacheableDependency($access); - if ($access->isAllowed()) { - // @todo Move this to BlockBase in https://www.drupal.org/node/2931040. - $output = [ - '#theme' => 'block', - '#configuration' => $plugin->getConfiguration(), - '#plugin_id' => $plugin->getPluginId(), - '#base_plugin_id' => $plugin->getBaseId(), - '#derivative_plugin_id' => $plugin->getDerivativeId(), - '#weight' => $this->getWeight(), - 'content' => $plugin->build(), - ]; - } - $cacheability->applyTo($output); - } + $event = new SectionComponentBuildRenderArrayEvent($this, $contexts, $in_preview); + $this->eventDispatcher()->dispatch(LayoutBuilderEvents::SECTION_COMPONENT_BUILD_RENDER_ARRAY, $event); + $output = $event->getBuild(); + $event->getCacheableMetadata()->applyTo($output); return $output; } @@ -293,6 +264,8 @@ public function getPlugin(array $contexts = []) { * The plugin manager. */ protected function pluginManager() { + // @todo Figure out the best way to unify fields and blocks and components + // in https://www.drupal.org/node/1875974. return \Drupal::service('plugin.manager.block'); } @@ -307,13 +280,13 @@ protected function contextHandler() { } /** - * Wraps the current user. + * Wraps the event dispatcher. * - * @return \Drupal\Core\Session\AccountInterface - * The current user. + * @return \Symfony\Component\EventDispatcher\EventDispatcherInterface + * The event dispatcher. */ - protected function currentUser() { - return \Drupal::currentUser(); + protected function eventDispatcher() { + return \Drupal::service('event_dispatcher'); } /** diff --git a/core/modules/layout_builder/tests/src/Unit/BlockComponentRenderArrayTest.php b/core/modules/layout_builder/tests/src/Unit/BlockComponentRenderArrayTest.php new file mode 100644 index 0000000000000000000000000000000000000000..6571a8ff1a6d2130d27aa3bdfdfa98fa2e1cd91b --- /dev/null +++ b/core/modules/layout_builder/tests/src/Unit/BlockComponentRenderArrayTest.php @@ -0,0 +1,222 @@ +<?php + +namespace Drupal\Tests\layout_builder\Unit; + +use Drupal\Core\Access\AccessResult; +use Drupal\Core\Block\BlockManagerInterface; +use Drupal\Core\Block\BlockPluginInterface; +use Drupal\Core\Cache\Cache; +use Drupal\Core\DependencyInjection\ContainerBuilder; +use Drupal\Core\Session\AccountInterface; +use Drupal\layout_builder\Event\SectionComponentBuildRenderArrayEvent; +use Drupal\layout_builder\EventSubscriber\BlockComponentRenderArray; +use Drupal\layout_builder\SectionComponent; +use Drupal\Tests\UnitTestCase; + +/** + * @coversDefaultClass \Drupal\layout_builder\EventSubscriber\BlockComponentRenderArray + * @group layout_builder + */ +class BlockComponentRenderArrayTest extends UnitTestCase { + + /** + * The current user. + * + * @var \Drupal\Core\Session\AccountInterface + */ + protected $account; + + /** + * The block plugin manager. + * + * @var \Drupal\Core\Block\BlockManagerInterface + */ + protected $blockManager; + + /** + * {@inheritdoc} + */ + protected function setUp() { + parent::setUp(); + + $this->blockManager = $this->prophesize(BlockManagerInterface::class); + $this->account = $this->prophesize(AccountInterface::class); + + $container = new ContainerBuilder(); + $container->set('plugin.manager.block', $this->blockManager->reveal()); + \Drupal::setContainer($container); + } + + /** + * @covers ::onBuildRender + */ + public function testOnBuildRender() { + $block = $this->prophesize(BlockPluginInterface::class); + $access_result = AccessResult::allowed(); + $block->access($this->account->reveal(), TRUE)->willReturn($access_result)->shouldBeCalled(); + $block->getCacheContexts()->willReturn([]); + $block->getCacheTags()->willReturn(['test']); + $block->getCacheMaxAge()->willReturn(Cache::PERMANENT); + $block->getConfiguration()->willReturn([]); + $block->getPluginId()->willReturn('block_plugin_id'); + $block->getBaseId()->willReturn('block_plugin_id'); + $block->getDerivativeId()->willReturn(NULL); + + $block_content = ['#markup' => 'The block content.']; + $block->build()->willReturn($block_content); + $this->blockManager->createInstance('some_block_id', ['id' => 'some_block_id'])->willReturn($block->reveal()); + + $component = new SectionComponent('some-uuid', 'some-region', ['id' => 'some_block_id']); + $contexts = []; + $in_preview = FALSE; + $event = new SectionComponentBuildRenderArrayEvent($component, $contexts, $in_preview); + + $subscriber = new BlockComponentRenderArray($this->account->reveal()); + + $expected_build = [ + '#theme' => 'block', + '#weight' => 0, + '#configuration' => [], + '#plugin_id' => 'block_plugin_id', + '#base_plugin_id' => 'block_plugin_id', + '#derivative_plugin_id' => NULL, + 'content' => $block_content, + ]; + + $expected_cache = $expected_build + [ + '#cache' => [ + 'contexts' => [], + 'tags' => ['test'], + 'max-age' => -1, + ], + ]; + + $subscriber->onBuildRender($event); + $result = $event->getBuild(); + $this->assertEquals($expected_build, $result); + $event->getCacheableMetadata()->applyTo($result); + $this->assertEquals($expected_cache, $result); + } + + /** + * @covers ::onBuildRender + */ + public function testOnBuildRenderDenied() { + $block = $this->prophesize(BlockPluginInterface::class); + $access_result = AccessResult::forbidden(); + $block->access($this->account->reveal(), TRUE)->willReturn($access_result)->shouldBeCalled(); + $block->getCacheContexts()->shouldNotBeCalled(); + $block->getCacheTags()->shouldNotBeCalled(); + $block->getCacheMaxAge()->shouldNotBeCalled(); + $block->getConfiguration()->shouldNotBeCalled(); + $block->getPluginId()->shouldNotBeCalled(); + $block->getBaseId()->shouldNotBeCalled(); + $block->getDerivativeId()->shouldNotBeCalled(); + + $block_content = ['#markup' => 'The block content.']; + $block->build()->willReturn($block_content); + $this->blockManager->createInstance('some_block_id', ['id' => 'some_block_id'])->willReturn($block->reveal()); + + $component = new SectionComponent('some-uuid', 'some-region', ['id' => 'some_block_id']); + $contexts = []; + $in_preview = FALSE; + $event = new SectionComponentBuildRenderArrayEvent($component, $contexts, $in_preview); + + $subscriber = new BlockComponentRenderArray($this->account->reveal()); + + $expected_build = []; + $expected_cache = [ + '#cache' => [ + 'contexts' => [], + 'tags' => [], + 'max-age' => -1, + ], + ]; + + $subscriber->onBuildRender($event); + $result = $event->getBuild(); + $this->assertEquals($expected_build, $result); + $event->getCacheableMetadata()->applyTo($result); + $this->assertEquals($expected_cache, $result); + } + + /** + * @covers ::onBuildRender + */ + public function testOnBuildRenderInPreview() { + $block = $this->prophesize(BlockPluginInterface::class); + $block->access($this->account->reveal(), TRUE)->shouldNotBeCalled(); + $block->getCacheContexts()->willReturn([]); + $block->getCacheTags()->willReturn(['test']); + $block->getCacheMaxAge()->willReturn(Cache::PERMANENT); + $block->getConfiguration()->willReturn([]); + $block->getPluginId()->willReturn('block_plugin_id'); + $block->getBaseId()->willReturn('block_plugin_id'); + $block->getDerivativeId()->willReturn(NULL); + + $block_content = ['#markup' => 'The block content.']; + $block->build()->willReturn($block_content); + $this->blockManager->createInstance('some_block_id', ['id' => 'some_block_id'])->willReturn($block->reveal()); + + $component = new SectionComponent('some-uuid', 'some-region', ['id' => 'some_block_id']); + $contexts = []; + $in_preview = TRUE; + $event = new SectionComponentBuildRenderArrayEvent($component, $contexts, $in_preview); + + $subscriber = new BlockComponentRenderArray($this->account->reveal()); + + $expected_build = [ + '#theme' => 'block', + '#weight' => 0, + '#configuration' => [], + '#plugin_id' => 'block_plugin_id', + '#base_plugin_id' => 'block_plugin_id', + '#derivative_plugin_id' => NULL, + 'content' => $block_content, + ]; + + $expected_cache = $expected_build + [ + '#cache' => [ + 'contexts' => [], + 'tags' => ['test'], + 'max-age' => 0, + ], + ]; + + $subscriber->onBuildRender($event); + $result = $event->getBuild(); + $this->assertEquals($expected_build, $result); + $event->getCacheableMetadata()->applyTo($result); + $this->assertEquals($expected_cache, $result); + } + + /** + * @covers ::onBuildRender + */ + public function testOnBuildRenderNoBlock() { + $this->blockManager->createInstance('some_block_id', ['id' => 'some_block_id'])->willReturn(NULL); + + $component = new SectionComponent('some-uuid', 'some-region', ['id' => 'some_block_id']); + $contexts = []; + $in_preview = FALSE; + $event = new SectionComponentBuildRenderArrayEvent($component, $contexts, $in_preview); + + $subscriber = new BlockComponentRenderArray($this->account->reveal()); + + $expected_build = []; + $expected_cache = [ + '#cache' => [ + 'contexts' => [], + 'tags' => [], + 'max-age' => -1, + ], + ]; + + $subscriber->onBuildRender($event); + $result = $event->getBuild(); + $this->assertEquals($expected_build, $result); + $event->getCacheableMetadata()->applyTo($result); + $this->assertEquals($expected_cache, $result); + } + +} diff --git a/core/modules/layout_builder/tests/src/Unit/SectionComponentTest.php b/core/modules/layout_builder/tests/src/Unit/SectionComponentTest.php new file mode 100644 index 0000000000000000000000000000000000000000..48a24778c1ab83160649f1298188432f1a0352a3 --- /dev/null +++ b/core/modules/layout_builder/tests/src/Unit/SectionComponentTest.php @@ -0,0 +1,71 @@ +<?php + +namespace Drupal\Tests\layout_builder\Unit; + +use Drupal\Core\Block\BlockManagerInterface; +use Drupal\Core\Block\BlockPluginInterface; +use Drupal\Core\DependencyInjection\ContainerBuilder; +use Drupal\Core\Layout\LayoutInterface; +use Drupal\Core\Layout\LayoutPluginManagerInterface; +use Drupal\layout_builder\Event\SectionComponentBuildRenderArrayEvent; +use Drupal\layout_builder\LayoutBuilderEvents; +use Drupal\layout_builder\SectionComponent; +use Drupal\Tests\UnitTestCase; +use Prophecy\Argument; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; + +/** + * @coversDefaultClass \Drupal\layout_builder\SectionComponent + * @group layout_builder + */ +class SectionComponentTest extends UnitTestCase { + + /** + * @covers ::toRenderArray + */ + public function testToRenderArray() { + $existing_block = $this->prophesize(BlockPluginInterface::class); + $existing_block->getPluginId()->willReturn('block_plugin_id'); + + $block_manager = $this->prophesize(BlockManagerInterface::class); + $block_manager->createInstance('some_block_id', ['id' => 'some_block_id'])->willReturn($existing_block->reveal()); + + // Imitate an event subscriber by setting a resulting build on the event. + $event_dispatcher = $this->prophesize(EventDispatcherInterface::class); + $event_dispatcher + ->dispatch(LayoutBuilderEvents::SECTION_COMPONENT_BUILD_RENDER_ARRAY, Argument::type(SectionComponentBuildRenderArrayEvent::class)) + ->shouldBeCalled() + ->will(function ($args) { + /** @var \Drupal\layout_builder\Event\SectionComponentBuildRenderArrayEvent $event */ + $event = $args[1]; + $event->setBuild(['#markup' => $event->getPlugin()->getPluginId()]); + return; + }); + + $layout_plugin = $this->prophesize(LayoutInterface::class); + $layout_plugin->build(Argument::type('array'))->willReturnArgument(0); + + $layout_manager = $this->prophesize(LayoutPluginManagerInterface::class); + $layout_manager->createInstance('layout_onecol', [])->willReturn($layout_plugin->reveal()); + + $container = new ContainerBuilder(); + $container->set('plugin.manager.block', $block_manager->reveal()); + $container->set('event_dispatcher', $event_dispatcher->reveal()); + $container->set('plugin.manager.core.layout', $layout_manager->reveal()); + \Drupal::setContainer($container); + + $expected = [ + '#cache' => [ + 'contexts' => [], + 'tags' => [], + 'max-age' => -1, + ], + '#markup' => 'block_plugin_id', + ]; + + $component = new SectionComponent('some-uuid', 'some-region', ['id' => 'some_block_id']); + $result = $component->toRenderArray(); + $this->assertEquals($expected, $result); + } + +} diff --git a/core/modules/layout_builder/tests/src/Unit/SectionRenderTest.php b/core/modules/layout_builder/tests/src/Unit/SectionRenderTest.php index ed7dfd202f9f58530128641e73e4f87cba5bdc42..079a220a450e78ee968406e0f800ee6811dd4d17 100644 --- a/core/modules/layout_builder/tests/src/Unit/SectionRenderTest.php +++ b/core/modules/layout_builder/tests/src/Unit/SectionRenderTest.php @@ -2,6 +2,7 @@ namespace Drupal\Tests\layout_builder\Unit; +use Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher; use Drupal\Component\Plugin\Exception\PluginException; use Drupal\Core\Access\AccessResult; use Drupal\Core\Block\BlockManagerInterface; @@ -15,6 +16,7 @@ use Drupal\Core\Plugin\Context\ContextRepositoryInterface; use Drupal\Core\Plugin\ContextAwarePluginInterface; use Drupal\Core\Session\AccountInterface; +use Drupal\layout_builder\EventSubscriber\BlockComponentRenderArray; use Drupal\layout_builder\Section; use Drupal\layout_builder\SectionComponent; use Drupal\Tests\UnitTestCase; @@ -54,17 +56,29 @@ class SectionRenderTest extends UnitTestCase { */ protected $contextRepository; + /** + * The event dispatcher. + * + * @var \Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher + */ + protected $eventDispatcher; + /** * {@inheritdoc} */ protected function setUp() { parent::setUp(); - $this->account = $this->prophesize(AccountInterface::class); $layout_plugin_manager = $this->prophesize(LayoutPluginManagerInterface::class); $this->blockManager = $this->prophesize(BlockManagerInterface::class); $this->contextHandler = $this->prophesize(ContextHandlerInterface::class); $this->contextRepository = $this->prophesize(ContextRepositoryInterface::class); + // @todo Refactor this into some better tests in https://www.drupal.org/node/2942605. + $this->eventDispatcher = (new \ReflectionClass(ContainerAwareEventDispatcher::class))->newInstanceWithoutConstructor(); + + $this->account = $this->prophesize(AccountInterface::class); + $subscriber = new BlockComponentRenderArray($this->account->reveal()); + $this->eventDispatcher->addSubscriber($subscriber); $layout = $this->prophesize(LayoutInterface::class); $layout->getPluginDefinition()->willReturn(new LayoutDefinition([])); @@ -77,6 +91,7 @@ protected function setUp() { $container->set('plugin.manager.core.layout', $layout_plugin_manager->reveal()); $container->set('context.handler', $this->contextHandler->reveal()); $container->set('context.repository', $this->contextRepository->reveal()); + $container->set('event_dispatcher', $this->eventDispatcher); \Drupal::setContainer($container); }