AbstractDivLayoutTest.php 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Symfony\Component\Form\Tests;
  11. use Symfony\Component\Form\FormError;
  12. use Symfony\Component\Security\Csrf\CsrfToken;
  13. abstract class AbstractDivLayoutTest extends AbstractLayoutTest
  14. {
  15. public function testRow()
  16. {
  17. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TextType');
  18. $form->addError(new FormError('[trans]Error![/trans]'));
  19. $view = $form->createView();
  20. $html = $this->renderRow($view);
  21. $this->assertMatchesXpath($html,
  22. '/div
  23. [
  24. ./label[@for="name"]
  25. /following-sibling::ul
  26. [./li[.="[trans]Error![/trans]"]]
  27. [count(./li)=1]
  28. /following-sibling::input[@id="name"]
  29. ]
  30. '
  31. );
  32. }
  33. public function testRowOverrideVariables()
  34. {
  35. $view = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TextType')->createView();
  36. $html = $this->renderRow($view, array(
  37. 'attr' => array('class' => 'my&class'),
  38. 'label' => 'foo&bar',
  39. 'label_attr' => array('class' => 'my&label&class'),
  40. ));
  41. $this->assertMatchesXpath($html,
  42. '/div
  43. [
  44. ./label[@for="name"][@class="my&label&class required"][.="[trans]foo&bar[/trans]"]
  45. /following-sibling::input[@id="name"][@class="my&class"]
  46. ]
  47. '
  48. );
  49. }
  50. public function testRepeatedRow()
  51. {
  52. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\RepeatedType');
  53. $form->addError(new FormError('[trans]Error![/trans]'));
  54. $view = $form->createView();
  55. $html = $this->renderRow($view);
  56. // The errors of the form are not rendered by intention!
  57. // In practice, repeated fields cannot have errors as all errors
  58. // on them are mapped to the first child.
  59. // (see RepeatedTypeValidatorExtension)
  60. $this->assertMatchesXpath($html,
  61. '/div
  62. [
  63. ./label[@for="name_first"]
  64. /following-sibling::input[@id="name_first"]
  65. ]
  66. /following-sibling::div
  67. [
  68. ./label[@for="name_second"]
  69. /following-sibling::input[@id="name_second"]
  70. ]
  71. '
  72. );
  73. }
  74. public function testButtonRow()
  75. {
  76. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ButtonType');
  77. $view = $form->createView();
  78. $html = $this->renderRow($view);
  79. $this->assertMatchesXpath($html,
  80. '/div
  81. [
  82. ./button[@type="button"][@name="name"]
  83. ]
  84. [count(//label)=0]
  85. '
  86. );
  87. }
  88. public function testRest()
  89. {
  90. $view = $this->factory->createNamedBuilder('name', 'Symfony\Component\Form\Extension\Core\Type\FormType')
  91. ->add('field1', 'Symfony\Component\Form\Extension\Core\Type\TextType')
  92. ->add('field2', 'Symfony\Component\Form\Extension\Core\Type\RepeatedType')
  93. ->add('field3', 'Symfony\Component\Form\Extension\Core\Type\TextType')
  94. ->add('field4', 'Symfony\Component\Form\Extension\Core\Type\TextType')
  95. ->getForm()
  96. ->createView();
  97. // Render field2 row -> does not implicitly call renderWidget because
  98. // it is a repeated field!
  99. $this->renderRow($view['field2']);
  100. // Render field3 widget
  101. $this->renderWidget($view['field3']);
  102. // Rest should only contain field1 and field4
  103. $html = $this->renderRest($view);
  104. $this->assertMatchesXpath($html,
  105. '/div
  106. [
  107. ./label[@for="name_field1"]
  108. /following-sibling::input[@type="text"][@id="name_field1"]
  109. ]
  110. /following-sibling::div
  111. [
  112. ./label[@for="name_field4"]
  113. /following-sibling::input[@type="text"][@id="name_field4"]
  114. ]
  115. [count(../div)=2]
  116. [count(..//label)=2]
  117. [count(..//input)=3]
  118. /following-sibling::input
  119. [@type="hidden"]
  120. [@id="name__token"]
  121. '
  122. );
  123. }
  124. public function testRestWithChildrenForms()
  125. {
  126. $child1 = $this->factory->createNamedBuilder('child1', 'Symfony\Component\Form\Extension\Core\Type\FormType')
  127. ->add('field1', 'Symfony\Component\Form\Extension\Core\Type\TextType')
  128. ->add('field2', 'Symfony\Component\Form\Extension\Core\Type\TextType');
  129. $child2 = $this->factory->createNamedBuilder('child2', 'Symfony\Component\Form\Extension\Core\Type\FormType')
  130. ->add('field1', 'Symfony\Component\Form\Extension\Core\Type\TextType')
  131. ->add('field2', 'Symfony\Component\Form\Extension\Core\Type\TextType');
  132. $view = $this->factory->createNamedBuilder('parent', 'Symfony\Component\Form\Extension\Core\Type\FormType')
  133. ->add($child1)
  134. ->add($child2)
  135. ->getForm()
  136. ->createView();
  137. // Render child1.field1 row
  138. $this->renderRow($view['child1']['field1']);
  139. // Render child2.field2 widget (remember that widget don't render label)
  140. $this->renderWidget($view['child2']['field2']);
  141. // Rest should only contain child1.field2 and child2.field1
  142. $html = $this->renderRest($view);
  143. $this->assertMatchesXpath($html,
  144. '/div
  145. [
  146. ./label[not(@for)]
  147. /following-sibling::div[@id="parent_child1"]
  148. [
  149. ./div
  150. [
  151. ./label[@for="parent_child1_field2"]
  152. /following-sibling::input[@id="parent_child1_field2"]
  153. ]
  154. ]
  155. ]
  156. /following-sibling::div
  157. [
  158. ./label[not(@for)]
  159. /following-sibling::div[@id="parent_child2"]
  160. [
  161. ./div
  162. [
  163. ./label[@for="parent_child2_field1"]
  164. /following-sibling::input[@id="parent_child2_field1"]
  165. ]
  166. ]
  167. ]
  168. [count(//label)=4]
  169. [count(//input[@type="text"])=2]
  170. /following-sibling::input[@type="hidden"][@id="parent__token"]
  171. '
  172. );
  173. }
  174. public function testRestAndRepeatedWithRow()
  175. {
  176. $view = $this->factory->createNamedBuilder('name', 'Symfony\Component\Form\Extension\Core\Type\FormType')
  177. ->add('first', 'Symfony\Component\Form\Extension\Core\Type\TextType')
  178. ->add('password', 'Symfony\Component\Form\Extension\Core\Type\RepeatedType')
  179. ->getForm()
  180. ->createView();
  181. $this->renderRow($view['password']);
  182. $html = $this->renderRest($view);
  183. $this->assertMatchesXpath($html,
  184. '/div
  185. [
  186. ./label[@for="name_first"]
  187. /following-sibling::input[@type="text"][@id="name_first"]
  188. ]
  189. [count(.//input)=1]
  190. /following-sibling::input
  191. [@type="hidden"]
  192. [@id="name__token"]
  193. '
  194. );
  195. }
  196. public function testRestAndRepeatedWithRowPerChild()
  197. {
  198. $view = $this->factory->createNamedBuilder('name', 'Symfony\Component\Form\Extension\Core\Type\FormType')
  199. ->add('first', 'Symfony\Component\Form\Extension\Core\Type\TextType')
  200. ->add('password', 'Symfony\Component\Form\Extension\Core\Type\RepeatedType')
  201. ->getForm()
  202. ->createView();
  203. $this->renderRow($view['password']['first']);
  204. $this->renderRow($view['password']['second']);
  205. $html = $this->renderRest($view);
  206. $this->assertMatchesXpath($html,
  207. '/div
  208. [
  209. ./label[@for="name_first"]
  210. /following-sibling::input[@type="text"][@id="name_first"]
  211. ]
  212. [count(.//input)=1]
  213. [count(.//label)=1]
  214. /following-sibling::input
  215. [@type="hidden"]
  216. [@id="name__token"]
  217. '
  218. );
  219. }
  220. public function testRestAndRepeatedWithWidgetPerChild()
  221. {
  222. $view = $this->factory->createNamedBuilder('name', 'Symfony\Component\Form\Extension\Core\Type\FormType')
  223. ->add('first', 'Symfony\Component\Form\Extension\Core\Type\TextType')
  224. ->add('password', 'Symfony\Component\Form\Extension\Core\Type\RepeatedType')
  225. ->getForm()
  226. ->createView();
  227. // The password form is considered as rendered as all its children
  228. // are rendered
  229. $this->renderWidget($view['password']['first']);
  230. $this->renderWidget($view['password']['second']);
  231. $html = $this->renderRest($view);
  232. $this->assertMatchesXpath($html,
  233. '/div
  234. [
  235. ./label[@for="name_first"]
  236. /following-sibling::input[@type="text"][@id="name_first"]
  237. ]
  238. [count(//input)=2]
  239. [count(//label)=1]
  240. /following-sibling::input
  241. [@type="hidden"]
  242. [@id="name__token"]
  243. '
  244. );
  245. }
  246. public function testCollection()
  247. {
  248. $form = $this->factory->createNamed('names', 'Symfony\Component\Form\Extension\Core\Type\CollectionType', array('a', 'b'), array(
  249. 'entry_type' => 'Symfony\Component\Form\Extension\Core\Type\TextType',
  250. ));
  251. $this->assertWidgetMatchesXpath($form->createView(), array(),
  252. '/div
  253. [
  254. ./div[./input[@type="text"][@value="a"]]
  255. /following-sibling::div[./input[@type="text"][@value="b"]]
  256. ]
  257. [count(./div[./input])=2]
  258. '
  259. );
  260. }
  261. // https://github.com/symfony/symfony/issues/5038
  262. public function testCollectionWithAlternatingRowTypes()
  263. {
  264. $data = array(
  265. array('title' => 'a'),
  266. array('title' => 'b'),
  267. );
  268. $form = $this->factory->createNamed('names', 'Symfony\Component\Form\Extension\Core\Type\CollectionType', $data, array(
  269. 'entry_type' => 'Symfony\Component\Form\Tests\Fixtures\AlternatingRowType',
  270. ));
  271. $this->assertWidgetMatchesXpath($form->createView(), array(),
  272. '/div
  273. [
  274. ./div[./div/div/input[@type="text"][@value="a"]]
  275. /following-sibling::div[./div/div/textarea[.="b"]]
  276. ]
  277. [count(./div[./div/div/input])=1]
  278. [count(./div[./div/div/textarea])=1]
  279. '
  280. );
  281. }
  282. public function testEmptyCollection()
  283. {
  284. $form = $this->factory->createNamed('names', 'Symfony\Component\Form\Extension\Core\Type\CollectionType', array(), array(
  285. 'entry_type' => 'Symfony\Component\Form\Extension\Core\Type\TextType',
  286. ));
  287. $this->assertWidgetMatchesXpath($form->createView(), array(),
  288. '/div
  289. [./input[@type="hidden"][@id="names__token"]]
  290. [count(./div)=0]
  291. '
  292. );
  293. }
  294. public function testCollectionRow()
  295. {
  296. $collection = $this->factory->createNamedBuilder(
  297. 'collection',
  298. 'Symfony\Component\Form\Extension\Core\Type\CollectionType',
  299. array('a', 'b'),
  300. array('entry_type' => 'Symfony\Component\Form\Extension\Core\Type\TextType')
  301. );
  302. $form = $this->factory->createNamedBuilder('form', 'Symfony\Component\Form\Extension\Core\Type\FormType')
  303. ->add($collection)
  304. ->getForm();
  305. $this->assertWidgetMatchesXpath($form->createView(), array(),
  306. '/div
  307. [
  308. ./div
  309. [
  310. ./label[not(@for)]
  311. /following-sibling::div
  312. [
  313. ./div
  314. [
  315. ./label[@for="form_collection_0"]
  316. /following-sibling::input[@type="text"][@value="a"]
  317. ]
  318. /following-sibling::div
  319. [
  320. ./label[@for="form_collection_1"]
  321. /following-sibling::input[@type="text"][@value="b"]
  322. ]
  323. ]
  324. ]
  325. /following-sibling::input[@type="hidden"][@id="form__token"]
  326. ]
  327. [count(.//input)=3]
  328. '
  329. );
  330. }
  331. public function testForm()
  332. {
  333. $form = $this->factory->createNamedBuilder('name', 'Symfony\Component\Form\Extension\Core\Type\FormType')
  334. ->setMethod('PUT')
  335. ->setAction('http://example.com')
  336. ->add('firstName', 'Symfony\Component\Form\Extension\Core\Type\TextType')
  337. ->add('lastName', 'Symfony\Component\Form\Extension\Core\Type\TextType')
  338. ->getForm();
  339. // include ampersands everywhere to validate escaping
  340. $html = $this->renderForm($form->createView(), array(
  341. 'id' => 'my&id',
  342. 'attr' => array('class' => 'my&class'),
  343. ));
  344. $this->assertMatchesXpath($html,
  345. '/form
  346. [
  347. ./input[@type="hidden"][@name="_method"][@value="PUT"]
  348. /following-sibling::div
  349. [
  350. ./div
  351. [
  352. ./label[@for="name_firstName"]
  353. /following-sibling::input[@type="text"][@id="name_firstName"]
  354. ]
  355. /following-sibling::div
  356. [
  357. ./label[@for="name_lastName"]
  358. /following-sibling::input[@type="text"][@id="name_lastName"]
  359. ]
  360. /following-sibling::input[@type="hidden"][@id="name__token"]
  361. ]
  362. [count(.//input)=3]
  363. [@id="my&id"]
  364. [@class="my&class"]
  365. ]
  366. [@method="post"]
  367. [@action="http://example.com"]
  368. [@class="my&class"]
  369. '
  370. );
  371. }
  372. public function testFormWidget()
  373. {
  374. $form = $this->factory->createNamedBuilder('name', 'Symfony\Component\Form\Extension\Core\Type\FormType')
  375. ->add('firstName', 'Symfony\Component\Form\Extension\Core\Type\TextType')
  376. ->add('lastName', 'Symfony\Component\Form\Extension\Core\Type\TextType')
  377. ->getForm();
  378. $this->assertWidgetMatchesXpath($form->createView(), array(),
  379. '/div
  380. [
  381. ./div
  382. [
  383. ./label[@for="name_firstName"]
  384. /following-sibling::input[@type="text"][@id="name_firstName"]
  385. ]
  386. /following-sibling::div
  387. [
  388. ./label[@for="name_lastName"]
  389. /following-sibling::input[@type="text"][@id="name_lastName"]
  390. ]
  391. /following-sibling::input[@type="hidden"][@id="name__token"]
  392. ]
  393. [count(.//input)=3]
  394. '
  395. );
  396. }
  397. // https://github.com/symfony/symfony/issues/2308
  398. public function testNestedFormError()
  399. {
  400. $form = $this->factory->createNamedBuilder('name', 'Symfony\Component\Form\Extension\Core\Type\FormType')
  401. ->add($this->factory
  402. ->createNamedBuilder('child', 'Symfony\Component\Form\Extension\Core\Type\FormType', null, array('error_bubbling' => false))
  403. ->add('grandChild', 'Symfony\Component\Form\Extension\Core\Type\FormType')
  404. )
  405. ->getForm();
  406. $form->get('child')->addError(new FormError('[trans]Error![/trans]'));
  407. $this->assertWidgetMatchesXpath($form->createView(), array(),
  408. '/div
  409. [
  410. ./div/label
  411. /following-sibling::ul[./li[.="[trans]Error![/trans]"]]
  412. ]
  413. [count(.//li[.="[trans]Error![/trans]"])=1]
  414. '
  415. );
  416. }
  417. public function testCsrf()
  418. {
  419. $this->csrfTokenManager->expects($this->any())
  420. ->method('getToken')
  421. ->will($this->returnValue(new CsrfToken('token_id', 'foo&bar')));
  422. $form = $this->factory->createNamedBuilder('name', 'Symfony\Component\Form\Extension\Core\Type\FormType')
  423. ->add($this->factory
  424. // No CSRF protection on nested forms
  425. ->createNamedBuilder('child', 'Symfony\Component\Form\Extension\Core\Type\FormType')
  426. ->add($this->factory->createNamedBuilder('grandchild', 'Symfony\Component\Form\Extension\Core\Type\TextType'))
  427. )
  428. ->getForm();
  429. $this->assertWidgetMatchesXpath($form->createView(), array(),
  430. '/div
  431. [
  432. ./div
  433. /following-sibling::input[@type="hidden"][@id="name__token"][@value="foo&bar"]
  434. ]
  435. [count(.//input[@type="hidden"])=1]
  436. '
  437. );
  438. }
  439. public function testRepeated()
  440. {
  441. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\RepeatedType', 'foobar', array(
  442. 'type' => 'Symfony\Component\Form\Extension\Core\Type\TextType',
  443. ));
  444. $this->assertWidgetMatchesXpath($form->createView(), array(),
  445. '/div
  446. [
  447. ./div
  448. [
  449. ./label[@for="name_first"]
  450. /following-sibling::input[@type="text"][@id="name_first"]
  451. ]
  452. /following-sibling::div
  453. [
  454. ./label[@for="name_second"]
  455. /following-sibling::input[@type="text"][@id="name_second"]
  456. ]
  457. /following-sibling::input[@type="hidden"][@id="name__token"]
  458. ]
  459. [count(.//input)=3]
  460. '
  461. );
  462. }
  463. public function testRepeatedWithCustomOptions()
  464. {
  465. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\RepeatedType', null, array(
  466. // the global required value cannot be overridden
  467. 'first_options' => array('label' => 'Test', 'required' => false),
  468. 'second_options' => array('label' => 'Test2'),
  469. ));
  470. $this->assertWidgetMatchesXpath($form->createView(), array(),
  471. '/div
  472. [
  473. ./div
  474. [
  475. ./label[@for="name_first"][.="[trans]Test[/trans]"]
  476. /following-sibling::input[@type="text"][@id="name_first"][@required="required"]
  477. ]
  478. /following-sibling::div
  479. [
  480. ./label[@for="name_second"][.="[trans]Test2[/trans]"]
  481. /following-sibling::input[@type="text"][@id="name_second"][@required="required"]
  482. ]
  483. /following-sibling::input[@type="hidden"][@id="name__token"]
  484. ]
  485. [count(.//input)=3]
  486. '
  487. );
  488. }
  489. public function testSearchInputName()
  490. {
  491. $form = $this->factory->createNamedBuilder('full', 'Symfony\Component\Form\Extension\Core\Type\FormType')
  492. ->add('name', 'Symfony\Component\Form\Extension\Core\Type\SearchType')
  493. ->getForm();
  494. $this->assertWidgetMatchesXpath($form->createView(), array(),
  495. '/div
  496. [
  497. ./div
  498. [
  499. ./label[@for="full_name"]
  500. /following-sibling::input[@type="search"][@id="full_name"][@name="full[name]"]
  501. ]
  502. /following-sibling::input[@type="hidden"][@id="full__token"]
  503. ]
  504. [count(//input)=2]
  505. '
  506. );
  507. }
  508. public function testLabelHasNoId()
  509. {
  510. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TextType');
  511. $html = $this->renderRow($form->createView());
  512. $this->assertMatchesXpath($html,
  513. '/div
  514. [
  515. ./label[@for="name"][not(@id)]
  516. /following-sibling::input[@id="name"]
  517. ]
  518. '
  519. );
  520. }
  521. public function testLabelIsNotRenderedWhenSetToFalse()
  522. {
  523. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TextType', null, array(
  524. 'label' => false,
  525. ));
  526. $html = $this->renderRow($form->createView());
  527. $this->assertMatchesXpath($html,
  528. '/div
  529. [
  530. ./input[@id="name"]
  531. ]
  532. [count(//label)=0]
  533. '
  534. );
  535. }
  536. /**
  537. * @dataProvider themeBlockInheritanceProvider
  538. */
  539. public function testThemeBlockInheritance($theme)
  540. {
  541. $view = $this->factory
  542. ->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\EmailType')
  543. ->createView()
  544. ;
  545. $this->setTheme($view, $theme);
  546. $this->assertMatchesXpath(
  547. $this->renderWidget($view),
  548. '/input[@type="email"][@rel="theme"]'
  549. );
  550. }
  551. /**
  552. * @dataProvider themeInheritanceProvider
  553. */
  554. public function testThemeInheritance($parentTheme, $childTheme)
  555. {
  556. $child = $this->factory->createNamedBuilder('child', 'Symfony\Component\Form\Extension\Core\Type\FormType')
  557. ->add('field', 'Symfony\Component\Form\Extension\Core\Type\TextType');
  558. $view = $this->factory->createNamedBuilder('parent', 'Symfony\Component\Form\Extension\Core\Type\FormType')
  559. ->add('field', 'Symfony\Component\Form\Extension\Core\Type\TextType')
  560. ->add($child)
  561. ->getForm()
  562. ->createView()
  563. ;
  564. $this->setTheme($view, $parentTheme);
  565. $this->setTheme($view['child'], $childTheme);
  566. $this->assertWidgetMatchesXpath($view, array(),
  567. '/div
  568. [
  569. ./div
  570. [
  571. ./label[.="parent"]
  572. /following-sibling::input[@type="text"]
  573. ]
  574. /following-sibling::div
  575. [
  576. ./label[.="child"]
  577. /following-sibling::div
  578. [
  579. ./div
  580. [
  581. ./label[.="child"]
  582. /following-sibling::input[@type="text"]
  583. ]
  584. ]
  585. ]
  586. /following-sibling::input[@type="hidden"]
  587. ]
  588. '
  589. );
  590. }
  591. /**
  592. * The block "_name_child_label" should be overridden in the theme of the
  593. * implemented driver.
  594. */
  595. public function testCollectionRowWithCustomBlock()
  596. {
  597. $collection = array('one', 'two', 'three');
  598. $form = $this->factory->createNamedBuilder('names', 'Symfony\Component\Form\Extension\Core\Type\CollectionType', $collection)
  599. ->getForm();
  600. $this->assertWidgetMatchesXpath($form->createView(), array(),
  601. '/div
  602. [
  603. ./div[./label[.="Custom label: [trans]0[/trans]"]]
  604. /following-sibling::div[./label[.="Custom label: [trans]1[/trans]"]]
  605. /following-sibling::div[./label[.="Custom label: [trans]2[/trans]"]]
  606. ]
  607. '
  608. );
  609. }
  610. /**
  611. * The block "_name_c_entry_label" should be overridden in the theme of the
  612. * implemented driver.
  613. */
  614. public function testChoiceRowWithCustomBlock()
  615. {
  616. $form = $this->factory->createNamedBuilder('name_c', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', 'a', array(
  617. 'choices' => array('ChoiceA' => 'a', 'ChoiceB' => 'b'),
  618. 'choices_as_values' => true,
  619. 'expanded' => true,
  620. ))
  621. ->getForm();
  622. $this->assertWidgetMatchesXpath($form->createView(), array(),
  623. '/div
  624. [
  625. ./label[.="Custom name label: [trans]ChoiceA[/trans]"]
  626. /following-sibling::label[.="Custom name label: [trans]ChoiceB[/trans]"]
  627. ]
  628. '
  629. );
  630. }
  631. public function testSingleChoiceExpandedWithLabelsAsFalse()
  632. {
  633. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array(
  634. 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'),
  635. 'choices_as_values' => true,
  636. 'choice_label' => false,
  637. 'multiple' => false,
  638. 'expanded' => true,
  639. ));
  640. $this->assertWidgetMatchesXpath($form->createView(), array(),
  641. '/div
  642. [
  643. ./input[@type="radio"][@name="name"][@id="name_0"][@value="&a"][@checked]
  644. /following-sibling::input[@type="radio"][@name="name"][@id="name_1"][@value="&b"][not(@checked)]
  645. /following-sibling::input[@type="hidden"][@id="name__token"]
  646. ]
  647. [count(./input)=3]
  648. [count(./label)=1]
  649. '
  650. );
  651. }
  652. public function testSingleChoiceExpandedWithLabelsSetByCallable()
  653. {
  654. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array(
  655. 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b', 'Choice&C' => '&c'),
  656. 'choices_as_values' => true,
  657. 'choice_label' => function ($choice, $label, $value) {
  658. if ('&b' === $choice) {
  659. return false;
  660. }
  661. return 'label.'.$value;
  662. },
  663. 'multiple' => false,
  664. 'expanded' => true,
  665. ));
  666. $this->assertWidgetMatchesXpath($form->createView(), array(),
  667. '/div
  668. [
  669. ./input[@type="radio"][@name="name"][@id="name_0"][@value="&a"][@checked]
  670. /following-sibling::label[@for="name_0"][.="[trans]label.&a[/trans]"]
  671. /following-sibling::input[@type="radio"][@name="name"][@id="name_1"][@value="&b"][not(@checked)]
  672. /following-sibling::input[@type="radio"][@name="name"][@id="name_2"][@value="&c"][not(@checked)]
  673. /following-sibling::label[@for="name_2"][.="[trans]label.&c[/trans]"]
  674. /following-sibling::input[@type="hidden"][@id="name__token"]
  675. ]
  676. [count(./input)=4]
  677. [count(./label)=3]
  678. '
  679. );
  680. }
  681. public function testSingleChoiceExpandedWithLabelsSetFalseByCallable()
  682. {
  683. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array(
  684. 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'),
  685. 'choices_as_values' => true,
  686. 'choice_label' => function () {
  687. return false;
  688. },
  689. 'multiple' => false,
  690. 'expanded' => true,
  691. ));
  692. $this->assertWidgetMatchesXpath($form->createView(), array(),
  693. '/div
  694. [
  695. ./input[@type="radio"][@name="name"][@id="name_0"][@value="&a"][@checked]
  696. /following-sibling::input[@type="radio"][@name="name"][@id="name_1"][@value="&b"][not(@checked)]
  697. /following-sibling::input[@type="hidden"][@id="name__token"]
  698. ]
  699. [count(./input)=3]
  700. [count(./label)=1]
  701. '
  702. );
  703. }
  704. public function testMultipleChoiceExpandedWithLabelsAsFalse()
  705. {
  706. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', array('&a'), array(
  707. 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'),
  708. 'choices_as_values' => true,
  709. 'choice_label' => false,
  710. 'multiple' => true,
  711. 'expanded' => true,
  712. ));
  713. $this->assertWidgetMatchesXpath($form->createView(), array(),
  714. '/div
  715. [
  716. ./input[@type="checkbox"][@name="name[]"][@id="name_0"][@value="&a"][@checked]
  717. /following-sibling::input[@type="checkbox"][@name="name[]"][@id="name_1"][@value="&b"][not(@checked)]
  718. /following-sibling::input[@type="hidden"][@id="name__token"]
  719. ]
  720. [count(./input)=3]
  721. [count(./label)=1]
  722. '
  723. );
  724. }
  725. public function testMultipleChoiceExpandedWithLabelsSetByCallable()
  726. {
  727. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', array('&a'), array(
  728. 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b', 'Choice&C' => '&c'),
  729. 'choices_as_values' => true,
  730. 'choice_label' => function ($choice, $label, $value) {
  731. if ('&b' === $choice) {
  732. return false;
  733. }
  734. return 'label.'.$value;
  735. },
  736. 'multiple' => true,
  737. 'expanded' => true,
  738. ));
  739. $this->assertWidgetMatchesXpath($form->createView(), array(),
  740. '/div
  741. [
  742. ./input[@type="checkbox"][@name="name[]"][@id="name_0"][@value="&a"][@checked]
  743. /following-sibling::label[@for="name_0"][.="[trans]label.&a[/trans]"]
  744. /following-sibling::input[@type="checkbox"][@name="name[]"][@id="name_1"][@value="&b"][not(@checked)]
  745. /following-sibling::input[@type="checkbox"][@name="name[]"][@id="name_2"][@value="&c"][not(@checked)]
  746. /following-sibling::label[@for="name_2"][.="[trans]label.&c[/trans]"]
  747. /following-sibling::input[@type="hidden"][@id="name__token"]
  748. ]
  749. [count(./input)=4]
  750. [count(./label)=3]
  751. '
  752. );
  753. }
  754. public function testMultipleChoiceExpandedWithLabelsSetFalseByCallable()
  755. {
  756. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', array('&a'), array(
  757. 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'),
  758. 'choices_as_values' => true,
  759. 'choice_label' => function () {
  760. return false;
  761. },
  762. 'multiple' => true,
  763. 'expanded' => true,
  764. ));
  765. $this->assertWidgetMatchesXpath($form->createView(), array(),
  766. '/div
  767. [
  768. ./input[@type="checkbox"][@name="name[]"][@id="name_0"][@value="&a"][@checked]
  769. /following-sibling::input[@type="checkbox"][@name="name[]"][@id="name_1"][@value="&b"][not(@checked)]
  770. /following-sibling::input[@type="hidden"][@id="name__token"]
  771. ]
  772. [count(./input)=3]
  773. [count(./label)=1]
  774. '
  775. );
  776. }
  777. public function testFormEndWithRest()
  778. {
  779. $view = $this->factory->createNamedBuilder('name', 'Symfony\Component\Form\Extension\Core\Type\FormType')
  780. ->add('field1', 'Symfony\Component\Form\Extension\Core\Type\TextType')
  781. ->add('field2', 'Symfony\Component\Form\Extension\Core\Type\TextType')
  782. ->getForm()
  783. ->createView();
  784. $this->renderWidget($view['field1']);
  785. // Rest should only contain field2
  786. $html = $this->renderEnd($view);
  787. // Insert the start tag, the end tag should be rendered by the helper
  788. $this->assertMatchesXpath('<form>'.$html,
  789. '/form
  790. [
  791. ./div
  792. [
  793. ./label[@for="name_field2"]
  794. /following-sibling::input[@type="text"][@id="name_field2"]
  795. ]
  796. /following-sibling::input
  797. [@type="hidden"]
  798. [@id="name__token"]
  799. ]
  800. '
  801. );
  802. }
  803. public function testFormEndWithoutRest()
  804. {
  805. $view = $this->factory->createNamedBuilder('name', 'Symfony\Component\Form\Extension\Core\Type\FormType')
  806. ->add('field1', 'Symfony\Component\Form\Extension\Core\Type\TextType')
  807. ->add('field2', 'Symfony\Component\Form\Extension\Core\Type\TextType')
  808. ->getForm()
  809. ->createView();
  810. $this->renderWidget($view['field1']);
  811. // Rest should only contain field2, but isn't rendered
  812. $html = $this->renderEnd($view, array('render_rest' => false));
  813. $this->assertEquals('</form>', $html);
  814. }
  815. public function testWidgetContainerAttributes()
  816. {
  817. $form = $this->factory->createNamed('form', 'Symfony\Component\Form\Extension\Core\Type\FormType', null, array(
  818. 'attr' => array('class' => 'foobar', 'data-foo' => 'bar'),
  819. ));
  820. $form->add('text', 'Symfony\Component\Form\Extension\Core\Type\TextType');
  821. $html = $this->renderWidget($form->createView());
  822. // compare plain HTML to check the whitespace
  823. $this->assertContains('<div id="form" class="foobar" data-foo="bar">', $html);
  824. }
  825. public function testWidgetContainerAttributeNameRepeatedIfTrue()
  826. {
  827. $form = $this->factory->createNamed('form', 'Symfony\Component\Form\Extension\Core\Type\FormType', null, array(
  828. 'attr' => array('foo' => true),
  829. ));
  830. $html = $this->renderWidget($form->createView());
  831. // foo="foo"
  832. $this->assertContains('<div id="form" foo="foo">', $html);
  833. }
  834. }