annotate core/modules/block/tests/src/Kernel/BlockViewBuilderTest.php @ 0:c75dbcec494b

Initial commit from drush-created site
author Chris Cannam
date Thu, 05 Jul 2018 14:24:15 +0000
parents
children
rev   line source
Chris@0 1 <?php
Chris@0 2
Chris@0 3 namespace Drupal\Tests\block\Kernel;
Chris@0 4
Chris@0 5 use Drupal\Component\Utility\Html;
Chris@0 6 use Drupal\Core\Cache\Cache;
Chris@0 7 use Drupal\Core\Language\LanguageInterface;
Chris@0 8 use Drupal\KernelTests\KernelTestBase;
Chris@0 9 use Drupal\block\Entity\Block;
Chris@0 10
Chris@0 11 /**
Chris@0 12 * Tests the block view builder.
Chris@0 13 *
Chris@0 14 * @group block
Chris@0 15 */
Chris@0 16 class BlockViewBuilderTest extends KernelTestBase {
Chris@0 17
Chris@0 18 /**
Chris@0 19 * Modules to install.
Chris@0 20 *
Chris@0 21 * @var array
Chris@0 22 */
Chris@0 23 public static $modules = ['block', 'block_test', 'system', 'user'];
Chris@0 24
Chris@0 25 /**
Chris@0 26 * The block being tested.
Chris@0 27 *
Chris@0 28 * @var \Drupal\block\Entity\BlockInterface
Chris@0 29 */
Chris@0 30 protected $block;
Chris@0 31
Chris@0 32 /**
Chris@0 33 * The block storage.
Chris@0 34 *
Chris@0 35 * @var \Drupal\Core\Config\Entity\ConfigEntityStorageInterface
Chris@0 36 */
Chris@0 37 protected $controller;
Chris@0 38
Chris@0 39 /**
Chris@0 40 * The renderer.
Chris@0 41 *
Chris@0 42 * @var \Drupal\Core\Render\RendererInterface
Chris@0 43 */
Chris@0 44 protected $renderer;
Chris@0 45
Chris@0 46 /**
Chris@0 47 * {@inheritdoc}
Chris@0 48 */
Chris@0 49 protected function setUp() {
Chris@0 50 parent::setUp();
Chris@0 51
Chris@0 52 $this->controller = $this->container
Chris@0 53 ->get('entity_type.manager')
Chris@0 54 ->getStorage('block');
Chris@0 55
Chris@0 56 \Drupal::state()->set('block_test.content', 'Llamas &gt; unicorns!');
Chris@0 57
Chris@0 58 // Create a block with only required values.
Chris@0 59 $this->block = $this->controller->create([
Chris@0 60 'id' => 'test_block',
Chris@0 61 'theme' => 'stark',
Chris@0 62 'plugin' => 'test_cache',
Chris@0 63 ]);
Chris@0 64 $this->block->save();
Chris@0 65
Chris@0 66 $this->container->get('cache.render')->deleteAll();
Chris@0 67
Chris@0 68 $this->renderer = $this->container->get('renderer');
Chris@0 69 }
Chris@0 70
Chris@0 71 /**
Chris@0 72 * Tests the rendering of blocks.
Chris@0 73 */
Chris@0 74 public function testBasicRendering() {
Chris@0 75 \Drupal::state()->set('block_test.content', '');
Chris@0 76
Chris@0 77 $entity = $this->controller->create([
Chris@0 78 'id' => 'test_block1',
Chris@0 79 'theme' => 'stark',
Chris@0 80 'plugin' => 'test_html',
Chris@0 81 ]);
Chris@0 82 $entity->save();
Chris@0 83
Chris@0 84 // Test the rendering of a block.
Chris@0 85 $entity = Block::load('test_block1');
Chris@0 86 $output = entity_view($entity, 'block');
Chris@0 87 $expected = [];
Chris@0 88 $expected[] = '<div id="block-test-block1">';
Chris@0 89 $expected[] = ' ';
Chris@0 90 $expected[] = ' ';
Chris@0 91 $expected[] = ' ';
Chris@0 92 $expected[] = ' </div>';
Chris@0 93 $expected[] = '';
Chris@0 94 $expected_output = implode("\n", $expected);
Chris@0 95 $this->assertEqual($this->renderer->renderRoot($output), $expected_output);
Chris@0 96
Chris@0 97 // Reset the HTML IDs so that the next render is not affected.
Chris@0 98 Html::resetSeenIds();
Chris@0 99
Chris@0 100 // Test the rendering of a block with a given title.
Chris@0 101 $entity = $this->controller->create([
Chris@0 102 'id' => 'test_block2',
Chris@0 103 'theme' => 'stark',
Chris@0 104 'plugin' => 'test_html',
Chris@0 105 'settings' => [
Chris@0 106 'label' => 'Powered by Bananas',
Chris@0 107 ],
Chris@0 108 ]);
Chris@0 109 $entity->save();
Chris@0 110 $output = entity_view($entity, 'block');
Chris@0 111 $expected = [];
Chris@0 112 $expected[] = '<div id="block-test-block2">';
Chris@0 113 $expected[] = ' ';
Chris@0 114 $expected[] = ' <h2>Powered by Bananas</h2>';
Chris@0 115 $expected[] = ' ';
Chris@0 116 $expected[] = ' ';
Chris@0 117 $expected[] = ' </div>';
Chris@0 118 $expected[] = '';
Chris@0 119 $expected_output = implode("\n", $expected);
Chris@0 120 $this->assertEqual($this->renderer->renderRoot($output), $expected_output);
Chris@0 121 }
Chris@0 122
Chris@0 123 /**
Chris@0 124 * Tests block render cache handling.
Chris@0 125 */
Chris@0 126 public function testBlockViewBuilderCache() {
Chris@0 127 // Verify cache handling for a non-empty block.
Chris@0 128 $this->verifyRenderCacheHandling();
Chris@0 129
Chris@0 130 // Create an empty block.
Chris@0 131 $this->block = $this->controller->create([
Chris@0 132 'id' => 'test_block',
Chris@0 133 'theme' => 'stark',
Chris@0 134 'plugin' => 'test_cache',
Chris@0 135 ]);
Chris@0 136 $this->block->save();
Chris@0 137 \Drupal::state()->set('block_test.content', NULL);
Chris@0 138
Chris@0 139 // Verify cache handling for an empty block.
Chris@0 140 $this->verifyRenderCacheHandling();
Chris@0 141 }
Chris@0 142
Chris@0 143 /**
Chris@0 144 * Verifies render cache handling of the block being tested.
Chris@0 145 *
Chris@0 146 * @see ::testBlockViewBuilderCache()
Chris@0 147 */
Chris@0 148 protected function verifyRenderCacheHandling() {
Chris@0 149 // Force a request via GET so we can test the render cache.
Chris@0 150 $request = \Drupal::request();
Chris@0 151 $request_method = $request->server->get('REQUEST_METHOD');
Chris@0 152 $request->setMethod('GET');
Chris@0 153
Chris@0 154 // Test that a cache entry is created.
Chris@0 155 $build = $this->getBlockRenderArray();
Chris@0 156 $cid = 'entity_view:block:test_block:' . implode(':', \Drupal::service('cache_contexts_manager')->convertTokensToKeys(['languages:' . LanguageInterface::TYPE_INTERFACE, 'theme', 'user.permissions'])->getKeys());
Chris@0 157 $this->renderer->renderRoot($build);
Chris@0 158 $this->assertTrue($this->container->get('cache.render')->get($cid), 'The block render element has been cached.');
Chris@0 159
Chris@0 160 // Re-save the block and check that the cache entry has been deleted.
Chris@0 161 $this->block->save();
Chris@0 162 $this->assertFalse($this->container->get('cache.render')->get($cid), 'The block render cache entry has been cleared when the block was saved.');
Chris@0 163
Chris@0 164 // Rebuild the render array (creating a new cache entry in the process) and
Chris@0 165 // delete the block to check the cache entry is deleted.
Chris@0 166 unset($build['#printed']);
Chris@0 167 // Re-add the block because \Drupal\block\BlockViewBuilder::buildBlock()
Chris@0 168 // removes it.
Chris@0 169 $build['#block'] = $this->block;
Chris@0 170
Chris@0 171 $this->renderer->renderRoot($build);
Chris@0 172 $this->assertTrue($this->container->get('cache.render')->get($cid), 'The block render element has been cached.');
Chris@0 173 $this->block->delete();
Chris@0 174 $this->assertFalse($this->container->get('cache.render')->get($cid), 'The block render cache entry has been cleared when the block was deleted.');
Chris@0 175
Chris@0 176 // Restore the previous request method.
Chris@0 177 $request->setMethod($request_method);
Chris@0 178 }
Chris@0 179
Chris@0 180 /**
Chris@0 181 * Tests block view altering.
Chris@0 182 *
Chris@0 183 * @see hook_block_view_alter()
Chris@0 184 * @see hook_block_view_BASE_BLOCK_ID_alter()
Chris@0 185 */
Chris@0 186 public function testBlockViewBuilderViewAlter() {
Chris@0 187 // Establish baseline.
Chris@0 188 $build = $this->getBlockRenderArray();
Chris@0 189 $this->setRawContent((string) $this->renderer->renderRoot($build));
Chris@0 190 $this->assertIdentical(trim((string) $this->cssSelect('div')[0]), 'Llamas > unicorns!');
Chris@0 191
Chris@0 192 // Enable the block view alter hook that adds a foo=bar attribute.
Chris@0 193 \Drupal::state()->set('block_test_view_alter_suffix', TRUE);
Chris@0 194 Cache::invalidateTags($this->block->getCacheTagsToInvalidate());
Chris@0 195 $build = $this->getBlockRenderArray();
Chris@0 196 $this->setRawContent((string) $this->renderer->renderRoot($build));
Chris@0 197 $this->assertIdentical(trim((string) $this->cssSelect('[foo=bar]')[0]), 'Llamas > unicorns!');
Chris@0 198 \Drupal::state()->set('block_test_view_alter_suffix', FALSE);
Chris@0 199
Chris@0 200 \Drupal::state()->set('block_test.content', NULL);
Chris@0 201 Cache::invalidateTags($this->block->getCacheTagsToInvalidate());
Chris@0 202
Chris@0 203 // Advanced: cached block, but an alter hook adds a #pre_render callback to
Chris@0 204 // alter the eventual content.
Chris@0 205 \Drupal::state()->set('block_test_view_alter_append_pre_render_prefix', TRUE);
Chris@0 206 $build = $this->getBlockRenderArray();
Chris@0 207 $this->assertFalse(isset($build['#prefix']), 'The appended #pre_render callback has not yet run before rendering.');
Chris@0 208 $this->assertIdentical((string) $this->renderer->renderRoot($build), 'Hiya!<br>');
Chris@0 209 $this->assertTrue(isset($build['#prefix']) && $build['#prefix'] === 'Hiya!<br>', 'A cached block without content is altered.');
Chris@0 210 }
Chris@0 211
Chris@0 212 /**
Chris@0 213 * Tests block build altering.
Chris@0 214 *
Chris@0 215 * @see hook_block_build_alter()
Chris@0 216 * @see hook_block_build_BASE_BLOCK_ID_alter()
Chris@0 217 */
Chris@0 218 public function testBlockViewBuilderBuildAlter() {
Chris@0 219 // Force a request via GET so we can test the render cache.
Chris@0 220 $request = \Drupal::request();
Chris@0 221 $request_method = $request->server->get('REQUEST_METHOD');
Chris@0 222 $request->setMethod('GET');
Chris@0 223
Chris@0 224 $default_keys = ['entity_view', 'block', 'test_block'];
Chris@0 225 $default_contexts = [];
Chris@0 226 $default_tags = ['block_view', 'config:block.block.test_block'];
Chris@0 227 $default_max_age = Cache::PERMANENT;
Chris@0 228
Chris@0 229 // hook_block_build_alter() adds an additional cache key.
Chris@0 230 $alter_add_key = $this->randomMachineName();
Chris@0 231 \Drupal::state()->set('block_test_block_alter_cache_key', $alter_add_key);
Chris@0 232 $this->assertBlockRenderedWithExpectedCacheability(array_merge($default_keys, [$alter_add_key]), $default_contexts, $default_tags, $default_max_age);
Chris@0 233 \Drupal::state()->set('block_test_block_alter_cache_key', NULL);
Chris@0 234
Chris@0 235 // hook_block_build_alter() adds an additional cache context.
Chris@0 236 $alter_add_context = 'url.query_args:' . $this->randomMachineName();
Chris@0 237 \Drupal::state()->set('block_test_block_alter_cache_context', $alter_add_context);
Chris@0 238 $this->assertBlockRenderedWithExpectedCacheability($default_keys, Cache::mergeContexts($default_contexts, [$alter_add_context]), $default_tags, $default_max_age);
Chris@0 239 \Drupal::state()->set('block_test_block_alter_cache_context', NULL);
Chris@0 240
Chris@0 241 // hook_block_build_alter() adds an additional cache tag.
Chris@0 242 $alter_add_tag = $this->randomMachineName();
Chris@0 243 \Drupal::state()->set('block_test_block_alter_cache_tag', $alter_add_tag);
Chris@0 244 $this->assertBlockRenderedWithExpectedCacheability($default_keys, $default_contexts, Cache::mergeTags($default_tags, [$alter_add_tag]), $default_max_age);
Chris@0 245 \Drupal::state()->set('block_test_block_alter_cache_tag', NULL);
Chris@0 246
Chris@0 247 // hook_block_build_alter() alters the max-age.
Chris@0 248 $alter_max_age = 300;
Chris@0 249 \Drupal::state()->set('block_test_block_alter_cache_max_age', $alter_max_age);
Chris@0 250 $this->assertBlockRenderedWithExpectedCacheability($default_keys, $default_contexts, $default_tags, $alter_max_age);
Chris@0 251 \Drupal::state()->set('block_test_block_alter_cache_max_age', NULL);
Chris@0 252
Chris@0 253 // hook_block_build_alter() alters cache keys, contexts, tags and max-age.
Chris@0 254 \Drupal::state()->set('block_test_block_alter_cache_key', $alter_add_key);
Chris@0 255 \Drupal::state()->set('block_test_block_alter_cache_context', $alter_add_context);
Chris@0 256 \Drupal::state()->set('block_test_block_alter_cache_tag', $alter_add_tag);
Chris@0 257 \Drupal::state()->set('block_test_block_alter_cache_max_age', $alter_max_age);
Chris@0 258 $this->assertBlockRenderedWithExpectedCacheability(array_merge($default_keys, [$alter_add_key]), Cache::mergeContexts($default_contexts, [$alter_add_context]), Cache::mergeTags($default_tags, [$alter_add_tag]), $alter_max_age);
Chris@0 259 \Drupal::state()->set('block_test_block_alter_cache_key', NULL);
Chris@0 260 \Drupal::state()->set('block_test_block_alter_cache_context', NULL);
Chris@0 261 \Drupal::state()->set('block_test_block_alter_cache_tag', NULL);
Chris@0 262 \Drupal::state()->set('block_test_block_alter_cache_max_age', NULL);
Chris@0 263
Chris@0 264 // hook_block_build_alter() sets #create_placeholder.
Chris@0 265 foreach ([TRUE, FALSE] as $value) {
Chris@0 266 \Drupal::state()->set('block_test_block_alter_create_placeholder', $value);
Chris@0 267 $build = $this->getBlockRenderArray();
Chris@0 268 $this->assertTrue(isset($build['#create_placeholder']));
Chris@0 269 $this->assertIdentical($value, $build['#create_placeholder']);
Chris@0 270 }
Chris@0 271 \Drupal::state()->set('block_test_block_alter_create_placeholder', NULL);
Chris@0 272
Chris@0 273 // Restore the previous request method.
Chris@0 274 $request->setMethod($request_method);
Chris@0 275 }
Chris@0 276
Chris@0 277 /**
Chris@0 278 * Asserts that a block is built/rendered/cached with expected cacheability.
Chris@0 279 *
Chris@0 280 * @param string[] $expected_keys
Chris@0 281 * The expected cache keys.
Chris@0 282 * @param string[] $expected_contexts
Chris@0 283 * The expected cache contexts.
Chris@0 284 * @param string[] $expected_tags
Chris@0 285 * The expected cache tags.
Chris@0 286 * @param int $expected_max_age
Chris@0 287 * The expected max-age.
Chris@0 288 */
Chris@0 289 protected function assertBlockRenderedWithExpectedCacheability(array $expected_keys, array $expected_contexts, array $expected_tags, $expected_max_age) {
Chris@0 290 $required_cache_contexts = ['languages:' . LanguageInterface::TYPE_INTERFACE, 'theme', 'user.permissions'];
Chris@0 291
Chris@0 292 // Check that the expected cacheability metadata is present in:
Chris@0 293 // - the built render array;
Chris@0 294 $this->pass('Built render array');
Chris@0 295 $build = $this->getBlockRenderArray();
Chris@0 296 $this->assertIdentical($expected_keys, $build['#cache']['keys']);
Chris@0 297 $this->assertIdentical($expected_contexts, $build['#cache']['contexts']);
Chris@0 298 $this->assertIdentical($expected_tags, $build['#cache']['tags']);
Chris@0 299 $this->assertIdentical($expected_max_age, $build['#cache']['max-age']);
Chris@0 300 $this->assertFalse(isset($build['#create_placeholder']));
Chris@0 301 // - the rendered render array;
Chris@0 302 $this->pass('Rendered render array');
Chris@0 303 $this->renderer->renderRoot($build);
Chris@0 304 // - the render cache item.
Chris@0 305 $this->pass('Render cache item');
Chris@0 306 $final_cache_contexts = Cache::mergeContexts($expected_contexts, $required_cache_contexts);
Chris@0 307 $cid = implode(':', $expected_keys) . ':' . implode(':', \Drupal::service('cache_contexts_manager')->convertTokensToKeys($final_cache_contexts)->getKeys());
Chris@0 308 $cache_item = $this->container->get('cache.render')->get($cid);
Chris@0 309 $this->assertTrue($cache_item, 'The block render element has been cached with the expected cache ID.');
Chris@0 310 $this->assertIdentical(Cache::mergeTags($expected_tags, ['rendered']), $cache_item->tags);
Chris@0 311 $this->assertIdentical($final_cache_contexts, $cache_item->data['#cache']['contexts']);
Chris@0 312 $this->assertIdentical($expected_tags, $cache_item->data['#cache']['tags']);
Chris@0 313 $this->assertIdentical($expected_max_age, $cache_item->data['#cache']['max-age']);
Chris@0 314
Chris@0 315 $this->container->get('cache.render')->delete($cid);
Chris@0 316 }
Chris@0 317
Chris@0 318 /**
Chris@0 319 * Get a fully built render array for a block.
Chris@0 320 *
Chris@0 321 * @return array
Chris@0 322 * The render array.
Chris@0 323 */
Chris@0 324 protected function getBlockRenderArray() {
Chris@0 325 return $this->container->get('entity_type.manager')->getViewBuilder('block')->view($this->block, 'block');
Chris@0 326 }
Chris@0 327
Chris@0 328 }