CellTest.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526
  1. <?php
  2. /**
  3. * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
  4. * Copyright (c) Cake Software Foundation, Inc. (https://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. (https://cakefoundation.org)
  11. * @link https://cakephp.org CakePHP(tm) Project
  12. * @since 3.0.0
  13. * @license https://opensource.org/licenses/mit-license.php MIT License
  14. */
  15. namespace Cake\Test\TestCase\View;
  16. use Cake\Cache\Cache;
  17. use Cake\Core\Plugin;
  18. use Cake\TestSuite\TestCase;
  19. use Cake\View\Exception\MissingCellViewException;
  20. use Cake\View\Exception\MissingTemplateException;
  21. use Cake\View\View;
  22. use TestApp\Controller\CellTraitTestController;
  23. use TestApp\View\CustomJsonView;
  24. /**
  25. * CellTest class.
  26. *
  27. * For testing both View\Cell & Utility\CellTrait
  28. */
  29. class CellTest extends TestCase
  30. {
  31. /**
  32. * @var \Cake\View\View
  33. */
  34. public $View;
  35. /**
  36. * setUp method
  37. *
  38. * @return void
  39. */
  40. public function setUp()
  41. {
  42. parent::setUp();
  43. static::setAppNamespace();
  44. Plugin::load(['TestPlugin', 'TestTheme']);
  45. $request = $this->getMockBuilder('Cake\Http\ServerRequest')->getMock();
  46. $response = $this->getMockBuilder('Cake\Http\Response')->getMock();
  47. $this->View = new View($request, $response);
  48. }
  49. /**
  50. * tearDown method
  51. *
  52. * @return void
  53. */
  54. public function tearDown()
  55. {
  56. parent::tearDown();
  57. Plugin::unload('TestPlugin');
  58. Plugin::unload('TestTheme');
  59. unset($this->View);
  60. }
  61. /**
  62. * Tests basic cell rendering.
  63. *
  64. * @return void
  65. */
  66. public function testCellRender()
  67. {
  68. $cell = $this->View->cell('Articles::teaserList');
  69. $render = "{$cell}";
  70. $this->assertEquals('teaser_list', $cell->template);
  71. $this->assertContains('<h2>Lorem ipsum</h2>', $render);
  72. $this->assertContains('<h2>Usectetur adipiscing eli</h2>', $render);
  73. $this->assertContains('<h2>Topis semper blandit eu non</h2>', $render);
  74. $this->assertContains('<h2>Suspendisse gravida neque</h2>', $render);
  75. $cell = $this->View->cell('Cello');
  76. $this->assertInstanceOf('TestApp\View\Cell\CelloCell', $cell);
  77. $this->assertEquals("Cellos\n", $cell->render());
  78. }
  79. /**
  80. * Test __toString() hitting an error when rendering views.
  81. *
  82. * @return void
  83. */
  84. public function testCellImplictRenderWithError()
  85. {
  86. $capture = function ($errno, $msg) {
  87. restore_error_handler();
  88. $this->assertEquals(E_USER_WARNING, $errno);
  89. $this->assertContains('Could not render cell - Cell view file', $msg);
  90. };
  91. set_error_handler($capture);
  92. $cell = $this->View->cell('Articles::teaserList');
  93. $cell->template = 'nope';
  94. $result = "{$cell}";
  95. }
  96. /**
  97. * Tests that we are able pass multiple arguments to cell methods.
  98. *
  99. * This test sets its own error handler, as PHPUnit won't convert
  100. * errors into exceptions when the caller is a __toString() method.
  101. *
  102. * @return void
  103. */
  104. public function testCellWithArguments()
  105. {
  106. $cell = $this->View->cell('Articles::doEcho', ['msg1' => 'dummy', 'msg2' => ' message']);
  107. $render = "{$cell}";
  108. $this->assertContains('dummy message', $render);
  109. }
  110. /**
  111. * Tests that cell runs default action when none is provided.
  112. *
  113. * @return void
  114. */
  115. public function testDefaultCellAction()
  116. {
  117. $appCell = $this->View->cell('Articles');
  118. $this->assertEquals('display', $appCell->template);
  119. $this->assertContains('dummy', "{$appCell}");
  120. $pluginCell = $this->View->cell('TestPlugin.Dummy');
  121. $this->assertContains('dummy', "{$pluginCell}");
  122. $this->assertEquals('display', $pluginCell->template);
  123. }
  124. /**
  125. * Tests that cell action setting the template using the property renders the correct template
  126. *
  127. * @return void
  128. */
  129. public function testSettingCellTemplateFromAction()
  130. {
  131. $appCell = $this->View->cell('Articles::customTemplate');
  132. $this->assertContains('This is the alternate template', "{$appCell}");
  133. $this->assertEquals('alternate_teaser_list', $appCell->template);
  134. $this->assertEquals('alternate_teaser_list', $appCell->viewBuilder()->getTemplate());
  135. }
  136. /**
  137. * Tests that cell action setting the templatePath
  138. *
  139. * @return void
  140. */
  141. public function testSettingCellTemplatePathFromAction()
  142. {
  143. $appCell = $this->View->cell('Articles::customTemplatePath');
  144. $this->assertContains('Articles subdir custom_template_path template', "{$appCell}");
  145. $this->assertEquals('custom_template_path', $appCell->template);
  146. $this->assertEquals('Cell/Articles/Subdir', $appCell->viewBuilder()->getTemplatePath());
  147. }
  148. /**
  149. * Tests that cell action setting the template using the ViewBuilder renders the correct template
  150. *
  151. * @return void
  152. */
  153. public function testSettingCellTemplateFromActionViewBuilder()
  154. {
  155. $appCell = $this->View->cell('Articles::customTemplateViewBuilder');
  156. $this->assertContains('This is the alternate template', "{$appCell}");
  157. $this->assertEquals('alternate_teaser_list', $appCell->viewBuilder()->getTemplate());
  158. }
  159. /**
  160. * Tests manual render() invocation.
  161. *
  162. * @return void
  163. */
  164. public function testCellManualRender()
  165. {
  166. $cell = $this->View->cell('Articles::doEcho', ['msg1' => 'dummy', 'msg2' => ' message']);
  167. $this->assertContains('dummy message', $cell->render());
  168. $cell->teaserList();
  169. $this->assertContains('<h2>Lorem ipsum</h2>', $cell->render('teaser_list'));
  170. }
  171. /**
  172. * Tests manual render() invocation with error
  173. *
  174. * @return void
  175. */
  176. public function testCellManualRenderError()
  177. {
  178. $cell = $this->View->cell('Articles');
  179. $e = null;
  180. try {
  181. $cell->render('derp');
  182. } catch (MissingCellViewException $e) {
  183. }
  184. $this->assertNotNull($e);
  185. $this->assertEquals('Cell view file "derp" is missing.', $e->getMessage());
  186. $this->assertInstanceOf(MissingTemplateException::class, $e->getPrevious());
  187. }
  188. /**
  189. * Test rendering a cell with a theme.
  190. *
  191. * @return void
  192. */
  193. public function testCellRenderThemed()
  194. {
  195. $this->View->theme = 'TestTheme';
  196. $cell = $this->View->cell('Articles', ['msg' => 'hello world!']);
  197. $this->assertEquals($this->View->theme, $cell->viewBuilder()->getTheme());
  198. $this->assertContains('Themed cell content.', $cell->render());
  199. }
  200. /**
  201. * Test that a cell can render a plugin view.
  202. *
  203. * @return void
  204. */
  205. public function testCellRenderPluginTemplate()
  206. {
  207. $cell = $this->View->cell('Articles');
  208. $this->assertContains(
  209. 'TestPlugin Articles/display',
  210. $cell->render('TestPlugin.display')
  211. );
  212. $cell = $this->View->cell('Articles');
  213. $cell->plugin = 'TestPlugin';
  214. $this->assertContains(
  215. 'TestPlugin Articles/display',
  216. $cell->render('display')
  217. );
  218. }
  219. /**
  220. * Tests that using plugin's cells works.
  221. *
  222. * @return void
  223. */
  224. public function testPluginCell()
  225. {
  226. $cell = $this->View->cell('TestPlugin.Dummy::echoThis', ['msg' => 'hello world!']);
  227. $this->assertContains('hello world!', "{$cell}");
  228. }
  229. /**
  230. * Tests that using namespaced cells works.
  231. *
  232. * @return void
  233. */
  234. public function testNamespacedCell()
  235. {
  236. $cell = $this->View->cell('Admin/Menu');
  237. $this->assertContains('Admin Menu Cell', $cell->render());
  238. }
  239. /**
  240. * Tests that using namespaced cells in plugins works
  241. *
  242. * @return void
  243. */
  244. public function testPluginNamespacedCell()
  245. {
  246. $cell = $this->View->cell('TestPlugin.Admin/Menu');
  247. $this->assertContains('Test Plugin Admin Menu Cell', $cell->render());
  248. }
  249. /**
  250. * Test that plugin cells can render other view templates.
  251. *
  252. * @return void
  253. */
  254. public function testPluginCellAlternateTemplate()
  255. {
  256. $cell = $this->View->cell('TestPlugin.Dummy::echoThis', ['msg' => 'hello world!']);
  257. $cell->template = '../../Element/translate';
  258. $this->assertContains('This is a translatable string', "{$cell}");
  259. }
  260. /**
  261. * Test that plugin cells can render other view templates.
  262. *
  263. * @return void
  264. */
  265. public function testPluginCellAlternateTemplateRenderParam()
  266. {
  267. $cell = $this->View->cell('TestPlugin.Dummy::echoThis', ['msg' => 'hello world!']);
  268. $result = $cell->render('../../Element/translate');
  269. $this->assertContains('This is a translatable string', $result);
  270. }
  271. /**
  272. * Tests that using an non-existent cell throws an exception.
  273. *
  274. * @return void
  275. */
  276. public function testNonExistentCell()
  277. {
  278. $this->expectException(\Cake\View\Exception\MissingCellException::class);
  279. $cell = $this->View->cell('TestPlugin.Void::echoThis', ['arg1' => 'v1']);
  280. $cell = $this->View->cell('Void::echoThis', ['arg1' => 'v1', 'arg2' => 'v2']);
  281. }
  282. /**
  283. * Tests missing method errors
  284. *
  285. * @return void
  286. */
  287. public function testCellMissingMethod()
  288. {
  289. $this->expectException(\BadMethodCallException::class);
  290. $this->expectExceptionMessage('Class TestApp\View\Cell\ArticlesCell does not have a "nope" method.');
  291. $cell = $this->View->cell('Articles::nope');
  292. $cell->render();
  293. }
  294. /**
  295. * Test that cell options are passed on.
  296. *
  297. * @return void
  298. */
  299. public function testCellOptions()
  300. {
  301. $cell = $this->View->cell('Articles', [], ['limit' => 10, 'nope' => 'nope']);
  302. $this->assertEquals(10, $cell->limit);
  303. $this->assertObjectNotHasAttribute('nope', $cell, 'Not a valid option');
  304. }
  305. /**
  306. * Test that cells get the helper configuration from the view that created them.
  307. *
  308. * @return void
  309. */
  310. public function testCellInheritsHelperConfig()
  311. {
  312. $this->View->helpers = ['Url', 'Form', 'Banana'];
  313. $cell = $this->View->cell('Articles');
  314. $this->assertSame($this->View->helpers, $cell->helpers);
  315. }
  316. /**
  317. * Test that cells the view class name of a custom view passed on.
  318. *
  319. * @return void
  320. */
  321. public function testCellInheritsCustomViewClass()
  322. {
  323. $request = $this->getMockBuilder('Cake\Http\ServerRequest')->getMock();
  324. $response = $this->getMockBuilder('Cake\Http\Response')->getMock();
  325. $view = new CustomJsonView($request, $response);
  326. $view->theme = 'Pretty';
  327. $cell = $view->cell('Articles');
  328. $this->assertSame('TestApp\View\CustomJsonView', $cell->viewClass);
  329. $this->assertSame('TestApp\View\CustomJsonView', $cell->viewBuilder()->getClassName());
  330. $this->assertSame('Pretty', $cell->viewBuilder()->getTheme());
  331. }
  332. /**
  333. * Test that cells the view class name of a controller passed on.
  334. *
  335. * @return void
  336. */
  337. public function testCellInheritsController()
  338. {
  339. $request = $this->getMockBuilder('Cake\Http\ServerRequest')->getMock();
  340. $response = $this->getMockBuilder('Cake\Http\Response')->getMock();
  341. $controller = new CellTraitTestController($request, $response);
  342. $controller->viewBuilder()->setTheme('Pretty');
  343. $controller->viewClass = 'Json';
  344. $cell = $controller->cell('Articles');
  345. $this->assertSame('Json', $cell->viewClass);
  346. $this->assertSame('Json', $cell->viewBuilder()->getClassName());
  347. $this->assertSame('Pretty', $cell->viewBuilder()->getTheme());
  348. }
  349. /**
  350. * Test cached render.
  351. *
  352. * @return void
  353. */
  354. public function testCachedRenderSimple()
  355. {
  356. $mock = $this->getMockBuilder('Cake\Cache\CacheEngine')->getMock();
  357. $mock->method('init')
  358. ->will($this->returnValue(true));
  359. $mock->method('read')
  360. ->will($this->returnValue(false));
  361. $mock->expects($this->once())
  362. ->method('write')
  363. ->with('cell_test_app_view_cell_articles_cell_display_default', "dummy\n");
  364. Cache::setConfig('default', $mock);
  365. $cell = $this->View->cell('Articles', [], ['cache' => true]);
  366. $result = $cell->render();
  367. $this->assertEquals("dummy\n", $result);
  368. Cache::drop('default');
  369. }
  370. /**
  371. * Test read cached cell.
  372. *
  373. * @return void
  374. */
  375. public function testReadCachedCell()
  376. {
  377. $mock = $this->getMockBuilder('Cake\Cache\CacheEngine')->getMock();
  378. $mock->method('init')
  379. ->will($this->returnValue(true));
  380. $mock->method('read')
  381. ->will($this->returnValue("dummy\n"));
  382. $mock->expects($this->never())
  383. ->method('write');
  384. Cache::setConfig('default', $mock);
  385. $cell = $this->View->cell('Articles', [], ['cache' => true]);
  386. $result = $cell->render();
  387. $this->assertEquals("dummy\n", $result);
  388. Cache::drop('default');
  389. }
  390. /**
  391. * Test cached render array config
  392. *
  393. * @return void
  394. */
  395. public function testCachedRenderArrayConfig()
  396. {
  397. $mock = $this->getMockBuilder('Cake\Cache\CacheEngine')->getMock();
  398. $mock->method('init')
  399. ->will($this->returnValue(true));
  400. $mock->method('read')
  401. ->will($this->returnValue(false));
  402. $mock->expects($this->once())
  403. ->method('write')
  404. ->with('my_key', "dummy\n");
  405. Cache::setConfig('cell', $mock);
  406. $cell = $this->View->cell('Articles', [], [
  407. 'cache' => ['key' => 'my_key', 'config' => 'cell']
  408. ]);
  409. $result = $cell->render();
  410. $this->assertEquals("dummy\n", $result);
  411. Cache::drop('cell');
  412. }
  413. /**
  414. * Test cached render when using an action changing the template used
  415. *
  416. * @return void
  417. */
  418. public function testCachedRenderSimpleCustomTemplate()
  419. {
  420. $mock = $this->getMockBuilder('Cake\Cache\CacheEngine')->getMock();
  421. $mock->method('init')
  422. ->will($this->returnValue(true));
  423. $mock->method('read')
  424. ->will($this->returnValue(false));
  425. $mock->expects($this->once())
  426. ->method('write')
  427. ->with('cell_test_app_view_cell_articles_cell_customTemplate_default', "<h1>This is the alternate template</h1>\n");
  428. Cache::setConfig('default', $mock);
  429. $cell = $this->View->cell('Articles::customTemplate', [], ['cache' => true]);
  430. $result = $cell->render();
  431. $this->assertContains('This is the alternate template', $result);
  432. Cache::drop('default');
  433. }
  434. /**
  435. * Test that when the cell cache is enabled, the cell action is only invoke the first
  436. * time the cell is rendered
  437. *
  438. * @return void
  439. */
  440. public function testCachedRenderSimpleCustomTemplateViewBuilder()
  441. {
  442. Cache::setConfig('default', [
  443. 'className' => 'File',
  444. 'path' => CACHE,
  445. ]);
  446. $cell = $this->View->cell('Articles::customTemplateViewBuilder', [], ['cache' => ['key' => 'celltest']]);
  447. $result = $cell->render();
  448. $this->assertEquals(1, $cell->counter);
  449. $cell->render();
  450. $this->assertEquals(1, $cell->counter);
  451. $this->assertContains('This is the alternate template', $result);
  452. Cache::delete('celltest');
  453. Cache::drop('default');
  454. }
  455. /**
  456. * Test that when the cell cache is enabled, the cell action is only invoke the first
  457. * time the cell is rendered
  458. *
  459. * @return void
  460. */
  461. public function testACachedViewCellReRendersWhenGivenADifferentTemplate()
  462. {
  463. Cache::setConfig('default', [
  464. 'className' => 'File',
  465. 'path' => CACHE,
  466. ]);
  467. $cell = $this->View->cell('Articles::customTemplateViewBuilder', [], ['cache' => true]);
  468. $result = $cell->render('alternate_teaser_list');
  469. $result2 = $cell->render('not_the_alternate_teaser_list');
  470. $this->assertContains('This is the alternate template', $result);
  471. $this->assertContains('This is NOT the alternate template', $result2);
  472. Cache::delete('celltest');
  473. Cache::drop('default');
  474. }
  475. }