Updated Drupal to 8.6. This goes with the following updates because it's possible...
[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     $container = new Container();
138     $container->set('cache_contexts_manager', $cache_contexts_manager);
139     \Drupal::setContainer($container);
140   }
141
142   /**
143    * Tests the build method on the frontpage.
144    *
145    * @covers ::build
146    */
147   public function testBuildOnFrontpage() {
148     $this->pathMatcher->expects($this->once())
149       ->method('isFrontPage')
150       ->willReturn(TRUE);
151
152     $breadcrumb = $this->builder->build($this->getMock('Drupal\Core\Routing\RouteMatchInterface'));
153     $this->assertEquals([], $breadcrumb->getLinks());
154     $this->assertEquals(['url.path.is_front', 'url.path.parent'], $breadcrumb->getCacheContexts());
155     $this->assertEquals([], $breadcrumb->getCacheTags());
156     $this->assertEquals(Cache::PERMANENT, $breadcrumb->getCacheMaxAge());
157   }
158
159   /**
160    * Tests the build method with one path element.
161    *
162    * @covers ::build
163    */
164   public function testBuildWithOnePathElement() {
165     $this->context->expects($this->once())
166       ->method('getPathInfo')
167       ->will($this->returnValue('/example'));
168
169     $breadcrumb = $this->builder->build($this->getMock('Drupal\Core\Routing\RouteMatchInterface'));
170     $this->assertEquals([0 => new Link('Home', new Url('<front>'))], $breadcrumb->getLinks());
171     $this->assertEquals(['url.path.is_front', 'url.path.parent'], $breadcrumb->getCacheContexts());
172     $this->assertEquals([], $breadcrumb->getCacheTags());
173     $this->assertEquals(Cache::PERMANENT, $breadcrumb->getCacheMaxAge());
174   }
175
176   /**
177    * Tests the build method with two path elements.
178    *
179    * @covers ::build
180    * @covers ::getRequestForPath
181    */
182   public function testBuildWithTwoPathElements() {
183     $this->context->expects($this->once())
184       ->method('getPathInfo')
185       ->will($this->returnValue('/example/baz'));
186     $this->setupStubPathProcessor();
187
188     $route_1 = new Route('/example');
189
190     $this->requestMatcher->expects($this->exactly(1))
191       ->method('matchRequest')
192       ->will($this->returnCallback(function (Request $request) use ($route_1) {
193         if ($request->getPathInfo() == '/example') {
194           return [
195             RouteObjectInterface::ROUTE_NAME => 'example',
196             RouteObjectInterface::ROUTE_OBJECT => $route_1,
197             '_raw_variables' => new ParameterBag([]),
198           ];
199         }
200       }));
201
202     $this->setupAccessManagerToAllow();
203
204     $breadcrumb = $this->builder->build($this->getMock('Drupal\Core\Routing\RouteMatchInterface'));
205     $this->assertEquals([0 => new Link('Home', new Url('<front>')), 1 => new Link('Example', new Url('example'))], $breadcrumb->getLinks());
206     $this->assertEquals([
207       'url.path.is_front',
208       'url.path.parent',
209       'user.permissions',
210     ], $breadcrumb->getCacheContexts());
211     $this->assertEquals([], $breadcrumb->getCacheTags());
212     $this->assertEquals(Cache::PERMANENT, $breadcrumb->getCacheMaxAge());
213   }
214
215   /**
216    * Tests the build method with three path elements.
217    *
218    * @covers ::build
219    * @covers ::getRequestForPath
220    */
221   public function testBuildWithThreePathElements() {
222     $this->context->expects($this->once())
223       ->method('getPathInfo')
224       ->will($this->returnValue('/example/bar/baz'));
225     $this->setupStubPathProcessor();
226
227     $route_1 = new Route('/example/bar');
228     $route_2 = new Route('/example');
229
230     $this->requestMatcher->expects($this->exactly(2))
231       ->method('matchRequest')
232       ->will($this->returnCallback(function (Request $request) use ($route_1, $route_2) {
233         if ($request->getPathInfo() == '/example/bar') {
234           return [
235             RouteObjectInterface::ROUTE_NAME => 'example_bar',
236             RouteObjectInterface::ROUTE_OBJECT => $route_1,
237             '_raw_variables' => new ParameterBag([]),
238           ];
239         }
240         elseif ($request->getPathInfo() == '/example') {
241           return [
242             RouteObjectInterface::ROUTE_NAME => 'example',
243             RouteObjectInterface::ROUTE_OBJECT => $route_2,
244             '_raw_variables' => new ParameterBag([]),
245           ];
246         }
247       }));
248
249     $this->accessManager->expects($this->any())
250       ->method('check')
251       ->willReturnOnConsecutiveCalls(
252         AccessResult::allowed()->cachePerPermissions(),
253         AccessResult::allowed()->addCacheContexts(['bar'])->addCacheTags(['example'])
254       );
255     $breadcrumb = $this->builder->build($this->getMock('Drupal\Core\Routing\RouteMatchInterface'));
256     $this->assertEquals([
257       new Link('Home', new Url('<front>')),
258       new Link('Example', new Url('example')),
259       new Link('Bar', new Url('example_bar')),
260     ], $breadcrumb->getLinks());
261     $this->assertEquals([
262       'bar',
263       'url.path.is_front',
264       'url.path.parent',
265       'user.permissions',
266     ], $breadcrumb->getCacheContexts());
267     $this->assertEquals(['example'], $breadcrumb->getCacheTags());
268     $this->assertEquals(Cache::PERMANENT, $breadcrumb->getCacheMaxAge());
269   }
270
271   /**
272    * Tests that exceptions during request matching are caught.
273    *
274    * @covers ::build
275    * @covers ::getRequestForPath
276    *
277    * @dataProvider providerTestBuildWithException
278    */
279   public function testBuildWithException($exception_class, $exception_argument) {
280     $this->context->expects($this->once())
281       ->method('getPathInfo')
282       ->will($this->returnValue('/example/bar'));
283     $this->setupStubPathProcessor();
284
285     $this->requestMatcher->expects($this->any())
286       ->method('matchRequest')
287       ->will($this->throwException(new $exception_class($exception_argument)));
288
289     $breadcrumb = $this->builder->build($this->getMock('Drupal\Core\Routing\RouteMatchInterface'));
290
291     // No path matched, though at least the frontpage is displayed.
292     $this->assertEquals([0 => new Link('Home', new Url('<front>'))], $breadcrumb->getLinks());
293     $this->assertEquals(['url.path.is_front', 'url.path.parent'], $breadcrumb->getCacheContexts());
294     $this->assertEquals([], $breadcrumb->getCacheTags());
295     $this->assertEquals(Cache::PERMANENT, $breadcrumb->getCacheMaxAge());
296   }
297
298   /**
299    * Provides exception types for testBuildWithException.
300    *
301    * @return array
302    *   The list of exception test cases.
303    *
304    * @see \Drupal\Tests\system\Unit\Breadcrumbs\PathBasedBreadcrumbBuilderTest::testBuildWithException()
305    */
306   public function providerTestBuildWithException() {
307     return [
308       ['Drupal\Core\ParamConverter\ParamNotConvertedException', ''],
309       ['Symfony\Component\Routing\Exception\MethodNotAllowedException', []],
310       ['Symfony\Component\Routing\Exception\ResourceNotFoundException', ''],
311     ];
312   }
313
314   /**
315    * Tests the build method with a non processed path.
316    *
317    * @covers ::build
318    * @covers ::getRequestForPath
319    */
320   public function testBuildWithNonProcessedPath() {
321     $this->context->expects($this->once())
322       ->method('getPathInfo')
323       ->will($this->returnValue('/example/bar'));
324
325     $this->pathProcessor->expects($this->once())
326       ->method('processInbound')
327       ->will($this->returnValue(FALSE));
328
329     $this->requestMatcher->expects($this->any())
330       ->method('matchRequest')
331       ->will($this->returnValue([]));
332
333     $breadcrumb = $this->builder->build($this->getMock('Drupal\Core\Routing\RouteMatchInterface'));
334
335     // No path matched, though at least the frontpage is displayed.
336     $this->assertEquals([0 => new Link('Home', new Url('<front>'))], $breadcrumb->getLinks());
337     $this->assertEquals(['url.path.is_front', 'url.path.parent'], $breadcrumb->getCacheContexts());
338     $this->assertEquals([], $breadcrumb->getCacheTags());
339     $this->assertEquals(Cache::PERMANENT, $breadcrumb->getCacheMaxAge());
340   }
341
342   /**
343    * Tests the applied method.
344    *
345    * @covers ::applies
346    */
347   public function testApplies() {
348     $this->assertTrue($this->builder->applies($this->getMock('Drupal\Core\Routing\RouteMatchInterface')));
349   }
350
351   /**
352    * Tests the breadcrumb for a user path.
353    *
354    * @covers ::build
355    * @covers ::getRequestForPath
356    */
357   public function testBuildWithUserPath() {
358     $this->context->expects($this->once())
359       ->method('getPathInfo')
360       ->will($this->returnValue('/user/1/edit'));
361     $this->setupStubPathProcessor();
362
363     $route_1 = new Route('/user/1');
364
365     $this->requestMatcher->expects($this->exactly(1))
366       ->method('matchRequest')
367       ->will($this->returnCallback(function (Request $request) use ($route_1) {
368         if ($request->getPathInfo() == '/user/1') {
369           return [
370             RouteObjectInterface::ROUTE_NAME => 'user_page',
371             RouteObjectInterface::ROUTE_OBJECT => $route_1,
372             '_raw_variables' => new ParameterBag([]),
373           ];
374         }
375       }));
376
377     $this->setupAccessManagerToAllow();
378     $this->titleResolver->expects($this->once())
379       ->method('getTitle')
380       ->with($this->anything(), $route_1)
381       ->will($this->returnValue('Admin'));
382
383     $breadcrumb = $this->builder->build($this->getMock('Drupal\Core\Routing\RouteMatchInterface'));
384     $this->assertEquals([0 => new Link('Home', new Url('<front>')), 1 => new Link('Admin', new Url('user_page'))], $breadcrumb->getLinks());
385     $this->assertEquals([
386       'url.path.is_front',
387       'url.path.parent',
388       'user.permissions',
389     ], $breadcrumb->getCacheContexts());
390     $this->assertEquals([], $breadcrumb->getCacheTags());
391     $this->assertEquals(Cache::PERMANENT, $breadcrumb->getCacheMaxAge());
392   }
393
394   /**
395    * Setup the access manager to always allow access to routes.
396    */
397   public function setupAccessManagerToAllow() {
398     $this->accessManager->expects($this->any())
399       ->method('check')
400       ->willReturn((new AccessResultAllowed())->cachePerPermissions());
401   }
402
403   protected function setupStubPathProcessor() {
404     $this->pathProcessor->expects($this->any())
405       ->method('processInbound')
406       ->will($this->returnArgument(0));
407   }
408
409 }
410
411 /**
412  * Helper class for testing purposes only.
413  */
414 class TestPathBasedBreadcrumbBuilder extends PathBasedBreadcrumbBuilder {
415
416   public function setStringTranslation(TranslationInterface $string_translation) {
417     $this->stringTranslation = $string_translation;
418   }
419
420   public function setLinkGenerator(LinkGeneratorInterface $link_generator) {
421     $this->linkGenerator = $link_generator;
422   }
423
424 }