Newer
Older
<?php
/**
* @file
* Contains Drupal\Tests\Core\Routing\UrlGeneratorTest.
*/
namespace Drupal\Tests\Core\Routing;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\DependencyInjection\ContainerBuilder;
use Drupal\Core\PathProcessor\PathProcessorAlias;
use Drupal\Core\PathProcessor\PathProcessorManager;
Angie Byron
committed
use Drupal\Core\Routing\RequestContext;
use Drupal\Core\Routing\UrlGenerator;
use Drupal\Tests\UnitTestCase;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;
/**
* Confirm that the UrlGenerator is functioning properly.
*
* @group Routing
*/
class UrlGeneratorTest extends UnitTestCase {
Alex Pott
committed
/**
* The url generator to test.
*
* @var \Drupal\Core\Routing\UrlGenerator
*/
protected $generator;
/**
* The alias manager.
*
* @var \Drupal\Core\Path\AliasManager|\PHPUnit_Framework_MockObject_MockObject
*/
protected $aliasManager;
/**
* The mock route processor manager.
*
* @var \Drupal\Core\RouteProcessor\RouteProcessorManager|\PHPUnit_Framework_MockObject_MockObject
*/
protected $routeProcessorManager;
/**
* The request stack.
*
* @var \Symfony\Component\HttpFoundation\RequestStack
*/
protected $requestStack;
/**
* {@inheritdoc}
*/
Alex Pott
committed
protected function setUp() {
$cache_contexts_manager = $this->getMockBuilder('Drupal\Core\Cache\CacheContextsManager')
->disableOriginalConstructor()
->getMock();
$container = new ContainerBuilder();
$container->set('cache_contexts_manager', $cache_contexts_manager);
\Drupal::setContainer($container);
$routes = new RouteCollection();
$first_route = new Route('/test/one');
$second_route = new Route('/test/two/{narf}');
Alex Pott
committed
$third_route = new Route('/test/two/');
Alex Pott
committed
$fourth_route = new Route('/test/four', array(), array(), array(), '', ['https']);
$routes->add('test_1', $first_route);
$routes->add('test_2', $second_route);
Alex Pott
committed
$routes->add('test_3', $third_route);
$routes->add('test_4', $fourth_route);
// Create a route provider stub.
$provider = $this->getMockBuilder('Drupal\Core\Routing\RouteProvider')
->disableOriginalConstructor()
->getMock();
Alex Pott
committed
// We need to set up return value maps for both the getRouteByName() and the
// getRoutesByNames() method calls on the route provider. The parameters
Angie Byron
committed
// are not passed in and default to an empty array.
Alex Pott
committed
$route_name_return_map = $routes_names_return_map = array();
$return_map_values = array(
array(
'route_name' => 'test_1',
'return' => $first_route,
),
array(
'route_name' => 'test_2',
'return' => $second_route,
),
array(
'route_name' => 'test_3',
'return' => $third_route,
),
array(
'route_name' => 'test_4',
'return' => $fourth_route,
),
);
Alex Pott
committed
foreach ($return_map_values as $values) {
$route_name_return_map[] = array($values['route_name'], $values['return']);
$routes_names_return_map[] = array(array($values['route_name']), $values['return']);
Alex Pott
committed
}
$provider->expects($this->any())
->method('getRouteByName')
->will($this->returnValueMap($route_name_return_map));
$provider->expects($this->any())
->method('getRoutesByNames')
->will($this->returnValueMap($routes_names_return_map));
// Create an alias manager stub.
$alias_manager = $this->getMockBuilder('Drupal\Core\Path\AliasManager')
->disableOriginalConstructor()
->getMock();
Alex Pott
committed
$alias_manager->expects($this->any())
->method('getAliasByPath')
Alex Pott
committed
->will($this->returnCallback(array($this, 'aliasManagerCallback')));
$this->aliasManager = $alias_manager;
Angie Byron
committed
$this->requestStack = new RequestStack();
$request = Request::create('/some/path');
$this->requestStack->push($request);
$context = new RequestContext();
Angie Byron
committed
$context->fromRequestStack($this->requestStack);
$processor = new PathProcessorAlias($this->aliasManager);
$processor_manager = new PathProcessorManager();
$processor_manager->addOutbound($processor, 1000);
$this->routeProcessorManager = $this->getMockBuilder('Drupal\Core\RouteProcessor\RouteProcessorManager')
->disableOriginalConstructor()
->getMock();
$config_factory_stub = $this->getConfigFactoryStub(array('system.filter' => array('protocols' => array('http', 'https'))));
$generator = new UrlGenerator($provider, $processor_manager, $this->routeProcessorManager, $config_factory_stub, $this->requestStack);
$generator->setContext($context);
}
Alex Pott
committed
/**
* Return value callback for the getAliasByPath() method on the mock alias
* manager.
Alex Pott
committed
*
* Ensures that by default the call to getAliasByPath() will return the first
* argument that was passed in. We special-case the paths for which we wish it
* to return an actual alias.
Alex Pott
committed
*
* @return string
*/
public function aliasManagerCallback() {
$args = func_get_args();
switch($args[0]) {
case 'test/one':
return 'hello/world';
case 'test/two/5':
return 'goodbye/cruel/world';
case '<front>':
return '';
default:
return $args[0];
}
}
/**
* Confirms that generated routes will have aliased paths.
*/
public function testAliasGeneration() {
$url = $this->generator->generate('test_1');
$this->assertEquals('/hello/world', $url);
// No cacheability to test; UrlGenerator::generate() doesn't support
// collecting cacheability metadata.
Alex Pott
committed
$this->routeProcessorManager->expects($this->exactly(3))
->method('processOutbound')
->with($this->anything());
Angie Byron
committed
// Check that the two generate methods return the same result.
$this->assertGenerateFromRoute('test_1', [], [], $url, (new CacheableMetadata())->setCacheMaxAge(Cache::PERMANENT));
Angie Byron
committed
Alex Pott
committed
$path = $this->generator->getPathFromRoute('test_1');
$this->assertEquals('test/one', $path);
}
/**
* Tests URL generation in a subdirectory.
*/
public function testGetPathFromRouteWithSubdirectory() {
$this->routeProcessorManager->expects($this->once())
->method('processOutbound');
Alex Pott
committed
$path = $this->generator->getPathFromRoute('test_1');
$this->assertEquals('test/one', $path);
}
/**
* Confirms that generated routes will have aliased paths.
*/
public function testAliasGenerationWithParameters() {
$url = $this->generator->generate('test_2', array('narf' => '5'));
Angie Byron
committed
$this->assertEquals('/goodbye/cruel/world', $url);
// No cacheability to test; UrlGenerator::generate() doesn't support
// collecting cacheability metadata.
Angie Byron
committed
$this->routeProcessorManager->expects($this->exactly(7))
->method('processOutbound')
->with($this->anything());
Angie Byron
committed
$options = array('fragment' => 'top');
// Extra parameters should appear in the query string.
$this->assertGenerateFromRoute('test_1', ['zoo' => 5], $options, '/hello/world?zoo=5#top', (new CacheableMetadata())->setCacheMaxAge(Cache::PERMANENT));
Angie Byron
committed
$options = array('query' => array('page' => '1'), 'fragment' => 'bottom');
$this->assertGenerateFromRoute('test_2', ['narf' => 5], $options, '/goodbye/cruel/world?page=1#bottom', (new CacheableMetadata())->setCacheMaxAge(Cache::PERMANENT));
Angie Byron
committed
// Changing the parameters, the route still matches but there is no alias.
$this->assertGenerateFromRoute('test_2', ['narf' => 7], $options, '/test/two/7?page=1#bottom', (new CacheableMetadata())->setCacheMaxAge(Cache::PERMANENT));
Alex Pott
committed
$path = $this->generator->getPathFromRoute('test_2', array('narf' => '5'));
$this->assertEquals('test/two/5', $path);
}
/**
* Confirms that generated routes will have aliased paths with options.
*
* @dataProvider providerTestAliasGenerationWithOptions
*/
public function testAliasGenerationWithOptions($route_name, $route_parameters, $options, $expected) {
$this->assertGenerateFromRoute($route_name, $route_parameters, $options, $expected, (new CacheableMetadata())->setCacheMaxAge(Cache::PERMANENT));
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
}
/**
* Provides test data for testAliasGenerationWithOptions.
*/
public function providerTestAliasGenerationWithOptions() {
$data = [];
// Extra parameters should appear in the query string.
$data[] = [
'test_1',
['zoo' => '5'],
['fragment' => 'top'],
'/hello/world?zoo=5#top',
];
$data[] = [
'test_2',
['narf' => '5'],
['query' => ['page' => '1'], 'fragment' => 'bottom'],
'/goodbye/cruel/world?page=1#bottom',
];
// Changing the parameters, the route still matches but there is no alias.
$data[] = [
'test_2',
['narf' => '7'],
['query' => ['page' => '1'], 'fragment' => 'bottom'],
'/test/two/7?page=1#bottom',
];
// Query string values containing '/' should be decoded.
$data[] = [
'test_2',
['narf' => '7'],
['query' => ['page' => '1/2'], 'fragment' => 'bottom'],
'/test/two/7?page=1/2#bottom',
];
return $data;
}
Alex Pott
committed
/**
* Tests URL generation from route with trailing start and end slashes.
*/
public function testGetPathFromRouteTrailing() {
$this->routeProcessorManager->expects($this->once())
->method('processOutbound');
Alex Pott
committed
$path = $this->generator->getPathFromRoute('test_3');
$this->assertEquals($path, 'test/two');
}
/**
* Confirms that absolute URLs work with generated routes.
*/
public function testAbsoluteURLGeneration() {
$url = $this->generator->generate('test_1', array(), TRUE);
$this->assertEquals('http://localhost/hello/world', $url);
// No cacheability to test; UrlGenerator::generate() doesn't support
// collecting cacheability metadata.
Angie Byron
committed
$this->routeProcessorManager->expects($this->exactly(2))
->method('processOutbound')
->with($this->anything());
Angie Byron
committed
$options = array('absolute' => TRUE, 'fragment' => 'top');
// Extra parameters should appear in the query string.
$this->assertGenerateFromRoute('test_1', ['zoo' => 5], $options, 'http://localhost/hello/world?zoo=5#top', (new CacheableMetadata())->setCacheMaxAge(Cache::PERMANENT)->setCacheContexts(['url.site']));
}
catch
committed
/**
* Confirms that explicitly setting the base_url works with generated routes
*/
public function testBaseURLGeneration() {
$options = array('base_url' => 'http://www.example.com:8888');
$this->assertGenerateFromRoute('test_1', [], $options, 'http://www.example.com:8888/hello/world', (new CacheableMetadata())->setCacheMaxAge(Cache::PERMANENT));
catch
committed
$options = array('base_url' => 'http://www.example.com:8888', 'https' => TRUE);
$this->assertGenerateFromRoute('test_1', [], $options, 'https://www.example.com:8888/hello/world', (new CacheableMetadata())->setCacheMaxAge(Cache::PERMANENT));
catch
committed
$options = array('base_url' => 'https://www.example.com:8888', 'https' => FALSE);
$this->assertGenerateFromRoute('test_1', [], $options, 'http://www.example.com:8888/hello/world', (new CacheableMetadata())->setCacheMaxAge(Cache::PERMANENT));
catch
committed
$this->routeProcessorManager->expects($this->exactly(2))
catch
committed
->method('processOutbound')
->with($this->anything());
$options = array('base_url' => 'http://www.example.com:8888', 'fragment' => 'top');
// Extra parameters should appear in the query string.
$this->assertGenerateFromRoute('test_1', ['zoo' => 5], $options, 'http://www.example.com:8888/hello/world?zoo=5#top', (new CacheableMetadata())->setCacheMaxAge(Cache::PERMANENT));
catch
committed
}
Alex Pott
committed
/**
* Test that the 'scheme' route requirement is respected during url generation.
*/
public function testUrlGenerationWithHttpsRequirement() {
$url = $this->generator->generate('test_4', array(), TRUE);
$this->assertEquals('https://localhost/test/four', $url);
// No cacheability to test; UrlGenerator::generate() doesn't support
// collecting cacheability metadata.
Angie Byron
committed
$this->routeProcessorManager->expects($this->exactly(2))
->method('processOutbound')
->with($this->anything());
Angie Byron
committed
$options = array('absolute' => TRUE, 'https' => TRUE);
$this->assertGenerateFromRoute('test_1', [], $options, 'https://localhost/hello/world', (new CacheableMetadata())->setCacheMaxAge(Cache::PERMANENT)->setCacheContexts(['url.site']));
Alex Pott
committed
}
/**
* Tests path-based URL generation.
*/
public function testPathBasedURLGeneration() {
$base_path = '/subdir';
$base_url = 'http://www.example.com' . $base_path;
foreach (array('', 'index.php/') as $script_path) {
foreach (array(FALSE, TRUE) as $absolute) {
// Setup a fake request which looks like a Drupal installed under the
// subdir "subdir" on the domain www.example.com.
// To reproduce the values install Drupal like that and use a debugger.
$server = [
'SCRIPT_NAME' => '/subdir/index.php',
'SCRIPT_FILENAME' => $this->root . '/index.php',
'SERVER_NAME' => 'http://www.example.com',
];
$request = Request::create('/subdir/' . $script_path, 'GET', [], [], [], $server);
$request->headers->set('host', ['www.example.com']);
$this->requestStack->push($request);
// Determine the expected cacheability.
$expected_cacheability = (new CacheableMetadata())
->setCacheContexts($absolute ? ['url.site'] : [])
->setCacheMaxAge(Cache::PERMANENT);
// Get the expected start of the path string.
$base = ($absolute ? $base_url . '/' : $base_path . '/') . $script_path;
$url = $base . 'node/123';
$result = $this->generator->generateFromPath('node/123', array('absolute' => $absolute));
$this->assertEquals($url, $result, "$url == $result");
$generated_url = $this->generator->generateFromPath('node/123', array('absolute' => $absolute), TRUE);
$this->assertEquals($url, $generated_url->getGeneratedUrl(), "$url == $result");
$this->assertEquals($expected_cacheability, CacheableMetadata::createFromObject($generated_url));
$url = $base . 'node/123#foo';
$result = $this->generator->generateFromPath('node/123', array('fragment' => 'foo', 'absolute' => $absolute));
$this->assertEquals($url, $result, "$url == $result");
$generated_url = $this->generator->generateFromPath('node/123', array('fragment' => 'foo', 'absolute' => $absolute), TRUE);
$this->assertEquals($url, $generated_url->getGeneratedUrl(), "$url == $result");
$this->assertEquals($expected_cacheability, CacheableMetadata::createFromObject($generated_url));
$url = $base . 'node/123?foo';
$result = $this->generator->generateFromPath('node/123', array('query' => array('foo' => NULL), 'absolute' => $absolute));
$this->assertEquals($url, $result, "$url == $result");
$generated_url = $this->generator->generateFromPath('node/123', array('query' => array('foo' => NULL), 'absolute' => $absolute), TRUE);
$this->assertEquals($url, $generated_url->getGeneratedUrl(), "$url == $result");
$this->assertEquals($expected_cacheability, CacheableMetadata::createFromObject($generated_url));
$url = $base . 'node/123?foo=bar&bar=baz';
$result = $this->generator->generateFromPath('node/123', array('query' => array('foo' => 'bar', 'bar' => 'baz'), 'absolute' => $absolute));
$this->assertEquals($url, $result, "$url == $result");
$generated_url = $this->generator->generateFromPath('node/123', array('query' => array('foo' => 'bar', 'bar' => 'baz'), 'absolute' => $absolute), TRUE);
$this->assertEquals($url, $generated_url->getGeneratedUrl(), "$url == $result");
$this->assertEquals($expected_cacheability, CacheableMetadata::createFromObject($generated_url));
$url = $base . 'node/123?foo#bar';
$result = $this->generator->generateFromPath('node/123', array('query' => array('foo' => NULL), 'fragment' => 'bar', 'absolute' => $absolute));
$this->assertEquals($url, $result, "$url == $result");
$generated_url = $this->generator->generateFromPath('node/123', array('query' => array('foo' => NULL), 'fragment' => 'bar', 'absolute' => $absolute), TRUE);
$this->assertEquals($url, $generated_url->getGeneratedUrl(), "$url == $result");
$this->assertEquals($expected_cacheability, CacheableMetadata::createFromObject($generated_url));
$url = $base;
$result = $this->generator->generateFromPath('<front>', array('absolute' => $absolute));
$this->assertEquals($url, $result, "$url == $result");
$generated_url = $this->generator->generateFromPath('<front>', array('absolute' => $absolute), TRUE);
$this->assertEquals($url, $generated_url->getGeneratedUrl(), "$url == $result");
$this->assertEquals($expected_cacheability, CacheableMetadata::createFromObject($generated_url));
}
}
}
/**
* Asserts \Drupal\Core\Routing\UrlGenerator::generateFromRoute()'s output.
*
* @param $route_name
* The route name to test.
* @param array $route_parameters
* The route parameters to test.
* @param array $options
* The options to test.
* @param $expected_url
* The expected generated URL string.
* @param \Drupal\Core\Cache\CacheableMetadata $expected_cacheability
* The expected generated cacheability metadata.
*/
protected function assertGenerateFromRoute($route_name, array $route_parameters, array $options, $expected_url, CacheableMetadata $expected_cacheability) {
// First, test with $collect_cacheability_metadata set to the default value.
$url = $this->generator->generateFromRoute($route_name, $route_parameters, $options);
$this->assertSame($expected_url, $url);
// Second, test with it set to TRUE.
$generated_url = $this->generator->generateFromRoute($route_name, $route_parameters, $options, TRUE);
$this->assertSame($expected_url, $generated_url->getGeneratedUrl());
$this->assertEquals($expected_cacheability, CacheableMetadata::createFromObject($generated_url));
}