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