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