Refactored UrlManagerTest to cover more use cases

UrlManager is a complex class with a large bunch of options that needs
to be tested
and we had a few regressions in the past even though there are already a
lot of tests
covering it. Test coverage measured by lines does not help us determine
how good it is tested,
we need test coverage for every major path through the code.

I have refactored the UrlManager tests to reflect the different options
and cases to
have a better overview of which cases are covered and which are not.

UrlManager has two main operation modes:

- "default" url format, which is the simple case. These are covered by
  methods in `UrlManagerTest`.

- "pretty" url format. This is the complex case, which involves UrlRules
  and url parsing.
  I have created two separate classes for this case:
  Url creation for "pretty" url format is covered by
`UrlManagerCreateUrlTest`.
  Url parsing for "pretty" url format is covered by
`UrlManagerParseUrlTest`.

Each of the test classes have a `getUrlManager` method that creates a
UrlManager instance
with a specific configuration and certain variations in options.
It is also tested that options that are not relevant in a certain
operation mode have no effect on the result.

To make sure to not remove tests that have existed before, here is a map
of where code has been moved.
The following test methods existed in the [old test
class](4187718c14/tests/framework/web/UrlManagerTest.php):

- `testCreateUrl()` split between UrlManagerTest and
  UrlManagerCreateUrlTest variations should all be covered by
`variationsProvider()`.
- `testCreateUrlWithNullParams()` covered by UrlManagerCreateUrlTest by
  `testWithNullParams()`
- `testCreateUrlWithEmptyPattern()`
- `testCreateAbsoluteUrl()` covered in UrlManagerCreateUrlTest by new
  tests via `variationsProvider()`.
- `testCreateAbsoluteUrlWithSuffix()` covered in UrlManagerCreateUrlTest
  by `testAbsolutePatterns`.

- `testParseRequest()` covered by UrlManagerParseUrlTest, UrlNormalizer
  related parts moved to UrlNormalizerTest.
- `testParseRESTRequest()` moved to UrlManagerParseUrlTest
- `testHash()` covered in different tests in UrlManagerCreateUrlTest.
- `testMultipleHostsRules($host)` kept as is.

Before:

    $ vendor/bin/phpunit tests/framework/web/UrlManagerTest.php
    ...
    OK (12 tests, 89 assertions)

After:

    $ vendor/bin/phpunit tests/framework/web/UrlManager*.php
    ...
    OK (72 tests, 648 assertions)
This commit is contained in:
Carsten Brandt
2017-01-04 02:58:49 +01:00
parent 4187718c14
commit 8faedcbd7c
7 changed files with 1128 additions and 483 deletions

View File

@ -43,7 +43,7 @@ The [[yii\web\UrlManager|URL manager]] supports two URL formats:
- the default URL format;
- the pretty URL format.
The default URL format uses a query parameter named `r` to represent the route and normal query parameters
The default URL format uses a [[yii\web\UrlManager::$routeParam|query parameter]] named `r` to represent the route and normal query parameters
to represent the query parameters associated with the route. For example, the URL `/index.php?r=post/view&id=100` represents
the route `post/view` and the `id` query parameter `100`. The default URL format does not require any configuration of
the [[yii\web\UrlManager|URL manager]] and works in any Web server setup.

View File

@ -32,6 +32,9 @@ use yii\helpers\Url;
* ]
* ```
*
* Rules are classes implementing the [[UrlRuleInterface]], by default that is [[UrlRule]].
* For nesting rules, there is also a [[GroupUrlRule]] class.
*
* For more details and usage information on UrlManager, see the [guide article on routing](guide:runtime-routing).
*
* @property string $baseUrl The base URL that is used by [[createUrl()]] to prepend to created URLs.
@ -542,7 +545,7 @@ class UrlManager extends Component
/**
* Returns the host info that is used by [[createAbsoluteUrl()]] to prepend to created URLs.
* @return string the host info (e.g. "http://www.example.com") that is used by [[createAbsoluteUrl()]] to prepend to created URLs.
* @return string the host info (e.g. `http://www.example.com`) that is used by [[createAbsoluteUrl()]] to prepend to created URLs.
* @throws InvalidConfigException if running in console application and [[hostInfo]] is not configured.
*/
public function getHostInfo()

View File

