DispatcherTest.php 29 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114
  1. <?php
  2. /**
  3. * CakePHP(tm) Tests <http://book.cakephp.org/2.0/en/development/testing.html>
  4. * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  5. *
  6. * Licensed under The MIT License
  7. * Redistributions of files must retain the above copyright notice
  8. *
  9. * @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  10. * @link http://book.cakephp.org/2.0/en/development/testing.html CakePHP(tm) Tests
  11. * @since 1.2.0
  12. * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
  13. */
  14. namespace Cake\Test\TestCase\Routing;
  15. use Cake\Cache\Cache;
  16. use Cake\Controller\Controller;
  17. use Cake\Controller\Error\MissingActionException;
  18. use Cake\Controller\Error\MissingControllerException;
  19. use Cake\Core\App;
  20. use Cake\Core\Configure;
  21. use Cake\Core\Plugin;
  22. use Cake\Error;
  23. use Cake\Event\Event;
  24. use Cake\Network\Request;
  25. use Cake\Network\Response;
  26. use Cake\Routing\Dispatcher;
  27. use Cake\Routing\Error\MissingDispatcherFilterException;
  28. use Cake\Routing\Router;
  29. use Cake\TestSuite\TestCase;
  30. use Cake\Utility\Inflector;
  31. /**
  32. * A testing stub that doesn't send headers.
  33. *
  34. */
  35. class DispatcherMockResponse extends Response {
  36. protected function _sendHeader($name, $value = null) {
  37. return $name . ' ' . $value;
  38. }
  39. }
  40. /**
  41. * TestDispatcher class
  42. *
  43. */
  44. class TestDispatcher extends Dispatcher {
  45. /**
  46. * Controller instance, made publicly available for testing
  47. *
  48. * @var Controller
  49. */
  50. public $controller;
  51. /**
  52. * invoke method
  53. *
  54. * @param \Cake\Controller\Controller $controller
  55. * @return \Cake\Network\Response $response
  56. */
  57. protected function _invoke(Controller $controller) {
  58. $this->controller = $controller;
  59. return parent::_invoke($controller);
  60. }
  61. /**
  62. * Helper function to test single method attaching for dispatcher filters
  63. *
  64. * @param \Cake\Event\Event $event
  65. * @return void
  66. */
  67. public function filterTest($event) {
  68. $event->data['request']->params['eventName'] = $event->name();
  69. }
  70. /**
  71. * Helper function to test single method attaching for dispatcher filters
  72. *
  73. * @param \Cake\Event\Event
  74. * @return void
  75. */
  76. public function filterTest2($event) {
  77. $event->stopPropagation();
  78. return $event->data['response'];
  79. }
  80. }
  81. /**
  82. * MyPluginAppController class
  83. *
  84. */
  85. class MyPluginAppController extends Controller {
  86. }
  87. interface DispatcherTestInterfaceController {
  88. public function index();
  89. }
  90. /**
  91. * MyPluginController class
  92. *
  93. */
  94. class MyPluginController extends MyPluginAppController {
  95. /**
  96. * name property
  97. *
  98. * @var string
  99. */
  100. public $name = 'MyPlugin';
  101. /**
  102. * uses property
  103. *
  104. * @var array
  105. */
  106. public $uses = [];
  107. /**
  108. * index method
  109. *
  110. * @return void
  111. */
  112. public function index() {
  113. return true;
  114. }
  115. /**
  116. * add method
  117. *
  118. * @return void
  119. */
  120. public function add() {
  121. return true;
  122. }
  123. /**
  124. * admin_add method
  125. *
  126. * @param mixed $id
  127. * @return void
  128. */
  129. public function admin_add($id = null) {
  130. return $id;
  131. }
  132. }
  133. /**
  134. * OtherPagesController class
  135. *
  136. */
  137. class OtherPagesController extends MyPluginAppController {
  138. /**
  139. * name property
  140. *
  141. * @var string
  142. */
  143. public $name = 'OtherPages';
  144. /**
  145. * uses property
  146. *
  147. * @var array
  148. */
  149. public $uses = [];
  150. /**
  151. * display method
  152. *
  153. * @param string $page
  154. * @return void
  155. */
  156. public function display($page = null) {
  157. return $page;
  158. }
  159. /**
  160. * index method
  161. *
  162. * @return void
  163. */
  164. public function index() {
  165. return true;
  166. }
  167. }
  168. /**
  169. * ArticlesTestAppController class
  170. *
  171. */
  172. class ArticlesTestAppController extends Controller {
  173. }
  174. /**
  175. * ArticlesTestController class
  176. *
  177. */
  178. class ArticlesTestController extends ArticlesTestAppController {
  179. /**
  180. * name property
  181. *
  182. * @var string
  183. */
  184. public $name = 'ArticlesTest';
  185. /**
  186. * uses property
  187. *
  188. * @var array
  189. */
  190. public $uses = [];
  191. /**
  192. * admin_index method
  193. *
  194. * @return void
  195. */
  196. public function admin_index() {
  197. return true;
  198. }
  199. /**
  200. * fake index method.
  201. *
  202. * @return void
  203. */
  204. public function index() {
  205. return true;
  206. }
  207. }
  208. /**
  209. * DispatcherTest class
  210. *
  211. */
  212. class DispatcherTest extends TestCase {
  213. /**
  214. * setUp method
  215. *
  216. * @return void
  217. */
  218. public function setUp() {
  219. parent::setUp();
  220. $_GET = [];
  221. Configure::write('App.base', false);
  222. Configure::write('App.baseUrl', false);
  223. Configure::write('App.dir', 'app');
  224. Configure::write('App.webroot', 'webroot');
  225. Configure::write('App.namespace', 'TestApp');
  226. Cache::disable();
  227. App::objects('Plugin', null, false);
  228. }
  229. /**
  230. * tearDown method
  231. *
  232. * @return void
  233. */
  234. public function tearDown() {
  235. parent::tearDown();
  236. Plugin::unload();
  237. Configure::write('Dispatcher.filters', []);
  238. }
  239. /**
  240. * testParseParamsWithoutZerosAndEmptyPost method
  241. *
  242. * @return void
  243. */
  244. public function testParseParamsWithoutZerosAndEmptyPost() {
  245. Router::connect('/:controller/:action/*');
  246. $Dispatcher = new Dispatcher();
  247. $request = new Request("/testcontroller/testaction/params1/params2/params3");
  248. $event = new Event(__CLASS__, $Dispatcher, array('request' => $request));
  249. $Dispatcher->parseParams($event);
  250. $this->assertSame($request['controller'], 'testcontroller');
  251. $this->assertSame($request['action'], 'testaction');
  252. $this->assertSame($request['pass'][0], 'params1');
  253. $this->assertSame($request['pass'][1], 'params2');
  254. $this->assertSame($request['pass'][2], 'params3');
  255. $this->assertFalse(!empty($request['form']));
  256. }
  257. /**
  258. * testQueryStringOnRoot method
  259. *
  260. * @return void
  261. */
  262. public function testQueryStringOnRoot() {
  263. Router::reload();
  264. Router::connect('/', array('controller' => 'pages', 'action' => 'display', 'home'));
  265. Router::connect('/pages/*', array('controller' => 'pages', 'action' => 'display'));
  266. Router::connect('/:controller/:action/*');
  267. $_GET = array('coffee' => 'life', 'sleep' => 'sissies');
  268. $Dispatcher = new Dispatcher();
  269. $request = new Request('posts/home/?coffee=life&sleep=sissies');
  270. $event = new Event(__CLASS__, $Dispatcher, array('request' => $request));
  271. $Dispatcher->parseParams($event);
  272. $this->assertRegExp('/posts/', $request['controller']);
  273. $this->assertRegExp('/home/', $request['action']);
  274. $this->assertTrue(isset($request['url']['sleep']));
  275. $this->assertTrue(isset($request['url']['coffee']));
  276. $Dispatcher = new Dispatcher();
  277. $request = new Request('/?coffee=life&sleep=sissy');
  278. $event = new Event(__CLASS__, $Dispatcher, array('request' => $request));
  279. $Dispatcher->parseParams($event);
  280. $this->assertRegExp('/pages/', $request['controller']);
  281. $this->assertRegExp('/display/', $request['action']);
  282. $this->assertTrue(isset($request['url']['sleep']));
  283. $this->assertTrue(isset($request['url']['coffee']));
  284. $this->assertEquals('life', $request['url']['coffee']);
  285. }
  286. /**
  287. * testMissingController method
  288. *
  289. * @expectedException \Cake\Controller\Error\MissingControllerException
  290. * @expectedExceptionMessage Controller class SomeController could not be found.
  291. * @return void
  292. */
  293. public function testMissingController() {
  294. Router::connect('/:controller/:action/*');
  295. $Dispatcher = new TestDispatcher();
  296. Configure::write('App.baseUrl', '/index.php');
  297. $url = new Request('some_controller/home');
  298. $response = $this->getMock('Cake\Network\Response');
  299. $Dispatcher->dispatch($url, $response, array('return' => 1));
  300. }
  301. /**
  302. * testMissingControllerInterface method
  303. *
  304. * @expectedException \Cake\Controller\Error\MissingControllerException
  305. * @expectedExceptionMessage Controller class DispatcherTestInterface could not be found.
  306. * @return void
  307. */
  308. public function testMissingControllerInterface() {
  309. Router::connect('/:controller/:action/*');
  310. $Dispatcher = new TestDispatcher();
  311. Configure::write('App.baseUrl', '/index.php');
  312. $url = new Request('dispatcher_test_interface/index');
  313. $response = $this->getMock('Cake\Network\Response');
  314. $Dispatcher->dispatch($url, $response, array('return' => 1));
  315. }
  316. /**
  317. * testMissingControllerInterface method
  318. *
  319. * @expectedException \Cake\Controller\Error\MissingControllerException
  320. * @expectedExceptionMessage Controller class Abstract could not be found.
  321. * @return void
  322. */
  323. public function testMissingControllerAbstract() {
  324. Router::connect('/:controller/:action/*');
  325. $Dispatcher = new TestDispatcher();
  326. $url = new Request('abstract/index');
  327. $response = $this->getMock('Cake\Network\Response');
  328. $Dispatcher->dispatch($url, $response, array('return' => 1));
  329. }
  330. /**
  331. * testDispatch method
  332. *
  333. * @return void
  334. */
  335. public function testDispatchBasic() {
  336. Router::connect('/pages/*', array('controller' => 'Pages', 'action' => 'display'));
  337. Router::connect('/:controller/:action/*');
  338. $Dispatcher = new TestDispatcher();
  339. $url = new Request('pages/home');
  340. $response = $this->getMock('Cake\Network\Response');
  341. $Dispatcher->dispatch($url, $response, array('return' => 1));
  342. $expected = 'Pages';
  343. $this->assertEquals($expected, $Dispatcher->controller->name);
  344. $expected = array('0' => 'home');
  345. $this->assertSame($expected, $Dispatcher->controller->request->params['pass']);
  346. Configure::write('App.baseUrl', '/pages/index.php');
  347. $url = new Request('pages/home');
  348. $Dispatcher->dispatch($url, $response, array('return' => 1));
  349. $expected = 'Pages';
  350. $this->assertEquals($expected, $Dispatcher->controller->name);
  351. $url = new Request('pages/home/');
  352. $Dispatcher->dispatch($url, $response, array('return' => 1));
  353. $this->assertNull($Dispatcher->controller->plugin);
  354. $expected = 'Pages';
  355. $this->assertEquals($expected, $Dispatcher->controller->name);
  356. unset($Dispatcher);
  357. require CAKE . 'Config/routes.php';
  358. $Dispatcher = new TestDispatcher();
  359. $url = new Request('test_dispatch_pages/camelCased');
  360. $Dispatcher->dispatch($url, $response, array('return' => 1));
  361. $this->assertEquals('TestDispatchPages', $Dispatcher->controller->name);
  362. $url = new Request('test_dispatch_pages/camelCased/something. .');
  363. $Dispatcher->dispatch($url, $response, array('return' => 1));
  364. $this->assertEquals(
  365. 'something. .',
  366. $url->params['pass'][0],
  367. 'Period was chopped off. %s'
  368. );
  369. }
  370. /**
  371. * Test that Dispatcher handles actions that return response objects.
  372. *
  373. * @return void
  374. */
  375. public function testDispatchActionReturnsResponse() {
  376. Router::connect('/:controller/:action');
  377. $Dispatcher = new Dispatcher();
  378. $request = new Request('some_pages/responseGenerator');
  379. $response = $this->getMock('Cake\Network\Response', array('_sendHeader'));
  380. ob_start();
  381. $Dispatcher->dispatch($request, $response);
  382. $result = ob_get_clean();
  383. $this->assertEquals('new response', $result);
  384. }
  385. /**
  386. * testPrefixDispatch method
  387. *
  388. * @return void
  389. */
  390. public function testPrefixDispatch() {
  391. $Dispatcher = new TestDispatcher();
  392. Configure::write('Routing.prefixes', array('admin'));
  393. $request = new Request('admin/posts/index');
  394. $response = $this->getMock('Cake\Network\Response');
  395. Router::reload();
  396. require CAKE . 'Config/routes.php';
  397. $Dispatcher->dispatch($request, $response, array('return' => 1));
  398. $this->assertInstanceOf(
  399. 'TestApp\Controller\Admin\PostsController',
  400. $Dispatcher->controller
  401. );
  402. $this->assertEquals('admin', $request->params['prefix']);
  403. $this->assertEquals('posts', $request->params['controller']);
  404. $this->assertEquals('index', $request->params['action']);
  405. $expected = '/admin/posts/index';
  406. $this->assertSame($expected, $request->here);
  407. }
  408. /**
  409. * test prefix dispatching in a plugin.
  410. *
  411. * @return void
  412. */
  413. public function testPrefixDispatchPlugin() {
  414. Configure::write('Routing.prefixes', array('admin'));
  415. Plugin::load('TestPlugin');
  416. $request = new Request('admin/posts/index');
  417. $response = $this->getMock('Cake\Network\Response');
  418. Router::reload();
  419. require CAKE . 'Config/routes.php';
  420. $Dispatcher = new TestDispatcher();
  421. $Dispatcher->dispatch($request, $response, array('return' => 1));
  422. $this->assertInstanceOf(
  423. 'TestApp\Controller\Admin\PostsController',
  424. $Dispatcher->controller
  425. );
  426. $this->assertEquals('admin', $request->params['prefix']);
  427. $this->assertEquals('posts', $request->params['controller']);
  428. $this->assertEquals('index', $request->params['action']);
  429. $expected = '/admin/posts/index';
  430. $this->assertSame($expected, $request->here);
  431. }
  432. /**
  433. * test plugin shortcut urls with controllers that need to be loaded,
  434. * the above test uses a controller that has already been included.
  435. *
  436. * @return void
  437. */
  438. public function testPluginShortCutUrlsWithControllerThatNeedsToBeLoaded() {
  439. Router::reload();
  440. Plugin::load(['TestPlugin', 'TestPluginTwo']);
  441. $Dispatcher = new TestDispatcher();
  442. $Dispatcher->base = false;
  443. $url = new Request('test_plugin/');
  444. $response = $this->getMock('Cake\Network\Response');
  445. $Dispatcher->dispatch($url, $response, array('return' => 1));
  446. $this->assertEquals('test_plugin', $url->params['controller']);
  447. $this->assertEquals('test_plugin', $url->params['plugin']);
  448. $this->assertEquals('index', $url->params['action']);
  449. $this->assertFalse(isset($url->params['pass'][0]));
  450. $url = new Request('/test_plugin/tests/index');
  451. $Dispatcher->dispatch($url, $response, array('return' => 1));
  452. $this->assertEquals('tests', $url->params['controller']);
  453. $this->assertEquals('test_plugin', $url->params['plugin']);
  454. $this->assertEquals('index', $url->params['action']);
  455. $this->assertFalse(isset($url->params['pass'][0]));
  456. $url = new Request('/test_plugin/tests/index/some_param');
  457. $Dispatcher->dispatch($url, $response, array('return' => 1));
  458. $this->assertEquals('tests', $url->params['controller']);
  459. $this->assertEquals('test_plugin', $url->params['plugin']);
  460. $this->assertEquals('index', $url->params['action']);
  461. $this->assertEquals('some_param', $url->params['pass'][0]);
  462. }
  463. /**
  464. * Test dispatching into the TestPlugin in the TestApp
  465. *
  466. * @return void
  467. */
  468. public function testTestPluginDispatch() {
  469. $Dispatcher = new TestDispatcher();
  470. Plugin::load(array('TestPlugin', 'TestPluginTwo'));
  471. Router::reload();
  472. Router::parse('/');
  473. $url = new Request('/test_plugin/tests/index');
  474. $response = $this->getMock('Cake\Network\Response');
  475. $Dispatcher->dispatch($url, $response, array('return' => 1));
  476. $this->assertTrue(class_exists('TestPlugin\Controller\TestsController'));
  477. $this->assertTrue(class_exists('TestPlugin\Controller\TestPluginAppController'));
  478. $this->assertTrue(class_exists('TestPlugin\Controller\Component\PluginsComponent'));
  479. $this->assertEquals('tests', $url->params['controller']);
  480. $this->assertEquals('test_plugin', $url->params['plugin']);
  481. $this->assertEquals('index', $url->params['action']);
  482. }
  483. /**
  484. * Tests that it is possible to attach filter classes to the dispatch cycle
  485. *
  486. * @return void
  487. */
  488. public function testDispatcherFilterSuscriber() {
  489. Plugin::load('TestPlugin');
  490. Configure::write('Dispatcher.filters', array(
  491. array('callable' => 'TestPlugin.TestDispatcherFilter')
  492. ));
  493. $dispatcher = new TestDispatcher();
  494. $request = new Request('/');
  495. $request->params['altered'] = false;
  496. $response = $this->getMock('Cake\Network\Response', array('send'));
  497. $dispatcher->dispatch($request, $response);
  498. $this->assertTrue($request->params['altered']);
  499. $this->assertEquals(304, $response->statusCode());
  500. Configure::write('Dispatcher.filters', array(
  501. 'TestPlugin.Test2DispatcherFilter',
  502. 'TestPlugin.TestDispatcherFilter'
  503. ));
  504. $dispatcher = new TestDispatcher();
  505. $request = new Request('/');
  506. $request->params['altered'] = false;
  507. $response = $this->getMock('Cake\Network\Response', array('send'));
  508. $dispatcher->dispatch($request, $response);
  509. $this->assertFalse($request->params['altered']);
  510. $this->assertEquals(500, $response->statusCode());
  511. $this->assertNull($dispatcher->controller);
  512. }
  513. /**
  514. * Tests that attaching an inexistent class as filter will throw an exception
  515. *
  516. * @expectedException \Cake\Routing\Error\MissingDispatcherFilterException
  517. * @return void
  518. */
  519. public function testDispatcherFilterSuscriberMissing() {
  520. Plugin::load('TestPlugin');
  521. Configure::write('Dispatcher.filters', array(
  522. array('callable' => 'TestPlugin.NotAFilter')
  523. ));
  524. $dispatcher = new TestDispatcher();
  525. $request = new Request('/');
  526. $response = $this->getMock('Cake\Network\Response', array('send'));
  527. $dispatcher->dispatch($request, $response);
  528. }
  529. /**
  530. * Tests it is possible to attach single callables as filters
  531. *
  532. * @return void
  533. */
  534. public function testDispatcherFilterCallable() {
  535. $dispatcher = new TestDispatcher();
  536. Configure::write('Dispatcher.filters', array(
  537. array('callable' => array($dispatcher, 'filterTest'), 'on' => 'before')
  538. ));
  539. $request = new Request('/');
  540. $response = $this->getMock('Cake\Network\Response', array('send'));
  541. $dispatcher->dispatch($request, $response);
  542. $this->assertEquals('Dispatcher.beforeDispatch', $request->params['eventName']);
  543. $dispatcher = new TestDispatcher();
  544. Configure::write('Dispatcher.filters', array(
  545. array('callable' => array($dispatcher, 'filterTest'), 'on' => 'after')
  546. ));
  547. $request = new Request('/');
  548. $response = $this->getMock('Cake\Network\Response', array('send'));
  549. $dispatcher->dispatch($request, $response);
  550. $this->assertEquals('Dispatcher.afterDispatch', $request->params['eventName']);
  551. // Test that it is possible to skip the route connection process
  552. $dispatcher = new TestDispatcher();
  553. Configure::write('Dispatcher.filters', array(
  554. array('callable' => array($dispatcher, 'filterTest2'), 'on' => 'before', 'priority' => 1)
  555. ));
  556. $request = new Request('/');
  557. $response = $this->getMock('Cake\Network\Response', array('send'));
  558. $dispatcher->dispatch($request, $response);
  559. $this->assertEmpty($dispatcher->controller);
  560. $expected = array('controller' => null, 'action' => null, 'plugin' => null, '_ext' => null, 'pass' => []);
  561. $this->assertEquals($expected, $request->params);
  562. $dispatcher = new TestDispatcher();
  563. Configure::write('Dispatcher.filters', array(
  564. array('callable' => array($dispatcher, 'filterTest2'), 'on' => 'before', 'priority' => 1)
  565. ));
  566. $request = new Request('/');
  567. $request->params['return'] = true;
  568. $response = $this->getMock('Cake\Network\Response', array('send'));
  569. $response->body('this is a body');
  570. $result = $dispatcher->dispatch($request, $response);
  571. $this->assertEquals('this is a body', $result);
  572. $request = new Request('/');
  573. $response = $this->getMock('Cake\Network\Response', array('send'));
  574. $response->expects($this->once())->method('send');
  575. $response->body('this is a body');
  576. $result = $dispatcher->dispatch($request, $response);
  577. $this->assertNull($result);
  578. }
  579. /**
  580. * testChangingParamsFromBeforeFilter method
  581. *
  582. * @return void
  583. */
  584. public function testChangingParamsFromBeforeFilter() {
  585. Router::connect('/:controller/:action/*');
  586. $Dispatcher = new TestDispatcher();
  587. $response = $this->getMock('Cake\Network\Response');
  588. $url = new Request('some_posts/index/param:value/param2:value2');
  589. try {
  590. $Dispatcher->dispatch($url, $response, array('return' => 1));
  591. $this->fail('No exception.');
  592. } catch (MissingActionException $e) {
  593. $this->assertEquals('Action SomePostsController::view() could not be found.', $e->getMessage());
  594. }
  595. $url = new Request('some_posts/something_else/param:value/param2:value2');
  596. $Dispatcher->dispatch($url, $response, array('return' => 1));
  597. $expected = 'SomePosts';
  598. $this->assertEquals($expected, $Dispatcher->controller->name);
  599. $expected = 'change';
  600. $this->assertEquals($expected, $url->action);
  601. $expected = array('changed');
  602. $this->assertSame($expected, $url->params['pass']);
  603. }
  604. /**
  605. * testStaticAssets method
  606. *
  607. * @return void
  608. */
  609. public function testAssets() {
  610. Router::reload();
  611. Plugin::load(array('TestPlugin', 'TestPluginTwo'));
  612. Configure::write('Dispatcher.filters', array('AssetDispatcher'));
  613. $Dispatcher = new TestDispatcher();
  614. $response = $this->getMock('Cake\Network\Response', array('_sendHeader'));
  615. try {
  616. $Dispatcher->dispatch(new Request('theme/test_theme/../webroot/css/test_asset.css'), $response);
  617. $this->fail('No exception');
  618. } catch (MissingControllerException $e) {
  619. $this->assertEquals('Controller class Theme could not be found.', $e->getMessage());
  620. }
  621. try {
  622. $Dispatcher->dispatch(new Request('theme/test_theme/pdfs'), $response);
  623. $this->fail('No exception');
  624. } catch (MissingControllerException $e) {
  625. $this->assertEquals('Controller class Theme could not be found.', $e->getMessage());
  626. }
  627. }
  628. /**
  629. * Data provider for asset filter
  630. *
  631. * - theme assets.
  632. * - plugin assets.
  633. * - plugin assets in sub directories.
  634. * - unknown plugin assets.
  635. *
  636. * @return array
  637. */
  638. public static function assetProvider() {
  639. return array(
  640. array(
  641. 'theme/test_theme/flash/theme_test.swf',
  642. 'TestApp/Template/Themed/TestTheme/webroot/flash/theme_test.swf'
  643. ),
  644. array(
  645. 'theme/test_theme/pdfs/theme_test.pdf',
  646. 'TestApp/Template/Themed/TestTheme/webroot/pdfs/theme_test.pdf'
  647. ),
  648. array(
  649. 'theme/test_theme/img/test.jpg',
  650. 'TestApp/Template/Themed/TestTheme/webroot/img/test.jpg'
  651. ),
  652. array(
  653. 'theme/test_theme/css/test_asset.css',
  654. 'TestApp/Template/Themed/TestTheme/webroot/css/test_asset.css'
  655. ),
  656. array(
  657. 'theme/test_theme/js/theme.js',
  658. 'TestApp/Template/Themed/TestTheme/webroot/js/theme.js'
  659. ),
  660. array(
  661. 'theme/test_theme/js/one/theme_one.js',
  662. 'TestApp/Template/Themed/TestTheme/webroot/js/one/theme_one.js'
  663. ),
  664. array(
  665. 'theme/test_theme/space%20image.text',
  666. 'TestApp/Template/Themed/TestTheme/webroot/space image.text'
  667. ),
  668. array(
  669. 'test_plugin/root.js',
  670. 'Plugin/TestPlugin/webroot/root.js'
  671. ),
  672. array(
  673. 'test_plugin/flash/plugin_test.swf',
  674. 'Plugin/TestPlugin/webroot/flash/plugin_test.swf'
  675. ),
  676. array(
  677. 'test_plugin/pdfs/plugin_test.pdf',
  678. 'Plugin/TestPlugin/webroot/pdfs/plugin_test.pdf'
  679. ),
  680. array(
  681. 'test_plugin/js/test_plugin/test.js',
  682. 'Plugin/TestPlugin/webroot/js/test_plugin/test.js'
  683. ),
  684. array(
  685. 'test_plugin/css/test_plugin_asset.css',
  686. 'Plugin/TestPlugin/webroot/css/test_plugin_asset.css'
  687. ),
  688. array(
  689. 'test_plugin/img/cake.icon.gif',
  690. 'Plugin/TestPlugin/webroot/img/cake.icon.gif'
  691. ),
  692. array(
  693. 'plugin_js/js/plugin_js.js',
  694. 'Plugin/PluginJs/webroot/js/plugin_js.js'
  695. ),
  696. array(
  697. 'plugin_js/js/one/plugin_one.js',
  698. 'Plugin/PluginJs/webroot/js/one/plugin_one.js'
  699. ),
  700. array(
  701. 'test_plugin/css/unknown.extension',
  702. 'Plugin/TestPlugin/webroot/css/unknown.extension'
  703. ),
  704. array(
  705. 'test_plugin/css/theme_one.htc',
  706. 'Plugin/TestPlugin/webroot/css/theme_one.htc'
  707. ),
  708. );
  709. }
  710. /**
  711. * Test assets
  712. *
  713. * @dataProvider assetProvider
  714. * @outputBuffering enabled
  715. * @return void
  716. */
  717. public function testAsset($url, $file) {
  718. Router::reload();
  719. Plugin::load(array('TestPlugin', 'PluginJs'));
  720. Configure::write('Dispatcher.filters', array('AssetDispatcher'));
  721. $Dispatcher = new TestDispatcher();
  722. $response = $this->getMock('Cake\Network\Response', array('_sendHeader'));
  723. $Dispatcher->dispatch(new Request($url), $response);
  724. $result = ob_get_clean();
  725. $path = TEST_APP . str_replace('/', DS, $file);
  726. $file = file_get_contents($path);
  727. $this->assertEquals($file, $result);
  728. $expected = filesize($path);
  729. $headers = $response->header();
  730. $this->assertEquals($expected, $headers['Content-Length']);
  731. }
  732. /**
  733. * Data provider for cached actions.
  734. *
  735. * - Test simple views
  736. * - Test views with nocache tags
  737. * - Test requests with named + passed params.
  738. * - Test requests with query string params
  739. * - Test themed views.
  740. *
  741. * @return array
  742. */
  743. public static function cacheActionProvider() {
  744. return array(
  745. array('/'),
  746. array('test_cached_pages/index'),
  747. array('TestCachedPages/index'),
  748. array('test_cached_pages/test_nocache_tags'),
  749. array('TestCachedPages/test_nocache_tags'),
  750. array('test_cached_pages/view/param/param'),
  751. array('test_cached_pages/view?q=cakephp'),
  752. array('test_cached_pages/themed'),
  753. );
  754. }
  755. /**
  756. * testFullPageCachingDispatch method
  757. *
  758. * @dataProvider cacheActionProvider
  759. * @return void
  760. */
  761. public function testFullPageCachingDispatch($url) {
  762. Cache::enable();
  763. Configure::write('Cache.disable', false);
  764. Configure::write('Cache.check', true);
  765. Configure::write('debug', true);
  766. Router::reload();
  767. Router::connect('/', array('controller' => 'test_cached_pages', 'action' => 'index'));
  768. Router::connect('/:controller/:action/*');
  769. $dispatcher = new TestDispatcher();
  770. $request = new Request($url);
  771. $response = $this->getMock('Cake\Network\Response', array('send'));
  772. $dispatcher->dispatch($request, $response);
  773. $out = $response->body();
  774. Configure::write('Dispatcher.filters', array('CacheDispatcher'));
  775. $request = new Request($url);
  776. $response = $this->getMock('Cake\Network\Response', array('send'));
  777. $dispatcher = new TestDispatcher();
  778. $dispatcher->dispatch($request, $response);
  779. $cached = $response->body();
  780. $cached = preg_replace('/<!--+[^<>]+-->/', '', $cached);
  781. $this->assertTextEquals($out, $cached);
  782. $filename = $this->_cachePath($request->here());
  783. unlink($filename);
  784. }
  785. /**
  786. * testHttpMethodOverrides method
  787. *
  788. * @return void
  789. */
  790. public function testHttpMethodOverrides() {
  791. Router::reload();
  792. Router::mapResources('Posts');
  793. $dispatcher = new Dispatcher();
  794. $request = new Request([
  795. 'url' => '/posts',
  796. 'environment' => ['REQUEST_METHOD' => 'POST']
  797. ]);
  798. $event = new Event(__CLASS__, $dispatcher, array('request' => $request));
  799. $dispatcher->parseParams($event);
  800. $expected = array(
  801. 'pass' => [],
  802. 'plugin' => null,
  803. 'controller' => 'posts',
  804. 'action' => 'add',
  805. '[method]' => 'POST'
  806. );
  807. foreach ($expected as $key => $value) {
  808. $this->assertEquals($value, $request[$key], 'Value mismatch for ' . $key . ' %s');
  809. }
  810. $request = new Request([
  811. 'url' => '/posts/5',
  812. 'environment' => [
  813. 'REQUEST_METHOD' => 'GET',
  814. 'HTTP_X_HTTP_METHOD_OVERRIDE' => 'PUT'
  815. ]
  816. ]);
  817. $event = new Event(__CLASS__, $dispatcher, array('request' => $request));
  818. $dispatcher->parseParams($event);
  819. $expected = array(
  820. 'pass' => array('5'),
  821. 'id' => '5',
  822. 'plugin' => null,
  823. 'controller' => 'posts',
  824. 'action' => 'edit',
  825. '[method]' => 'PUT'
  826. );
  827. foreach ($expected as $key => $value) {
  828. $this->assertEquals($value, $request[$key], 'Value mismatch for ' . $key . ' %s');
  829. }
  830. $request = new Request([
  831. 'url' => '/posts/5',
  832. 'environment' => [
  833. 'REQUEST_METHOD' => 'GET'
  834. ]
  835. ]);
  836. $event = new Event(__CLASS__, $dispatcher, array('request' => $request));
  837. $dispatcher->parseParams($event);
  838. $expected = array(
  839. 'pass' => array('5'),
  840. 'id' => '5',
  841. 'plugin' => null,
  842. 'controller' => 'posts',
  843. 'action' => 'view',
  844. '[method]' => 'GET'
  845. );
  846. foreach ($expected as $key => $value) {
  847. $this->assertEquals($value, $request[$key], 'Value mismatch for ' . $key . ' %s');
  848. }
  849. $request = new Request([
  850. 'url' => '/posts/5',
  851. 'post' => array('_method' => 'PUT')
  852. ]);
  853. $event = new Event(__CLASS__, $dispatcher, array('request' => $request));
  854. $dispatcher->parseParams($event);
  855. $expected = array(
  856. 'pass' => array('5'),
  857. 'id' => '5',
  858. 'plugin' => null,
  859. 'controller' => 'posts',
  860. 'action' => 'edit',
  861. '[method]' => 'PUT'
  862. );
  863. foreach ($expected as $key => $value) {
  864. $this->assertEquals($value, $request[$key], 'Value mismatch for ' . $key . ' %s');
  865. }
  866. $request = new Request(array(
  867. 'url' => '/posts',
  868. 'post' => array(
  869. '_method' => 'POST',
  870. 'Post' => array('title' => 'New Post'),
  871. 'extra' => 'data'
  872. ),
  873. ));
  874. $event = new Event(__CLASS__, $dispatcher, array('request' => $request));
  875. $dispatcher->parseParams($event);
  876. $expected = array(
  877. 'pass' => [],
  878. 'plugin' => null,
  879. 'controller' => 'posts',
  880. 'action' => 'add',
  881. '[method]' => 'POST',
  882. 'data' => array('extra' => 'data', 'Post' => array('title' => 'New Post')),
  883. );
  884. foreach ($expected as $key => $value) {
  885. $this->assertEquals($value, $request[$key], 'Value mismatch for ' . $key . ' %s');
  886. }
  887. }
  888. /**
  889. * backupEnvironment method
  890. *
  891. * @return void
  892. */
  893. protected function _backupEnvironment() {
  894. return array(
  895. 'App' => Configure::read('App'),
  896. 'GET' => $_GET,
  897. 'POST' => $_POST,
  898. 'SERVER' => $_SERVER
  899. );
  900. }
  901. /**
  902. * reloadEnvironment method
  903. *
  904. * @return void
  905. */
  906. protected function _reloadEnvironment() {
  907. foreach ($_GET as $key => $val) {
  908. unset($_GET[$key]);
  909. }
  910. foreach ($_POST as $key => $val) {
  911. unset($_POST[$key]);
  912. }
  913. foreach ($_SERVER as $key => $val) {
  914. unset($_SERVER[$key]);
  915. }
  916. Configure::write('App', []);
  917. }
  918. /**
  919. * loadEnvironment method
  920. *
  921. * @param array $env
  922. * @return void
  923. */
  924. protected function _loadEnvironment($env) {
  925. if ($env['reload']) {
  926. $this->_reloadEnvironment();
  927. }
  928. if (isset($env['App'])) {
  929. Configure::write('App', $env['App']);
  930. }
  931. if (isset($env['GET'])) {
  932. foreach ($env['GET'] as $key => $val) {
  933. $_GET[$key] = $val;
  934. }
  935. }
  936. if (isset($env['POST'])) {
  937. foreach ($env['POST'] as $key => $val) {
  938. $_POST[$key] = $val;
  939. }
  940. }
  941. if (isset($env['SERVER'])) {
  942. foreach ($env['SERVER'] as $key => $val) {
  943. $_SERVER[$key] = $val;
  944. }
  945. }
  946. }
  947. /**
  948. * cachePath method
  949. *
  950. * @param string $here
  951. * @return string
  952. */
  953. protected function _cachePath($here) {
  954. $path = $here;
  955. if ($here === '/') {
  956. $path = 'home';
  957. }
  958. $path = strtolower(Inflector::slug($path));
  959. $filename = CACHE . 'views/' . $path . '.php';
  960. if (!file_exists($filename)) {
  961. $filename = CACHE . 'views/' . $path . '_index.php';
  962. }
  963. return $filename;
  964. }
  965. }