[ 'languages:language_interface', 'theme', ], 'auto_placeholder_conditions' => [ 'max-age' => 0, 'contexts' => ['session', 'user'], 'tags' => ['current-temperature'], ], ]; /** * {@inheritdoc} */ protected function setUp() { parent::setUp(); $this->controllerResolver = $this->getMock('Drupal\Core\Controller\ControllerResolverInterface'); $this->themeManager = $this->getMock('Drupal\Core\Theme\ThemeManagerInterface'); $this->elementInfo = $this->getMock('Drupal\Core\Render\ElementInfoManagerInterface'); $this->elementInfo->expects($this->any()) ->method('getInfo') ->willReturnCallback(function ($type) { switch ($type) { case 'details': $info = ['#theme_wrappers' => ['details']]; break; case 'link': $info = ['#theme' => 'link']; break; default: $info = []; } $info['#defaults_loaded'] = TRUE; return $info; }); $this->requestStack = new RequestStack(); $request = new Request(); $request->server->set('REQUEST_TIME', $_SERVER['REQUEST_TIME']); $this->requestStack->push($request); $this->cacheFactory = $this->getMock('Drupal\Core\Cache\CacheFactoryInterface'); $this->cacheContextsManager = $this->getMockBuilder('Drupal\Core\Cache\Context\CacheContextsManager') ->disableOriginalConstructor() ->getMock(); $this->cacheContextsManager->method('assertValidTokens')->willReturn(TRUE); $current_user_role = &$this->currentUserRole; $this->cacheContextsManager->expects($this->any()) ->method('convertTokensToKeys') ->willReturnCallback(function ($context_tokens) use (&$current_user_role) { $keys = []; foreach ($context_tokens as $context_id) { switch ($context_id) { case 'user.roles': $keys[] = 'r.' . $current_user_role; break; case 'languages:language_interface': $keys[] = 'en'; break; case 'theme': $keys[] = 'stark'; break; default: $keys[] = $context_id; } } return new ContextCacheKeys($keys, new CacheableMetadata()); }); $this->placeholderGenerator = new PlaceholderGenerator($this->rendererConfig); $this->renderCache = new PlaceholderingRenderCache($this->requestStack, $this->cacheFactory, $this->cacheContextsManager, $this->placeholderGenerator); $this->renderer = new Renderer($this->controllerResolver, $this->themeManager, $this->elementInfo, $this->placeholderGenerator, $this->renderCache, $this->requestStack, $this->rendererConfig); $container = new ContainerBuilder(); $container->set('cache_contexts_manager', $this->cacheContextsManager); $container->set('render_cache', $this->renderCache); $container->set('renderer', $this->renderer); \Drupal::setContainer($container); } /** * Generates a random context value for the placeholder tests. * * The #context array used by the placeholder #lazy_builder callback will * generally be used to provide metadata like entity IDs, field machine names, * paths, etc. for JavaScript replacement of content or assets. In this test, * the #lazy_builder callback PlaceholdersTest::callback() renders the context * inside test HTML, so using any random string would sometimes cause random * test failures because the test output would be unparseable. Instead, we * provide random tokens for replacement. * * @see PlaceholdersTest::callback() * @see https://www.drupal.org/node/2151609 */ protected function randomContextValue() { $tokens = ['llama', 'alpaca', 'camel', 'moose', 'elk']; return $tokens[mt_rand(0, 4)]; } /** * Sets up a render cache back-end that is asserted to be never used. */ protected function setUpUnusedCache() { $this->cacheFactory->expects($this->never()) ->method('get'); } /** * Sets up a memory-based render cache back-end. */ protected function setupMemoryCache() { $this->memoryCache = $this->memoryCache ?: new MemoryBackend(); $this->cacheFactory->expects($this->atLeastOnce()) ->method('get') ->with('render') ->willReturn($this->memoryCache); } /** * Sets up a request object on the request stack. * * @param string $method * The HTTP method to use for the request. Defaults to 'GET'. */ protected function setUpRequest($method = 'GET') { $request = Request::create('/', $method); // Ensure that the request time is set as expected. $request->server->set('REQUEST_TIME', (int) $_SERVER['REQUEST_TIME']); $this->requestStack->push($request); } /** * Asserts a render cache item. * * @param string $cid * The expected cache ID. * @param mixed $data * The expected data for that cache ID. * @param string $bin * The expected cache bin. */ protected function assertRenderCacheItem($cid, $data, $bin = 'render') { $cache_backend = $this->cacheFactory->get($bin); $cached = $cache_backend->get($cid); $this->assertNotFalse($cached, sprintf('Expected cache item "%s" exists.', $cid)); if ($cached !== FALSE) { $this->assertEquals($data, $cached->data, sprintf('Cache item "%s" has the expected data.', $cid)); $this->assertSame(Cache::mergeTags($data['#cache']['tags'], ['rendered']), $cached->tags, "The cache item's cache tags also has the 'rendered' cache tag."); } } } class PlaceholdersTest { /** * #lazy_builder callback; attaches setting, generates markup. * * @param string $animal * An animal. * * @return array * A renderable array. */ public static function callback($animal, $use_animal_as_array_key = FALSE) { $value = $animal; if ($use_animal_as_array_key) { $value = [$animal => TRUE]; } return [ '#markup' => '

This is a rendered placeholder!

', '#attached' => [ 'drupalSettings' => [ 'dynamic_animal' => $value, ], ], ]; } /** * #lazy_builder callback; attaches setting, generates markup, user-specific. * * @param string $animal * An animal. * * @return array * A renderable array. */ public static function callbackPerUser($animal) { $build = static::callback($animal); $build['#cache']['contexts'][] = 'user'; return $build; } /** * #lazy_builder callback; attaches setting, generates markup, cache tag. * * @param string $animal * An animal. * * @return array * A renderable array. */ public static function callbackTagCurrentTemperature($animal) { $build = static::callback($animal); $build['#cache']['tags'][] = 'current-temperature'; return $build; } }