@ -67,7 +67,7 @@ class UrlRule extends Object implements UrlRuleInterface
/**
* @var string the URL suffix used for this rule.
* For example, ".html" can be used so that the URL looks like pointing to a static HTML page.
* If not, the value of [[UrlManager::suffix]] will be used.
* If not set, the value of [[UrlManager::suffix]] will be used.
*/
public $suffix;
/**

View File

@ -0,0 +1,566 @@
<?php
namespace yiiunit\framework\web;
use yii\web\UrlManager;
use yiiunit\TestCase;
/**
* This class implements the tests for URL creation with "pretty" url format.
*
* See [[UrlManagerTest]] for tests with "default" URL format.
* See [[UrlManagerParseUrlTest]] for url parsing with "pretty" URL format.
*
* Behavior of UrlManager::createUrl() for the "pretty" URL format varies among the following options:
* - show script name = true / false
* - rules format
* - key => value
* - array config
*
* The following features are tested:
* - route only Url::to(['post/index']);
* - with params Url::to(['post/view', 'id' => 100]);
* - with anchor Url::to(['post/view', 'id' => 100, '#' => 'content']);
* - named parameters
* - as query params
* - as controller/actions '<controller:(post|comment)>/<id:\d+>/<action:(update|delete)>' => '<controller>/<action>',
* - Rules with Server Names
* - with protocol (TODO)
* - without protocol i.e protocol relative, see https://github.com/yiisoft/yii2/pull/12697 (TODO)
* - with parameters
* - with suffix
* - with default values
* - with HTTP methods (TODO)
* - absolute/relative
*
* - Adding rules dynamically (TODO)
* - Test custom rules that only implement the interface (TODO)
*
* NOTE: if a test is added here, you probably also need to add one in UrlManagerParseUrlTest.
*
* @group web
*/
class UrlManagerCreateUrlTest extends TestCase
{
protected function getUrlManager($config = [], $showScriptName = true)
{
// in this test class, all tests have enablePrettyUrl enabled.
$config['enablePrettyUrl'] = true;
$config['cache'] = null;
// set default values if they are not set
$config = array_merge([
'baseUrl' => '',
'scriptUrl' => '/index.php',
'hostInfo' => 'http://www.example.com',
'showScriptName' => $showScriptName,
], $config);
return new UrlManager($config);
}
public function variationsProvider()
{
$baseUrlConfig = [
'baseUrl' => '/test',
'scriptUrl' => '/test/index.php',
];
return [
// method name, $showScriptName, expected URL prefix
['createUrl', true, '/index.php', []],
['createUrl', false, '', []],
['createAbsoluteUrl', true, 'http://www.example.com/index.php', []],
['createAbsoluteUrl', false, 'http://www.example.com', []],
// with different baseUrl
['createUrl', true, '/test/index.php', $baseUrlConfig],
['createUrl', false, '/test', $baseUrlConfig],
['createAbsoluteUrl', true, 'http://www.example.com/test/index.php', $baseUrlConfig],
['createAbsoluteUrl', false, 'http://www.example.com/test', $baseUrlConfig],
];
}
/**
* Test createUrl() and createAbsoluteUrl()
* with varying $showScriptName
* without rules.
*
* @dataProvider variationsProvider
*/
public function testWithoutRules($method, $showScriptName, $prefix, $config)
{
$manager = $this->getUrlManager($config, $showScriptName);
$url = $manager->$method('post/view');
$this->assertEquals("$prefix/post/view", $url);
$url = $manager->$method(['post/view']);
$this->assertEquals("$prefix/post/view", $url);
$url = $manager->$method(['post/view', 'id' => 1, 'title' => 'sample post']);
$this->assertEquals("$prefix/post/view?id=1&title=sample+post", $url);
$url = $manager->$method(['post/view', '#' => 'testhash']);
$this->assertEquals("$prefix/post/view#testhash", $url);
$url = $manager->$method(['post/view', 'id' => 1, 'title' => 'sample post', '#' => 'testhash']);
$this->assertEquals("$prefix/post/view?id=1&title=sample+post#testhash", $url);
// with defaultAction
$url = $manager->$method(['/post', 'page' => 1]);
$this->assertEquals("$prefix/post?page=1", $url);
}
/**
* Test createUrl() and createAbsoluteUrl()
* with varying $showScriptName
* without rules.
* With UrlManager::$suffix
*
* @dataProvider variationsProvider
*/
public function testWithoutRulesWithSuffix($method, $showScriptName, $prefix, $config)
{
$config['suffix'] = '.html';
$manager = $this->getUrlManager($config, $showScriptName);
$url = $manager->$method('post/view');
$this->assertEquals("$prefix/post/view.html", $url);
$url = $manager->$method(['post/view']);
$this->assertEquals("$prefix/post/view.html", $url);
$url = $manager->$method(['post/view', 'id' => 1, 'title' => 'sample post']);
$this->assertEquals("$prefix/post/view.html?id=1&title=sample+post", $url);
$url = $manager->$method(['post/view', '#' => 'testhash']);
$this->assertEquals("$prefix/post/view.html#testhash", $url);
$url = $manager->$method(['post/view', 'id' => 1, 'title' => 'sample post', '#' => 'testhash']);
$this->assertEquals("$prefix/post/view.html?id=1&title=sample+post#testhash", $url);
// with defaultAction
$url = $manager->$method(['/post', 'page' => 1]);
$this->assertEquals("$prefix/post.html?page=1", $url);
// test suffix '/' as it may be trimmed
$config['suffix'] = '/';
$manager = $this->getUrlManager($config, $showScriptName);
$url = $manager->$method('post/view');
$this->assertEquals("$prefix/post/view/", $url);
$url = $manager->$method(['post/view']);
$this->assertEquals("$prefix/post/view/", $url);
$url = $manager->$method(['post/view', 'id' => 1, 'title' => 'sample post']);
$this->assertEquals("$prefix/post/view/?id=1&title=sample+post", $url);
$url = $manager->$method(['post/view', '#' => 'testhash']);
$this->assertEquals("$prefix/post/view/#testhash", $url);
$url = $manager->$method(['post/view', 'id' => 1, 'title' => 'sample post', '#' => 'testhash']);
$this->assertEquals("$prefix/post/view/?id=1&title=sample+post#testhash", $url);
// with defaultAction
$url = $manager->$method(['/post', 'page' => 1]);
$this->assertEquals("$prefix/post/?page=1", $url);
}
/**
* Test createUrl() and createAbsoluteUrl()
* with varying $showScriptName
* with simple rules.
*
* @dataProvider variationsProvider
*/
public function testSimpleRules($method, $showScriptName, $prefix, $config)
{
$config['rules'] = [
'post/<id:\d+>' => 'post/view',
'posts' => 'post/index',
'book/<id:\d+>/<title>' => 'book/view',
];
$manager = $this->getUrlManager($config, $showScriptName);
// does not match any rule
$url = $manager->$method(['post/view']);
$this->assertEquals("$prefix/post/view", $url);
// with defaultAction also does not match any rule
$url = $manager->$method(['/post', 'page' => 1]);
$this->assertEquals("$prefix/post?page=1", $url);
// match first rule
$url = $manager->$method(['post/view', 'id' => 1]);
$this->assertEquals("$prefix/post/1", $url);
// match first rule with additional param
$url = $manager->$method(['post/view', 'id' => 1, 'title' => 'sample post']);
$this->assertEquals("$prefix/post/1?title=sample+post", $url);
// match first rule with hash
$url = $manager->$method(['post/view', 'id' => 1, '#' => 'testhash']);
$this->assertEquals("$prefix/post/1#testhash", $url);
// match second rule
$url = $manager->$method(['post/index']);
$this->assertEquals("$prefix/posts", $url);
// match second rule with additional param
$url = $manager->$method(['post/index', 'category' => 'test']);
$this->assertEquals("$prefix/posts?category=test", $url);
// match third rule, ensure encoding of params
$url = $manager->$method(['book/view', 'id' => 1, 'title' => 'sample post']);
$this->assertEquals("$prefix/book/1/sample+post", $url);
}
/**
* Test createUrl() and createAbsoluteUrl()
* with varying $showScriptName
* with simple rules.
* With UrlManager::$suffix
*
* @dataProvider variationsProvider
*/
public function testSimpleRulesWithSuffix($method, $showScriptName, $prefix, $config)
{
$config['rules'] = [
'post/<id:\d+>' => 'post/view',
'posts' => 'post/index',
'book/<id:\d+>/<title>' => 'book/view',
];
$config['suffix'] = '/';
$manager = $this->getUrlManager($config, $showScriptName);
// does not match any rule
$url = $manager->$method(['post/view']);
$this->assertEquals("$prefix/post/view/", $url);
// with defaultAction also does not match any rule
$url = $manager->$method(['/post', 'page' => 1]);
$this->assertEquals("$prefix/post/?page=1", $url);
// match first rule
$url = $manager->$method(['post/view', 'id' => 1]);
$this->assertEquals("$prefix/post/1/", $url);
// match first rule with additional param
$url = $manager->$method(['post/view', 'id' => 1, 'title' => 'sample post']);
$this->assertEquals("$prefix/post/1/?title=sample+post", $url);
// match first rule with hash
$url = $manager->$method(['post/view', 'id' => 1, '#' => 'testhash']);
$this->assertEquals("$prefix/post/1/#testhash", $url);
// match second rule
$url = $manager->$method(['post/index']);
$this->assertEquals("$prefix/posts/", $url);
// match second rule with additional param
$url = $manager->$method(['post/index', 'category' => 'test']);
$this->assertEquals("$prefix/posts/?category=test", $url);
// match third rule, ensure encoding of params
$url = $manager->$method(['book/view', 'id' => 1, 'title' => 'sample post']);
$this->assertEquals("$prefix/book/1/sample+post/", $url);
}
/**
* Test createUrl() and createAbsoluteUrl()
* with varying $showScriptName
* with rules that have varadic controller/actions.
*
* @dataProvider variationsProvider
*/
public function testControllerActionParams($method, $showScriptName, $prefix, $config)
{
$config['rules'] = [
'<controller>/<id:\d+>' => '<controller>/view',
'<controller>s' => '<controller>/index',
'<controller>/default' => '<controller>', // rule to match default action
'<controller>/test/<action:\w+>' => '<controller>/<action>',
];
$manager = $this->getUrlManager($config, $showScriptName);
// match last rule
$url = $manager->$method(['post/view']);
$this->assertEquals("$prefix/post/test/view", $url);
// defaultAction should match third rule
$url = $manager->$method(['/post/']);
$this->assertEquals("$prefix/post/default", $url);
$url = $manager->$method(['/post']);
$this->assertEquals("$prefix/post/default", $url);
// match first rule
$url = $manager->$method(['post/view', 'id' => 1]);
$this->assertEquals("$prefix/post/1", $url);
// match first rule with additional param
$url = $manager->$method(['post/view', 'id' => 1, 'title' => 'sample post']);
$this->assertEquals("$prefix/post/1?title=sample+post", $url);
// match first rule with hash
$url = $manager->$method(['post/view', 'id' => 1, '#' => 'testhash']);
$this->assertEquals("$prefix/post/1#testhash", $url);
// match second rule
$url = $manager->$method(['post/index']);
$this->assertEquals("$prefix/posts", $url);
// match second rule with additional param
$url = $manager->$method(['post/index', 'category' => 'test']);
$this->assertEquals("$prefix/posts?category=test", $url);
}
/**
* Test createUrl() and createAbsoluteUrl()
* with varying $showScriptName
* with rules that have default values for parameters.
*
* @dataProvider variationsProvider
*/
public function testRulesWithDefaultParams($method, $showScriptName, $prefix, $config)
{
$config['rules'] = [
[
'pattern' => '',
'route' => 'frontend/page/view',
'defaults' => ['slug' => 'index'],
],
'page/<slug>' => 'frontend/page/view',
];
$manager = $this->getUrlManager($config, $showScriptName);
// match first rule
$url = $manager->$method(['frontend/page/view', 'slug' => 'index']);
$this->assertEquals("$prefix/", $url);
// match first rule with additional param
$url = $manager->$method(['frontend/page/view', 'slug' => 'index', 'sort' => 'name']);
$this->assertEquals("$prefix/?sort=name", $url);
// match first rule with hash
$url = $manager->$method(['frontend/page/view', 'slug' => 'index', '#' => 'testhash']);
$this->assertEquals("$prefix/#testhash", $url);
// match second rule
$url = $manager->$method(['frontend/page/view', 'slug' => 'services']);
$this->assertEquals("$prefix/page/services", $url);
// match second rule with additional param
$url = $manager->$method(['frontend/page/view', 'slug' => 'services', 'sort' => 'name']);
$this->assertEquals("$prefix/page/services?sort=name", $url);
// match second rule with hash
$url = $manager->$method(['frontend/page/view', 'slug' => 'services', '#' => 'testhash']);
$this->assertEquals("$prefix/page/services#testhash", $url);
// matches none of the rules
$url = $manager->$method(['frontend/page/view']);
$this->assertEquals("$prefix/frontend/page/view", $url);
}
/**
* Test createUrl() and createAbsoluteUrl()
* with varying $showScriptName
* with empty or null parameters.
*
* @dataProvider variationsProvider
* @see https://github.com/yiisoft/yii2/issues/10935
*/
public function testWithNullParams($method, $showScriptName, $prefix, $config)
{
$config['rules'] = [
'<param1>/<param2>' => 'site/index',
'<param1>' => 'site/index',
];
$manager = $this->getUrlManager($config, $showScriptName);
// match first rule
$url = $manager->$method(['site/index', 'param1' => 111, 'param2' => 222]);
$this->assertEquals("$prefix/111/222", $url);
$url = $manager->$method(['site/index', 'param1' => 112, 'param2' => 222]);
$this->assertEquals("$prefix/112/222", $url);
// match second rule
$url = $manager->$method(['site/index', 'param1' => 111, 'param2' => null]);
$this->assertEquals("$prefix/111", $url);
$url = $manager->$method(['site/index', 'param1' => 123, 'param2' => null]);
$this->assertEquals("$prefix/123", $url);
// match none of the rules
$url = $manager->$method(['site/index', 'param1' => null, 'param2' => 111]);
$this->assertEquals("$prefix/site/index?param2=111", $url);
$url = $manager->$method(['site/index', 'param1' => null, 'param2' => 123]);
$this->assertEquals("$prefix/site/index?param2=123", $url);
}
/**
* Test createUrl() and createAbsoluteUrl()
* with varying $showScriptName
* with empty pattern.
*
* @dataProvider variationsProvider
* @see https://github.com/yiisoft/yii2/issues/6717
*/
public function testWithEmptyPattern($method, $showScriptName, $prefix, $config)
{
$assertations = function($manager) use ($method, $prefix) {
// match first rule
$url = $manager->$method(['front/site/index']);
$this->assertEquals("$prefix/", $url);
$url = $manager->$method(['/front/site/index']);
$this->assertEquals("$prefix/", $url);
// match first rule with additional parameter
$url = $manager->$method(['front/site/index', 'page' => 1]);
$this->assertEquals("$prefix/?page=1", $url);
$url = $manager->$method(['/front/site/index', 'page' => 1]);
$this->assertEquals("$prefix/?page=1", $url);
};
// normal rule
$config['rules'] = [
'' => 'front/site/index',
];
$manager = $this->getUrlManager($config, $showScriptName);
$assertations($manager);
// rule prefixed with /
$config['rules'] = [
'' => '/front/site/index',
];
$manager = $this->getUrlManager($config, $showScriptName);
$assertations($manager);
// with suffix
$config['rules'] = [
'' => 'front/site/index',
];
$config['suffix'] = '/';
$manager = $this->getUrlManager($config, $showScriptName);
$assertations($manager);
}
public function absolutePatternsVariations()
{
$baseUrlConfig = [
'baseUrl' => '/test',
'scriptUrl' => '/test/index.php',
];
return [
// $showScriptName, expected URL prefix
[true, '/index.php', []],
[false, '', []],
// with different baseUrl
[true, '/test/index.php', $baseUrlConfig],
[false, '/test', $baseUrlConfig],
];
}
/**
* Test rules that have host info in the patterns.
* @dataProvider absolutePatternsVariations
*/
public function testAbsolutePatterns($showScriptName, $prefix, $config)
{
$config['rules'] = [
[
'pattern' => 'post/<id>/<title>',
'route' => 'post/view',
'host' => 'http://<lang:en|fr>.example.com', // TODO variation of scheme https://github.com/yiisoft/yii2/issues/12691
],
// note: baseUrl is not included in the pattern
'http://www.example.com/login' => 'site/login',
];
$manager = $this->getUrlManager($config, $showScriptName);
// first rule matches
$urlParams = ['post/view', 'id' => 1, 'title' => 'sample post', 'lang' => 'en'];
$expected = "http://en.example.com$prefix/post/1/sample+post";
$this->assertEquals($expected, $manager->createUrl($urlParams));
$this->assertEquals($expected, $manager->createAbsoluteUrl($urlParams));
$this->assertEquals($expected, $manager->createAbsoluteUrl($urlParams, true));
$this->assertEquals($expected, $manager->createAbsoluteUrl($urlParams, 'http'));
$this->assertEquals('https' . substr($expected, 4), $manager->createAbsoluteUrl($urlParams, 'https'));
$this->assertEquals(substr($expected, 5), $manager->createAbsoluteUrl($urlParams, '')); // protocol relative Url
$urlParams = ['post/view', 'id' => 1, 'title' => 'sample post', 'lang' => 'en', '#' => 'testhash'];
$expected = "http://en.example.com$prefix/post/1/sample+post#testhash";
$this->assertEquals($expected, $manager->createUrl($urlParams));
$this->assertEquals($expected, $manager->createAbsoluteUrl($urlParams));
// second rule matches
$urlParams = ['site/login'];
$expected = "http://www.example.com$prefix/login";
$this->assertEquals($expected, $manager->createUrl($urlParams));
$this->assertEquals($expected, $manager->createAbsoluteUrl($urlParams));
$this->assertEquals($expected, $manager->createAbsoluteUrl($urlParams, true));
$this->assertEquals($expected, $manager->createAbsoluteUrl($urlParams, 'http'));
$this->assertEquals('https' . substr($expected, 4), $manager->createAbsoluteUrl($urlParams, 'https'));
$this->assertEquals(substr($expected, 5), $manager->createAbsoluteUrl($urlParams, '')); // protocol relative Url
// none of the rules matches
$urlParams = ['post/index', 'page' => 1];
$this->assertEquals("$prefix/post/index?page=1", $manager->createUrl($urlParams));
$expected = "http://www.example.com$prefix/post/index?page=1";
$this->assertEquals($expected, $manager->createAbsoluteUrl($urlParams));
$this->assertEquals($expected, $manager->createAbsoluteUrl($urlParams, true));
$this->assertEquals($expected, $manager->createAbsoluteUrl($urlParams, 'http'));
$this->assertEquals('https' . substr($expected, 4), $manager->createAbsoluteUrl($urlParams, 'https'));
$this->assertEquals(substr($expected, 5), $manager->createAbsoluteUrl($urlParams, '')); // protocol relative Url
$urlParams = ['post/index', 'page' => 1, '#' => 'testhash'];
$this->assertEquals("$prefix/post/index?page=1#testhash", $manager->createUrl($urlParams));
$expected = "http://www.example.com$prefix/post/index?page=1#testhash";
$this->assertEquals($expected, $manager->createAbsoluteUrl($urlParams));
}
public function multipleHostsRulesDataProvider()
{
return [
['http://example.com'],
['https://example.com'],
['http://example.fr'],
['https://example.fr'],
];
}
/**
* Test matching of Url rules dependent on the current host info
*
* @dataProvider multipleHostsRulesDataProvider
* @see https://github.com/yiisoft/yii2/issues/7948
*/
public function testMultipleHostsRules($host)
{
$manager = new UrlManager([
'enablePrettyUrl' => true,
'cache' => null,
'rules' => [
['host' => 'http://example.com', 'pattern' => '<slug:(search)>', 'route' => 'products/search', 'defaults' => ['lang' => 'en']],
['host' => 'http://example.fr', 'pattern' => '<slug:(search)>', 'route' => 'products/search', 'defaults' => ['lang' => 'fr']],
],
'hostInfo' => $host,
'baseUrl' => '/',
'scriptUrl' => '',
]);
$url = $manager->createAbsoluteUrl(['products/search', 'lang' => 'en', 'slug' => 'search'], 'https');
$this->assertEquals('https://example.com/search', $url);
$url = $manager->createUrl(['products/search', 'lang' => 'en', 'slug' => 'search']);
$this->assertEquals('http://example.com/search', $url);
$url = $manager->createUrl(['products/search', 'lang' => 'en', 'slug' => 'search', 'param1' => 'value1']);
$this->assertEquals('http://example.com/search?param1=value1', $url);
$url = $manager->createAbsoluteUrl(['products/search', 'lang' => 'fr', 'slug' => 'search'], 'https');
$this->assertEquals('https://example.fr/search', $url);
$url = $manager->createUrl(['products/search', 'lang' => 'fr', 'slug' => 'search']);
$this->assertEquals('http://example.fr/search', $url);
$url = $manager->createUrl(['products/search', 'lang' => 'fr', 'slug' => 'search', 'param1' => 'value1']);
$this->assertEquals('http://example.fr/search?param1=value1', $url);
}
}

