1<?php 2 3namespace Drupal\Tests\Core\Render; 4 5/** 6 * @coversDefaultClass \Drupal\Core\Render\Renderer 7 * @group Render 8 */ 9class RendererRecursionTest extends RendererTestBase { 10 11 protected function setUpRenderRecursionComplexElements() { 12 $complex_child_markup = '<p>Imagine this is a render array for an entity.</p>'; 13 $parent_markup = '<p>Rendered!</p>'; 14 15 $complex_child_template = [ 16 '#cache' => [ 17 'tags' => [ 18 'test:complex_child', 19 ], 20 ], 21 '#lazy_builder' => ['Drupal\Tests\Core\Render\PlaceholdersTest::callback', [$this->getRandomGenerator()->string()]], 22 '#create_placeholder' => TRUE, 23 ]; 24 25 return [$complex_child_markup, $parent_markup, $complex_child_template]; 26 } 27 28 /** 29 * ::renderRoot() may not be called inside of another ::renderRoot() call. 30 * 31 * @covers ::renderRoot 32 * @covers ::render 33 * @covers ::doRender 34 */ 35 public function testRenderRecursionWithNestedRenderRoot() { 36 list($complex_child_markup, $parent_markup, $complex_child_template) = $this->setUpRenderRecursionComplexElements(); 37 $renderer = $this->renderer; 38 $this->setUpRequest(); 39 40 $complex_child = $complex_child_template; 41 $callable = function () use ($renderer, $complex_child) { 42 $this->expectException(\LogicException::class); 43 $renderer->renderRoot($complex_child); 44 }; 45 46 $page = [ 47 'content' => [ 48 '#pre_render' => [ 49 $callable, 50 ], 51 '#suffix' => $parent_markup, 52 ], 53 ]; 54 $renderer->renderRoot($page); 55 } 56 57 /** 58 * ::render() may be called from anywhere. 59 * 60 * Including from inside of another ::renderRoot() call. Bubbling must be 61 * performed. 62 * 63 * @covers ::renderRoot 64 * @covers ::render 65 * @covers ::doRender 66 */ 67 public function testRenderRecursionWithNestedRender() { 68 list($complex_child_markup, $parent_markup, $complex_child_template) = $this->setUpRenderRecursionComplexElements(); 69 $renderer = $this->renderer; 70 $this->setUpRequest(); 71 72 $callable = function ($markup) use ($renderer, $complex_child_template) { 73 $this->assertStringStartsWith('<drupal-render-placeholder', $markup, 'Rendered complex child output as expected, without the placeholder replaced, i.e. with just the placeholder.'); 74 return $markup; 75 }; 76 77 $page = [ 78 'content' => [ 79 'complex_child' => $complex_child_template, 80 '#post_render' => [ 81 $callable, 82 ], 83 '#suffix' => $parent_markup, 84 ], 85 ]; 86 $output = $renderer->renderRoot($page); 87 88 $this->assertEquals('<p>This is a rendered placeholder!</p><p>Rendered!</p>', $output, 'Rendered output as expected, with the placeholder replaced.'); 89 $this->assertContains('test:complex_child', $page['#cache']['tags'], 'Cache tag bubbling performed.'); 90 $this->assertContains('dynamic_animal', array_keys($page['#attached']['drupalSettings']), 'Asset bubbling performed.'); 91 } 92 93 /** 94 * ::renderPlain() may be called from anywhere. 95 * 96 * Including from inside of another ::renderRoot() call. 97 * 98 * @covers ::renderRoot 99 * @covers ::renderPlain 100 */ 101 public function testRenderRecursionWithNestedRenderPlain() { 102 list($complex_child_markup, $parent_markup, $complex_child_template) = $this->setUpRenderRecursionComplexElements(); 103 $renderer = $this->renderer; 104 $this->setUpRequest(); 105 106 $complex_child = $complex_child_template; 107 108 $callable = function ($elements) use ($renderer, $complex_child, $parent_markup) { 109 $elements['#markup'] = $renderer->renderPlain($complex_child); 110 $this->assertEquals('<p>This is a rendered placeholder!</p>', $elements['#markup'], 'Rendered complex child output as expected, with the placeholder replaced.'); 111 return $elements; 112 }; 113 114 $page = [ 115 'content' => [ 116 '#pre_render' => [ 117 $callable, 118 ], 119 '#suffix' => $parent_markup, 120 ], 121 ]; 122 $output = $renderer->renderRoot($page); 123 $this->assertEquals('<p>This is a rendered placeholder!</p>' . $parent_markup, $output, 'Rendered output as expected, with the placeholder replaced.'); 124 $this->assertNotContains('test:complex_child', $page['#cache']['tags'], 'Cache tag bubbling not performed.'); 125 $this->assertTrue(empty($page['#attached']), 'Asset bubbling not performed.'); 126 } 127 128} 129