5 * Contains \Drupal\Tests\system\Unit\Breadcrumbs\PathBasedBreadcrumbBuilderTest.
8 namespace Drupal\Tests\system\Unit\Breadcrumbs;
10 use Drupal\Core\Access\AccessResult;
11 use Drupal\Core\Cache\Cache;
13 use Drupal\Core\Access\AccessResultAllowed;
14 use Drupal\Core\StringTranslation\TranslationInterface;
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;
26 * @coversDefaultClass \Drupal\system\PathBasedBreadcrumbBuilder
29 class PathBasedBreadcrumbBuilderTest extends UnitTestCase {
32 * The path based breadcrumb builder object to test.
34 * @var \Drupal\system\PathBasedBreadcrumbBuilder
39 * The mocked title resolver.
41 * @var \Drupal\Core\Controller\TitleResolverInterface|\PHPUnit_Framework_MockObject_MockObject
43 protected $titleResolver;
46 * The mocked access manager.
48 * @var \Drupal\Core\Access\AccessManagerInterface|\PHPUnit_Framework_MockObject_MockObject
50 protected $accessManager;
53 * The request matching mock object.
55 * @var \Symfony\Component\Routing\Matcher\RequestMatcherInterface|\PHPUnit_Framework_MockObject_MockObject
57 protected $requestMatcher;
60 * The mocked route request context.
62 * @var \Drupal\Core\Routing\RequestContext|\PHPUnit_Framework_MockObject_MockObject
67 * The mocked current user.
69 * @var \Drupal\Core\Session\AccountInterface|\PHPUnit_Framework_MockObject_MockObject
71 protected $currentUser;
74 * The mocked path processor.
76 * @var \Drupal\Core\PathProcessor\InboundPathProcessorInterface|\PHPUnit_Framework_MockObject_MockObject
78 protected $pathProcessor;
81 * The mocked current path.
83 * @var \Drupal\Core\Path\CurrentPathStack|\PHPUnit_Framework_MockObject_MockObject
85 protected $currentPath;
90 * @covers ::__construct
92 protected function setUp() {
95 $this->requestMatcher = $this->getMock('\Symfony\Component\Routing\Matcher\RequestMatcherInterface');
97 $config_factory = $this->getConfigFactoryStub(['system.site' => ['front' => 'test_frontpage']]);
99 $this->pathProcessor = $this->getMock('\Drupal\Core\PathProcessor\InboundPathProcessorInterface');
100 $this->context = $this->getMock('\Drupal\Core\Routing\RequestContext');
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()
109 $this->builder = new TestPathBasedBreadcrumbBuilder(
111 $this->accessManager,
112 $this->requestMatcher,
113 $this->pathProcessor,
115 $this->titleResolver,
120 $this->builder->setStringTranslation($this->getStringTranslationStub());
122 $cache_contexts_manager = $this->getMockBuilder('Drupal\Core\Cache\Context\CacheContextsManager')
123 ->disableOriginalConstructor()
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);
134 * Tests the build method on the frontpage.
138 public function testBuildOnFrontpage() {
139 $this->context->expects($this->once())
140 ->method('getPathInfo')
141 ->will($this->returnValue('/'));
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());
151 * Tests the build method with one path element.
155 public function testBuildWithOnePathElement() {
156 $this->context->expects($this->once())
157 ->method('getPathInfo')
158 ->will($this->returnValue('/example'));
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());
168 * Tests the build method with two path elements.
171 * @covers ::getRequestForPath
173 public function testBuildWithTwoPathElements() {
174 $this->context->expects($this->once())
175 ->method('getPathInfo')
176 ->will($this->returnValue('/example/baz'));
177 $this->setupStubPathProcessor();
179 $route_1 = new Route('/example');
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') {
186 RouteObjectInterface::ROUTE_NAME => 'example',
187 RouteObjectInterface::ROUTE_OBJECT => $route_1,
188 '_raw_variables' => new ParameterBag([]),
193 $this->setupAccessManagerToAllow();
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());
203 * Tests the build method with three path elements.
206 * @covers ::getRequestForPath
208 public function testBuildWithThreePathElements() {
209 $this->context->expects($this->once())
210 ->method('getPathInfo')
211 ->will($this->returnValue('/example/bar/baz'));
212 $this->setupStubPathProcessor();
214 $route_1 = new Route('/example/bar');
215 $route_2 = new Route('/example');
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') {
222 RouteObjectInterface::ROUTE_NAME => 'example_bar',
223 RouteObjectInterface::ROUTE_OBJECT => $route_1,
224 '_raw_variables' => new ParameterBag([]),
227 elseif ($request->getPathInfo() == '/example') {
229 RouteObjectInterface::ROUTE_NAME => 'example',
230 RouteObjectInterface::ROUTE_OBJECT => $route_2,
231 '_raw_variables' => new ParameterBag([]),
236 $this->accessManager->expects($this->any())
238 ->willReturnOnConsecutiveCalls(
239 AccessResult::allowed()->cachePerPermissions(),
240 AccessResult::allowed()->addCacheContexts(['bar'])->addCacheTags(['example'])
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());
254 * Tests that exceptions during request matching are caught.
257 * @covers ::getRequestForPath
259 * @dataProvider providerTestBuildWithException
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();
267 $this->requestMatcher->expects($this->any())
268 ->method('matchRequest')
269 ->will($this->throwException(new $exception_class($exception_argument)));
271 $breadcrumb = $this->builder->build($this->getMock('Drupal\Core\Routing\RouteMatchInterface'));
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());
281 * Provides exception types for testBuildWithException.
284 * The list of exception test cases.
286 * @see \Drupal\Tests\system\Unit\Breadcrumbs\PathBasedBreadcrumbBuilderTest::testBuildWithException()
288 public function providerTestBuildWithException() {
290 ['Drupal\Core\ParamConverter\ParamNotConvertedException', ''],
291 ['Symfony\Component\Routing\Exception\MethodNotAllowedException', []],
292 ['Symfony\Component\Routing\Exception\ResourceNotFoundException', ''],
297 * Tests the build method with a non processed path.
300 * @covers ::getRequestForPath
302 public function testBuildWithNonProcessedPath() {
303 $this->context->expects($this->once())
304 ->method('getPathInfo')
305 ->will($this->returnValue('/example/bar'));
307 $this->pathProcessor->expects($this->once())
308 ->method('processInbound')
309 ->will($this->returnValue(FALSE));
311 $this->requestMatcher->expects($this->any())
312 ->method('matchRequest')
313 ->will($this->returnValue([]));
315 $breadcrumb = $this->builder->build($this->getMock('Drupal\Core\Routing\RouteMatchInterface'));
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());
325 * Tests the applied method.
329 public function testApplies() {
330 $this->assertTrue($this->builder->applies($this->getMock('Drupal\Core\Routing\RouteMatchInterface')));
334 * Tests the breadcrumb for a user path.
337 * @covers ::getRequestForPath
339 public function testBuildWithUserPath() {
340 $this->context->expects($this->once())
341 ->method('getPathInfo')
342 ->will($this->returnValue('/user/1/edit'));
343 $this->setupStubPathProcessor();
345 $route_1 = new Route('/user/1');
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') {
352 RouteObjectInterface::ROUTE_NAME => 'user_page',
353 RouteObjectInterface::ROUTE_OBJECT => $route_1,
354 '_raw_variables' => new ParameterBag([]),
359 $this->setupAccessManagerToAllow();
360 $this->titleResolver->expects($this->once())
362 ->with($this->anything(), $route_1)
363 ->will($this->returnValue('Admin'));
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());
373 * Setup the access manager to always allow access to routes.
375 public function setupAccessManagerToAllow() {
376 $this->accessManager->expects($this->any())
378 ->willReturn((new AccessResultAllowed())->cachePerPermissions());
381 protected function setupStubPathProcessor() {
382 $this->pathProcessor->expects($this->any())
383 ->method('processInbound')
384 ->will($this->returnArgument(0));
390 * Helper class for testing purposes only.
392 class TestPathBasedBreadcrumbBuilder extends PathBasedBreadcrumbBuilder {
394 public function setStringTranslation(TranslationInterface $string_translation) {
395 $this->stringTranslation = $string_translation;
398 public function setLinkGenerator(LinkGeneratorInterface $link_generator) {
399 $this->linkGenerator = $link_generator;