function RendererPlaceholdersTest::testCacheableParent

Same name and namespace in other branches
  1. 9 core/tests/Drupal/Tests/Core/Render/RendererPlaceholdersTest.php \Drupal\Tests\Core\Render\RendererPlaceholdersTest::testCacheableParent()
  2. 8.9.x core/tests/Drupal/Tests/Core/Render/RendererPlaceholdersTest.php \Drupal\Tests\Core\Render\RendererPlaceholdersTest::testCacheableParent()
  3. 11.x core/tests/Drupal/Tests/Core/Render/RendererPlaceholdersTest.php \Drupal\Tests\Core\Render\RendererPlaceholdersTest::testCacheableParent()

@covers ::render
@covers ::doRender
@covers \Drupal\Core\Render\RenderCache::get
@covers \Drupal\Core\Render\RenderCache::set

@dataProvider providerPlaceholders

File

core/tests/Drupal/Tests/Core/Render/RendererPlaceholdersTest.php, line 598

Class

RendererPlaceholdersTest
@coversDefaultClass \Drupal\Core\Render\Renderer[[api-linebreak]] @covers \Drupal\Core\Render\RenderCache[[api-linebreak]] @covers \Drupal\Core\Render\PlaceholderingRenderCache[[api-linebreak]] @group Render

Namespace

Drupal\Tests\Core\Render

Code

