RadioWidgetTest.php 16 KB

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