RouteBuilderTest.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430
  1. <?php
  2. /**
  3. * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
  4. * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  5. *
  6. * Licensed under The MIT License
  7. * For full copyright and license information, please see the LICENSE.txt
  8. * Redistributions of files must retain the above copyright notice.
  9. *
  10. * @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  11. * @link http://cakephp.org CakePHP(tm) Project
  12. * @since 3.0.0
  13. * @license http://www.opensource.org/licenses/mit-license.php MIT License
  14. */
  15. namespace Cake\Test\TestCase\Routing;
  16. use Cake\Routing\RouteBuilder;
  17. use Cake\Routing\RouteCollection;
  18. use Cake\Routing\Router;
  19. use Cake\Routing\Route\Route;
  20. use Cake\TestSuite\TestCase;
  21. /**
  22. * RouteBuilder test case
  23. */
  24. class RouteBuilderTest extends TestCase {
  25. /**
  26. * Setup method
  27. *
  28. * @return void
  29. */
  30. public function setUp() {
  31. parent::setUp();
  32. $this->collection = new RouteCollection();
  33. }
  34. /**
  35. * Test path()
  36. *
  37. * @return void
  38. */
  39. public function testPath() {
  40. $routes = new RouteBuilder($this->collection, '/some/path');
  41. $this->assertEquals('/some/path', $routes->path());
  42. $routes = new RouteBuilder($this->collection, '/:book_id');
  43. $this->assertEquals('/', $routes->path());
  44. $routes = new RouteBuilder($this->collection, '/path/:book_id');
  45. $this->assertEquals('/path/', $routes->path());
  46. $routes = new RouteBuilder($this->collection, '/path/book:book_id');
  47. $this->assertEquals('/path/book', $routes->path());
  48. }
  49. /**
  50. * Test params()
  51. *
  52. * @return void
  53. */
  54. public function testParams() {
  55. $routes = new RouteBuilder($this->collection, '/api', ['prefix' => 'api']);
  56. $this->assertEquals(['prefix' => 'api'], $routes->params());
  57. }
  58. /**
  59. * Test getting connected routes.
  60. *
  61. * @return void
  62. */
  63. public function testRoutes() {
  64. $routes = new RouteBuilder($this->collection, '/l');
  65. $routes->connect('/:controller', ['action' => 'index']);
  66. $routes->connect('/:controller/:action/*');
  67. $all = $this->collection->routes();
  68. $this->assertCount(2, $all);
  69. $this->assertInstanceOf('Cake\Routing\Route\Route', $all[0]);
  70. $this->assertInstanceOf('Cake\Routing\Route\Route', $all[1]);
  71. }
  72. /**
  73. * Test connecting an instance routes.
  74. *
  75. * @return void
  76. */
  77. public function testConnectInstance() {
  78. $routes = new RouteBuilder($this->collection, '/l', ['prefix' => 'api']);
  79. $route = new Route('/:controller');
  80. $this->assertNull($routes->connect($route));
  81. $result = $this->collection->routes()[0];
  82. $this->assertSame($route, $result);
  83. }
  84. /**
  85. * Test connecting basic routes.
  86. *
  87. * @return void
  88. */
  89. public function testConnectBasic() {
  90. $routes = new RouteBuilder($this->collection, '/l', ['prefix' => 'api']);
  91. $this->assertNull($routes->connect('/:controller'));
  92. $route = $this->collection->routes()[0];
  93. $this->assertInstanceOf('Cake\Routing\Route\Route', $route);
  94. $this->assertEquals('/l/:controller', $route->template);
  95. $expected = ['prefix' => 'api', 'action' => 'index', 'plugin' => null];
  96. $this->assertEquals($expected, $route->defaults);
  97. }
  98. /**
  99. * Test that compiling a route results in an trailing / optional pattern.
  100. *
  101. * @return void
  102. */
  103. public function testConnectTrimTrailingSlash() {
  104. $routes = new RouteBuilder($this->collection, '/articles', ['controller' => 'Articles']);
  105. $routes->connect('/', ['action' => 'index']);
  106. $expected = ['plugin' => null, 'controller' => 'Articles', 'action' => 'index', 'pass' => []];
  107. $this->assertEquals($expected, $this->collection->parse('/articles'));
  108. $this->assertEquals($expected, $this->collection->parse('/articles/'));
  109. }
  110. /**
  111. * Test extensions being connected to routes.
  112. *
  113. * @return void
  114. */
  115. public function testConnectExtensions() {
  116. $routes = new RouteBuilder($this->collection, '/l', [], ['json']);
  117. $this->assertEquals(['json'], $routes->extensions());
  118. $routes->connect('/:controller');
  119. $route = $this->collection->routes()[0];
  120. $this->assertEquals(['json'], $route->options['_ext']);
  121. $routes->extensions(['xml', 'json']);
  122. $routes->connect('/:controller/:action');
  123. $new = $this->collection->routes()[1];
  124. $this->assertEquals(['json'], $route->options['_ext']);
  125. $this->assertEquals(['xml', 'json'], $new->options['_ext']);
  126. }
  127. /**
  128. * test that extensions() accepts a string.
  129. *
  130. * @return void
  131. */
  132. public function testExtensionsString() {
  133. $routes = new RouteBuilder($this->collection, '/l');
  134. $routes->extensions('json');
  135. $this->assertEquals(['json'], $routes->extensions());
  136. }
  137. /**
  138. * Test error on invalid route class
  139. *
  140. * @expectedException \InvalidArgumentException
  141. * @expectedExceptionMessage Route class not found, or route class is not a subclass of
  142. * @return void
  143. */
  144. public function testConnectErrorInvalidRouteClass() {
  145. $routes = new RouteBuilder($this->collection, '/l', [], ['json']);
  146. $routes->connect('/:controller', [], ['routeClass' => '\StdClass']);
  147. }
  148. /**
  149. * Test conflicting parameters raises an exception.
  150. *
  151. * @expectedException \BadMethodCallException
  152. * @expectedExceptionMessage You cannot define routes that conflict with the scope.
  153. * @return void
  154. */
  155. public function testConnectConflictingParameters() {
  156. $routes = new RouteBuilder($this->collection, '/admin', ['prefix' => 'admin'], []);
  157. $routes->connect('/', ['prefix' => 'manager', 'controller' => 'Dashboard', 'action' => 'view']);
  158. }
  159. /**
  160. * Test connecting redirect routes.
  161. *
  162. * @return void
  163. */
  164. public function testRedirect() {
  165. $routes = new RouteBuilder($this->collection, '/');
  166. $routes->redirect('/p/:id', ['controller' => 'posts', 'action' => 'view'], ['status' => 301]);
  167. $route = $this->collection->routes()[0];
  168. $this->assertInstanceOf('Cake\Routing\Route\RedirectRoute', $route);
  169. $routes->redirect('/old', '/forums', ['status' => 301]);
  170. $route = $this->collection->routes()[1];
  171. $this->assertInstanceOf('Cake\Routing\Route\RedirectRoute', $route);
  172. $this->assertEquals('/forums', $route->redirect[0]);
  173. }
  174. /**
  175. * Test creating sub-scopes with prefix()
  176. *
  177. * @return void
  178. */
  179. public function testPrefix() {
  180. $routes = new RouteBuilder($this->collection, '/path', ['key' => 'value']);
  181. $res = $routes->prefix('admin', function($r) {
  182. $this->assertInstanceOf('Cake\Routing\RouteBuilder', $r);
  183. $this->assertCount(0, $this->collection->routes());
  184. $this->assertEquals('/path/admin', $r->path());
  185. $this->assertEquals(['prefix' => 'admin', 'key' => 'value'], $r->params());
  186. });
  187. $this->assertNull($res);
  188. }
  189. /**
  190. * Test creating sub-scopes with prefix()
  191. *
  192. * @return void
  193. */
  194. public function testNestedPrefix() {
  195. $routes = new RouteBuilder($this->collection, '/admin', ['prefix' => 'admin']);
  196. $res = $routes->prefix('api', function($r) {
  197. $this->assertEquals('/admin/api', $r->path());
  198. $this->assertEquals(['prefix' => 'admin/api'], $r->params());
  199. });
  200. $this->assertNull($res);
  201. }
  202. /**
  203. * Test creating sub-scopes with plugin()
  204. *
  205. * @return void
  206. */
  207. public function testNestedPlugin() {
  208. $routes = new RouteBuilder($this->collection, '/b', ['key' => 'value']);
  209. $res = $routes->plugin('Contacts', function($r) {
  210. $this->assertEquals('/b/contacts', $r->path());
  211. $this->assertEquals(['plugin' => 'Contacts', 'key' => 'value'], $r->params());
  212. $r->connect('/:controller');
  213. $route = $this->collection->routes()[0];
  214. $this->assertEquals(
  215. ['key' => 'value', 'plugin' => 'Contacts', 'action' => 'index'],
  216. $route->defaults
  217. );
  218. });
  219. $this->assertNull($res);
  220. }
  221. /**
  222. * Test creating sub-scopes with plugin() + path option
  223. *
  224. * @return void
  225. */
  226. public function testNestedPluginPathOption() {
  227. $routes = new RouteBuilder($this->collection, '/b', ['key' => 'value']);
  228. $routes->plugin('Contacts', ['path' => '/people'], function($r) {
  229. $this->assertEquals('/b/people', $r->path());
  230. $this->assertEquals(['plugin' => 'Contacts', 'key' => 'value'], $r->params());
  231. });
  232. }
  233. /**
  234. * Test connecting resources.
  235. *
  236. * @return void
  237. */
  238. public function testResources() {
  239. $routes = new RouteBuilder($this->collection, '/api', ['prefix' => 'api']);
  240. $routes->resources('Articles', ['_ext' => 'json']);
  241. $all = $this->collection->routes();
  242. $this->assertCount(5, $all);
  243. $this->assertEquals('/api/articles', $all[0]->template);
  244. $this->assertEquals('json', $all[0]->defaults['_ext']);
  245. $this->assertEquals('Articles', $all[0]->defaults['controller']);
  246. }
  247. /**
  248. * Test resource parsing.
  249. *
  250. * @return void
  251. */
  252. public function testResourcesParsing() {
  253. $routes = new RouteBuilder($this->collection, '/');
  254. $routes->resources('Articles');
  255. $_SERVER['REQUEST_METHOD'] = 'GET';
  256. $result = $this->collection->parse('/articles');
  257. $this->assertEquals('Articles', $result['controller']);
  258. $this->assertEquals('index', $result['action']);
  259. $this->assertEquals([], $result['pass']);
  260. $result = $this->collection->parse('/articles/1');
  261. $this->assertEquals('Articles', $result['controller']);
  262. $this->assertEquals('view', $result['action']);
  263. $this->assertEquals([1], $result['pass']);
  264. $_SERVER['REQUEST_METHOD'] = 'POST';
  265. $result = $this->collection->parse('/articles');
  266. $this->assertEquals('Articles', $result['controller']);
  267. $this->assertEquals('add', $result['action']);
  268. $this->assertEquals([], $result['pass']);
  269. $_SERVER['REQUEST_METHOD'] = 'PUT';
  270. $result = $this->collection->parse('/articles/1');
  271. $this->assertEquals('Articles', $result['controller']);
  272. $this->assertEquals('edit', $result['action']);
  273. $this->assertEquals([1], $result['pass']);
  274. $_SERVER['REQUEST_METHOD'] = 'DELETE';
  275. $result = $this->collection->parse('/articles/1');
  276. $this->assertEquals('Articles', $result['controller']);
  277. $this->assertEquals('delete', $result['action']);
  278. $this->assertEquals([1], $result['pass']);
  279. }
  280. /**
  281. * Test the only option of RouteBuilder.
  282. *
  283. * @return void
  284. */
  285. public function testResourcesOnlyString() {
  286. $routes = new RouteBuilder($this->collection, '/');
  287. $routes->resources('Articles', ['only' => 'index']);
  288. $result = $this->collection->routes();
  289. $this->assertCount(1, $result);
  290. $this->assertEquals('/articles', $result[0]->template);
  291. }
  292. /**
  293. * Test the only option of RouteBuilder.
  294. *
  295. * @return void
  296. */
  297. public function testResourcesOnlyArray() {
  298. $routes = new RouteBuilder($this->collection, '/');
  299. $routes->resources('Articles', ['only' => ['index', 'delete']]);
  300. $result = $this->collection->routes();
  301. $this->assertCount(2, $result);
  302. $this->assertEquals('/articles', $result[0]->template);
  303. $this->assertEquals('index', $result[0]->defaults['action']);
  304. $this->assertEquals('GET', $result[0]->defaults['_method']);
  305. $this->assertEquals('/articles/:id', $result[1]->template);
  306. $this->assertEquals('delete', $result[1]->defaults['action']);
  307. $this->assertEquals('DELETE', $result[1]->defaults['_method']);
  308. }
  309. /**
  310. * Test the actions option of RouteBuilder.
  311. *
  312. * @return void
  313. */
  314. public function testResourcesActions() {
  315. $routes = new RouteBuilder($this->collection, '/');
  316. $routes->resources('Articles', [
  317. 'only' => ['index', 'delete'],
  318. 'actions' => ['index' => 'showList']
  319. ]);
  320. $result = $this->collection->routes();
  321. $this->assertCount(2, $result);
  322. $this->assertEquals('/articles', $result[0]->template);
  323. $this->assertEquals('showList', $result[0]->defaults['action']);
  324. $this->assertEquals('/articles/:id', $result[1]->template);
  325. $this->assertEquals('delete', $result[1]->defaults['action']);
  326. }
  327. /**
  328. * Test nesting resources
  329. *
  330. * @return void
  331. */
  332. public function testResourcesNested() {
  333. $routes = new RouteBuilder($this->collection, '/api', ['prefix' => 'api']);
  334. $routes->resources('Articles', function($routes) {
  335. $this->assertEquals('/api/articles/', $routes->path());
  336. $this->assertEquals(['prefix' => 'api'], $routes->params());
  337. $routes->resources('Comments');
  338. $route = $this->collection->routes()[6];
  339. $this->assertEquals('/api/articles/:article_id/comments', $route->template);
  340. });
  341. }
  342. /**
  343. * Test connecting fallback routes.
  344. *
  345. * @return void
  346. */
  347. public function testFallbacks() {
  348. $routes = new RouteBuilder($this->collection, '/api', ['prefix' => 'api']);
  349. $routes->fallbacks();
  350. $all = $this->collection->routes();
  351. $this->assertEquals('/api/:controller', $all[0]->template);
  352. $this->assertEquals('/api/:controller/:action/*', $all[1]->template);
  353. }
  354. /**
  355. * Test adding a scope.
  356. *
  357. * @return void
  358. */
  359. public function testScope() {
  360. $routes = new RouteBuilder($this->collection, '/api', ['prefix' => 'api']);
  361. $routes->scope('/v1', ['version' => 1], function($routes) {
  362. $this->assertEquals('/api/v1', $routes->path());
  363. $this->assertEquals(['prefix' => 'api', 'version' => 1], $routes->params());
  364. });
  365. $routes = new RouteBuilder($this->collection, '/api', ['prefix' => 'api']);
  366. $routes->scope('/v1', function($routes) {
  367. $this->assertEquals('/api/v1', $routes->path());
  368. $this->assertEquals(['prefix' => 'api'], $routes->params());
  369. });
  370. }
  371. }