public function testCacheableParent(array $test_element, array $args, array $expected_placeholder_render_array, array|false $placeholder_cache_keys, array $bubbled_cache_contexts, array $bubbled_cache_tags, array $placeholder_expected_render_cache_array) : void {
  $element = $test_element;
  $this->setupMemoryCache();
  $this->setUpRequest('GET');
  $token = Crypt::hashBase64(serialize($expected_placeholder_render_array));
  $placeholder_callback = $expected_placeholder_render_array['#lazy_builder'][0];
  $expected_placeholder_markup = '<drupal-render-placeholder callback="' . $placeholder_callback . '" arguments="0=' . $args[0] . '" token="' . $token . '"></drupal-render-placeholder>';
  $this->assertSame($expected_placeholder_markup, Html::normalize($expected_placeholder_markup), 'Placeholder unaltered by Html::normalize() which is used by FilterHtmlCorrector.');
  // GET request: #cache enabled, cache miss.
  $element['#cache'] = [
    'keys' => [
      'placeholder_test_GET',
    ],
  ];
  $element['#prefix'] = '<p>#cache enabled, GET</p>';
  $output = $this->renderer
    ->renderRoot($element);
  $this->assertSame('<p>#cache enabled, GET</p><p>This is a rendered placeholder!</p>', (string) $output, 'Output is overridden.');
  $this->assertTrue(isset($element['#printed']), 'No cache hit');
  $this->assertSame('<p>#cache enabled, GET</p><p>This is a rendered placeholder!</p>', (string) $element['#markup'], '#markup is overridden.');
  $expected_js_settings = [
    'foo' => 'bar',
    'dynamic_animal' => $args[0],
  ];
  $this->assertSame($element['#attached']['drupalSettings'], $expected_js_settings, '#attached is modified; both the original JavaScript setting and the one added by the placeholder #lazy_builder callback exist.');
  $this->assertPlaceholderRenderCache($placeholder_cache_keys, $placeholder_expected_render_cache_array);
  // GET request: validate cached data.
  $cached = $this->memoryCache
    ->get([
    'placeholder_test_GET',
  ], CacheableMetadata::createFromRenderArray($test_element));
  // There are three edge cases, where the shape of the render cache item for
  // the parent (with CID 'placeholder_test_GET') is vastly different. These
  // are the cases where:
  // - the placeholder is uncacheable (because it has no #cache[keys]), and;
  // - cacheability metadata that meets auto_placeholder_conditions is bubbled
  $has_uncacheable_lazy_builder = !isset($test_element['placeholder']['#cache']['keys']) && isset($test_element['placeholder']['#lazy_builder']);
  // Edge cases: always where both bubbling of an auto-placeholdering
  // condition happens from within a #lazy_builder that is uncacheable.
  // - uncacheable + A5 (cache max-age)
  // @todo in https://www.drupal.org/node/2559847
  // - uncacheable + A6 (cache context)
  $edge_case_a6_uncacheable = $has_uncacheable_lazy_builder && $test_element['placeholder']['#lazy_builder'][0] === 'Drupal\\Tests\\Core\\Render\\PlaceholdersTest::callbackPerUser';
  // - uncacheable + A7 (cache tag)
  $edge_case_a7_uncacheable = $has_uncacheable_lazy_builder && $test_element['placeholder']['#lazy_builder'][0] === 'Drupal\\Tests\\Core\\Render\\PlaceholdersTest::callbackTagCurrentTemperature';
  // The redirect-cacheable edge case: a high-cardinality cache context is
  // bubbled from a #lazy_builder callback for an uncacheable placeholder. The
  // element containing the uncacheable placeholder has cache keys set, and
  // due to the bubbled cache contexts it creates a cache redirect.
  if ($edge_case_a6_uncacheable) {
    $cached_element = $cached->data;
    $expected_element = [
      '#markup' => '<p>#cache enabled, GET</p><p>This is a rendered placeholder!</p>',
      '#attached' => [
        'drupalSettings' => [
          'foo' => 'bar',
          'dynamic_animal' => $args[0],
        ],
      ],
      '#cache' => [
        'contexts' => $bubbled_cache_contexts,
        'tags' => [],
        'max-age' => Cache::PERMANENT,
      ],
    ];
    $this->assertEquals($expected_element, $cached_element, 'The parent is render cached with a redirect in ase a cache context is bubbled from an uncacheable child (no #cache[keys]) with a #lazy_builder.');
  }
  elseif ($edge_case_a7_uncacheable) {
    $cached_element = $cached->data;
    $expected_element = [
      '#markup' => '<p>#cache enabled, GET</p><p>This is a rendered placeholder!</p>',
      '#attached' => [
        'drupalSettings' => [
          'foo' => 'bar',
          'dynamic_animal' => $args[0],
        ],
      ],
      '#cache' => [
        'contexts' => [],
        'tags' => $bubbled_cache_tags,
        'max-age' => Cache::PERMANENT,
      ],
    ];
    $this->assertEquals($expected_element, $cached_element, 'The correct data is cached: the stored #markup and #attached properties are not affected by placeholder #lazy_builder callbacks.');
  }
  else {
    $cached_element = $cached->data;
    $expected_element = [
      '#markup' => '<p>#cache enabled, GET</p>' . $expected_placeholder_markup,
      '#attached' => [
        'drupalSettings' => [
          'foo' => 'bar',
        ],
        'placeholders' => [
          $expected_placeholder_markup => [
            '#lazy_builder' => [
              'Drupal\\Tests\\Core\\Render\\PlaceholdersTest::callback',
              $args,
            ],
          ],
        ],
      ],
      '#cache' => [
        'contexts' => [],
        'tags' => $bubbled_cache_tags,
        'max-age' => Cache::PERMANENT,
      ],
    ];
    $expected_element['#attached']['placeholders'][$expected_placeholder_markup] = $expected_placeholder_render_array;
    $this->assertEquals($expected_element, $cached_element, 'The correct data is cached: the stored #markup and #attached properties are not affected by placeholder #lazy_builder callbacks.');
  }
  // GET request: #cache enabled, cache hit.
  $element = $test_element;
  $element['#cache'] = [
    'keys' => [
      'placeholder_test_GET',
    ],
  ];
  $element['#prefix'] = '<p>#cache enabled, GET</p>';
  $output = $this->renderer
    ->renderRoot($element);
  $this->assertSame('<p>#cache enabled, GET</p><p>This is a rendered placeholder!</p>', (string) $output, 'Output is overridden.');
  $this->assertFalse(isset($element['#printed']), 'Cache hit');
  $this->assertSame('<p>#cache enabled, GET</p><p>This is a rendered placeholder!</p>', (string) $element['#markup'], '#markup is overridden.');
  $expected_js_settings = [
    'foo' => 'bar',
    'dynamic_animal' => $args[0],
  ];
  $this->assertSame($element['#attached']['drupalSettings'], $expected_js_settings, '#attached is modified; both the original JavaScript setting and the one added by the placeholder #lazy_builder callback exist.');
}

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.