panopoly_magic-8.x-2.x-dev/tests/src/Unit/BlockPreviewRendererTest.php
tests/src/Unit/BlockPreviewRendererTest.php
<?php
namespace Drupal\Tests\panopoly_magic\Unit;
use Drupal\Component\Plugin\Exception\ContextException;
use Drupal\Core\Block\BlockBase;
use Drupal\Core\Block\BlockManagerInterface;
use Drupal\Core\Block\BlockPluginInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Plugin\Context\ContextDefinitionInterface;
use Drupal\Core\Plugin\Context\ContextHandlerInterface;
use Drupal\Core\Plugin\Context\ContextInterface;
use Drupal\panopoly_magic\BlockPreviewInterface;
use Drupal\panopoly_magic\BlockPreviewRenderer;
use Drupal\Tests\UnitTestCase;
/**
* Unit tests for the BlockPreviewRenderer.
*
* @group PanopolyMagic
* @group Panopoly
*/
class BlockPreviewRendererTest extends UnitTestCase {
/**
* The block manager.
*
* @var \Drupal\Core\Block\BlockManagerInterface|\Prophecy\Prophecy\ObjectProphecy
*/
protected $blockManager;
/**
* The context handler.
*
* @var \Drupal\Core\Plugin\Context\ContextHandlerInterface|\Prophecy\Prophecy\ObjectProphecy
*/
protected $contextHandler;
/**
* The entity type manager service.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface|\Prophecy\Prophecy\ObjectProphecy
*/
protected $entityTypeManager;
/**
* The block preview renderer.
*
* @var \Drupal\panopoly_magic\BlockPreviewRenderer
*/
protected $renderer;
/**
* {@inheritDoc}
*/
public function setUp(): void {
parent::setUp();
$this->blockManager = $this->prophesize(BlockManagerInterface::class);
$this->contextHandler = $this->prophesize(ContextHandlerInterface::class);
$this->entityTypeManager = $this->prophesize(EntityTypeManagerInterface::class);
$this->renderer = new BlockPreviewRenderer($this->blockManager->reveal(), $this->contextHandler->reveal(), $this->entityTypeManager->reveal());
$this->renderer->setStringTranslation($this->getStringTranslationStub());
}
/**
* Tests a simple block that is just rendered normally as its preview.
*/
public function testPlainBlock() {
$block_id = 'system_powered_by_block';
$block_definition = [
'id' => $block_id,
'admin_label' => 'Powered by Drupal',
];
// BlockPluginInterface doesn't extend ContextAwarePluginInterface.
/** @var Drupal\Core\Block\BlockPluginInterface|\Prophecy\Prophecy\ObjectProphecy $block_plugin */
$block_plugin = $this->prophesize(BlockPluginInterface::class);
$block_plugin->getConfiguration()
->willReturn(['prop' => 'value']);
$block_plugin->getPluginId()
->willReturn('awesome:plugin');
$block_plugin->getBaseId()
->willReturn('awesome');
$block_plugin->getDerivativeId()
->willReturn('plugin');
$block_plugin->build()
->willReturn(['#markup' => 'Powered by Drupal']);
$this->blockManager->getDefinition($block_id)
->willReturn($block_definition);
$this->blockManager->createInstance($block_id)
->willReturn($block_plugin->reveal());
$rendered = $this->renderer->buildBlockPreview($block_id);
$expected = [
'#theme' => 'block',
'#attributes' => [],
'#configuration' => ['prop' => 'value'],
'#plugin_id' => 'awesome:plugin',
'#base_plugin_id' => 'awesome',
'#derivative_plugin_id' => 'plugin',
'content' => [
'#markup' => 'Powered by Drupal',
],
];
$this->assertEquals($expected, $rendered);
}
/**
* Creates a mock context definition.
*
* @param bool $required
* Whether or not the context is required.
*
* @return \Drupal\Core\Plugin\Context\ContextDefinitionInterface|\Prophecy\Prophecy\ObjectProphecy
* The mock context definition.
*/
protected function mockContextDefinition($required = FALSE) {
/** @var \Drupal\Core\Plugin\Context\ContextDefinitionInterface|\Prophecy\Prophecy\ObjectProphecy $context_definition */
$context_definition = $this->prophesize(ContextDefinitionInterface::class);
$context_definition->isRequired()->willReturn($required);
return $context_definition;
}
/**
* Tests a simple block that is context aware.
*/
public function testPlainContextAwareBlock() {
$block_id = 'context_aware_block';
$block_definition = [
'id' => $block_id,
'admin_label' => 'Feel the context',
];
$block_context_mapping = [
'slot_1' => '@service.context1',
];
$block_context_definitions = [
'slot_1' => $this->mockContextDefinition(TRUE),
'slot_2' => $this->mockContextDefinition(TRUE),
'slot_3' => $this->mockContextDefinition(FALSE),
];
$contexts = [
'@service.context1' => $this->prophesize(ContextInterface::class)->reveal(),
'@service.context2' => $this->prophesize(ContextInterface::class)->reveal(),
'@service.context3' => $this->prophesize(ContextInterface::class)->reveal(),
];
// BlockBase extends ContextAwarePluginInterface (even though most plugins
// don't actually do anything with context).
/** @var \Drupal\Core\Block\BlockBase|\Prophecy\Prophecy\ObjectProphecy $block_plugin */
$block_plugin = $this->prophesize(BlockBase::class);
$block_plugin->build()
->willReturn(['#markup' => 'Feel the context']);
$block_plugin->getConfiguration()
->willReturn(['prop' => 'value']);
$block_plugin->getPluginId()
->willReturn('awesome:plugin');
$block_plugin->getBaseId()
->willReturn('awesome');
$block_plugin->getDerivativeId()
->willReturn('plugin');
$block_plugin->getContextMapping()
->willReturn($block_context_mapping);
$block_plugin->getContextDefinitions()
->willReturn($block_context_definitions);
$this->blockManager->getDefinition($block_id)
->willReturn($block_definition);
$this->blockManager->createInstance($block_id)
->willReturn($block_plugin->reveal());
$this->contextHandler->getMatchingContexts($contexts, $block_context_definitions['slot_2'])
->willReturn([
'@service.context2' => $contexts['@service.context2'],
'@service.context3' => $contexts['@service.context3'],
])
->shouldBeCalledTimes(1);
$this->contextHandler->applyContextMapping($block_plugin->reveal(), $contexts, [
'slot_1' => '@service.context1',
'slot_2' => '@service.context2',
])
->shouldBeCalledTimes(1);
$rendered = $this->renderer->buildBlockPreview($block_id, $contexts);
$expected = [
'#theme' => 'block',
'#attributes' => [],
'#configuration' => ['prop' => 'value'],
'#plugin_id' => 'awesome:plugin',
'#base_plugin_id' => 'awesome',
'#derivative_plugin_id' => 'plugin',
'content' => [
'#markup' => 'Feel the context',
],
];
$this->assertEquals($expected, $rendered);
}
/**
* Tests a block that provides its own preview.
*/
public function testBlockWithPreview() {
$block_id = 'block_with_preview';
$block_definition = [
'id' => $block_id,
'admin_label' => 'Block with preview',
];
/** @var BlockWithPreviewInterface|\Prophecy\Prophecy\ObjectProphecy $block_plugin */
$block_plugin = $this->prophesize(BlockWithPreviewInterface::class);
$block_plugin->build()
->willReturn(['#markup' => 'Normal block content'])
->shouldNotBeCalled();
$block_plugin->buildPreview()
->willReturn(['#markup' => 'Block preview content'])
->shouldBeCalledTimes(1);
$this->blockManager->getDefinition($block_id)
->willReturn($block_definition);
$this->blockManager->createInstance($block_id)
->willReturn($block_plugin->reveal());
$rendered = $this->renderer->buildBlockPreview($block_id);
$this->assertEquals(['#markup' => 'Block preview content'], $rendered);
}
/**
* Renders a preview for a block plugin.
*
* @param \Drupal\Core\Block\BlockPluginInterface $block_plugin
* The block plugin.
*
* @return array
* A render array.
*/
public function previewCallback(BlockPluginInterface $block_plugin) {
return ['#markup' => 'Block preview callback content'];
}
/**
* Tests a block that has been altered to use a preview callback.
*/
public function testBlockWithPreviewCallback() {
$block_id = 'block_with_preview_callback';
$block_definition = [
'id' => $block_id,
'admin_label' => 'Block with preview callback',
'preview_callback' => [$this, 'previewCallback'],
];
/** @var \Drupal\Core\Block\BlockPluginInterface|\Prophecy\Prophecy\ObjectProphecy $block_plugin */
$block_plugin = $this->prophesize(BlockPluginInterface::class);
$block_plugin->build()
->willReturn(['#markup' => 'Normal block content'])
->shouldNotBeCalled();
$this->blockManager->getDefinition($block_id)
->willReturn($block_definition);
$this->blockManager->createInstance($block_id)
->willReturn($block_plugin->reveal());
$rendered = $this->renderer->buildBlockPreview($block_id);
$this->assertEquals(['#markup' => 'Block preview callback content'], $rendered);
}
/**
* Tests a block that has been altered to use a preview image.
*/
public function testBlockWithPreviewImage() {
$block_id = 'block_with_preview_image';
$block_definition = [
'id' => $block_id,
'admin_label' => 'Block with preview image',
'preview_image' => 'http://example.com/image.png',
'preview_alt' => 'Preview image alt',
];
/** @var \Drupal\Core\Block\BlockPluginInterface|\Prophecy\Prophecy\ObjectProphecy $block_plugin */
$block_plugin = $this->prophesize(BlockPluginInterface::class);
$block_plugin->build()
->willReturn(['#markup' => 'Normal block content'])
->shouldNotBeCalled();
$this->blockManager->getDefinition($block_id)
->willReturn($block_definition);
$this->blockManager->createInstance($block_id)
->willReturn($block_plugin->reveal());
$rendered = $this->renderer->buildBlockPreview($block_id);
$expected = [
'#theme' => 'image',
'#uri' => 'http://example.com/image.png',
'#alt' => 'Preview image alt',
];
$this->assertEquals($expected, $rendered);
}
/**
* Tests that the first exception is handled correctly.
*/
public function testException1() {
$block_id = 'context_aware_block';
$block_definition = [
'id' => $block_id,
'admin_label' => 'Feel the context',
];
// BlockBase extends ContextAwarePluginInterface (even though most plugins
// don't actually do anything with context).
/** @var \Drupal\Core\Block\BlockBase|\Prophecy\Prophecy\ObjectProphecy $block_plugin */
$block_plugin = $this->prophesize(BlockBase::class);
$block_plugin->build()
->willReturn(['#markup' => 'Feel the context'])
->shouldNotBeCalled();
$block_plugin->getContextMapping()
->willReturn([]);
$block_plugin->getContextDefinitions()
->willReturn([]);
$this->blockManager->getDefinition($block_id)
->willReturn($block_definition);
$this->blockManager->createInstance($block_id)
->willReturn($block_plugin->reveal());
$this->contextHandler->applyContextMapping($block_plugin->reveal(), [], [])
->willThrow(new ContextException("TEST"));
$rendered = $this->renderer->buildBlockPreview($block_id, []);
$this->assertEquals(['#markup' => 'Missing required context'], $rendered);
}
/**
* Tests that the second exception is handled correctly.
*/
public function testException2() {
$block_id = 'system_powered_by_block';
$block_definition = [
'id' => $block_id,
'admin_label' => 'Powered by Drupal',
];
/** @var Drupal\Core\Block\BlockPluginInterface|\Prophecy\Prophecy\ObjectProphecy $block_plugin */
$block_plugin = $this->prophesize(BlockPluginInterface::class);
$block_plugin->getConfiguration()
->shouldBeCalledOnce();
$block_plugin->getPluginId()
->shouldBeCalledOnce();
$block_plugin->getBaseId()
->shouldBeCalledOnce();
$block_plugin->getDerivativeId()
->shouldBeCalledOnce();
$block_plugin->build()
->willThrow(new ContextException("TEST"));
$this->blockManager->getDefinition($block_id)
->willReturn($block_definition);
$this->blockManager->createInstance($block_id)
->willReturn($block_plugin->reveal());
$rendered = $this->renderer->buildBlockPreview($block_id);
$this->assertEquals(['#markup' => 'Missing required context'], $rendered);
}
}
/**
* Test interface for a block that provides its own preview.
*/
interface BlockWithPreviewInterface extends BlockPluginInterface, BlockPreviewInterface {}
