Security update for Core, with self-updated composer
[yaffs-website] / web / core / modules / system / tests / src / Unit / Breadcrumbs / PathBasedBreadcrumbBuilderTest.php
1 <?php
2
3 /**
4  * @file
5  * Contains \Drupal\Tests\system\Unit\Breadcrumbs\PathBasedBreadcrumbBuilderTest.
6  */
7
8 namespace Drupal\Tests\system\Unit\Breadcrumbs;
9
10 use Drupal\Core\Access\AccessResult;
11 use Drupal\Core\Cache\Cache;
12 use Drupal\Core\Link;
13 use Drupal\Core\Access\AccessResultAllowed;
14 use Drupal\Core\Path\PathMatcherInterface;
15 use Drupal\Core\StringTranslation\TranslationInterface;
16 use Drupal\Core\Url;
17 use Drupal\Core\Utility\LinkGeneratorInterface;
18 use Drupal\system\PathBasedBreadcrumbBuilder;
19 use Drupal\Tests\UnitTestCase;
20 use Symfony\Cmf\Component\Routing\RouteObjectInterface;
21 use Symfony\Component\DependencyInjection\Container;
22 use Symfony\Component\HttpFoundation\ParameterBag;
23 use Symfony\Component\HttpFoundation\Request;
24 use Symfony\Component\Routing\Route;
25
26 /**
27  * @coversDefaultClass \Drupal\system\PathBasedBreadcrumbBuilder
28  * @group system
29  */
30 class PathBasedBreadcrumbBuilderTest extends UnitTestCase {
31
32   /**
33    * The path based breadcrumb builder object to test.
34    *
35    * @var \Drupal\system\PathBasedBreadcrumbBuilder
36    */
37   protected $builder;
38
39   /**
40    * The mocked title resolver.
41    *
42    * @var \Drupal\Core\Controller\TitleResolverInterface|\PHPUnit_Framework_MockObject_MockObject
43    */
44   protected $titleResolver;
45
46   /**
47    * The mocked access manager.
48    *
49    * @var \Drupal\Core\Access\AccessManagerInterface|\PHPUnit_Framework_MockObject_MockObject
50    */
51   protected $accessManager;
52
53   /**
54    * The request matching mock object.
55    *
56    * @var \Symfony\Component\Routing\Matcher\RequestMatcherInterface|\PHPUnit_Framework_MockObject_MockObject
57    */
58   protected $requestMatcher;
59
60   /**
61    * The mocked route request context.
62    *
63    * @var \Drupal\Core\Routing\RequestContext|\PHPUnit_Framework_MockObject_MockObject
64    */
65   protected $context;
66
67   /**
68    * The mocked current user.
69    *
70    * @var \Drupal\Core\Session\AccountInterface|\PHPUnit_Framework_MockObject_MockObject
71    */
72   protected $currentUser;
73
74   /**
75    * The mocked path processor.
76    *
77    * @var \Drupal\Core\PathProcessor\InboundPathProcessorInterface|\PHPUnit_Framework_MockObject_MockObject
78    */
79   protected $pathProcessor;
80
81   /**
82    * The mocked current path.
83    *
84    * @var \Drupal\Core\Path\CurrentPathStack|\PHPUnit_Framework_MockObject_MockObject
85    */
86   protected $currentPath;
87
88   /**
89    * The mocked path matcher service.
90    *
91    * @var \Drupal\Core\Path\PathMatcherInterface|\PHPUnit_Framework_MockObject_MockObject
92    */
93   protected $pathMatcher;
94
95   /**
96    * {@inheritdoc}
97    *
98    * @covers ::__construct
99    */
100   protected function setUp() {
101     parent::setUp();
102
103     $this->requestMatcher = $this->getMock('\Symfony\Component\Routing\Matcher\RequestMatcherInterface');
104
105     $config_factory = $this->getConfigFactoryStub(['system.site' => ['front' => 'test_frontpage']]);
106
107     $this->pathProcessor = $this->getMock('\Drupal\Core\PathProcessor\InboundPathProcessorInterface');
108     $this->context = $this->getMock('\Drupal\Core\Routing\RequestContext');
109
110     $this->accessManager = $this->getMock('\Drupal\Core\Access\AccessManagerInterface');
111     $this->titleResolver = $this->getMock('\Drupal\Core\Controller\TitleResolverInterface');
112     $this->currentUser = $this->getMock('Drupal\Core\Session\AccountInterface');
113     $this->currentPath = $this->getMockBuilder('Drupal\Core\Path\CurrentPathStack')
114       ->disableOriginalConstructor()
115       ->getMock();
116
117     $this->pathMatcher = $this->getMock(PathMatcherInterface::class);
118
119     $this->builder = new TestPathBasedBreadcrumbBuilder(
120       $this->context,
121       $this->accessManager,
122       $this->requestMatcher,
123       $this->pathProcessor,
124       $config_factory,
125       $this->titleResolver,
126       $this->currentUser,
127       $this->currentPath,
128       $this->pathMatcher
129     );
130
131     $this->builder->setStringTranslation($this->getStringTranslationStub());
132
133     $cache_contexts_manager = $this->getMockBuilder('Drupal\Core\Cache\Context\CacheContextsManager')
134       ->disableOriginalConstructor()
135       ->getMock();
136     $cache_contexts_manager->method('assertValidTokens')->willReturn(TRUE);
137     $cache_contexts_manager->expects($this->any())
138       ->method('validate_tokens');
139     $container = new Container();
140     $container->set('cache_contexts_manager', $cache_contexts_manager);
141     \Drupal::setContainer($container);
142   }
143
144   /**
145    * Tests the build method on the frontpage.
146    *
147    * @covers ::build
148    */
149   public function testBuildOnFrontpage() {
150     $this->pathMatcher->expects($this->once())
151       ->method('isFrontPage')
152       ->willReturn(TRUE);
153
154     $breadcrumb = $this->builder->build($this->getMock('Drupal\Core\Routing\RouteMatchInterface'));
155     $this->assertEquals([], $breadcrumb->getLinks());
156     $this->assertEquals(['url.path.parent'], $breadcrumb->getCacheContexts());
157     $this->assertEquals([], $breadcrumb->getCacheTags());
158     $this->assertEquals(Cache::PERMANENT, $breadcrumb->getCacheMaxAge());
159   }
160
161   /**
162    * Tests the build method with one path element.
163    *
164    * @covers ::build
165    */
166   public function testBuildWithOnePathElement() {
167     $this->context->expects($this->once())
168       ->method('getPathInfo')
169       ->will($this->returnValue('/example'));
170
171     $breadcrumb = $this->builder->build($this->getMock('Drupal\Core\Routing\RouteMatchInterface'));
172     $this->assertEquals([0 => new Link('Home', new Url('<front>'))], $breadcrumb->getLinks());
173     $this->assertEquals(['url.path.parent'], $breadcrumb->getCacheContexts());
174     $this->assertEquals([], $breadcrumb->getCacheTags());
175     $this->assertEquals(Cache::PERMANENT, $breadcrumb->getCacheMaxAge());
176   }
177
178   /**
179    * Tests the build method with two path elements.
180    *
181    * @covers ::build
182    * @covers ::getRequestForPath
183    */
184   public function testBuildWithTwoPathElements() {
185     $this->context->expects($this->once())
186       ->method('getPathInfo')
187       ->will($this->returnValue('/example/baz'));
188     $this->setupStubPathProcessor();
189
190     $route_1 = new Route('/example');
191
192     $this->requestMatcher->expects($this->exactly(1))
193       ->method('matchRequest')
194       ->will($this->returnCallback(function (Request $request) use ($route_1) {
195         if ($request->getPathInfo() == '/example') {
196           return [
197             RouteObjectInterface::ROUTE_NAME => 'example',
198             RouteObjectInterface::ROUTE_OBJECT => $route_1,
199             '_raw_variables' => new ParameterBag([]),
200           ];
201         }
202       }));
203
204     $this->setupAccessManagerToAllow();
205
206     $breadcrumb = $this->builder->build($this->getMock('Drupal\Core\Routing\RouteMatchInterface'));
207     $this->assertEquals([0 => new Link('Home', new Url('<front>')), 1 => new Link('Example', new Url('example'))], $breadcrumb->getLinks());
208     $this->assertEquals(['url.path.parent', 'user.permissions'], $breadcrumb->getCacheContexts());
209     $this->assertEquals([], $breadcrumb->getCacheTags());
210     $this->assertEquals(Cache::PERMANENT, $breadcrumb->getCacheMaxAge());
211   }
212
213   /**
214    * Tests the build method with three path elements.
215    *
216    * @covers ::build
217    * @covers ::getRequestForPath
218    */
219   public function testBuildWithThreePathElements() {
220     $this->context->expects($this->once())
221       ->method('getPathInfo')
222       ->will($this->returnValue('/example/bar/baz'));
223     $this->setupStubPathProcessor();
224
225     $route_1 = new Route('/example/bar');
226     $route_2 = new Route('/example');
227
228     $this->requestMatcher->expects($this->exactly(2))
229       ->method('matchRequest')
230       ->will($this->returnCallback(function (Request $request) use ($route_1, $route_2) {
231         if ($request->getPathInfo() == '/example/bar') {
232           return [
233             RouteObjectInterface::ROUTE_NAME => 'example_bar',
234             RouteObjectInterface::ROUTE_OBJECT => $route_1,
235             '_raw_variables' => new ParameterBag([]),
236           ];
237         }
238         elseif ($request->getPathInfo() == '/example') {
239           return [
240             RouteObjectInterface::ROUTE_NAME => 'example',
241             RouteObjectInterface::ROUTE_OBJECT => $route_2,
242             '_raw_variables' => new ParameterBag([]),
243           ];
244         }
245       }));
246
247     $this->accessManager->expects($this->any())
248       ->method('check')
249       ->willReturnOnConsecutiveCalls(
250         AccessResult::allowed()->cachePerPermissions(),
251         AccessResult::allowed()->addCacheContexts(['bar'])->addCacheTags(['example'])
252       );
253     $breadcrumb = $this->builder->build($this->getMock('Drupal\Core\Routing\RouteMatchInterface'));
254     $this->assertEquals([
255       new Link('Home', new Url('<front>')),
256       new Link('Example', new Url('example')),
257       new Link('Bar', new Url('example_bar')),
258     ], $breadcrumb->getLinks());
259     $this->assertEquals(['bar', 'url.path.parent', 'user.permissions'], $breadcrumb->getCacheContexts());
260     $this->assertEquals(['example'], $breadcrumb->getCacheTags());
261     $this->assertEquals(Cache::PERMANENT, $breadcrumb->getCacheMaxAge());
262   }
263
264   /**
265    * Tests that exceptions during request matching are caught.
266    *
267    * @covers ::build
268    * @covers ::getRequestForPath
269    *
270    * @dataProvider providerTestBuildWithException
271    */
272   public function testBuildWithException($exception_class, $exception_argument) {
273     $this->context->expects($this->once())
274       ->method('getPathInfo')
275       ->will($this->returnValue('/example/bar'));
276     $this->setupStubPathProcessor();
277
278     $this->requestMatcher->expects($this->any())
279       ->method('matchRequest')
280       ->will($this->throwException(new $exception_class($exception_argument)));
281
282     $breadcrumb = $this->builder->build($this->getMock('Drupal\Core\Routing\RouteMatchInterface'));
283
284     // No path matched, though at least the frontpage is displayed.
285     $this->assertEquals([0 => new Link('Home', new Url('<front>'))], $breadcrumb->getLinks());
286     $this->assertEquals(['url.path.parent'], $breadcrumb->getCacheContexts());
287     $this->assertEquals([], $breadcrumb->getCacheTags());
288     $this->assertEquals(Cache::PERMANENT, $breadcrumb->getCacheMaxAge());
289   }
290
291   /**
292    * Provides exception types for testBuildWithException.
293    *
294    * @return array
295    *   The list of exception test cases.
296    *
297    * @see \Drupal\Tests\system\Unit\Breadcrumbs\PathBasedBreadcrumbBuilderTest::testBuildWithException()
298    */
299   public function providerTestBuildWithException() {
300     return [
301       ['Drupal\Core\ParamConverter\ParamNotConvertedException', ''],
302       ['Symfony\Component\Routing\Exception\MethodNotAllowedException', []],
303       ['Symfony\Component\Routing\Exception\ResourceNotFoundException', ''],
304     ];
305   }
306
307   /**
308    * Tests the build method with a non processed path.
309    *
310    * @covers ::build
311    * @covers ::getRequestForPath
312    */
313   public function testBuildWithNonProcessedPath() {
314     $this->context->expects($this->once())
315       ->method('getPathInfo')
316       ->will($this->returnValue('/example/bar'));
317
318     $this->pathProcessor->expects($this->once())
319       ->method('processInbound')
320       ->will($this->returnValue(FALSE));
321
322     $this->requestMatcher->expects($this->any())
323       ->method('matchRequest')
324       ->will($this->returnValue([]));
325
326     $breadcrumb = $this->builder->build($this->getMock('Drupal\Core\Routing\RouteMatchInterface'));
327
328     // No path matched, though at least the frontpage is displayed.
329     $this->assertEquals([0 => new Link('Home', new Url('<front>'))], $breadcrumb->getLinks());
330     $this->assertEquals(['url.path.parent'], $breadcrumb->getCacheContexts());
331     $this->assertEquals([], $breadcrumb->getCacheTags());
332     $this->assertEquals(Cache::PERMANENT, $breadcrumb->getCacheMaxAge());
333   }
334
335   /**
336    * Tests the applied method.
337    *
338    * @covers ::applies
339    */
340   public function testApplies() {
341     $this->assertTrue($this->builder->applies($this->getMock('Drupal\Core\Routing\RouteMatchInterface')));
342   }
343
344   /**
345    * Tests the breadcrumb for a user path.
346    *
347    * @covers ::build
348    * @covers ::getRequestForPath
349    */
350   public function testBuildWithUserPath() {
351     $this->context->expects($this->once())
352       ->method('getPathInfo')
353       ->will($this->returnValue('/user/1/edit'));
354     $this->setupStubPathProcessor();
355
356     $route_1 = new Route('/user/1');
357
358     $this->requestMatcher->expects($this->exactly(1))
359       ->method('matchRequest')
360       ->will($this->returnCallback(function (Request $request) use ($route_1) {
361         if ($request->getPathInfo() == '/user/1') {
362           return [
363             RouteObjectInterface::ROUTE_NAME => 'user_page',
364             RouteObjectInterface::ROUTE_OBJECT => $route_1,
365             '_raw_variables' => new ParameterBag([]),
366           ];
367         }
368       }));
369
370     $this->setupAccessManagerToAllow();
371     $this->titleResolver->expects($this->once())
372       ->method('getTitle')
373       ->with($this->anything(), $route_1)
374       ->will($this->returnValue('Admin'));
375
376     $breadcrumb = $this->builder->build($this->getMock('Drupal\Core\Routing\RouteMatchInterface'));
377     $this->assertEquals([0 => new Link('Home', new Url('<front>')), 1 => new Link('Admin', new Url('user_page'))], $breadcrumb->getLinks());
378     $this->assertEquals(['url.path.parent', 'user.permissions'], $breadcrumb->getCacheContexts());
379     $this->assertEquals([], $breadcrumb->getCacheTags());
380     $this->assertEquals(Cache::PERMANENT, $breadcrumb->getCacheMaxAge());
381   }
382
383   /**
384    * Setup the access manager to always allow access to routes.
385    */
386   public function setupAccessManagerToAllow() {
387     $this->accessManager->expects($this->any())
388       ->method('check')
389       ->willReturn((new AccessResultAllowed())->cachePerPermissions());
390   }
391
392   protected function setupStubPathProcessor() {
393     $this->pathProcessor->expects($this->any())
394       ->method('processInbound')
395       ->will($this->returnArgument(0));
396   }
397
398 }
399
400 /**
401  * Helper class for testing purposes only.
402  */
403 class TestPathBasedBreadcrumbBuilder extends PathBasedBreadcrumbBuilder {
404
405   public function setStringTranslation(TranslationInterface $string_translation) {
406     $this->stringTranslation = $string_translation;
407   }
408
409   public function setLinkGenerator(LinkGeneratorInterface $link_generator) {
410     $this->linkGenerator = $link_generator;
411   }
412
413 }