RadioWidgetTest.php 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660
  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\View\Widget;
  16. use Cake\Collection\Collection;
  17. use Cake\TestSuite\TestCase;
  18. use Cake\View\StringTemplate;
  19. use Cake\View\Widget\NestingLabelWidget;
  20. use Cake\View\Widget\RadioWidget;
  21. /**
  22. * Radio test case
  23. */
  24. class RadioWidgetTest extends TestCase
  25. {
  26. /**
  27. * setup method.
  28. *
  29. * @return void
  30. */
  31. public function setUp()
  32. {
  33. parent::setUp();
  34. $templates = [
  35. 'radio' => '<input type="radio" name="{{name}}" value="{{value}}"{{attrs}}>',
  36. 'nestingLabel' => '<label{{attrs}}>{{input}}{{text}}</label>',
  37. 'radioWrapper' => '{{label}}',
  38. ];
  39. $this->templates = new StringTemplate($templates);
  40. $this->context = $this->getMock('Cake\View\Form\ContextInterface');
  41. }
  42. /**
  43. * Test rendering basic radio buttons without nested inputs
  44. *
  45. * @return void
  46. */
  47. public function testRenderSimpleNotNested()
  48. {
  49. $this->templates->add([
  50. 'nestingLabel' => '<label{{attrs}}>{{text}}</label>',
  51. 'radioWrapper' => '{{input}}{{label}}'
  52. ]);
  53. $label = new NestingLabelWidget($this->templates);
  54. $radio = new RadioWidget($this->templates, $label);
  55. $data = [
  56. 'name' => 'Crayons[color]',
  57. 'label' => null,
  58. 'options' => ['r' => 'Red', 'b' => 'Black']
  59. ];
  60. $result = $radio->render($data, $this->context);
  61. $expected = [
  62. ['input' => [
  63. 'type' => 'radio',
  64. 'name' => 'Crayons[color]',
  65. 'value' => 'r',
  66. 'id' => 'crayons-color-r'
  67. ]],
  68. ['label' => ['for' => 'crayons-color-r']],
  69. 'Red',
  70. '/label',
  71. ['input' => [
  72. 'type' => 'radio',
  73. 'name' => 'Crayons[color]',
  74. 'value' => 'b',
  75. 'id' => 'crayons-color-b'
  76. ]],
  77. ['label' => ['for' => 'crayons-color-b']],
  78. 'Black',
  79. '/label',
  80. ];
  81. $this->assertHtml($expected, $result);
  82. $data = [
  83. 'name' => 'Crayons[color]',
  84. 'label' => false,
  85. 'options' => ['r' => 'Red', 'b' => 'Black']
  86. ];
  87. $result = $radio->render($data, $this->context);
  88. $expected = [
  89. ['input' => [
  90. 'type' => 'radio',
  91. 'name' => 'Crayons[color]',
  92. 'value' => 'r',
  93. 'id' => 'crayons-color-r'
  94. ]],
  95. ['input' => [
  96. 'type' => 'radio',
  97. 'name' => 'Crayons[color]',
  98. 'value' => 'b',
  99. 'id' => 'crayons-color-b'
  100. ]],
  101. ];
  102. $this->assertHtml($expected, $result);
  103. }
  104. /**
  105. * Test rendering basic radio buttons.
  106. *
  107. * @return void
  108. */
  109. public function testRenderSimple()
  110. {
  111. $label = new NestingLabelWidget($this->templates);
  112. $radio = new RadioWidget($this->templates, $label);
  113. $data = [
  114. 'name' => 'Crayons[color]',
  115. 'label' => null,
  116. 'options' => ['r' => 'Red', 'b' => 'Black']
  117. ];
  118. $result = $radio->render($data, $this->context);
  119. $expected = [
  120. ['label' => ['for' => 'crayons-color-r']],
  121. ['input' => [
  122. 'type' => 'radio',
  123. 'name' => 'Crayons[color]',
  124. 'value' => 'r',
  125. 'id' => 'crayons-color-r'
  126. ]],
  127. 'Red',
  128. '/label',
  129. ['label' => ['for' => 'crayons-color-b']],
  130. ['input' => [
  131. 'type' => 'radio',
  132. 'name' => 'Crayons[color]',
  133. 'value' => 'b',
  134. 'id' => 'crayons-color-b'
  135. ]],
  136. 'Black',
  137. '/label',
  138. ];
  139. $this->assertHtml($expected, $result);
  140. $data = [
  141. 'name' => 'Crayons[color]',
  142. 'options' => new Collection(['r' => 'Red', 'b' => 'Black'])
  143. ];
  144. $result = $radio->render($data, $this->context);
  145. $this->assertHtml($expected, $result);
  146. }
  147. /**
  148. * Test rendering inputs with the complex option form.
  149. *
  150. * @return void
  151. */
  152. public function testRenderComplex()
  153. {
  154. $label = new NestingLabelWidget($this->templates);
  155. $radio = new RadioWidget($this->templates, $label);
  156. $data = [
  157. 'name' => 'Crayons[color]',
  158. 'options' => [
  159. ['value' => 'r', 'text' => 'Red', 'id' => 'my_id'],
  160. ['value' => 'b', 'text' => 'Black', 'id' => 'my_id_2', 'data-test' => 'test'],
  161. ]
  162. ];
  163. $result = $radio->render($data, $this->context);
  164. $expected = [
  165. ['label' => ['for' => 'my_id']],
  166. ['input' => [
  167. 'type' => 'radio',
  168. 'name' => 'Crayons[color]',
  169. 'value' => 'r',
  170. 'id' => 'my_id'
  171. ]],
  172. 'Red',
  173. '/label',
  174. ['label' => ['for' => 'my_id_2']],
  175. ['input' => [
  176. 'type' => 'radio',
  177. 'name' => 'Crayons[color]',
  178. 'value' => 'b',
  179. 'id' => 'my_id_2',
  180. 'data-test' => 'test'
  181. ]],
  182. 'Black',
  183. '/label',
  184. ];
  185. $this->assertHtml($expected, $result);
  186. }
  187. /**
  188. * Test that id suffixes are generated to not collide
  189. *
  190. * @return void
  191. */
  192. public function testRenderIdSuffixGeneration()
  193. {
  194. $label = new NestingLabelWidget($this->templates);
  195. $radio = new RadioWidget($this->templates, $label);
  196. $data = [
  197. 'name' => 'Thing[value]',
  198. 'options' => ['a>b' => 'First', 'a<b' => 'Second']
  199. ];
  200. $result = $radio->render($data, $this->context);
  201. $expected = [
  202. ['label' => ['for' => 'thing-value-a-b']],
  203. ['input' => [
  204. 'type' => 'radio',
  205. 'name' => 'Thing[value]',
  206. 'value' => 'a&gt;b',
  207. 'id' => 'thing-value-a-b'
  208. ]],
  209. 'First',
  210. '/label',
  211. ['label' => ['for' => 'thing-value-a-b1']],
  212. ['input' => [
  213. 'type' => 'radio',
  214. 'name' => 'Thing[value]',
  215. 'value' => 'a&lt;b',
  216. 'id' => 'thing-value-a-b1',
  217. ]],
  218. 'Second',
  219. '/label',
  220. ];
  221. $this->assertHtml($expected, $result);
  222. }
  223. /**
  224. * Test rendering checks the right option with booleanish values.
  225. *
  226. * @return void
  227. */
  228. public function testRenderBooleanishValues()
  229. {
  230. $label = new NestingLabelWidget($this->templates);
  231. $radio = new RadioWidget($this->templates, $label);
  232. $data = [
  233. 'name' => 'Model[field]',
  234. 'options' => ['1' => 'Yes', '0' => 'No'],
  235. 'val' => '0'
  236. ];
  237. $result = $radio->render($data, $this->context);
  238. $expected = [
  239. ['label' => ['for' => 'model-field-1']],
  240. ['input' => ['type' => 'radio', 'name' => 'Model[field]', 'value' => '1', 'id' => 'model-field-1']],
  241. 'Yes',
  242. '/label',
  243. ['label' => ['for' => 'model-field-0']],
  244. ['input' => ['type' => 'radio', 'name' => 'Model[field]', 'value' => '0', 'id' => 'model-field-0', 'checked' => 'checked']],
  245. 'No',
  246. '/label',
  247. ];
  248. $this->assertHtml($expected, $result);
  249. $data['val'] = 0;
  250. $result = $radio->render($data, $this->context);
  251. $this->assertHtml($expected, $result);
  252. $data['val'] = false;
  253. $result = $radio->render($data, $this->context);
  254. $this->assertHtml($expected, $result);
  255. $expected = [
  256. ['label' => ['for' => 'model-field-1']],
  257. ['input' => ['type' => 'radio', 'name' => 'Model[field]', 'value' => '1', 'id' => 'model-field-1']],
  258. 'Yes',
  259. '/label',
  260. ['label' => ['for' => 'model-field-0']],
  261. ['input' => ['type' => 'radio', 'name' => 'Model[field]', 'value' => '0', 'id' => 'model-field-0']],
  262. 'No',
  263. '/label',
  264. ];
  265. $data['val'] = null;
  266. $result = $radio->render($data, $this->context);
  267. $this->assertHtml($expected, $result);
  268. $data['val'] = '';
  269. $result = $radio->render($data, $this->context);
  270. $this->assertHtml($expected, $result);
  271. $expected = [
  272. ['label' => ['for' => 'model-field-1']],
  273. ['input' => ['type' => 'radio', 'name' => 'Model[field]', 'value' => '1', 'id' => 'model-field-1', 'checked' => 'checked']],
  274. 'Yes',
  275. '/label',
  276. ['label' => ['for' => 'model-field-0']],
  277. ['input' => ['type' => 'radio', 'name' => 'Model[field]', 'value' => '0', 'id' => 'model-field-0']],
  278. 'No',
  279. '/label',
  280. ];
  281. $data['val'] = '1';
  282. $result = $radio->render($data, $this->context);
  283. $this->assertHtml($expected, $result);
  284. $data['val'] = 1;
  285. $result = $radio->render($data, $this->context);
  286. $this->assertHtml($expected, $result);
  287. $data['val'] = true;
  288. $result = $radio->render($data, $this->context);
  289. $this->assertHtml($expected, $result);
  290. }
  291. /**
  292. * Test that render() works with the required attribute.
  293. *
  294. * @return void
  295. */
  296. public function testRenderRequiredAndFormAttribute()
  297. {
  298. $label = new NestingLabelWidget($this->templates);
  299. $radio = new RadioWidget($this->templates, $label);
  300. $data = [
  301. 'name' => 'published',
  302. 'options' => ['option A', 'option B'],
  303. 'required' => true,
  304. 'form' => 'my-form',
  305. ];
  306. $result = $radio->render($data, $this->context);
  307. $expected = [
  308. ['label' => ['for' => 'published-0']],
  309. ['input' => ['type' => 'radio', 'name' => 'published', 'value' => '0',
  310. 'id' => 'published-0', 'required' => 'required', 'form' => 'my-form']],
  311. 'option A',
  312. '/label',
  313. ['label' => ['for' => 'published-1']],
  314. ['input' => ['type' => 'radio', 'name' => 'published', 'value' => '1',
  315. 'id' => 'published-1', 'required' => 'required', 'form' => 'my-form']],
  316. 'option B',
  317. '/label',
  318. ];
  319. $this->assertHtml($expected, $result);
  320. }
  321. /**
  322. * Test rendering the empty option.
  323. *
  324. * @return void
  325. */
  326. public function testRenderEmptyOption()
  327. {
  328. $label = new NestingLabelWidget($this->templates);
  329. $radio = new RadioWidget($this->templates, $label);
  330. $data = [
  331. 'name' => 'Crayons[color]',
  332. 'options' => ['r' => 'Red'],
  333. 'empty' => true,
  334. ];
  335. $result = $radio->render($data, $this->context);
  336. $expected = [
  337. ['label' => ['for' => 'crayons-color']],
  338. ['input' => [
  339. 'type' => 'radio',
  340. 'name' => 'Crayons[color]',
  341. 'value' => '',
  342. 'id' => 'crayons-color'
  343. ]],
  344. 'empty',
  345. '/label',
  346. ['label' => ['for' => 'crayons-color-r']],
  347. ['input' => [
  348. 'type' => 'radio',
  349. 'name' => 'Crayons[color]',
  350. 'value' => 'r',
  351. 'id' => 'crayons-color-r'
  352. ]],
  353. 'Red',
  354. '/label',
  355. ];
  356. $this->assertHtml($expected, $result);
  357. $data['empty'] = 'Choose one';
  358. $result = $radio->render($data, $this->context);
  359. $expected = [
  360. ['label' => ['for' => 'crayons-color']],
  361. ['input' => [
  362. 'type' => 'radio',
  363. 'name' => 'Crayons[color]',
  364. 'value' => '',
  365. 'id' => 'crayons-color'
  366. ]],
  367. 'Choose one',
  368. '/label',
  369. ['label' => ['for' => 'crayons-color-r']],
  370. ['input' => [
  371. 'type' => 'radio',
  372. 'name' => 'Crayons[color]',
  373. 'value' => 'r',
  374. 'id' => 'crayons-color-r'
  375. ]],
  376. 'Red',
  377. '/label',
  378. ];
  379. $this->assertHtml($expected, $result);
  380. }
  381. /**
  382. * Test rendering the input inside the label.
  383. *
  384. * @return void
  385. */
  386. public function testRenderInputInsideLabel()
  387. {
  388. $this->templates->add([
  389. 'label' => '<label{{attrs}}>{{input}}{{text}}</label>',
  390. 'radioWrapper' => '{{label}}',
  391. ]);
  392. $label = new NestingLabelWidget($this->templates);
  393. $radio = new RadioWidget($this->templates, $label);
  394. $data = [
  395. 'name' => 'Crayons[color]',
  396. 'options' => ['r' => 'Red'],
  397. ];
  398. $result = $radio->render($data, $this->context);
  399. $expected = [
  400. ['label' => ['for' => 'crayons-color-r']],
  401. ['input' => [
  402. 'type' => 'radio',
  403. 'name' => 'Crayons[color]',
  404. 'value' => 'r',
  405. 'id' => 'crayons-color-r'
  406. ]],
  407. 'Red',
  408. '/label',
  409. ];
  410. $this->assertHtml($expected, $result);
  411. }
  412. /**
  413. * test render() and selected inputs.
  414. *
  415. * @return void
  416. */
  417. public function testRenderSelected()
  418. {
  419. $label = new NestingLabelWidget($this->templates);
  420. $radio = new RadioWidget($this->templates, $label);
  421. $data = [
  422. 'name' => 'Versions[ver]',
  423. 'val' => '1',
  424. 'options' => [
  425. 1 => 'one',
  426. '1x' => 'one x',
  427. '2' => 'two',
  428. ]
  429. ];
  430. $result = $radio->render($data, $this->context);
  431. $expected = [
  432. ['label' => ['for' => 'versions-ver-1']],
  433. ['input' => [
  434. 'id' => 'versions-ver-1',
  435. 'name' => 'Versions[ver]',
  436. 'type' => 'radio',
  437. 'value' => '1',
  438. 'checked' => 'checked'
  439. ]],
  440. 'one',
  441. '/label',
  442. ['label' => ['for' => 'versions-ver-1x']],
  443. ['input' => [
  444. 'id' => 'versions-ver-1x',
  445. 'name' => 'Versions[ver]',
  446. 'type' => 'radio',
  447. 'value' => '1x'
  448. ]],
  449. 'one x',
  450. '/label',
  451. ['label' => ['for' => 'versions-ver-2']],
  452. ['input' => [
  453. 'id' => 'versions-ver-2',
  454. 'name' => 'Versions[ver]',
  455. 'type' => 'radio',
  456. 'value' => '2'
  457. ]],
  458. 'two',
  459. '/label',
  460. ];
  461. $this->assertHtml($expected, $result);
  462. }
  463. /**
  464. * Test rendering with disable inputs
  465. *
  466. * @return void
  467. */
  468. public function testRenderDisabled()
  469. {
  470. $label = new NestingLabelWidget($this->templates);
  471. $radio = new RadioWidget($this->templates, $label);
  472. $data = [
  473. 'name' => 'Versions[ver]',
  474. 'options' => [
  475. 1 => 'one',
  476. '1x' => 'one x',
  477. '2' => 'two',
  478. ],
  479. 'disabled' => true,
  480. ];
  481. $result = $radio->render($data, $this->context);
  482. $expected = [
  483. ['label' => ['for' => 'versions-ver-1']],
  484. ['input' => [
  485. 'id' => 'versions-ver-1',
  486. 'name' => 'Versions[ver]',
  487. 'type' => 'radio',
  488. 'value' => '1',
  489. 'disabled' => 'disabled'
  490. ]],
  491. 'one',
  492. '/label',
  493. ['label' => ['for' => 'versions-ver-1x']],
  494. ['input' => [
  495. 'id' => 'versions-ver-1x',
  496. 'name' => 'Versions[ver]',
  497. 'type' => 'radio',
  498. 'value' => '1x',
  499. 'disabled' => 'disabled'
  500. ]],
  501. 'one x',
  502. '/label',
  503. ];
  504. $this->assertHtml($expected, $result);
  505. $data['disabled'] = 'a string';
  506. $result = $radio->render($data, $this->context);
  507. $this->assertHtml($expected, $result);
  508. $data['disabled'] = ['1'];
  509. $result = $radio->render($data, $this->context);
  510. $expected = [
  511. ['label' => ['for' => 'versions-ver-1']],
  512. ['input' => [
  513. 'id' => 'versions-ver-1',
  514. 'name' => 'Versions[ver]',
  515. 'type' => 'radio',
  516. 'value' => '1',
  517. 'disabled' => 'disabled'
  518. ]],
  519. 'one',
  520. '/label',
  521. ['label' => ['for' => 'versions-ver-1x']],
  522. ['input' => [
  523. 'id' => 'versions-ver-1x',
  524. 'name' => 'Versions[ver]',
  525. 'type' => 'radio',
  526. 'value' => '1x',
  527. ]],
  528. 'one x',
  529. '/label',
  530. ];
  531. $this->assertHtml($expected, $result);
  532. }
  533. /**
  534. * Test rendering with label options.
  535. *
  536. * @return void
  537. */
  538. public function testRenderLabelOptions()
  539. {
  540. $label = new NestingLabelWidget($this->templates);
  541. $radio = new RadioWidget($this->templates, $label);
  542. $data = [
  543. 'name' => 'Versions[ver]',
  544. 'options' => [
  545. 1 => 'one',
  546. '1x' => 'one x',
  547. '2' => 'two',
  548. ],
  549. 'label' => false,
  550. ];
  551. $result = $radio->render($data, $this->context);
  552. $expected = [
  553. ['input' => [
  554. 'id' => 'versions-ver-1',
  555. 'name' => 'Versions[ver]',
  556. 'type' => 'radio',
  557. 'value' => '1',
  558. ]],
  559. ['input' => [
  560. 'id' => 'versions-ver-1x',
  561. 'name' => 'Versions[ver]',
  562. 'type' => 'radio',
  563. 'value' => '1x',
  564. ]],
  565. ];
  566. $this->assertHtml($expected, $result);
  567. $data = [
  568. 'name' => 'Versions[ver]',
  569. 'options' => [
  570. 1 => 'one',
  571. '1x' => 'one x',
  572. '2' => 'two',
  573. ],
  574. 'label' => [
  575. 'class' => 'my-class',
  576. ]
  577. ];
  578. $result = $radio->render($data, $this->context);
  579. $expected = [
  580. ['label' => ['class' => 'my-class', 'for' => 'versions-ver-1']],
  581. ['input' => [
  582. 'id' => 'versions-ver-1',
  583. 'name' => 'Versions[ver]',
  584. 'type' => 'radio',
  585. 'value' => '1',
  586. ]],
  587. 'one',
  588. '/label',
  589. ['label' => ['class' => 'my-class', 'for' => 'versions-ver-1x']],
  590. ['input' => [
  591. 'id' => 'versions-ver-1x',
  592. 'name' => 'Versions[ver]',
  593. 'type' => 'radio',
  594. 'value' => '1x',
  595. ]],
  596. 'one x',
  597. '/label',
  598. ];
  599. $this->assertHtml($expected, $result);
  600. }
  601. /**
  602. * Ensure that the input + label are composed with
  603. * a template.
  604. *
  605. * @return void
  606. */
  607. public function testRenderContainerTemplate()
  608. {
  609. $this->templates->add([
  610. 'radioWrapper' => '<div class="radio">{{input}}{{label}}</div>'
  611. ]);
  612. $label = new NestingLabelWidget($this->templates);
  613. $radio = new RadioWidget($this->templates, $label);
  614. $data = [
  615. 'name' => 'Versions[ver]',
  616. 'options' => [
  617. 1 => 'one',
  618. '1x' => 'one x',
  619. '2' => 'two',
  620. ],
  621. ];
  622. $result = $radio->render($data, $this->context);
  623. $this->assertContains(
  624. '<div class="radio"><input type="radio"',
  625. $result
  626. );
  627. $this->assertContains(
  628. '</label></div>',
  629. $result
  630. );
  631. }
  632. }