View File

@ -0,0 +1,343 @@
<?php
namespace yiiunit\framework\web;
use yii\web\Request;
use yii\web\UrlManager;
use yiiunit\TestCase;
/**
* This class implements the tests for URL parsing with "pretty" url format.
*
* See [[UrlManagerTest]] for tests with "default" URL format.
* See [[UrlManagerCreateUrlTest]] for url creation with "pretty" URL format.
*
* Behavior of UrlManager::parseRequest() for the "pretty" URL format varies among the following options:
* - strict parsing = true / false
* - rules format
* - key => value
* - array config
*
* The following features are tested:
* - named parameters
* - as query params
* - as controller/actions '<controller:(post|comment)>/<id:\d+>/<action:(update|delete)>' => '<controller>/<action>',
* - Rules with Server Names
* - with protocol
* - without protocol i.e protocol relative, see https://github.com/yiisoft/yii2/pull/12697
* - with parameters
* - with suffix
* - with default values
* - with HTTP methods
*
* - Adding rules dynamically
* - Test custom rules that only implement the interface
*
* NOTE: if a test is added here, you probably also need to add one in UrlManagerCreateUrlTest.
*
* @group web
*/
class UrlManagerParseUrlTest extends TestCase
{
protected function getUrlManager($config = [])
{
// in this test class, all tests have enablePrettyUrl enabled.
$config['enablePrettyUrl'] = true;
$config['cache'] = null;
// normalizer is tested in UrlNormalizerTest
$config['normalizer'] = false;
return new UrlManager($config);
}
protected function getRequest($pathInfo, $hostInfo = 'http://www.example.com', $method = 'GET', $config = [])
{
$config['pathInfo'] = $pathInfo;
$config['hostInfo'] = $hostInfo;
$_POST['_method'] = $method;
return new Request($config);
}
protected function tearDown()
{
unset($_POST['_method']);
parent::tearDown();
}
public function testWithoutRules()
{
$manager = $this->getUrlManager();
// empty pathinfo
$result = $manager->parseRequest($this->getRequest(''));
$this->assertEquals(['', []], $result);
// normal pathinfo
$result = $manager->parseRequest($this->getRequest('site/index'));
$this->assertEquals(['site/index', []], $result);
// pathinfo with module
$result = $manager->parseRequest($this->getRequest('module/site/index'));
$this->assertEquals(['module/site/index', []], $result);
// pathinfo with trailing slashes
$result = $manager->parseRequest($this->getRequest('module/site/index/'));
$this->assertEquals(['module/site/index/', []], $result);
}
public function testWithoutRulesStrict()
{
$manager = $this->getUrlManager();
$manager->enableStrictParsing = true;
// empty pathinfo
$this->assertFalse($manager->parseRequest($this->getRequest('')));
// normal pathinfo
$this->assertFalse($manager->parseRequest($this->getRequest('site/index')));
// pathinfo with module
$this->assertFalse($manager->parseRequest($this->getRequest('module/site/index')));
// pathinfo with trailing slashes
$this->assertFalse($manager->parseRequest($this->getRequest('module/site/index/')));
}
public function suffixProvider()
{
return [
['.html'],
['/'],
];
}
/**
* @dataProvider suffixProvider
*/
public function testWithoutRulesWithSuffix($suffix)
{
$manager = $this->getUrlManager(['suffix' => $suffix]);
// empty pathinfo
$result = $manager->parseRequest($this->getRequest(''));
$this->assertEquals(['', []], $result);
// normal pathinfo
$result = $manager->parseRequest($this->getRequest('site/index'));
$this->assertFalse($result);
$result = $manager->parseRequest($this->getRequest("site/index$suffix"));
$this->assertEquals(['site/index', []], $result);
// pathinfo with module
$result = $manager->parseRequest($this->getRequest('module/site/index'));
$this->assertFalse($result);
$result = $manager->parseRequest($this->getRequest("module/site/index$suffix"));
$this->assertEquals(['module/site/index', []], $result);
// pathinfo with trailing slashes
if ($suffix !== '/') {
$result = $manager->parseRequest($this->getRequest('module/site/index/'));
$this->assertFalse($result);
}
$result = $manager->parseRequest($this->getRequest("module/site/index/$suffix"));
$this->assertEquals(['module/site/index/', []], $result);
}
public function testSimpleRules()
{
$config = [
'rules' => [
'post/<id:\d+>' => 'post/view',
'posts' => 'post/index',
'book/<id:\d+>/<title>' => 'book/view',
],
];
$manager = $this->getUrlManager($config);
// matching pathinfo
$result = $manager->parseRequest($this->getRequest('book/123/this+is+sample'));
$this->assertEquals(['book/view', ['id' => '123', 'title' => 'this+is+sample']], $result);
// trailing slash is significant, no match
$result = $manager->parseRequest($this->getRequest('book/123/this+is+sample/'));
$this->assertEquals(['book/123/this+is+sample/', []], $result);
// empty pathinfo
$result = $manager->parseRequest($this->getRequest(''));
$this->assertEquals(['', []], $result);
// normal pathinfo
$result = $manager->parseRequest($this->getRequest('site/index'));
$this->assertEquals(['site/index', []], $result);
// pathinfo with module
$result = $manager->parseRequest($this->getRequest('module/site/index'));
$this->assertEquals(['module/site/index', []], $result);
}
public function testSimpleRulesStrict()
{
$config = [
'rules' => [
'post/<id:\d+>' => 'post/view',
'posts' => 'post/index',
'book/<id:\d+>/<title>' => 'book/view',
],
];
$manager = $this->getUrlManager($config);
$manager->enableStrictParsing = true;
// matching pathinfo
$result = $manager->parseRequest($this->getRequest('book/123/this+is+sample'));
$this->assertEquals(['book/view', ['id' => '123', 'title' => 'this+is+sample']], $result);
// trailing slash is significant, no match
$result = $manager->parseRequest($this->getRequest('book/123/this+is+sample/'));
$this->assertFalse($result);
// empty pathinfo
$result = $manager->parseRequest($this->getRequest(''));
$this->assertFalse($result);
// normal pathinfo
$result = $manager->parseRequest($this->getRequest('site/index'));
$this->assertFalse($result);
// pathinfo with module
$result = $manager->parseRequest($this->getRequest('module/site/index'));
$this->assertFalse($result);
}
/**
* @dataProvider suffixProvider
*/
public function testSimpleRulesWithSuffix($suffix)
{
$config = [
'rules' => [
'post/<id:\d+>' => 'post/view',
'posts' => 'post/index',
'book/<id:\d+>/<title>' => 'book/view',
],
'suffix' => $suffix,
];
$manager = $this->getUrlManager($config);
// matching pathinfo
$result = $manager->parseRequest($this->getRequest("book/123/this+is+sample"));
$this->assertFalse($result);
$result = $manager->parseRequest($this->getRequest("book/123/this+is+sample$suffix"));
$this->assertEquals(['book/view', ['id' => '123', 'title' => 'this+is+sample']], $result);
// trailing slash is significant, no match
$result = $manager->parseRequest($this->getRequest("book/123/this+is+sample/"));
if ($suffix === '/') {
$this->assertEquals(['book/view', ['id' => '123', 'title' => 'this+is+sample']], $result);
} else {
$this->assertFalse($result);
}
$result = $manager->parseRequest($this->getRequest("book/123/this+is+sample/$suffix"));
$this->assertEquals(['book/123/this+is+sample/', []], $result);
// empty pathinfo
$result = $manager->parseRequest($this->getRequest(''));
$this->assertEquals(['', []], $result);
// normal pathinfo
$result = $manager->parseRequest($this->getRequest("site/index"));
$this->assertFalse($result);
$result = $manager->parseRequest($this->getRequest("site/index$suffix"));
$this->assertEquals(['site/index', []], $result);
// pathinfo with module
$result = $manager->parseRequest($this->getRequest("module/site/index"));
$this->assertFalse($result);
$result = $manager->parseRequest($this->getRequest("module/site/index$suffix"));
$this->assertEquals(['module/site/index', []], $result);
}
/**
* @dataProvider suffixProvider
*/
public function testSimpleRulesWithSuffixStrict($suffix)
{
$config = [
'rules' => [
'post/<id:\d+>' => 'post/view',
'posts' => 'post/index',
'book/<id:\d+>/<title>' => 'book/view',
],
'suffix' => $suffix,
];
$manager = $this->getUrlManager($config);
$manager->enableStrictParsing = true;
// matching pathinfo
$result = $manager->parseRequest($this->getRequest("book/123/this+is+sample"));
$this->assertFalse($result);
$result = $manager->parseRequest($this->getRequest("book/123/this+is+sample$suffix"));
$this->assertEquals(['book/view', ['id' => '123', 'title' => 'this+is+sample']], $result);
// trailing slash is significant, no match
$result = $manager->parseRequest($this->getRequest("book/123/this+is+sample/"));
if ($suffix === '/') {
$this->assertEquals(['book/view', ['id' => '123', 'title' => 'this+is+sample']], $result);
} else {
$this->assertFalse($result);
}
$result = $manager->parseRequest($this->getRequest("book/123/this+is+sample/$suffix"));
$this->assertFalse($result);
// empty pathinfo
$result = $manager->parseRequest($this->getRequest(''));
$this->assertFalse($result);
// normal pathinfo
$result = $manager->parseRequest($this->getRequest("site/index"));
$this->assertFalse($result);
$result = $manager->parseRequest($this->getRequest("site/index$suffix"));
$this->assertFalse($result);
// pathinfo with module
$result = $manager->parseRequest($this->getRequest("module/site/index"));
$this->assertFalse($result);
$result = $manager->parseRequest($this->getRequest("module/site/index$suffix"));
$this->assertFalse($result);
}
// TODO implement with hostinfo
public function testParseRESTRequest()
{
$request = new Request;
// pretty URL rules
$manager = new UrlManager([
'enablePrettyUrl' => true,
'showScriptName' => false,
'cache' => null,
'rules' => [
'PUT,POST post/<id>/<title>' => 'post/create',
'DELETE post/<id>' => 'post/delete',
'post/<id>/<title>' => 'post/view',
'POST/GET' => 'post/get',
],
]);
// matching pathinfo GET request
$_SERVER['REQUEST_METHOD'] = 'GET';
$request->pathInfo = 'post/123/this+is+sample';
$result = $manager->parseRequest($request);
$this->assertEquals(['post/view', ['id' => '123', 'title' => 'this+is+sample']], $result);
// matching pathinfo PUT/POST request
$_SERVER['REQUEST_METHOD'] = 'PUT';
$request->pathInfo = 'post/123/this+is+sample';
$result = $manager->parseRequest($request);
$this->assertEquals(['post/create', ['id' => '123', 'title' => 'this+is+sample']], $result);
$_SERVER['REQUEST_METHOD'] = 'POST';
$request->pathInfo = 'post/123/this+is+sample';
$result = $manager->parseRequest($request);
$this->assertEquals(['post/create', ['id' => '123', 'title' => 'this+is+sample']], $result);
// no wrong matching
$_SERVER['REQUEST_METHOD'] = 'POST';
$request->pathInfo = 'POST/GET';
$result = $manager->parseRequest($request);
$this->assertEquals(['post/get', []], $result);
// createUrl should ignore REST rules
$this->mockApplication([
'components' => [
'request' => [
'hostInfo' => 'http://localhost/',
'baseUrl' => '/app'
]
]
], \yii\web\Application::className());
$this->assertEquals('/app/post/delete?id=123', $manager->createUrl(['post/delete', 'id' => 123]));
$this->destroyApplication();
unset($_SERVER['REQUEST_METHOD']);
}
}

