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