StringTemplateTest.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448
  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.0.0
  14. * @license https://opensource.org/licenses/mit-license.php MIT License
  15. */
  16. namespace Cake\Test\TestCase\View;
  17. use Cake\TestSuite\TestCase;
  18. use Cake\View\StringTemplate;
  19. class StringTemplateTest extends TestCase
  20. {
  21. /**
  22. * @var \Cake\View\StringTemplate
  23. */
  24. protected $template;
  25. /**
  26. * setUp
  27. *
  28. * @return void
  29. */
  30. public function setUp(): void
  31. {
  32. parent::setUp();
  33. $this->template = new StringTemplate();
  34. }
  35. /**
  36. * Test adding templates through the constructor.
  37. *
  38. * @return void
  39. */
  40. public function testConstructorAdd()
  41. {
  42. $templates = [
  43. 'link' => '<a href="{{url}}">{{text}}</a>',
  44. ];
  45. $template = new StringTemplate($templates);
  46. $this->assertEquals($templates['link'], $template->get('link'));
  47. }
  48. /**
  49. * test adding templates.
  50. *
  51. * @return void
  52. */
  53. public function testAdd()
  54. {
  55. $templates = [
  56. 'link' => '<a href="{{url}}">{{text}}</a>',
  57. ];
  58. $result = $this->template->add($templates);
  59. $this->assertSame(
  60. $this->template,
  61. $result,
  62. 'The same instance should be returned'
  63. );
  64. $this->assertEquals($templates['link'], $this->template->get('link'));
  65. }
  66. /**
  67. * Test remove.
  68. *
  69. * @return void
  70. */
  71. public function testRemove()
  72. {
  73. $templates = [
  74. 'link' => '<a href="{{url}}">{{text}}</a>',
  75. ];
  76. $this->template->add($templates);
  77. $this->assertNull($this->template->remove('link'), 'No return');
  78. $this->assertNull($this->template->get('link'), 'Template should be gone.');
  79. }
  80. /**
  81. * Test formatting strings.
  82. *
  83. * @return void
  84. */
  85. public function testFormat()
  86. {
  87. $templates = [
  88. 'link' => '<a href="{{url}}">{{text}}</a>',
  89. 'text' => '{{text}}',
  90. 'custom' => '<custom {{standard}} v1="{{var1}}" v2="{{var2}}" />',
  91. ];
  92. $this->template->add($templates);
  93. $result = $this->template->format('text', ['text' => '']);
  94. $this->assertSame('', $result);
  95. $result = $this->template->format('text', []);
  96. $this->assertSame('', $result);
  97. $result = $this->template->format('link', [
  98. 'url' => '/',
  99. 'text' => 'example',
  100. ]);
  101. $this->assertSame('<a href="/">example</a>', $result);
  102. $result = $this->template->format('custom', [
  103. 'standard' => 'default',
  104. 'templateVars' => ['var1' => 'foo'],
  105. ]);
  106. $this->assertSame('<custom default v1="foo" v2="" />', $result);
  107. }
  108. /**
  109. * Test formatting strings with URL encoding
  110. *
  111. * @return void
  112. */
  113. public function testFormatUrlEncoding()
  114. {
  115. $templates = [
  116. 'test' => '<img src="/img/foo%20bar.jpg">{{text}}',
  117. ];
  118. $this->template->add($templates);
  119. $result = $this->template->format('test', ['text' => 'stuff!']);
  120. $this->assertSame('<img src="/img/foo%20bar.jpg">stuff!', $result);
  121. }
  122. /**
  123. * Formatting array data should not trigger errors.
  124. *
  125. * @return void
  126. */
  127. public function testFormatArrayData()
  128. {
  129. $templates = [
  130. 'link' => '<a href="{{url}}">{{text}}</a>',
  131. ];
  132. $this->template->add($templates);
  133. $result = $this->template->format('link', [
  134. 'url' => '/',
  135. 'text' => ['example', 'text'],
  136. ]);
  137. $this->assertSame('<a href="/">exampletext</a>', $result);
  138. $result = $this->template->format('link', [
  139. 'url' => '/',
  140. 'text' => ['key' => 'example', 'text'],
  141. ]);
  142. $this->assertSame('<a href="/">exampletext</a>', $result);
  143. }
  144. /**
  145. * Test formatting a missing template.
  146. *
  147. * @return void
  148. */
  149. public function testFormatMissingTemplate()
  150. {
  151. $this->expectException(\RuntimeException::class);
  152. $this->expectExceptionMessage('Cannot find template named \'missing\'');
  153. $templates = [
  154. 'text' => '{{text}}',
  155. ];
  156. $this->template->add($templates);
  157. $this->template->format('missing', ['text' => 'missing']);
  158. }
  159. /**
  160. * Test loading templates files in the app.
  161. *
  162. * @return void
  163. */
  164. public function testLoad()
  165. {
  166. $this->template->remove('attribute');
  167. $this->template->remove('compactAttribute');
  168. $this->assertEquals([], $this->template->get());
  169. $this->assertNull($this->template->load('test_templates'));
  170. $this->assertSame('<a href="{{url}}">{{text}}</a>', $this->template->get('link'));
  171. }
  172. /**
  173. * Test loading templates files from a plugin
  174. *
  175. * @return void
  176. */
  177. public function testLoadPlugin()
  178. {
  179. $this->loadPlugins(['TestPlugin']);
  180. $this->assertNull($this->template->load('TestPlugin.test_templates'));
  181. $this->assertSame('<em>{{text}}</em>', $this->template->get('italic'));
  182. $this->clearPlugins();
  183. }
  184. /**
  185. * Test that loading non-existing templates causes errors.
  186. *
  187. */
  188. public function testLoadErrorNoFile()
  189. {
  190. $this->expectException(\Cake\Core\Exception\Exception::class);
  191. $this->expectExceptionMessage('Could not load configuration file');
  192. $this->template->load('no_such_file');
  193. }
  194. /**
  195. * Test formatting compact attributes.
  196. *
  197. * @return void
  198. */
  199. public function testFormatAttributesCompact()
  200. {
  201. $attrs = ['disabled' => true, 'selected' => 1, 'checked' => '1', 'multiple' => 'multiple'];
  202. $result = $this->template->formatAttributes($attrs);
  203. $this->assertEquals(
  204. ' disabled="disabled" selected="selected" checked="checked" multiple="multiple"',
  205. $result
  206. );
  207. $attrs = ['disabled' => false, 'selected' => 0, 'checked' => '0', 'multiple' => null];
  208. $result = $this->template->formatAttributes($attrs);
  209. $this->assertEquals(
  210. '',
  211. $result
  212. );
  213. }
  214. /**
  215. * Test formatting normal attributes.
  216. *
  217. * @return void
  218. */
  219. public function testFormatAttributes()
  220. {
  221. $attrs = ['name' => 'bruce', 'data-hero' => '<batman>', 'spellcheck' => 'true'];
  222. $result = $this->template->formatAttributes($attrs);
  223. $this->assertEquals(
  224. ' name="bruce" data-hero="&lt;batman&gt;" spellcheck="true"',
  225. $result
  226. );
  227. $attrs = ['escape' => false, 'name' => 'bruce', 'data-hero' => '<batman>'];
  228. $result = $this->template->formatAttributes($attrs);
  229. $this->assertEquals(
  230. ' name="bruce" data-hero="<batman>"',
  231. $result
  232. );
  233. $attrs = ['name' => 'bruce', 'data-hero' => '<batman>'];
  234. $result = $this->template->formatAttributes($attrs, ['name']);
  235. $this->assertEquals(
  236. ' data-hero="&lt;batman&gt;"',
  237. $result
  238. );
  239. $attrs = ['name' => 'bruce', 'data-hero' => '<batman>', 'templateVars' => ['foo' => 'bar']];
  240. $result = $this->template->formatAttributes($attrs, ['name']);
  241. $this->assertEquals(
  242. ' data-hero="&lt;batman&gt;"',
  243. $result
  244. );
  245. $evilKey = "><script>alert(1)</script>";
  246. $attrs = [$evilKey => 'some value'];
  247. $result = $this->template->formatAttributes($attrs);
  248. $this->assertEquals(
  249. ' &gt;&lt;script&gt;alert(1)&lt;/script&gt;="some value"',
  250. $result
  251. );
  252. }
  253. /**
  254. * Test formatting array attributes.
  255. *
  256. * @return void
  257. */
  258. public function testFormatAttributesArray()
  259. {
  260. $attrs = ['name' => ['bruce', 'wayne']];
  261. $result = $this->template->formatAttributes($attrs);
  262. $this->assertEquals(
  263. ' name="bruce wayne"',
  264. $result
  265. );
  266. }
  267. /**
  268. * test push/pop templates.
  269. *
  270. * @return void
  271. */
  272. public function testPushPopTemplates()
  273. {
  274. $this->template->add(['name' => '{{name}} is my name']);
  275. $this->assertNull($this->template->push());
  276. $this->template->add(['name' => 'my name']);
  277. $this->assertSame('my name', $this->template->get('name'));
  278. $this->assertNull($this->template->pop());
  279. $this->assertSame('{{name}} is my name', $this->template->get('name'));
  280. $this->assertNull($this->template->pop());
  281. $this->assertNull($this->template->pop());
  282. }
  283. /**
  284. * Test addClass method newClass parameter
  285. *
  286. * Tests null, string, array and false for `input`
  287. *
  288. * @return void
  289. */
  290. public function testAddClassMethodNewClass()
  291. {
  292. $result = $this->template->addClass([], 'new_class');
  293. $this->assertEquals($result, ['class' => ['new_class']]);
  294. $result = $this->template->addClass([], ['new_class']);
  295. $this->assertEquals($result, ['class' => ['new_class']]);
  296. $result = $this->template->addClass([], false);
  297. $this->assertEquals($result, []);
  298. $result = $this->template->addClass([], null);
  299. $this->assertEquals($result, []);
  300. $result = $this->template->addClass(null, null);
  301. $this->assertNull($result);
  302. }
  303. /**
  304. * Test addClass method input (currentClass) parameter
  305. *
  306. * Tests null, string, array, false and object
  307. *
  308. * @return void
  309. */
  310. public function testAddClassMethodCurrentClass()
  311. {
  312. $result = $this->template->addClass(['class' => ['current']], 'new_class');
  313. $this->assertEquals($result, ['class' => ['current', 'new_class']]);
  314. $result = $this->template->addClass('', 'new_class');
  315. $this->assertEquals($result, ['class' => ['new_class']]);
  316. $result = $this->template->addClass(null, 'new_class');
  317. $this->assertEquals($result, ['class' => ['new_class']]);
  318. $result = $this->template->addClass(false, 'new_class');
  319. $this->assertEquals($result, ['class' => ['new_class']]);
  320. $result = $this->template->addClass(new \stdClass(), 'new_class');
  321. $this->assertEquals($result, ['class' => ['new_class']]);
  322. }
  323. /**
  324. * Test addClass method string parameter, it should fallback to string
  325. *
  326. * @return void
  327. */
  328. public function testAddClassMethodFallbackToString()
  329. {
  330. $result = $this->template->addClass('current', 'new_class');
  331. $this->assertEquals($result, ['class' => ['current', 'new_class']]);
  332. }
  333. /**
  334. * Test addClass method to make sure the returned array is unique
  335. *
  336. * @return void
  337. */
  338. public function testAddClassMethodUnique()
  339. {
  340. $result = $this->template->addClass(['class' => ['new_class']], 'new_class');
  341. $this->assertEquals($result, ['class' => ['new_class']]);
  342. }
  343. /**
  344. * Test addClass method useIndex param
  345. *
  346. * Tests for useIndex being the default, 'my_class' and false
  347. *
  348. * @return void
  349. */
  350. public function testAddClassMethodUseIndex()
  351. {
  352. $result = $this->template->addClass(
  353. [
  354. 'class' => 'current_class',
  355. 'other_index1' => false,
  356. 'type' => 'text',
  357. ],
  358. 'new_class',
  359. 'class'
  360. );
  361. $this->assertEquals($result, [
  362. 'class' => ['current_class', 'new_class'],
  363. 'other_index1' => false,
  364. 'type' => 'text',
  365. ]);
  366. $result = $this->template->addClass(
  367. [
  368. 'my_class' => 'current_class',
  369. 'other_index1' => false,
  370. 'type' => 'text',
  371. ],
  372. 'new_class',
  373. 'my_class'
  374. );
  375. $this->assertEquals($result, [
  376. 'other_index1' => false,
  377. 'type' => 'text',
  378. 'my_class' => ['current_class', 'new_class'],
  379. ]);
  380. $result = $this->template->addClass(
  381. [
  382. 'class' => [
  383. 'current_class',
  384. 'text',
  385. ],
  386. ],
  387. 'new_class',
  388. 'non-existent'
  389. );
  390. $this->assertEquals($result, [
  391. 'class' => [
  392. 'current_class',
  393. 'text',
  394. ],
  395. 'non-existent' => ['new_class'],
  396. ]);
  397. }
  398. }