Updated to Drupal 8.5. Core Media not yet in use.
[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.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.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(['url.path.parent', 'user.permissions'], $breadcrumb->getCacheContexts());
207     $this->assertEquals([], $breadcrumb->getCacheTags());
208     $this->assertEquals(Cache::PERMANENT, $breadcrumb->getCacheMaxAge());
209   }
210
211   /**
212    * Tests the build method with three path elements.
213    *
214    * @covers ::build
215    * @covers ::getRequestForPath
216    */
217   public function testBuildWithThreePathElements() {
218     $this->context->expects($this->once())
219       ->method('getPathInfo')
220       ->will($this->returnValue('/example/bar/baz'));
221     $this->setupStubPathProcessor();
222
223     $route_1 = new Route('/example/bar');
224     $route_2 = new Route('/example');
225
226     $this->requestMatcher->expects($this->exactly(2))
227       ->method('matchRequest')
228       ->will($this->returnCallback(function (Request $request) use ($route_1, $route_2) {
229         if ($request->getPathInfo() == '/example/bar') {
230           return [
231             RouteObjectInterface::ROUTE_NAME => 'example_bar',
232             RouteObjectInterface::ROUTE_OBJECT => $route_1,
233             '_raw_variables' => new ParameterBag([]),
234           ];
235         }
236         elseif ($request->getPathInfo() == '/example') {
237           return [
238             RouteObjectInterface::ROUTE_NAME => 'example',
239             RouteObjectInterface::ROUTE_OBJECT => $route_2,
240             '_raw_variables' => new ParameterBag([]),
241           ];
242         }
243       }));
244
245     $this->accessManager->expects($this->any())
246       ->method('check')
247       ->willReturnOnConsecutiveCalls(
248         AccessResult::allowed()->cachePerPermissions(),
249         AccessResult::allowed()->addCacheContexts(['bar'])->addCacheTags(['example'])
250       );
251     $breadcrumb = $this->builder->build($this->getMock('Drupal\Core\Routing\RouteMatchInterface'));
252     $this->assertEquals([
253       new Link('Home', new Url('<front>')),
254       new Link('Example', new Url('example')),
255       new Link('Bar', new Url('example_bar')),
256     ], $breadcrumb->getLinks());
257     $this->assertEquals(['bar', 'url.path.parent', 'user.permissions'], $breadcrumb->getCacheContexts());
258     $this->assertEquals(['example'], $breadcrumb->getCacheTags());
259     $this->assertEquals(Cache::PERMANENT, $breadcrumb->getCacheMaxAge());
260   }
261
262   /**
263    * Tests that exceptions during request matching are caught.
264    *
265    * @covers ::build
266    * @covers ::getRequestForPath
267    *
268    * @dataProvider providerTestBuildWithException
269    */
270   public function testBuildWithException($exception_class, $exception_argument) {
271     $this->context->expects($this->once())
272       ->method('getPathInfo')
273       ->will($this->returnValue('/example/bar'));
274     $this->setupStubPathProcessor();
275
276     $this->requestMatcher->expects($this->any())
277       ->method('matchRequest')
278       ->will($this->throwException(new $exception_class($exception_argument)));
279
280     $breadcrumb = $this->builder->build($this->getMock('Drupal\Core\Routing\RouteMatchInterface'));
281
282     // No path matched, though at least the frontpage is displayed.
283     $this->assertEquals([0 => new Link('Home', new Url('<front>'))], $breadcrumb->getLinks());
284     $this->assertEquals(['url.path.parent'], $breadcrumb->getCacheContexts());
285     $this->assertEquals([], $breadcrumb->getCacheTags());
286     $this->assertEquals(Cache::PERMANENT, $breadcrumb->getCacheMaxAge());
287   }
288
289   /**
290    * Provides exception types for testBuildWithException.
291    *
292    * @return array
293    *   The list of exception test cases.
294    *
295    * @see \Drupal\Tests\system\Unit\Breadcrumbs\PathBasedBreadcrumbBuilderTest::testBuildWithException()
296    */
297   public function providerTestBuildWithException() {
298     return [
299       ['Drupal\Core\ParamConverter\ParamNotConvertedException', ''],
300       ['Symfony\Component\Routing\Exception\MethodNotAllowedException', []],
301       ['Symfony\Component\Routing\Exception\ResourceNotFoundException', ''],
302     ];
303   }
304
305   /**
306    * Tests the build method with a non processed path.
307    *
308    * @covers ::build
309    * @covers ::getRequestForPath
310    */
311   public function testBuildWithNonProcessedPath() {
312     $this->context->expects($this->once())
313       ->method('getPathInfo')
314       ->will($this->returnValue('/example/bar'));
315
316     $this->pathProcessor->expects($this->once())
317       ->method('processInbound')
318       ->will($this->returnValue(FALSE));
319
320     $this->requestMatcher->expects($this->any())
321       ->method('matchRequest')
322       ->will($this->returnValue([]));
323
324     $breadcrumb = $this->builder->build($this->getMock('Drupal\Core\Routing\RouteMatchInterface'));
325
326     // No path matched, though at least the frontpage is displayed.
327     $this->assertEquals([0 => new Link('Home', new Url('<front>'))], $breadcrumb->getLinks());
328     $this->assertEquals(['url.path.parent'], $breadcrumb->getCacheContexts());
329     $this->assertEquals([], $breadcrumb->getCacheTags());
330     $this->assertEquals(Cache::PERMANENT, $breadcrumb->getCacheMaxAge());
331   }
332
333   /**
334    * Tests the applied method.
335    *
336    * @covers ::applies
337    */
338   public function testApplies() {
339     $this->assertTrue($this->builder->applies($this->getMock('Drupal\Core\Routing\RouteMatchInterface')));
340   }
341
342   /**
343    * Tests the breadcrumb for a user path.
344    *
345    * @covers ::build
346    * @covers ::getRequestForPath
347    */
348   public function testBuildWithUserPath() {
349     $this->context->expects($this->once())
350       ->method('getPathInfo')
351       ->will($this->returnValue('/user/1/edit'));
352     $this->setupStubPathProcessor();
353
354     $route_1 = new Route('/user/1');
355
356     $this->requestMatcher->expects($this->exactly(1))
357       ->method('matchRequest')
358       ->will($this->returnCallback(function (Request $request) use ($route_1) {
359         if ($request->getPathInfo() == '/user/1') {
360           return [
361             RouteObjectInterface::ROUTE_NAME => 'user_page',
362             RouteObjectInterface::ROUTE_OBJECT => $route_1,
363             '_raw_variables' => new ParameterBag([]),
364           ];
365         }
366       }));
367
368     $this->setupAccessManagerToAllow();
369     $this->titleResolver->expects($this->once())
370       ->method('getTitle')
371       ->with($this->anything(), $route_1)
372       ->will($this->returnValue('Admin'));
373
374     $breadcrumb = $this->builder->build($this->getMock('Drupal\Core\Routing\RouteMatchInterface'));
375     $this->assertEquals([0 => new Link('Home', new Url('<front>')), 1 => new Link('Admin', new Url('user_page'))], $breadcrumb->getLinks());
376     $this->assertEquals(['url.path.parent', 'user.permissions'], $breadcrumb->getCacheContexts());
377     $this->assertEquals([], $breadcrumb->getCacheTags());
378     $this->assertEquals(Cache::PERMANENT, $breadcrumb->getCacheMaxAge());
379   }
380
381   /**
382    * Setup the access manager to always allow access to routes.
383    */
384   public function setupAccessManagerToAllow() {
385     $this->accessManager->expects($this->any())
386       ->method('check')
387       ->willReturn((new AccessResultAllowed())->cachePerPermissions());
388   }
389
390   protected function setupStubPathProcessor() {
391     $this->pathProcessor->expects($this->any())
392       ->method('processInbound')
393       ->will($this->returnArgument(0));
394   }
395
396 }
397
398 /**
399  * Helper class for testing purposes only.
400  */
401 class TestPathBasedBreadcrumbBuilder extends PathBasedBreadcrumbBuilder {
402
403   public function setStringTranslation(TranslationInterface $string_translation) {
404     $this->stringTranslation = $string_translation;
405   }
406
407   public function setLinkGenerator(LinkGeneratorInterface $link_generator) {
408     $this->linkGenerator = $link_generator;
409   }
410
411 }