Newer
Older
<?php
/**
* @file
* Contains \Drupal\block\Tests\BlockViewBuilderTest.
*/
namespace Drupal\block\Tests;
use Drupal\Component\Utility\Html;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Cache\UrlCacheContext;

Alex Pott
committed
use Drupal\simpletest\KernelTestBase;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Drupal\block\Entity\Block;
/**
* Tests the block view builder.
*
* @group block
*/

Alex Pott
committed
class BlockViewBuilderTest extends KernelTestBase {
/**

Jennifer Hodgdon
committed
* Modules to install.
*
* @var array
*/
public static $modules = array('block', 'block_test', 'system');
/**
* The block being tested.
*
* @var \Drupal\block\Entity\BlockInterface
*/
protected $block;
/**

catch
committed
* The block storage.
*

catch
committed
* @var \Drupal\Core\Config\Entity\ConfigEntityStorageInterface
*/
protected $controller;
/**
* {@inheritdoc}
*/

Alex Pott
committed
protected function setUp() {
parent::setUp();
$this->controller = $this->container
->get('entity.manager')

catch
committed
->getStorage('block');
\Drupal::state()->set('block_test.content', 'Llamas > unicorns!');
// Create a block with only required values.
$this->block = $this->controller->create(array(
'id' => 'test_block',
'theme' => 'stark',
'plugin' => 'test_cache',
));
$this->block->save();
$this->container->get('cache.render')->deleteAll();
}
/**
* Tests the rendering of blocks.
*/
public function testBasicRendering() {
\Drupal::state()->set('block_test.content', '');
$entity = $this->controller->create(array(
'id' => 'test_block1',
'theme' => 'stark',
'plugin' => 'test_html',
));
$entity->save();
// Test the rendering of a block.
$entity = Block::load('test_block1');
$output = entity_view($entity, 'block');
$expected = array();
$expected[] = '<div id="block-test-block1">';
$expected[] = ' ';
$expected[] = ' ';
$expected[] = ' ';
$expected[] = ' </div>';
$expected[] = '';
$expected_output = implode("\n", $expected);
$this->assertEqual(drupal_render($output), $expected_output);
// Reset the HTML IDs so that the next render is not affected.
Html::resetSeenIds();
// Test the rendering of a block with a given title.
$entity = $this->controller->create(array(
'id' => 'test_block2',
'theme' => 'stark',
'plugin' => 'test_html',
'settings' => array(
'label' => 'Powered by Bananas',
),
));
$entity->save();
$output = entity_view($entity, 'block');
$expected = array();
$expected[] = '<div id="block-test-block2">';
$expected[] = ' ';
$expected[] = ' <h2>Powered by Bananas</h2>';
$expected[] = ' ';
$expected[] = ' ';
$expected[] = ' </div>';
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
$expected[] = '';
$expected_output = implode("\n", $expected);
$this->assertEqual(drupal_render($output), $expected_output);
}
/**
* Tests block render cache handling.
*/
public function testBlockViewBuilderCache() {
// Verify cache handling for a non-empty block.
$this->verifyRenderCacheHandling();
// Create an empty block.
$this->block = $this->controller->create(array(
'id' => 'test_block',
'theme' => 'stark',
'plugin' => 'test_cache',
));
$this->block->save();
\Drupal::state()->set('block_test.content', NULL);
// Verify cache handling for an empty block.
$this->verifyRenderCacheHandling();
}
/**
* Verifies render cache handling of the block being tested.
*
* @see ::testBlockViewBuilderCache()
*/
protected function verifyRenderCacheHandling() {
// Force a request via GET so we can get drupal_render() cache working.
$request = \Drupal::request();
$request_method = $request->server->get('REQUEST_METHOD');
$request->setMethod('GET');
// Test that entities with caching disabled do not generate a cache entry.
$build = $this->getBlockRenderArray();
$this->assertTrue(isset($build['#cache']) && array_keys($build['#cache']) == array('contexts', 'tags', 'max-age'), 'The render array element of uncacheable blocks is not cached, but does have cache contexts, tags & max-age set.');
// Enable block caching.
$this->setBlockCacheConfig(array(
'max_age' => 600,
));
// Test that a cache entry is created.
$build = $this->getBlockRenderArray();
$cid = 'entity_view:block:test_block:en:core';
drupal_render($build);
$this->assertTrue($this->container->get('cache.render')->get($cid), 'The block render element has been cached.');
// Re-save the block and check that the cache entry has been deleted.
$this->block->save();
$this->assertFalse($this->container->get('cache.render')->get($cid), 'The block render cache entry has been cleared when the block was saved.');
// Rebuild the render array (creating a new cache entry in the process) and
// delete the block to check the cache entry is deleted.
unset($build['#printed']);

Angie Byron
committed
// Re-add the block because \Drupal\block\BlockViewBuilder::buildBlock()
// removes it.
$build['#block'] = $this->block;
drupal_render($build);
$this->assertTrue($this->container->get('cache.render')->get($cid), 'The block render element has been cached.');
$this->block->delete();
$this->assertFalse($this->container->get('cache.render')->get($cid), 'The block render cache entry has been cleared when the block was deleted.');
// Restore the previous request method.
$request->setMethod($request_method);
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
}
/**
* Tests block view altering.
*/
public function testBlockViewBuilderAlter() {
// Establish baseline.
$build = $this->getBlockRenderArray();
$this->assertIdentical(drupal_render($build), 'Llamas > unicorns!');
// Enable the block view alter hook that adds a suffix, for basic testing.
\Drupal::state()->set('block_test_view_alter_suffix', TRUE);
// Basic: non-empty block.
$build = $this->getBlockRenderArray();
$this->assertTrue(isset($build['#suffix']) && $build['#suffix'] === '<br>Goodbye!', 'A block with content is altered.');
$this->assertIdentical(drupal_render($build), 'Llamas > unicorns!<br>Goodbye!');
// Basic: empty block.
\Drupal::state()->set('block_test.content', NULL);
$build = $this->getBlockRenderArray();
$this->assertTrue(isset($build['#suffix']) && $build['#suffix'] === '<br>Goodbye!', 'A block without content is altered.');
$this->assertIdentical(drupal_render($build), '<br>Goodbye!');
// Disable the block view alter hook that adds a suffix, for basic testing.
\Drupal::state()->set('block_test_view_alter_suffix', FALSE);
// Force a request via GET so we can get drupal_render() cache working.
$request = \Drupal::request();
$request_method = $request->server->get('REQUEST_METHOD');
$request->setMethod('GET');
$default_keys = array('entity_view', 'block', 'test_block');

Alex Pott
committed
$default_tags = array('block_view', 'config:block.block.test_block');
// Advanced: cached block, but an alter hook adds an additional cache key.
$this->setBlockCacheConfig(array(
'max_age' => 600,
));

Alex Pott
committed
$alter_add_key = $this->randomMachineName();
\Drupal::state()->set('block_test_view_alter_cache_key', $alter_add_key);
$cid = 'entity_view:block:test_block:' . $alter_add_key . ':en:core';
$expected_keys = array_merge($default_keys, array($alter_add_key));
$build = $this->getBlockRenderArray();
$this->assertIdentical($expected_keys, $build['#cache']['keys'], 'An altered cacheable block has the expected cache keys.');
$this->assertIdentical(drupal_render($build), '');
$cache_entry = $this->container->get('cache.render')->get($cid);
$this->assertTrue($cache_entry, 'The block render element has been cached with the expected cache ID.');
$expected_tags = array_merge($default_tags, ['rendered']);
sort($expected_tags);
$this->assertIdentical($cache_entry->tags, $expected_tags, 'The block render element has been cached with the expected cache tags.');
$this->container->get('cache.render')->delete($cid);
// Advanced: cached block, but an alter hook adds an additional cache tag.

Alex Pott
committed
$alter_add_tag = $this->randomMachineName();
\Drupal::state()->set('block_test_view_alter_cache_tag', $alter_add_tag);
$expected_tags = Cache::mergeTags($default_tags, array($alter_add_tag));
$build = $this->getBlockRenderArray();
sort($build['#cache']['tags']);
$this->assertIdentical($expected_tags, $build['#cache']['tags'], 'An altered cacheable block has the expected cache tags.');
$this->assertIdentical(drupal_render($build), '');
$cache_entry = $this->container->get('cache.render')->get($cid);
$this->assertTrue($cache_entry, 'The block render element has been cached with the expected cache ID.');
$expected_tags = array_merge($default_tags, [$alter_add_tag, 'rendered']);
sort($expected_tags);
$this->assertIdentical($cache_entry->tags, $expected_tags, 'The block render element has been cached with the expected cache tags.');
$this->container->get('cache.render')->delete($cid);
// Advanced: cached block, but an alter hook adds a #pre_render callback to
// alter the eventual content.
\Drupal::state()->set('block_test_view_alter_append_pre_render_prefix', TRUE);
$build = $this->getBlockRenderArray();
$this->assertFalse(isset($build['#prefix']), 'The appended #pre_render callback has not yet run before calling drupal_render().');
$this->assertIdentical(drupal_render($build), 'Hiya!<br>');
$this->assertTrue(isset($build['#prefix']) && $build['#prefix'] === 'Hiya!<br>', 'A cached block without content is altered.');
// Restore the previous request method.
$request->setMethod($request_method);
}
/**
* Tests block render cache handling with configurable cache contexts.
*
* This is only intended to test that an existing block can be configured with
* additional contexts, not to test that each context works correctly.
*
* @see \Drupal\block\Tests\BlockCacheTest
*/
public function testBlockViewBuilderCacheContexts() {
$cache_contexts = \Drupal::service("cache_contexts");
// Force a request via GET so we can get drupal_render() cache working.
$request = \Drupal::request();
$request_method = $request->server->get('REQUEST_METHOD');
$request->setMethod('GET');
// First: no cache context.
$this->setBlockCacheConfig(array(
'max_age' => 600,
));
$build = $this->getBlockRenderArray();
$cid = implode(':', $build['#cache']['keys']);
drupal_render($build);
$this->assertTrue($this->container->get('cache.render', $cid), 'The block render element has been cached.');
// Second: the "per URL" cache context.
$this->setBlockCacheConfig(array(
'max_age' => 600,
'contexts' => array('url'),
));
$old_cid = $cid;
$build = $this->getBlockRenderArray();
$cid_parts = array_merge($build['#cache']['keys'], $cache_contexts->convertTokensToKeys($build['#cache']['contexts']));
$cid = implode(':', $cid_parts);
drupal_render($build);
$this->assertTrue($this->container->get('cache.render', $cid), 'The block render element has been cached.');
$this->assertNotEqual($cid, $old_cid, 'The cache ID has changed.');
// Third: the same block configuration, but a different URL.
$original_url_cache_context = $this->container->get('cache_context.url');
$request_stack = new RequestStack();
$request_stack->push(Request::create('/foo'));
$temp_context = new UrlCacheContext($request_stack);
$this->container->set('cache_context.url', $temp_context);
$old_cid = $cid;
$build = $this->getBlockRenderArray();
$cid_parts = array_merge($build['#cache']['keys'], $cache_contexts->convertTokensToKeys($build['#cache']['contexts']));
$cid = implode(':', $cid_parts);
drupal_render($build);
$this->assertTrue($this->container->get('cache.render', $cid), 'The block render element has been cached.');
$this->assertNotEqual($cid, $old_cid, 'The cache ID has changed.');
$this->container->set('cache_context.url', $original_url_cache_context);
// Restore the previous request method.
$request->setMethod($request_method);
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
}
/**
* Sets the test block's cache configuration.
*
* @param array $cache_config
* The desired cache configuration.
*/
protected function setBlockCacheConfig(array $cache_config) {
$block = $this->block->getPlugin();
$block->setConfigurationValue('cache', $cache_config);
$this->block->save();
}
/**
* Get a fully built render array for a block.
*
* @return array
* The render array.
*/
protected function getBlockRenderArray() {
$build = $this->container->get('entity.manager')->getViewBuilder('block')->view($this->block, 'block');
// Mock the build array to not require the theme registry.
unset($build['#theme']);
return $build;
}
}