RouteTest.php 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894
  1. <?php
  2. /**
  3. * Description of RouteTest
  4. *
  5. * @group ko7
  6. * @group ko7.core
  7. * @group ko7.core.route
  8. *
  9. * @package KO7
  10. * @category Tests
  11. *
  12. * @author BRMatt <matthew@sigswitch.com>
  13. * @copyright (c) 2007-2016 Kohana Team
  14. * @copyright (c) since 2016 Koseven Team
  15. * @license https://koseven.dev/LICENSE
  16. */
  17. include KO7::find_file('tests', 'test_data/callback_routes');
  18. class KO7_RouteTest extends Unittest_TestCase
  19. {
  20. /**
  21. * Remove all caches
  22. * @throws Cache_Exception
  23. */
  24. // @codingStandardsIgnoreStart
  25. public function setUp(): void
  26. // @codingStandardsIgnoreEnd
  27. {
  28. parent::setUp();
  29. KO7::$config->load('url')->set('trusted_hosts', ['koseven\.dev']);
  30. /**
  31. * If Unit-Testing a cache driver "KO7_Cache" is indeed available
  32. * while initialization and testing. Therefore we do not have to clear cache dir
  33. * we have to clear cache of selected user cache driver
  34. *
  35. * @see https://github.com/koseven/koseven/issues/275#issuecomment-447935858
  36. */
  37. $cache = Cache::instance();
  38. $cache->delete_all();
  39. // Otherwise just clean cache dir
  40. $this->cleanCacheDir();
  41. }
  42. /**
  43. * Removes cache files created during tests
  44. */
  45. // @codingStandardsIgnoreStart
  46. public function tearDown(): void
  47. // @codingStandardsIgnoreEnd
  48. {
  49. parent::tearDown();
  50. $this->cleanCacheDir();
  51. }
  52. /**
  53. * If Route::get() is asked for a route that does not exist then
  54. * it should throw a KO7_Exception
  55. *
  56. * @covers Route::get
  57. */
  58. public function test_get_throws_exception_if_route_dnx()
  59. {
  60. $this->expectException(KO7_Exception::class);
  61. Route::get('HAHAHAHAHAHAHAHAHA');
  62. }
  63. /**
  64. * Route::name() should fetch the name of a passed route
  65. * If route is not found then it should return FALSE
  66. *
  67. * @TODO: This test needs to segregate the Route::$_routes singleton
  68. * @test
  69. * @covers Route::name
  70. */
  71. public function test_name_returns_routes_name_or_false_if_dnx()
  72. {
  73. $route = Route::set('flamingo_people', 'flamingo/dance');
  74. $this->assertSame('flamingo_people', Route::name($route));
  75. $route = new Route('dance/dance');
  76. $this->assertFalse(Route::name($route));
  77. }
  78. /**
  79. * If Route::cache() was able to restore routes from the cache then
  80. * it should return TRUE and load the cached routes
  81. *
  82. * @test
  83. * @covers Route::cache
  84. */
  85. public function test_cache_stores_route_objects()
  86. {
  87. $routes = Route::all();
  88. // First we create the cache
  89. Route::cache(TRUE);
  90. // Now lets modify the "current" routes
  91. Route::set('nonsensical_route', 'flabbadaga/ding_dong');
  92. // Then try and load said cache
  93. $this->assertTrue(Route::cache());
  94. // Check the route cache flag
  95. $this->assertTrue(Route::$cache);
  96. // And if all went ok the nonsensical route should be gone...
  97. $this->assertEquals($routes, Route::all());
  98. }
  99. /**
  100. * Check appending cached routes. See http://koseven.dev/issues/4347
  101. *
  102. * @test
  103. * @covers Route::cache
  104. */
  105. public function test_cache_append_routes()
  106. {
  107. $cached = Route::all();
  108. // First we create the cache
  109. Route::cache(TRUE);
  110. // Now lets modify the "current" routes
  111. Route::set('nonsensical_route', 'flabbadaga/ding_dong');
  112. $modified = Route::all();
  113. // Then try and load said cache
  114. $this->assertTrue(Route::cache(NULL, TRUE));
  115. // Check the route cache flag
  116. $this->assertTrue(Route::$cache);
  117. // And if all went ok the nonsensical route should exist with the other routes...
  118. $this->assertEquals(Route::all(), $cached + $modified);
  119. }
  120. /**
  121. * Route::cache() should return FALSE if cached routes could not be found
  122. *
  123. * The cache is cleared before and after each test in setUp tearDown
  124. * by cleanCacheDir()
  125. *
  126. * @test
  127. * @covers Route::cache
  128. */
  129. public function test_cache_returns_false_if_cache_dnx()
  130. {
  131. $this->assertSame(FALSE, Route::cache(), 'Route cache was not empty');
  132. // Check the route cache flag
  133. $this->assertFalse(Route::$cache);
  134. }
  135. /**
  136. * If the constructor is passed a NULL uri then it should assume it's
  137. * being loaded from the cache & therefore shouldn't override the cached attributes
  138. *
  139. * @covers Route::__construct
  140. * @noinspection PhpUnitInvalidMockingEntityInspection
  141. */
  142. public function test_constructor_returns_if_uri_is_null()
  143. {
  144. // We use a mock object to make sure that the route wasn't recompiled
  145. $route = $this->createMock('Route', ['_compile'], [], '', FALSE);
  146. //TODO Error:(179, 13) 'static' methods cannot be mocked
  147. $route
  148. ->expects($this->never())
  149. ->method('compile');
  150. self::assertNull($route->__construct(NULL,NULL));
  151. }
  152. /**
  153. * Provider for test_matches_returns_false_on_failure
  154. *
  155. * @return array
  156. */
  157. public function provider_matches_returns_false_on_failure()
  158. {
  159. return [
  160. ['projects/(<project_id>/(<controller>(/<action>(/<id>))))', 'apple/pie'],
  161. ];
  162. }
  163. /**
  164. * Route::matches() should return false if the route doesn't match against a uri
  165. *
  166. * @dataProvider provider_matches_returns_false_on_failure
  167. *
  168. * @test
  169. * @covers Route::matches
  170. */
  171. public function test_matches_returns_false_on_failure($uri, $match)
  172. {
  173. $route = new Route($uri);
  174. // Mock a request class with the $match uri
  175. $stub = $this->get_request_mock($match);
  176. $this->assertSame(FALSE, $route->matches($stub));
  177. }
  178. /**
  179. * Provider for test_matches_returns_array_of_parameters_on_successful_match
  180. *
  181. * @return array
  182. */
  183. public function provider_matches_returns_array_of_parameters_on_successful_match()
  184. {
  185. return [
  186. [
  187. '(<controller>(/<action>(/<id>)))',
  188. 'welcome/index',
  189. 'Welcome',
  190. 'index',
  191. ],
  192. ];
  193. }
  194. /**
  195. * Route::matches() should return an array of parameters when a match is made
  196. * An parameters that are not matched should not be present in the array of matches
  197. *
  198. * @dataProvider provider_matches_returns_array_of_parameters_on_successful_match
  199. *
  200. * @test
  201. * @covers Route::matches
  202. */
  203. public function test_matches_returns_array_of_parameters_on_successful_match($uri, $m, $c, $a)
  204. {
  205. $route = new Route($uri);
  206. // Mock a request class with the $m uri
  207. $request = $this->get_request_mock($m);
  208. $matches = $route->matches($request);
  209. $this->assertIsArray($matches);
  210. $this->assertArrayHasKey('controller', $matches);
  211. $this->assertArrayHasKey('action', $matches);
  212. $this->assertArrayNotHasKey('id', $matches);
  213. // $this->assertSame(5, count($matches));
  214. $this->assertSame($c, $matches['controller']);
  215. $this->assertSame($a, $matches['action']);
  216. }
  217. /**
  218. * Provider for test_matches_returns_array_of_parameters_on_successful_match
  219. *
  220. * @return array
  221. */
  222. public function provider_defaults_are_used_if_params_arent_specified()
  223. {
  224. return [
  225. [
  226. '<controller>(/<action>(/<id>))',
  227. NULL,
  228. ['controller' => 'Welcome', 'action' => 'index'],
  229. 'Welcome',
  230. 'index',
  231. 'unit/test/1',
  232. [
  233. 'controller' => 'unit',
  234. 'action' => 'test',
  235. 'id' => '1'
  236. ],
  237. 'Welcome',
  238. ],
  239. [
  240. '(<controller>(/<action>(/<id>)))',
  241. NULL,
  242. ['controller' => 'welcome', 'action' => 'index'],
  243. 'Welcome',
  244. 'index',
  245. 'unit/test/1',
  246. [
  247. 'controller' => 'unit',
  248. 'action' => 'test',
  249. 'id' => '1'
  250. ],
  251. '',
  252. ],
  253. ];
  254. }
  255. /**
  256. * Defaults specified with defaults() should be used if their values aren't
  257. * present in the uri
  258. *
  259. * @dataProvider provider_defaults_are_used_if_params_arent_specified
  260. *
  261. * @test
  262. * @covers Route::matches
  263. */
  264. public function test_defaults_are_used_if_params_arent_specified($uri, $regex, $defaults, $c, $a, $test_uri, $test_uri_array, $default_uri)
  265. {
  266. $route = new Route($uri, $regex);
  267. $route->defaults($defaults);
  268. $this->assertSame($defaults, $route->defaults());
  269. // Mock a request class
  270. $request = $this->get_request_mock($default_uri);
  271. $matches = $route->matches($request);
  272. $this->assertIsArray($matches);
  273. $this->assertArrayHasKey('controller', $matches);
  274. $this->assertArrayHasKey('action', $matches);
  275. $this->assertArrayNotHasKey('id', $matches);
  276. // $this->assertSame(4, count($matches));
  277. $this->assertSame($c, $matches['controller']);
  278. $this->assertSame($a, $matches['action']);
  279. $this->assertSame($test_uri, $route->uri($test_uri_array));
  280. $this->assertSame($default_uri, $route->uri());
  281. }
  282. /**
  283. * Provider for test_optional_groups_containing_specified_params
  284. *
  285. * @return array
  286. */
  287. public function provider_optional_groups_containing_specified_params()
  288. {
  289. return [
  290. /**
  291. * Specifying this should cause controller and action to show up
  292. * refs #4113
  293. */
  294. [
  295. '(<controller>(/<action>(/<id>)))',
  296. ['controller' => 'welcome', 'action' => 'index'],
  297. ['id' => '1'],
  298. 'welcome/index/1',
  299. ],
  300. [
  301. '<controller>(/<action>(/<id>))',
  302. ['controller' => 'welcome', 'action' => 'index'],
  303. ['action' => 'foo'],
  304. 'welcome/foo',
  305. ],
  306. [
  307. '<controller>(/<action>(/<id>))',
  308. ['controller' => 'welcome', 'action' => 'index'],
  309. ['action' => 'index'],
  310. 'welcome',
  311. ],
  312. /**
  313. * refs #4630
  314. */
  315. [
  316. 'api(/<version>)/const(/<id>)(/<custom>)',
  317. ['version' => 1],
  318. NULL,
  319. 'api/const',
  320. ],
  321. [
  322. 'api(/<version>)/const(/<id>)(/<custom>)',
  323. ['version' => 1],
  324. ['version' => 9],
  325. 'api/9/const',
  326. ],
  327. [
  328. 'api(/<version>)/const(/<id>)(/<custom>)',
  329. ['version' => 1],
  330. ['id' => 2],
  331. 'api/const/2',
  332. ],
  333. [
  334. 'api(/<version>)/const(/<id>)(/<custom>)',
  335. ['version' => 1],
  336. ['custom' => 'x'],
  337. 'api/const/x',
  338. ],
  339. [
  340. '(<controller>(/<action>(/<id>)(/<type>)))',
  341. ['controller' => 'test', 'action' => 'index', 'type' => 'html'],
  342. ['type' => 'json'],
  343. 'test/index/json',
  344. ],
  345. [
  346. '(<controller>(/<action>(/<id>)(/<type>)))',
  347. ['controller' => 'test', 'action' => 'index', 'type' => 'html'],
  348. ['id' => 123],
  349. 'test/index/123',
  350. ],
  351. [
  352. '(<controller>(/<action>(/<id>)(/<type>)))',
  353. ['controller' => 'test', 'action' => 'index', 'type' => 'html'],
  354. ['id' => 123, 'type' => 'html'],
  355. 'test/index/123',
  356. ],
  357. [
  358. '(<controller>(/<action>(/<id>)(/<type>)))',
  359. ['controller' => 'test', 'action' => 'index', 'type' => 'html'],
  360. ['id' => 123, 'type' => 'json'],
  361. 'test/index/123/json',
  362. ],
  363. ];
  364. }
  365. /**
  366. * When an optional param is specified, the optional params leading up to it
  367. * must be in the URI.
  368. *
  369. * @dataProvider provider_optional_groups_containing_specified_params
  370. *
  371. * @ticket 4113
  372. * @ticket 4630
  373. */
  374. public function test_optional_groups_containing_specified_params($uri, $defaults, $params, $expected)
  375. {
  376. $route = new Route($uri, NULL);
  377. $route->defaults($defaults);
  378. $this->assertSame($expected, $route->uri($params));
  379. }
  380. /**
  381. * Optional params should not be used if what is passed in is identical
  382. * to the default.
  383. *
  384. * refs #4116
  385. *
  386. * @test
  387. * @covers Route::uri
  388. */
  389. public function test_defaults_are_not_used_if_param_is_identical()
  390. {
  391. $route = new Route('(<controller>(/<action>(/<id>)))');
  392. $route->defaults([
  393. 'controller' => 'welcome',
  394. 'action' => 'index'
  395. ]);
  396. $this->assertSame('', $route->uri(['controller' => 'welcome']));
  397. $this->assertSame('welcome2', $route->uri(['controller' => 'welcome2']));
  398. }
  399. /**
  400. * Provider for test_required_parameters_are_needed
  401. *
  402. * @return array
  403. */
  404. public function provider_required_parameters_are_needed()
  405. {
  406. return [
  407. [
  408. 'admin(/<controller>(/<action>(/<id>)))',
  409. 'admin',
  410. 'admin/users/add',
  411. ],
  412. ];
  413. }
  414. /**
  415. * This tests that routes with required parameters will not match uris without them present
  416. *
  417. * @dataProvider provider_required_parameters_are_needed
  418. *
  419. * @test
  420. * @covers Route::matches
  421. */
  422. public function test_required_parameters_are_needed($uri, $matches_route1, $matches_route2)
  423. {
  424. $route = new Route($uri);
  425. // Mock a request class that will return empty uri
  426. $request = $this->get_request_mock('');
  427. $this->assertFalse($route->matches($request));
  428. // Mock a request class that will return route1
  429. $request = $this->get_request_mock($matches_route1);
  430. $matches = $route->matches($request);
  431. $this->assertIsArray($matches);
  432. // Mock a request class that will return route2 uri
  433. $request = $this->get_request_mock($matches_route2);
  434. $matches = $route->matches($request);
  435. $this->assertIsArray($matches);
  436. // $this->assertSame(5, count($matches));
  437. $this->assertArrayHasKey('controller', $matches);
  438. $this->assertArrayHasKey('action', $matches);
  439. }
  440. /**
  441. * Provider for test_required_parameters_are_needed
  442. *
  443. * @return array
  444. */
  445. public function provider_reverse_routing_returns_routes_uri_if_route_is_static()
  446. {
  447. return [
  448. [
  449. 'info/about_us',
  450. NULL,
  451. 'info/about_us',
  452. ['some' => 'random', 'params' => 'to confuse'],
  453. ],
  454. ];
  455. }
  456. /**
  457. * This tests the reverse routing returns the uri specified in the route
  458. * if it's a static route
  459. *
  460. * A static route is a route without any parameters
  461. *
  462. * @dataProvider provider_reverse_routing_returns_routes_uri_if_route_is_static
  463. *
  464. * @test
  465. * @covers Route::uri
  466. */
  467. public function test_reverse_routing_returns_routes_uri_if_route_is_static($uri, $regex, $target_uri, $uri_params)
  468. {
  469. $route = new Route($uri, $regex);
  470. $this->assertSame($target_uri, $route->uri($uri_params));
  471. }
  472. /**
  473. * Provider for test_uri_throws_exception_if_required_params_are_missing
  474. *
  475. * @return array
  476. */
  477. public function provider_uri_throws_exception_if_required_params_are_missing()
  478. {
  479. return [
  480. [
  481. '<controller>(/<action)',
  482. NULL,
  483. ['action' => 'awesome-action'],
  484. ],
  485. /**
  486. * Optional params are required when they lead to a specified param
  487. * refs #4113
  488. */
  489. [
  490. '(<controller>(/<action>))',
  491. NULL,
  492. ['action' => 'awesome-action'],
  493. ],
  494. ];
  495. }
  496. /**
  497. * When Route::uri is working on a uri that requires certain parameters to be present
  498. * (i.e. <controller> in '<controller(/<action)') then it should throw an exception
  499. * if the param was not provided
  500. *
  501. * @dataProvider provider_uri_throws_exception_if_required_params_are_missing
  502. *
  503. * @test
  504. * @covers Route::uri
  505. */
  506. public function test_uri_throws_exception_if_required_params_are_missing($uri, $regex, $uri_array)
  507. {
  508. $route = new Route($uri, $regex);
  509. $this->expectException('KO7_Exception');
  510. $route->uri($uri_array);
  511. }
  512. /**
  513. * Provider for test_uri_fills_required_uri_segments_from_params
  514. *
  515. * @return array
  516. */
  517. public function provider_uri_fills_required_uri_segments_from_params()
  518. {
  519. return [
  520. [
  521. '<controller>/<action>(/<id>)',
  522. NULL,
  523. 'users/edit',
  524. [
  525. 'controller' => 'users',
  526. 'action' => 'edit',
  527. ],
  528. 'users/edit/god',
  529. [
  530. 'controller' => 'users',
  531. 'action' => 'edit',
  532. 'id' => 'god',
  533. ],
  534. ],
  535. ];
  536. }
  537. /**
  538. * The logic for replacing required segments is separate (but similar) to that for
  539. * replacing optional segments.
  540. *
  541. * This test asserts that Route::uri will replace required segments with provided
  542. * params
  543. *
  544. * @dataProvider provider_uri_fills_required_uri_segments_from_params
  545. *
  546. * @test
  547. * @covers Route::uri
  548. */
  549. public function test_uri_fills_required_uri_segments_from_params($uri, $regex, $uri_string1, $uri_array1, $uri_string2, $uri_array2)
  550. {
  551. $route = new Route($uri, $regex);
  552. $this->assertSame(
  553. $uri_string1,
  554. $route->uri($uri_array1)
  555. );
  556. $this->assertSame(
  557. $uri_string2,
  558. $route->uri($uri_array2)
  559. );
  560. }
  561. /**
  562. * Provides test data for test_composing_url_from_route()
  563. * @return array
  564. */
  565. public function provider_composing_url_from_route()
  566. {
  567. return [
  568. ['/'],
  569. ['/news/view/42', ['controller' => 'news', 'action' => 'view', 'id' => 42]],
  570. ['http://koseven.dev/news', ['controller' => 'news'], 'http']
  571. ];
  572. }
  573. /**
  574. * Tests Route::url()
  575. *
  576. * Checks the url composing from specific route via Route::url() shortcut
  577. *
  578. * @test
  579. * @dataProvider provider_composing_url_from_route
  580. * @param string $expected
  581. * @param array $params
  582. * @param boolean $protocol
  583. */
  584. public function test_composing_url_from_route($expected, $params = NULL, $protocol = NULL)
  585. {
  586. Route::set('foobar', '(<controller>(/<action>(/<id>)))')
  587. ->defaults([
  588. 'controller' => 'welcome',
  589. ]
  590. );
  591. $this->setEnvironment([
  592. '_SERVER' => ['HTTP_HOST' => 'koseven.dev'],
  593. 'KO7::$base_url' => '/',
  594. 'KO7::$index_file' => '',
  595. ]);
  596. $this->assertSame($expected, Route::url('foobar', $params, $protocol));
  597. }
  598. /**
  599. * Tests Route::compile()
  600. *
  601. * Makes sure that compile will use custom regex if specified
  602. *
  603. * @test
  604. * @covers Route::compile
  605. */
  606. public function test_compile_uses_custom_regex_if_specificed()
  607. {
  608. $compiled = Route::compile(
  609. '<controller>(/<action>(/<id>))',
  610. [
  611. 'controller' => '[a-z]+',
  612. 'id' => '\d+',
  613. ]
  614. );
  615. $this->assertSame('#^(?P<controller>[a-z]+)(?:/(?P<action>[^/.,;?\n]++)(?:/(?P<id>\d+))?)?$#uD', $compiled);
  616. }
  617. /**
  618. * Tests Route::is_external(), ensuring the host can return
  619. * whether internal or external host
  620. */
  621. public function test_is_external_route_from_host()
  622. {
  623. // Setup local route
  624. Route::set('internal', 'local/test/route')
  625. ->defaults([
  626. 'controller' => 'foo',
  627. 'action' => 'bar'
  628. ]
  629. );
  630. // Setup external route
  631. Route::set('external', 'local/test/route')
  632. ->defaults([
  633. 'controller' => 'foo',
  634. 'action' => 'bar',
  635. 'host' => 'http://koseven.dev/'
  636. ]
  637. );
  638. // Test internal route
  639. $this->assertFalse(Route::get('internal')->is_external());
  640. // Test external route
  641. $this->assertTrue(Route::get('external')->is_external());
  642. }
  643. /**
  644. * Provider for test_external_route_includes_params_in_uri
  645. *
  646. * @return array
  647. */
  648. public function provider_external_route_includes_params_in_uri()
  649. {
  650. return [
  651. [
  652. '<controller>/<action>',
  653. [
  654. 'controller' => 'foo',
  655. 'action' => 'bar',
  656. 'host' => 'koseven.dev'
  657. ],
  658. 'http://koseven.dev/foo/bar'
  659. ],
  660. [
  661. '<controller>/<action>',
  662. [
  663. 'controller' => 'foo',
  664. 'action' => 'bar',
  665. 'host' => 'http://koseven.dev/'
  666. ],
  667. 'http://koseven.dev/foo/bar'
  668. ],
  669. [
  670. 'foo/bar',
  671. [
  672. 'controller' => 'foo',
  673. 'host' => 'http://koseven.dev/'
  674. ],
  675. 'http://koseven.dev/foo/bar'
  676. ],
  677. ];
  678. }
  679. /**
  680. * Tests the external route include route parameters
  681. *
  682. * @dataProvider provider_external_route_includes_params_in_uri
  683. */
  684. public function test_external_route_includes_params_in_uri($route, $defaults, $expected_uri)
  685. {
  686. Route::set('test', $route)
  687. ->defaults($defaults);
  688. $this->assertSame($expected_uri, Route::get('test')->uri());
  689. }
  690. /**
  691. * Provider for test_route_filter_modify_params
  692. *
  693. * @return array
  694. */
  695. public function provider_route_filter_modify_params()
  696. {
  697. return [
  698. [
  699. '<controller>/<action>',
  700. [
  701. 'controller' => 'Test',
  702. 'action' => 'same',
  703. ],
  704. ['Route_Holder', 'route_filter_modify_params_array'],
  705. 'test/different',
  706. [
  707. 'controller' => 'Test',
  708. 'action' => 'modified',
  709. ],
  710. ],
  711. [
  712. '<controller>/<action>',
  713. [
  714. 'controller' => 'test',
  715. 'action' => 'same',
  716. ],
  717. ['Route_Holder', 'route_filter_modify_params_false'],
  718. 'test/fail',
  719. FALSE,
  720. ],
  721. ];
  722. }
  723. /**
  724. * Tests that route filters can modify parameters
  725. *
  726. * @covers Route::filter
  727. * @dataProvider provider_route_filter_modify_params
  728. */
  729. public function test_route_filter_modify_params($route, $defaults, $filter, $uri, $expected_params)
  730. {
  731. $route = new Route($route);
  732. // Mock a request class
  733. $request = $this->get_request_mock($uri);
  734. $params = $route->defaults($defaults)->filter($filter)->matches($request);
  735. $this->assertSame($expected_params, $params);
  736. }
  737. /**
  738. * Provides test data for test_route_uri_encode_parameters
  739. *
  740. * @return array
  741. */
  742. public function provider_route_uri_encode_parameters()
  743. {
  744. return [
  745. [
  746. 'article',
  747. 'blog/article/<article_name>',
  748. [
  749. 'controller' => 'home',
  750. 'action' => 'index'
  751. ],
  752. 'article_name',
  753. 'Article name with special chars \\ ##',
  754. 'blog/article/Article%20name%20with%20special%20chars%20\\%20%23%23'
  755. ]
  756. ];
  757. }
  758. /**
  759. * http://koseven.dev/issues/4079
  760. *
  761. * @test
  762. * @covers Route::get
  763. * @ticket 4079
  764. * @dataProvider provider_route_uri_encode_parameters
  765. */
  766. public function test_route_uri_encode_parameters($name, $uri_callback, $defaults, $uri_key, $uri_value, $expected)
  767. {
  768. Route::set($name, $uri_callback)->defaults($defaults);
  769. $get_route_uri = Route::get($name)->uri([$uri_key => $uri_value]);
  770. $this->assertSame($expected, $get_route_uri);
  771. }
  772. /**
  773. * Get a mock of the Request class with a mocked `uri` method
  774. *
  775. * We are also mocking `method` method as it conflicts with newer PHPUnit,
  776. * in order to avoid the fatal errors
  777. *
  778. * @param string $uri
  779. * @return type
  780. */
  781. public function get_request_mock($uri)
  782. {
  783. // Mock a request class with the $uri uri
  784. $request = $this->createMock('Request', ['uri', 'method'], [$uri]);
  785. // mock `uri` method
  786. $request->expects($this->any())
  787. ->method('uri')
  788. // Request::uri() called by Route::matches() in the tests will return $uri
  789. ->will($this->returnValue($uri));
  790. // also mock `method` method
  791. $request->expects($this->any())
  792. ->method('method')
  793. ->withAnyParameters();
  794. return $request;
  795. }
  796. }