View File

@ -1,547 +1,241 @@
<?php
namespace yiiunit\framework\web;
use Yii;
use yii\web\Request;
use yii\web\UrlManager;
use yii\web\UrlNormalizer;
use yiiunit\TestCase;
/**
* This tests verifies all features provided by UrlManager according to the documentation.
*
* UrlManager has two main operation modes:
*
* - "default" url format, which is the simple case. Tests in this class cover this case.
* Things to be covered in this mode are the following:
* - route only createUrl(['post/index']);
* - with params createUrl(['post/view', 'id' => 100]);
* - with anchor createUrl(['post/view', 'id' => 100, '#' => 'content']);
* Variations here are createUrl and createAbsoluteUrl, where absolute Urls also vary by schema.
*
* - "pretty" url format. This is the complex case, which involves UrlRules and url parsing.
* Url creation for "pretty" url format is covered by [[UrlManagerCreateUrlTest]].
* Url parsing for "pretty" url format is covered by [[UrlManagerParseUrlTest]].
*
* @group web
*/
class UrlManagerTest extends TestCase
{
protected function setUp()
protected function getUrlManager($config = [], $showScriptName = true, $enableStrictParsing = false)
{
parent::setUp();
// in this test class, all tests have enablePrettyUrl disabled.
$config['enablePrettyUrl'] = false;
$config['cache'] = null;
// baseUrl should not be used when prettyUrl is disabled
// trigger an exception here in case it gets called
$config['baseUrl'] = null;
$this->mockApplication();
Yii::$app->set('request', function() {
$this->fail('Request component should not be accessed by UrlManager with current settings.');
});
// set default values if they are not set
$config = array_merge([
'scriptUrl' => '/index.php',
'hostInfo' => 'http://www.example.com',
'showScriptName' => $showScriptName,
'enableStrictParsing' => $enableStrictParsing,
], $config);
return new UrlManager($config);
}
public function testCreateUrl()
/**
* $showScriptName and $enableStrictParsing should have no effect in default format.
* Passing these options ensures that.
*/
public function ignoredOptionsProvider()
{
return [
[false, false],
[true, false],
[false, true],
[true, true],
];
}
/**
* @dataProvider ignoredOptionsProvider
*/
public function testCreateUrlSimple($showScriptName, $enableStrictParsing)
{
// default setting with '/' as base url
$manager = new UrlManager([
'baseUrl' => '/',
'scriptUrl' => '',
'cache' => null,
]);
$manager = $this->getUrlManager([], $showScriptName, $enableStrictParsing);
$url = $manager->createUrl('post/view');
$this->assertEquals('/index.php?r=post%2Fview', $url);
$url = $manager->createUrl(['post/view']);
$this->assertEquals('?r=post%2Fview', $url);
$url = $manager->createUrl(['post/view', 'id' => 1, 'title' => 'sample post']);
$this->assertEquals('?r=post%2Fview&id=1&title=sample+post', $url);
$this->assertEquals('/index.php?r=post%2Fview', $url);
// default setting with '/test/' as base url
$manager = new UrlManager([
$manager = $this->getUrlManager([
'baseUrl' => '/test/',
'scriptUrl' => '/test',
'cache' => null,
]);
], $showScriptName, $enableStrictParsing);
$url = $manager->createUrl('post/view');
$this->assertEquals('/test?r=post%2Fview', $url);
$url = $manager->createUrl(['post/view']);
$this->assertEquals('/test?r=post%2Fview', $url);
}
/**
* @dataProvider ignoredOptionsProvider
*/
public function testCreateUrlWithParams($showScriptName, $enableStrictParsing)
{
// default setting with '/' as base url
$manager = $this->getUrlManager([], $showScriptName, $enableStrictParsing);
$url = $manager->createUrl(['post/view', 'id' => 1, 'title' => 'sample post']);
$this->assertEquals('/index.php?r=post%2Fview&id=1&title=sample+post', $url);
// default setting with '/test/' as base url
$manager = $this->getUrlManager([
'baseUrl' => '/test/',
'scriptUrl' => '/test',
], $showScriptName, $enableStrictParsing);
$url = $manager->createUrl(['post/view', 'id' => 1, 'title' => 'sample post']);
$this->assertEquals('/test?r=post%2Fview&id=1&title=sample+post', $url);
}
// pretty URL without rules
$manager = new UrlManager([
'enablePrettyUrl' => true,
'baseUrl' => '/',
'scriptUrl' => '',
'cache' => null,
]);
$url = $manager->createUrl(['post/view', 'id' => 1, 'title' => 'sample post']);
$this->assertEquals('/post/view?id=1&title=sample+post', $url);
$manager = new UrlManager([
'enablePrettyUrl' => true,
/**
* @dataProvider ignoredOptionsProvider
*
* @see https://github.com/yiisoft/yii2/pull/9596
*/
public function testCreateUrlWithAnchor($showScriptName, $enableStrictParsing)
{
// default setting with '/' as base url
$manager = $this->getUrlManager([], $showScriptName, $enableStrictParsing);
$url = $manager->createUrl(['post/view', '#' => 'anchor']);
$this->assertEquals('/index.php?r=post%2Fview#anchor', $url);
$url = $manager->createUrl(['post/view', 'id' => 1, 'title' => 'sample post', '#' => 'anchor']);
$this->assertEquals('/index.php?r=post%2Fview&id=1&title=sample+post#anchor', $url);
// default setting with '/test/' as base url
$manager = $this->getUrlManager([
'baseUrl' => '/test/',
'scriptUrl' => '/test',
'cache' => null,
]);
$url = $manager->createUrl(['post/view', 'id' => 1, 'title' => 'sample post']);
$this->assertEquals('/test/post/view?id=1&title=sample+post', $url);
$manager = new UrlManager([
'enablePrettyUrl' => true,
'baseUrl' => '/test',
'scriptUrl' => '/test/index.php',
'cache' => null,
]);
$url = $manager->createUrl(['post/view', 'id' => 1, 'title' => 'sample post']);
$this->assertEquals('/test/index.php/post/view?id=1&title=sample+post', $url);
// test showScriptName
$manager = new UrlManager([
'enablePrettyUrl' => true,
'baseUrl' => '/test',
'scriptUrl' => '/test/index.php',
'showScriptName' => true,
'cache' => null,
]);
$url = $manager->createUrl(['post/view', 'id' => 1, 'title' => 'sample post']);
$this->assertEquals('/test/index.php/post/view?id=1&title=sample+post', $url);
$url = $manager->createUrl(['/post/view', 'id' => 1, 'title' => 'sample post']);
$this->assertEquals('/test/index.php/post/view?id=1&title=sample+post', $url);
$manager = new UrlManager([
'enablePrettyUrl' => true,
'baseUrl' => '/test',
'scriptUrl' => '/test/index.php',
'showScriptName' => false,
'cache' => null,
]);
$url = $manager->createUrl(['post/view', 'id' => 1, 'title' => 'sample post']);
$this->assertEquals('/test/post/view?id=1&title=sample+post', $url);
$url = $manager->createUrl(['/post/view', 'id' => 1, 'title' => 'sample post']);
$this->assertEquals('/test/post/view?id=1&title=sample+post', $url);
// pretty URL with rules
$manager = new UrlManager([
'enablePrettyUrl' => true,
'cache' => null,
'rules' => [
[
'pattern' => 'post/<id>/<title>',
'route' => 'post/view',
],
],
'baseUrl' => '/',
'scriptUrl' => '',
]);
$url = $manager->createUrl(['post/view', 'id' => 1, 'title' => 'sample post']);
$this->assertEquals('/post/1/sample+post', $url);
$url = $manager->createUrl(['post/index', 'page' => 1]);
$this->assertEquals('/post/index?page=1', $url);
// rules with defaultAction
$url = $manager->createUrl(['/post', 'page' => 1]);
$this->assertEquals('/post?page=1', $url);
// pretty URL with rules and suffix
$manager = new UrlManager([
'enablePrettyUrl' => true,
'cache' => null,
'rules' => [
[
'pattern' => 'post/<id>/<title>',
'route' => 'post/view',
],
],
'baseUrl' => '/',
'scriptUrl' => '',
'suffix' => '.html',
]);
$url = $manager->createUrl(['post/view', 'id' => 1, 'title' => 'sample post']);
$this->assertEquals('/post/1/sample+post.html', $url);
$url = $manager->createUrl(['post/index', 'page' => 1]);
$this->assertEquals('/post/index.html?page=1', $url);
// pretty URL with rules that have host info
$manager = new UrlManager([
'enablePrettyUrl' => true,
'cache' => null,
'rules' => [
[
'pattern' => 'post/<id>/<title>',
'route' => 'post/view',
'host' => 'http://<lang:en|fr>.example.com',
],
],
'baseUrl' => '/test',
'scriptUrl' => '/test',
]);
$url = $manager->createUrl(['post/view', 'id' => 1, 'title' => 'sample post', 'lang' => 'en']);
$this->assertEquals('http://en.example.com/test/post/1/sample+post', $url);
$url = $manager->createUrl(['post/index', 'page' => 1]);
$this->assertEquals('/test/post/index?page=1', $url);
// create url with the same route but different params/defaults
$manager = new UrlManager([
'enablePrettyUrl' => true,
'cache' => null,
'rules' => [
[
'pattern' => '',
'route' => 'frontend/page/view',
'defaults' => ['slug' => 'index'],
],
'page/<slug>' => 'frontend/page/view',
],
'baseUrl' => '/test',
'scriptUrl' => '/test',
]);
$url = $manager->createUrl(['frontend/page/view', 'slug' => 'services']);
$this->assertEquals('/test/page/services', $url);
$url = $manager->createUrl(['frontend/page/view', 'slug' => 'index']);
$this->assertEquals('/test/', $url);
], $showScriptName, $enableStrictParsing);
$url = $manager->createUrl(['post/view', '#' => 'anchor']);
$this->assertEquals('/test?r=post%2Fview#anchor', $url);
$url = $manager->createUrl(['post/view', 'id' => 1, 'title' => 'sample post', '#' => 'anchor']);
$this->assertEquals('/test?r=post%2Fview&id=1&title=sample+post#anchor', $url);
}
/**
* @depends testCreateUrl
* @see https://github.com/yiisoft/yii2/issues/10935
* @dataProvider ignoredOptionsProvider
*/
public function testCreateUrlWithNullParams()
public function testCreateAbsoluteUrl($showScriptName, $enableStrictParsing)
{
$manager = new UrlManager([
'rules' => [
'<param1>/<param2>' => 'site/index',
'<param1>' => 'site/index',
],
'enablePrettyUrl' => true,
'scriptUrl' => '/test',
$manager = $this->getUrlManager([], $showScriptName, $enableStrictParsing);
$url = $manager->createAbsoluteUrl('post/view');
$this->assertEquals('http://www.example.com/index.php?r=post%2Fview', $url);
$url = $manager->createAbsoluteUrl(['post/view']);
$this->assertEquals('http://www.example.com/index.php?r=post%2Fview', $url);
]);
$this->assertEquals('/test/111', $manager->createUrl(['site/index', 'param1' => 111, 'param2' => null]));
$this->assertEquals('/test/123', $manager->createUrl(['site/index', 'param1' => 123, 'param2' => null]));
$this->assertEquals('/test/111/222', $manager->createUrl(['site/index', 'param1' => 111, 'param2' => 222]));
$this->assertEquals('/test/112/222', $manager->createUrl(['site/index', 'param1' => 112, 'param2' => 222]));
}
$url = $manager->createAbsoluteUrl('post/view', true);
$this->assertEquals('http://www.example.com/index.php?r=post%2Fview', $url);
$url = $manager->createAbsoluteUrl(['post/view'], true);
$this->assertEquals('http://www.example.com/index.php?r=post%2Fview', $url);
/**
* https://github.com/yiisoft/yii2/issues/6717
*/
public function testCreateUrlWithEmptyPattern()
{
$manager = new UrlManager([
'enablePrettyUrl' => true,
'cache' => null,
'rules' => [
'' => 'front/site/index',
],
'baseUrl' => '/',
'scriptUrl' => '',
]);
$url = $manager->createUrl(['front/site/index']);
$this->assertEquals('/', $url);
$url = $manager->createUrl(['/front/site/index']);
$this->assertEquals('/', $url);
$url = $manager->createUrl(['front/site/index', 'page' => 1]);
$this->assertEquals('/?page=1', $url);
$url = $manager->createUrl(['/front/site/index', 'page' => 1]);
$this->assertEquals('/?page=1', $url);
$url = $manager->createAbsoluteUrl('post/view', 'http');
$this->assertEquals('http://www.example.com/index.php?r=post%2Fview', $url);
$url = $manager->createAbsoluteUrl(['post/view'], 'http');
$this->assertEquals('http://www.example.com/index.php?r=post%2Fview', $url);
$manager = new UrlManager([
'enablePrettyUrl' => true,
'cache' => null,
'rules' => [
'' => '/front/site/index',
],
'baseUrl' => '/',
'scriptUrl' => '',
]);
$url = $manager->createUrl(['front/site/index']);
$this->assertEquals('/', $url);
$url = $manager->createUrl(['/front/site/index']);
$this->assertEquals('/', $url);
$url = $manager->createUrl(['front/site/index', 'page' => 1]);
$this->assertEquals('/?page=1', $url);
$url = $manager->createUrl(['/front/site/index', 'page' => 1]);
$this->assertEquals('/?page=1', $url);
}
$url = $manager->createAbsoluteUrl('post/view', 'https');
$this->assertEquals('https://www.example.com/index.php?r=post%2Fview', $url);
$url = $manager->createAbsoluteUrl(['post/view'], 'https');
$this->assertEquals('https://www.example.com/index.php?r=post%2Fview', $url);
public function testCreateAbsoluteUrl()
{
$manager = new UrlManager([
'baseUrl' => '/',
'scriptUrl' => '',
'hostInfo' => 'http://www.example.com',
'cache' => null,
]);
$url = $manager->createAbsoluteUrl(['post/view', 'id' => 1, 'title' => 'sample post']);
$this->assertEquals('http://www.example.com?r=post%2Fview&id=1&title=sample+post', $url);
$url = $manager->createAbsoluteUrl(['post/view', 'id' => 1, 'title' => 'sample post'], 'https');
$this->assertEquals('https://www.example.com?r=post%2Fview&id=1&title=sample+post', $url);
$url = $manager->createAbsoluteUrl(['post/view', 'id' => 1, 'title' => 'sample post'], '');
$this->assertEquals('//www.example.com?r=post%2Fview&id=1&title=sample+post', $url);
$url = $manager->createAbsoluteUrl('post/view', '');
$this->assertEquals('//www.example.com/index.php?r=post%2Fview', $url);
$url = $manager->createAbsoluteUrl(['post/view'], '');
$this->assertEquals('//www.example.com/index.php?r=post%2Fview', $url);
$manager->hostInfo = 'https://www.example.com';
$url = $manager->createAbsoluteUrl(['post/view', 'id' => 1, 'title' => 'sample post']);
$this->assertEquals('https://www.example.com/index.php?r=post%2Fview&id=1&title=sample+post', $url);
$url = $manager->createAbsoluteUrl(['post/view', 'id' => 1, 'title' => 'sample post'], 'https');
$this->assertEquals('https://www.example.com/index.php?r=post%2Fview&id=1&title=sample+post', $url);
$url = $manager->createAbsoluteUrl(['post/view', 'id' => 1, 'title' => 'sample post'], 'http');
$this->assertEquals('http://www.example.com?r=post%2Fview&id=1&title=sample+post', $url);
$this->assertEquals('http://www.example.com/index.php?r=post%2Fview&id=1&title=sample+post', $url);
$url = $manager->createAbsoluteUrl(['post/view', 'id' => 1, 'title' => 'sample post'], '');
$this->assertEquals('//www.example.com?r=post%2Fview&id=1&title=sample+post', $url);
$this->assertEquals('//www.example.com/index.php?r=post%2Fview&id=1&title=sample+post', $url);
}
public function testCreateAbsoluteUrlWithSuffix()
/**
* Test normalisation of different routes.
* @dataProvider ignoredOptionsProvider
*/
public function testCreateUrlRouteVariants($showScriptName, $enableStrictParsing)
{
$manager = new UrlManager([
'baseUrl' => '/',
'scriptUrl' => '',
'hostInfo' => 'http://app.example.com',
'cache' => null,
'enablePrettyUrl' => true,
'showScriptName' => false,
'suffix' => '/',
'rules' => [
'http://app.example.com/login' => 'site/login',
],
]);
$url = $manager->createAbsoluteUrl(['site/login']);
$this->assertEquals('http://app.example.com/login/', $url);
$url = $manager->createUrl(['site/login']);
$this->assertEquals('http://app.example.com/login/', $url);
// default setting with '/' as base url
$manager = $this->getUrlManager([], $showScriptName, $enableStrictParsing);
$url = $manager->createUrl(['/post/view']);
$this->assertEquals('/index.php?r=post%2Fview', $url);
$url = $manager->createUrl(['/post/view/']);
$this->assertEquals('/index.php?r=post%2Fview', $url);
$url = $manager->createUrl(['/module/post/view']);
$this->assertEquals('/index.php?r=module%2Fpost%2Fview', $url);
$url = $manager->createUrl(['/post/view/']);
$this->assertEquals('/index.php?r=post%2Fview', $url);
}
public function testParseRequest()
/**
* @return array provides different names for UrlManager::$routeParam
*/
public function routeParamProvider()
{
$manager = new UrlManager(['cache' => null]);
return [
['r'], // default value
['route'],
['_'],
];
}
/**
* @dataProvider routeParamProvider
*/
public function testParseRequest($routeParam)
{
$manager = $this->getUrlManager(['routeParam' => $routeParam]);
$request = new Request;
// default setting without 'r' param
unset($_GET['r']);
$request->setQueryParams([]);
$result = $manager->parseRequest($request);
$this->assertEquals(['', []], $result);
// default setting with 'r' param
$_GET['r'] = 'site/index';
$request->setQueryParams([$routeParam => 'site/index']);
$result = $manager->parseRequest($request);
$this->assertEquals(['site/index', []], $result);
// default setting with 'r' param as an array
$_GET['r'] = ['site/index'];
$request->setQueryParams([$routeParam => ['site/index']]);
$result = $manager->parseRequest($request);
$this->assertEquals(['', []], $result);
// pretty URL without rules
$manager = new UrlManager([
'enablePrettyUrl' => true,
'cache' => null,
'normalizer' => false,
]);
// empty pathinfo
$request->pathInfo = '';
$result = $manager->parseRequest($request);
$this->assertEquals(['', []], $result);
// normal pathinfo
$request->pathInfo = 'site/index';
// other parameters are not returned here
$request->setQueryParams([$routeParam => 'site/index', 'id' => 5]);
$result = $manager->parseRequest($request);
$this->assertEquals(['site/index', []], $result);
// pathinfo with module
$request->pathInfo = 'module/site/index';
$result = $manager->parseRequest($request);
$this->assertEquals(['module/site/index', []], $result);
// pathinfo with trailing slashes
$request->pathInfo = '/module/site/index/';
$result = $manager->parseRequest($request);
$this->assertEquals(['module/site/index/', []], $result);
// trailing slash is insignificant if normalizer is enabled
$manager->normalizer = new UrlNormalizer([
'action' => null,
]);
$request->pathInfo = '/module/site/index/';
$result = $manager->parseRequest($request);
$this->assertEquals(['module/site/index', []], $result);
// pretty URL rules
$manager = new UrlManager([
'enablePrettyUrl' => true,
'cache' => null,
'normalizer' => false,
'rules' => [
[
'pattern' => 'post/<id>/<title>',
'route' => 'post/view',
],
],
]);
// matching pathinfo
$request->pathInfo = 'post/123/this+is+sample';
$result = $manager->parseRequest($request);
$this->assertEquals(['post/view', ['id' => '123', 'title' => 'this+is+sample']], $result);
// trailing slash is significant
$request->pathInfo = 'post/123/this+is+sample/';
$result = $manager->parseRequest($request);
$this->assertEquals(['post/123/this+is+sample/', []], $result);
// empty pathinfo
$request->pathInfo = '';
$result = $manager->parseRequest($request);
$this->assertEquals(['', []], $result);
// normal pathinfo
$request->pathInfo = 'site/index';
$result = $manager->parseRequest($request);
$this->assertEquals(['site/index', []], $result);
// pathinfo with module
$request->pathInfo = 'module/site/index';
$result = $manager->parseRequest($request);
$this->assertEquals(['module/site/index', []], $result);
// trailing slash is insignificant if normalizer is enabled
$manager->normalizer = new UrlNormalizer([
'action' => null,
]);
$request->pathInfo = 'post/123/this+is+sample/';
$result = $manager->parseRequest($request);
$this->assertEquals(['post/view', ['id' => '123', 'title' => 'this+is+sample']], $result);
// pretty URL rules
$manager = new UrlManager([
'enablePrettyUrl' => true,
'suffix' => '.html',
'cache' => null,
'rules' => [
[
'pattern' => 'post/<id>/<title>',
'route' => 'post/view',
],
],
]);
// matching pathinfo
$request->pathInfo = 'post/123/this+is+sample.html';
$result = $manager->parseRequest($request);
$this->assertEquals(['post/view', ['id' => '123', 'title' => 'this+is+sample']], $result);
// matching pathinfo without suffix
$request->pathInfo = 'post/123/this+is+sample';
$result = $manager->parseRequest($request);
$this->assertFalse($result);
// empty pathinfo
$request->pathInfo = '';
$result = $manager->parseRequest($request);
$this->assertEquals(['', []], $result);
// normal pathinfo
$request->pathInfo = 'site/index.html';
$result = $manager->parseRequest($request);
$this->assertEquals(['site/index', []], $result);
// pathinfo without suffix
$request->pathInfo = 'site/index';
$result = $manager->parseRequest($request);
$this->assertFalse($result);
// strict parsing
$manager = new UrlManager([
'enablePrettyUrl' => true,
'enableStrictParsing' => true,
'suffix' => '.html',
'cache' => null,
'rules' => [
[
'pattern' => 'post/<id>/<title>',
'route' => 'post/view',
],
],
]);
// matching pathinfo
$request->pathInfo = 'post/123/this+is+sample.html';
$result = $manager->parseRequest($request);
$this->assertEquals(['post/view', ['id' => '123', 'title' => 'this+is+sample']], $result);
// unmatching pathinfo
$request->pathInfo = 'site/index.html';
$result = $manager->parseRequest($request);
$this->assertFalse($result);
}
public function testParseRESTRequest()
{
$request = new Request;
// pretty URL rules
$manager = new UrlManager([
'enablePrettyUrl' => true,
'showScriptName' => false,
'cache' => null,
'rules' => [
'PUT,POST post/<id>/<title>' => 'post/create',
'DELETE post/<id>' => 'post/delete',
'post/<id>/<title>' => 'post/view',
'POST/GET' => 'post/get',
],
]);
// matching pathinfo GET request
$_SERVER['REQUEST_METHOD'] = 'GET';
$request->pathInfo = 'post/123/this+is+sample';
$result = $manager->parseRequest($request);
$this->assertEquals(['post/view', ['id' => '123', 'title' => 'this+is+sample']], $result);
// matching pathinfo PUT/POST request
$_SERVER['REQUEST_METHOD'] = 'PUT';
$request->pathInfo = 'post/123/this+is+sample';
$result = $manager->parseRequest($request);
$this->assertEquals(['post/create', ['id' => '123', 'title' => 'this+is+sample']], $result);
$_SERVER['REQUEST_METHOD'] = 'POST';
$request->pathInfo = 'post/123/this+is+sample';
$result = $manager->parseRequest($request);
$this->assertEquals(['post/create', ['id' => '123', 'title' => 'this+is+sample']], $result);
// no wrong matching
$_SERVER['REQUEST_METHOD'] = 'POST';
$request->pathInfo = 'POST/GET';
$result = $manager->parseRequest($request);
$this->assertEquals(['post/get', []], $result);
// createUrl should ignore REST rules
$this->mockApplication([
'components' => [
'request' => [
'hostInfo' => 'http://localhost/',
'baseUrl' => '/app'
]
]
], \yii\web\Application::className());
$this->assertEquals('/app/post/delete?id=123', $manager->createUrl(['post/delete', 'id' => 123]));
$this->destroyApplication();
unset($_SERVER['REQUEST_METHOD']);
}
/**
* Tests if hash-anchor present
*
* https://github.com/yiisoft/yii2/pull/9596
*/
public function testHash()
{
$manager = new UrlManager([
'enablePrettyUrl' => true,
'cache' => null,
'rules' => [
'http://example.com/testPage' => 'site/test',
],
'hostInfo' => 'http://example.com',
'scriptUrl' => '/index.php',
]);
$url = $manager->createAbsoluteUrl(['site/test', '#' => 'testhash']);
$this->assertEquals('http://example.com/index.php/testPage#testhash', $url);
}
/**
* @dataProvider multipleHostsRulesDataProvider
*
* @see https://github.com/yiisoft/yii2/issues/7948
*
* @param string $host
*/
public function testMultipleHostsRules($host)
{
$manager = new UrlManager([
'enablePrettyUrl' => true,
'cache' => null,
'rules' => [
['host' => 'http://example.com', 'pattern' => '<slug:(search)>', 'route' => 'products/search', 'defaults' => ['lang' => 'en']],
['host' => 'http://example.fr', 'pattern' => '<slug:(search)>', 'route' => 'products/search', 'defaults' => ['lang' => 'fr']],
],
'hostInfo' => $host,
'baseUrl' => '/',
'scriptUrl' => '',
]);
$url = $manager->createAbsoluteUrl(['products/search', 'lang' => 'en', 'slug' => 'search'], 'https');
$this->assertEquals('https://example.com/search', $url);
$url = $manager->createUrl(['products/search', 'lang' => 'en', 'slug' => 'search']);
$this->assertEquals('http://example.com/search', $url);
$url = $manager->createUrl(['products/search', 'lang' => 'en', 'slug' => 'search', 'param1' => 'value1']);
$this->assertEquals('http://example.com/search?param1=value1', $url);
$url = $manager->createAbsoluteUrl(['products/search', 'lang' => 'fr', 'slug' => 'search'], 'https');
$this->assertEquals('https://example.fr/search', $url);
$url = $manager->createUrl(['products/search', 'lang' => 'fr', 'slug' => 'search']);
$this->assertEquals('http://example.fr/search', $url);
$url = $manager->createUrl(['products/search', 'lang' => 'fr', 'slug' => 'search', 'param1' => 'value1']);
$this->assertEquals('http://example.fr/search?param1=value1', $url);
}
public function multipleHostsRulesDataProvider()
{
return [
['http://example.com'],
['https://example.com'],
['http://example.fr'],
['https://example.fr'],
];
$this->assertEquals(5, $request->getQueryParam('id'));
}
}

