964898cb30c089858c608e6ca293ba3e2882a8e9
[yaffs-website] / web / core / tests / Drupal / Tests / Core / Render / RendererTestBase.php
1 <?php
2
3 /**
4  * @file
5  * Contains \Drupal\Tests\Core\Render\RendererTestBase.
6  */
7
8 namespace Drupal\Tests\Core\Render;
9
10 use Drupal\Core\Cache\Cache;
11 use Drupal\Core\Cache\CacheableMetadata;
12 use Drupal\Core\Cache\Context\ContextCacheKeys;
13 use Drupal\Core\Cache\MemoryBackend;
14 use Drupal\Core\Render\PlaceholderGenerator;
15 use Drupal\Core\Render\PlaceholderingRenderCache;
16 use Drupal\Core\Render\Renderer;
17 use Drupal\Tests\UnitTestCase;
18 use Symfony\Component\DependencyInjection\ContainerBuilder;
19 use Symfony\Component\HttpFoundation\Request;
20 use Symfony\Component\HttpFoundation\RequestStack;
21
22 /**
23  * Base class for the actual unit tests testing \Drupal\Core\Render\Renderer.
24  */
25 abstract class RendererTestBase extends UnitTestCase {
26
27   /**
28    * The tested renderer.
29    *
30    * @var \Drupal\Core\Render\Renderer
31    */
32   protected $renderer;
33
34   /**
35    * The tested render cache.
36    *
37    * @var \Drupal\Core\Render\PlaceholderingRenderCache
38    */
39   protected $renderCache;
40
41   /**
42    * The tested placeholder generator.
43    *
44    * @var \Drupal\Core\Render\PlaceholderGenerator
45    */
46   protected $placeholderGenerator;
47
48   /**
49    * @var \Symfony\Component\HttpFoundation\RequestStack
50    */
51   protected $requestStack;
52
53   /**
54    * @var \Drupal\Core\Cache\CacheFactoryInterface|\PHPUnit_Framework_MockObject_MockObject
55    */
56   protected $cacheFactory;
57
58   /**
59    * @var \Drupal\Core\Cache\Context\CacheContextsManager|\PHPUnit_Framework_MockObject_MockObject
60    */
61   protected $cacheContexts;
62
63   /**
64    * The mocked controller resolver.
65    *
66    * @var \Drupal\Core\Controller\ControllerResolverInterface|\PHPUnit_Framework_MockObject_MockObject
67    */
68   protected $controllerResolver;
69
70   /**
71    * The mocked theme manager.
72    *
73    * @var \Drupal\Core\Theme\ThemeManagerInterface|\PHPUnit_Framework_MockObject_MockObject
74    */
75   protected $themeManager;
76
77   /**
78    * The mocked element info.
79    *
80    * @var \Drupal\Core\Render\ElementInfoManagerInterface|\PHPUnit_Framework_MockObject_MockObject
81    */
82   protected $elementInfo;
83
84   /**
85    * @var \Drupal\Core\Cache\CacheBackendInterface
86    */
87   protected $memoryCache;
88
89   /**
90    * The simulated "current" user role, for use in tests with cache contexts.
91    *
92    * @var string
93    */
94   protected $currentUserRole;
95
96   /**
97    * The mocked renderer configuration.
98    *
99    * @var array
100    */
101   protected $rendererConfig = [
102     'required_cache_contexts' => [
103       'languages:language_interface',
104       'theme',
105     ],
106     'auto_placeholder_conditions' => [
107       'max-age' => 0,
108       'contexts' => ['session', 'user'],
109       'tags' => ['current-temperature'],
110     ],
111   ];
112
113   /**
114    * {@inheritdoc}
115    */
116   protected function setUp() {
117     parent::setUp();
118
119     $this->controllerResolver = $this->getMock('Drupal\Core\Controller\ControllerResolverInterface');
120     $this->themeManager = $this->getMock('Drupal\Core\Theme\ThemeManagerInterface');
121     $this->elementInfo = $this->getMock('Drupal\Core\Render\ElementInfoManagerInterface');
122     $this->elementInfo->expects($this->any())
123       ->method('getInfo')
124       ->willReturnCallback(function ($type) {
125         switch ($type) {
126           case 'details':
127             $info = ['#theme_wrappers' => ['details']];
128             break;
129           case 'link':
130             $info = ['#theme' => 'link'];
131             break;
132           default:
133             $info = [];
134         }
135         $info['#defaults_loaded'] = TRUE;
136         return $info;
137       });
138     $this->requestStack = new RequestStack();
139     $request = new Request();
140     $request->server->set('REQUEST_TIME', $_SERVER['REQUEST_TIME']);
141     $this->requestStack->push($request);
142     $this->cacheFactory = $this->getMock('Drupal\Core\Cache\CacheFactoryInterface');
143     $this->cacheContextsManager = $this->getMockBuilder('Drupal\Core\Cache\Context\CacheContextsManager')
144       ->disableOriginalConstructor()
145       ->getMock();
146     $this->cacheContextsManager->method('assertValidTokens')->willReturn(TRUE);
147     $current_user_role = &$this->currentUserRole;
148     $this->cacheContextsManager->expects($this->any())
149       ->method('convertTokensToKeys')
150       ->willReturnCallback(function($context_tokens) use (&$current_user_role) {
151         $keys = [];
152         foreach ($context_tokens as $context_id) {
153           switch ($context_id) {
154             case 'user.roles':
155               $keys[] = 'r.' . $current_user_role;
156               break;
157             case 'languages:language_interface':
158               $keys[] = 'en';
159               break;
160             case 'theme':
161               $keys[] = 'stark';
162               break;
163             default:
164               $keys[] = $context_id;
165           }
166         }
167         return new ContextCacheKeys($keys, new CacheableMetadata());
168       });
169     $this->placeholderGenerator = new PlaceholderGenerator($this->rendererConfig);
170     $this->renderCache = new PlaceholderingRenderCache($this->requestStack, $this->cacheFactory, $this->cacheContextsManager, $this->placeholderGenerator);
171     $this->renderer = new Renderer($this->controllerResolver, $this->themeManager, $this->elementInfo, $this->placeholderGenerator, $this->renderCache, $this->requestStack, $this->rendererConfig);
172
173     $container = new ContainerBuilder();
174     $container->set('cache_contexts_manager', $this->cacheContextsManager);
175     $container->set('render_cache', $this->renderCache);
176     $container->set('renderer', $this->renderer);
177     \Drupal::setContainer($container);
178   }
179
180   /**
181    * Generates a random context value for the placeholder tests.
182    *
183    * The #context array used by the placeholder #lazy_builder callback will
184    * generally be used to provide metadata like entity IDs, field machine names,
185    * paths, etc. for JavaScript replacement of content or assets. In this test,
186    * the #lazy_builder callback PlaceholdersTest::callback() renders the context
187    * inside test HTML, so using any random string would sometimes cause random
188    * test failures because the test output would be unparseable. Instead, we
189    * provide random tokens for replacement.
190    *
191    * @see PlaceholdersTest::callback()
192    * @see https://www.drupal.org/node/2151609
193    */
194   protected function randomContextValue() {
195     $tokens = ['llama', 'alpaca', 'camel', 'moose', 'elk'];
196     return $tokens[mt_rand(0, 4)];
197   }
198
199   /**
200    * Sets up a render cache back-end that is asserted to be never used.
201    */
202   protected function setUpUnusedCache() {
203     $this->cacheFactory->expects($this->never())
204       ->method('get');
205   }
206
207   /**
208    * Sets up a memory-based render cache back-end.
209    */
210   protected function setupMemoryCache() {
211     $this->memoryCache = $this->memoryCache ?: new MemoryBackend();
212
213     $this->cacheFactory->expects($this->atLeastOnce())
214       ->method('get')
215       ->with('render')
216       ->willReturn($this->memoryCache);
217   }
218
219   /**
220    * Sets up a request object on the request stack.
221    *
222    * @param string $method
223    *   The HTTP method to use for the request. Defaults to 'GET'.
224    */
225   protected function setUpRequest($method = 'GET') {
226     $request = Request::create('/', $method);
227     // Ensure that the request time is set as expected.
228     $request->server->set('REQUEST_TIME', (int) $_SERVER['REQUEST_TIME']);
229     $this->requestStack->push($request);
230   }
231
232   /**
233    * Asserts a render cache item.
234    *
235    * @param string $cid
236    *   The expected cache ID.
237    * @param mixed $data
238    *   The expected data for that cache ID.
239    * @param string $bin
240    *   The expected cache bin.
241    */
242   protected function assertRenderCacheItem($cid, $data, $bin = 'render') {
243     $cache_backend = $this->cacheFactory->get($bin);
244     $cached = $cache_backend->get($cid);
245     $this->assertNotFalse($cached, sprintf('Expected cache item "%s" exists.', $cid));
246     if ($cached !== FALSE) {
247       $this->assertEquals($data, $cached->data, sprintf('Cache item "%s" has the expected data.', $cid));
248       $this->assertSame(Cache::mergeTags($data['#cache']['tags'], ['rendered']), $cached->tags, "The cache item's cache tags also has the 'rendered' cache tag.");
249     }
250   }
251
252 }
253
254
255 class PlaceholdersTest {
256
257   /**
258    * #lazy_builder callback; attaches setting, generates markup.
259    *
260    * @param string $animal
261    *   An animal.
262    *
263    * @return array
264    *   A renderable array.
265    */
266   public static function callback($animal, $use_animal_as_array_key = FALSE) {
267     $value = $animal;
268     if ($use_animal_as_array_key) {
269       $value = [$animal => TRUE];
270     }
271     return [
272       '#markup' => '<p>This is a rendered placeholder!</p>',
273       '#attached' => [
274         'drupalSettings' => [
275           'dynamic_animal' => $value,
276         ],
277       ],
278     ];
279   }
280
281   /**
282    * #lazy_builder callback; attaches setting, generates markup, user-specific.
283    *
284    * @param string $animal
285    *   An animal.
286    *
287    * @return array
288    *   A renderable array.
289    */
290   public static function callbackPerUser($animal) {
291     $build = static::callback($animal);
292     $build['#cache']['contexts'][] = 'user';
293     return $build;
294   }
295
296   /**
297    * #lazy_builder callback; attaches setting, generates markup, cache tag.
298    *
299    * @param string $animal
300    *   An animal.
301    *
302    * @return array
303    *   A renderable array.
304    */
305   public static function callbackTagCurrentTemperature($animal) {
306     $build = static::callback($animal);
307     $build['#cache']['tags'][] = 'current-temperature';
308     return $build;
309   }
310
311 }