ViewBuilderTest.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
  5. * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
  6. *
  7. * Licensed under The MIT License
  8. * For full copyright and license information, please see the LICENSE.txt
  9. * Redistributions of files must retain the above copyright notice
  10. *
  11. * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
  12. * @link https://cakephp.org CakePHP(tm) Project
  13. * @since 3.1.0
  14. * @license https://opensource.org/licenses/mit-license.php MIT License
  15. */
  16. namespace Cake\Test\TestCase\View;
  17. use Cake\Event\EventManager;
  18. use Cake\Http\Response;
  19. use Cake\Http\ServerRequest;
  20. use Cake\TestSuite\TestCase;
  21. use Cake\View\AjaxView;
  22. use Cake\View\Exception\MissingViewException;
  23. use Cake\View\Helper\FormHelper;
  24. use Cake\View\Helper\HtmlHelper;
  25. use Cake\View\JsonView;
  26. use Cake\View\View;
  27. use Cake\View\ViewBuilder;
  28. use PHPUnit\Framework\Attributes\DataProvider;
  29. use TestApp\View\AppView;
  30. /**
  31. * View builder test case.
  32. */
  33. class ViewBuilderTest extends TestCase
  34. {
  35. public function testSetVar(): void
  36. {
  37. $builder = new ViewBuilder();
  38. $builder->setVar('testing', 'value');
  39. $this->assertSame('value', $builder->getVar('testing'));
  40. }
  41. public function testSetVars(): void
  42. {
  43. $builder = new ViewBuilder();
  44. $data = ['test' => 'val', 'foo' => 'bar'];
  45. $builder->setVars($data);
  46. $this->assertEquals($data, $builder->getVars());
  47. $update = ['test' => 'updated'];
  48. $builder->setVars($update);
  49. $this->assertEquals(
  50. ['foo' => 'bar', 'test' => 'updated'],
  51. $builder->getVars()
  52. );
  53. $update = ['overwrite' => 'yes'];
  54. $builder->setVars($update, false);
  55. $this->assertEquals(
  56. ['overwrite' => 'yes'],
  57. $builder->getVars()
  58. );
  59. }
  60. public function testHasVar(): void
  61. {
  62. $builder = new ViewBuilder();
  63. $this->assertFalse($builder->hasVar('foo'));
  64. $builder->setVar('foo', 'value');
  65. $this->assertTrue($builder->hasVar('foo'));
  66. $builder->setVar('bar', null);
  67. $this->assertTrue($builder->hasVar('bar'));
  68. }
  69. /**
  70. * data provider for string properties.
  71. *
  72. * @return array
  73. */
  74. public static function stringPropertyProvider(): array
  75. {
  76. return [
  77. ['layoutPath', 'Admin/'],
  78. ['templatePath', 'Admin/'],
  79. ['plugin', 'TestPlugin'],
  80. ['layout', 'admin'],
  81. ['theme', 'TestPlugin'],
  82. ['template', 'edit'],
  83. ['name', 'Articles'],
  84. ['className', JsonView::class],
  85. ];
  86. }
  87. /**
  88. * data provider for boolean properties.
  89. * Format: [key, expectedDefault, newValue]
  90. *
  91. * @return array
  92. */
  93. public static function boolPropertyProvider(): array
  94. {
  95. return [
  96. ['autoLayout', true, false],
  97. ];
  98. }
  99. /**
  100. * data provider for array properties.
  101. *
  102. * @return array
  103. */
  104. public static function arrayPropertyProvider(): array
  105. {
  106. return [
  107. ['options', ['key' => 'value']],
  108. ];
  109. }
  110. /**
  111. * Test string property accessor/mutator methods.
  112. */
  113. #[DataProvider('stringPropertyProvider')]
  114. public function testStringProperties(string $property, string $value): void
  115. {
  116. $get = 'get' . ucfirst($property);
  117. $set = 'set' . ucfirst($property);
  118. $builder = new ViewBuilder();
  119. $this->assertNull($builder->{$get}(), 'Default value should be null');
  120. $this->assertSame($builder, $builder->{$set}($value), 'Setter returns this');
  121. $this->assertSame($value, $builder->{$get}(), 'Getter gets value.');
  122. }
  123. /**
  124. * Test string property accessor/mutator methods.
  125. */
  126. #[DataProvider('boolPropertyProvider')]
  127. public function testBoolProperties(string $property, bool $default, bool $value): void
  128. {
  129. $set = 'enable' . ucfirst($property);
  130. $get = 'is' . ucfirst($property) . 'Enabled';
  131. $builder = new ViewBuilder();
  132. $this->assertSame($default, $builder->{$get}(), 'Default value not as expected');
  133. $this->assertSame($builder, $builder->{$set}($value), 'Setter returns this');
  134. $this->assertSame($value, $builder->{$get}(), 'Getter gets value.');
  135. }
  136. /**
  137. * Test array property accessor/mutator methods.
  138. */
  139. #[DataProvider('arrayPropertyProvider')]
  140. public function testArrayProperties(string $property, array $value): void
  141. {
  142. $get = 'get' . ucfirst($property);
  143. $set = 'set' . ucfirst($property);
  144. $builder = new ViewBuilder();
  145. $this->assertSame([], $builder->{$get}(), 'Default value should be empty list');
  146. $this->assertSame($builder, $builder->{$set}($value), 'Setter returns this');
  147. $this->assertSame($value, $builder->{$get}(), 'Getter gets value.');
  148. }
  149. /**
  150. * Test array property accessor/mutator methods.
  151. */
  152. #[DataProvider('arrayPropertyProvider')]
  153. public function testArrayPropertyMerge(string $property, array $value): void
  154. {
  155. $get = 'get' . ucfirst($property);
  156. $set = 'set' . ucfirst($property);
  157. $builder = new ViewBuilder();
  158. $builder->{$set}($value);
  159. $builder->{$set}(['merged' => 'Merged'], true);
  160. $this->assertSame(['merged' => 'Merged'] + $value, $builder->{$get}(), 'Should merge');
  161. $builder->{$set}($value, false);
  162. $this->assertSame($value, $builder->{$get}(), 'Should replace');
  163. }
  164. /**
  165. * Tests that adding non-assoc and assoc merge properly.
  166. *
  167. * @return void
  168. */
  169. public function testAddHelpers(): void
  170. {
  171. $builder = new ViewBuilder();
  172. $builder->addHelper('Form');
  173. $builder->addHelpers(['Form' => ['config' => 'value']]);
  174. $helpers = $builder->getHelpers();
  175. $expected = [
  176. 'Form' => [
  177. 'config' => 'value',
  178. ],
  179. ];
  180. $this->assertSame($expected, $helpers);
  181. }
  182. /**
  183. * test building with all the options.
  184. */
  185. public function testBuildComplete(): void
  186. {
  187. $request = new ServerRequest();
  188. $response = new Response();
  189. $events = new EventManager();
  190. $builder = new ViewBuilder();
  191. $builder->setName('Articles')
  192. ->setClassName('Ajax')
  193. ->setTemplate('edit')
  194. ->setLayout('default')
  195. ->setTemplatePath('Articles/')
  196. ->setHelpers(['Form', 'Html'])
  197. ->setLayoutPath('Admin/')
  198. ->setTheme('TestTheme')
  199. ->setPlugin('TestPlugin')
  200. ->setVars(['foo' => 'bar', 'x' => 'old']);
  201. $view = $builder->build(
  202. $request,
  203. $response,
  204. $events
  205. );
  206. $this->assertInstanceOf(AjaxView::class, $view);
  207. $this->assertSame('edit', $view->getTemplate());
  208. $this->assertSame('default', $view->getLayout());
  209. $this->assertSame('Articles/', $view->getTemplatePath());
  210. $this->assertSame('Admin/', $view->getLayoutPath());
  211. $this->assertSame('TestPlugin', $view->getPlugin());
  212. $this->assertSame('TestTheme', $view->getTheme());
  213. $this->assertSame($request, $view->getRequest());
  214. $this->assertInstanceOf(Response::class, $view->getResponse());
  215. $this->assertSame($events, $view->getEventManager());
  216. $this->assertSame(['foo', 'x'], $view->getVars());
  217. $this->assertSame('bar', $view->get('foo'));
  218. $this->assertInstanceOf(HtmlHelper::class, $view->Html);
  219. $this->assertInstanceOf(FormHelper::class, $view->Form);
  220. }
  221. /**
  222. * Test that the default is AppView.
  223. */
  224. public function testBuildAppViewMissing(): void
  225. {
  226. static::setAppNamespace('Nope');
  227. $builder = new ViewBuilder();
  228. $view = $builder->build();
  229. $this->assertInstanceOf(View::class, $view);
  230. }
  231. /**
  232. * Test that the default is AppView.
  233. */
  234. public function testBuildAppViewPresent(): void
  235. {
  236. static::setAppNamespace();
  237. $builder = new ViewBuilder();
  238. $view = $builder->build();
  239. $this->assertInstanceOf(AppView::class, $view);
  240. }
  241. /**
  242. * test missing view class
  243. */
  244. public function testBuildMissingViewClass(): void
  245. {
  246. $this->expectException(MissingViewException::class);
  247. $this->expectExceptionMessage('View class `Foo` is missing.');
  248. $builder = new ViewBuilder();
  249. $builder->setClassName('Foo');
  250. $builder->build();
  251. }
  252. /**
  253. * testJsonSerialize()
  254. */
  255. public function testJsonSerialize(): void
  256. {
  257. $builder = new ViewBuilder();
  258. $builder
  259. ->setTemplate('default')
  260. ->setLayout('test')
  261. ->setHelpers(['Html'])
  262. ->setClassName('JsonView');
  263. $result = json_decode(json_encode($builder), true);
  264. $expected = [
  265. '_template' => 'default',
  266. '_layout' => 'test',
  267. '_helpers' => ['Html' => []],
  268. '_className' => 'JsonView',
  269. '_autoLayout' => true,
  270. ];
  271. $this->assertEquals($expected, $result);
  272. $result = json_decode(json_encode(unserialize(serialize($builder))), true);
  273. $this->assertEquals($expected, $result);
  274. }
  275. /**
  276. * testCreateFromArray()
  277. */
  278. public function testCreateFromArray(): void
  279. {
  280. $builder = new ViewBuilder();
  281. $builder
  282. ->setTemplate('default')
  283. ->setLayout('test')
  284. ->setHelpers(['Html'])
  285. ->setClassName('JsonView');
  286. $result = json_encode($builder);
  287. $builder = new ViewBuilder();
  288. $builder->createFromArray(json_decode($result, true));
  289. $this->assertSame('default', $builder->getTemplate());
  290. $this->assertSame('test', $builder->getLayout());
  291. $this->assertEquals(['Html' => []], $builder->getHelpers());
  292. $this->assertSame('JsonView', $builder->getClassName());
  293. }
  294. /**
  295. * test setOptions() with 1 string param, merge true
  296. */
  297. public function testSetOptionsOne(): void
  298. {
  299. $builder = new ViewBuilder();
  300. $this->assertSame($builder, $builder->setOptions(['newOption']));
  301. $this->assertContains('newOption', $builder->getOptions());
  302. }
  303. /**
  304. * test setOptions() with 2 assoc strings in array, merge true.
  305. */
  306. public function testSetOptionsMultiple(): void
  307. {
  308. $builder = new ViewBuilder();
  309. $builder->setOptions(['key' => 'oldOption'], false);
  310. $option = ['anotherKey' => 'anotherOption', 'key' => 'newOption'];
  311. $builder->setOptions($option);
  312. $expects = ['key' => 'newOption', 'anotherKey' => 'anotherOption'];
  313. $result = $builder->getOptions();
  314. $this->assertEquals($expects, $result);
  315. }
  316. /**
  317. * test empty params reads _viewOptions.
  318. */
  319. public function testReadingViewOptions(): void
  320. {
  321. $builder = new ViewBuilder();
  322. $builder->setOptions(['one', 'two', 'three'], false);
  323. $this->assertEquals(['one', 'two', 'three'], $builder->getOptions());
  324. }
  325. /**
  326. * test setting $merge `false` overrides correct options.
  327. */
  328. public function testMergeFalseViewOptions(): void
  329. {
  330. $builder = new ViewBuilder();
  331. $builder->setOptions(['one', 'two', 'three'], false);
  332. $expected = ['four', 'five', 'six'];
  333. $builder->setOptions($expected, false);
  334. $this->assertEquals($expected, $builder->getOptions());
  335. }
  336. /**
  337. * test _viewOptions is undefined and $opts is null, an empty array is returned.
  338. */
  339. public function testUndefinedValidViewOptions(): void
  340. {
  341. $builder = new ViewBuilder();
  342. $builder->setOptions([], false);
  343. $result = $builder->getOptions();
  344. $this->assertIsArray($result);
  345. $this->assertEmpty($result);
  346. }
  347. public function testOptionSetGet(): void
  348. {
  349. $builder = new ViewBuilder();
  350. $result = $builder->setOption('foo', 'bar');
  351. $this->assertSame($builder, $result);
  352. $this->assertSame('bar', $builder->getOption('foo'));
  353. $builder->setOption('foo', 'overwrite');
  354. $this->assertSame('overwrite', $builder->getOption('foo'));
  355. $this->assertNull($builder->getOption('nonexistent'));
  356. }
  357. public function testDisableAutoLayout(): void
  358. {
  359. $builder = new ViewBuilder();
  360. $this->assertTrue($builder->isAutoLayoutEnabled());
  361. $builder->disableAutoLayout();
  362. $this->assertFalse($builder->isAutoLayoutEnabled());
  363. }
  364. public function testAddHelperChained(): void
  365. {
  366. $builder = new ViewBuilder();
  367. $builder->addHelper('Form')
  368. ->addHelper('Time')
  369. ->addHelper('Text');
  370. $helpers = $builder->getHelpers();
  371. $expected = [
  372. 'Form' => [],
  373. 'Time' => [],
  374. 'Text' => [],
  375. ];
  376. $this->assertSame($expected, $helpers);
  377. }
  378. public function testAddHelperOptions(): void
  379. {
  380. $builder = new ViewBuilder();
  381. $builder->addHelper('Form')
  382. ->addHelper('Text', ['foo' => 'bar']);
  383. $helpers = $builder->getHelpers();
  384. $this->assertSame(['foo' => 'bar'], $helpers['Text']);
  385. }
  386. public function testAddHelperPluginOptions(): void
  387. {
  388. $builder = new ViewBuilder();
  389. $builder->addHelper('Form', ['some' => 'config']);
  390. $builder->addHelper('Text', ['foo' => 'bar']);
  391. $builder->addHelper('MyPlugin.Form');
  392. $builder->addHelper('MyPlugin.Text', ['foo' => 'other']);
  393. $helpers = $builder->getHelpers();
  394. $expected = [
  395. 'Form' => [
  396. 'className' => 'MyPlugin.Form',
  397. ],
  398. 'Text' => [
  399. 'foo' => 'other',
  400. 'className' => 'MyPlugin.Text',
  401. ],
  402. ];
  403. $this->assertSame($expected, $helpers);
  404. }
  405. }