View File

@ -10,6 +10,8 @@ namespace yiiunit\framework\web;
use Yii;
use yii\web\NotFoundHttpException;
use yii\web\Request;
use yii\web\UrlManager;
use yii\web\UrlNormalizer;
use yii\web\UrlNormalizerRedirectException;
use yiiunit\TestCase;
@ -114,4 +116,41 @@ class UrlNormalizerTest extends TestCase
$this->assertEquals($expected, $exc);
}
}
/**
* Test usage of UrlNormalizer in UrlManager
*
* trailing slash is insignificant if normalizer is enabled
*/
public function testUrlManager()
{
$config = [
'enablePrettyUrl' => true,
'cache' => null,
'normalizer' => [
'class' => 'yii\web\UrlNormalizer',
'action' => null,
],
];
$request = new Request();
// pretty URL without rules
$config['rules'] = [];
$manager = new UrlManager($config);
$request->pathInfo = '/module/site/index/';
$result = $manager->parseRequest($request);
$this->assertEquals(['module/site/index', []], $result);
// pretty URL with rules
$config['rules'] = [
[
'pattern' => 'post/<id>/<title>',
'route' => 'post/view',
],
];
$manager = new UrlManager($config);
$request->pathInfo = 'post/123/this+is+sample/';
$result = $manager->parseRequest($request);
$this->assertEquals(['post/view', ['id' => '123', 'title' => 'this+is+sample']], $result);
}
}