AbstractLayoutTest.php 83 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557
  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\Extension\Csrf\CsrfExtension;
  12. use Symfony\Component\Form\FormError;
  13. use Symfony\Component\Form\FormView;
  14. use Symfony\Component\Form\Test\FormIntegrationTestCase;
  15. abstract class AbstractLayoutTest extends FormIntegrationTestCase
  16. {
  17. protected $csrfTokenManager;
  18. protected $testableFeatures = array();
  19. protected function setUp()
  20. {
  21. if (!\extension_loaded('intl')) {
  22. $this->markTestSkipped('Extension intl is required.');
  23. }
  24. \Locale::setDefault('en');
  25. $this->csrfTokenManager = $this->getMockBuilder('Symfony\Component\Security\Csrf\CsrfTokenManagerInterface')->getMock();
  26. parent::setUp();
  27. }
  28. protected function getExtensions()
  29. {
  30. return array(
  31. new CsrfExtension($this->csrfTokenManager),
  32. );
  33. }
  34. protected function tearDown()
  35. {
  36. $this->csrfTokenManager = null;
  37. parent::tearDown();
  38. }
  39. protected function assertXpathNodeValue(\DOMElement $element, $expression, $nodeValue)
  40. {
  41. $xpath = new \DOMXPath($element->ownerDocument);
  42. $nodeList = $xpath->evaluate($expression);
  43. $this->assertEquals(1, $nodeList->length);
  44. $this->assertEquals($nodeValue, $nodeList->item(0)->nodeValue);
  45. }
  46. protected function assertMatchesXpath($html, $expression, $count = 1)
  47. {
  48. $dom = new \DOMDocument('UTF-8');
  49. try {
  50. // Wrap in <root> node so we can load HTML with multiple tags at
  51. // the top level
  52. $dom->loadXML('<root>'.$html.'</root>');
  53. } catch (\Exception $e) {
  54. $this->fail(sprintf(
  55. "Failed loading HTML:\n\n%s\n\nError: %s",
  56. $html,
  57. $e->getMessage()
  58. ));
  59. }
  60. $xpath = new \DOMXPath($dom);
  61. $nodeList = $xpath->evaluate('/root'.$expression);
  62. if ($nodeList->length != $count) {
  63. $dom->formatOutput = true;
  64. $this->fail(sprintf(
  65. "Failed asserting that \n\n%s\n\nmatches exactly %s. Matches %s in \n\n%s",
  66. $expression,
  67. 1 == $count ? 'once' : $count.' times',
  68. 1 == $nodeList->length ? 'once' : $nodeList->length.' times',
  69. // strip away <root> and </root>
  70. substr($dom->saveHTML(), 6, -8)
  71. ));
  72. } else {
  73. $this->addToAssertionCount(1);
  74. }
  75. }
  76. protected function assertWidgetMatchesXpath(FormView $view, array $vars, $xpath)
  77. {
  78. // include ampersands everywhere to validate escaping
  79. $html = $this->renderWidget($view, array_merge(array(
  80. 'id' => 'my&id',
  81. 'attr' => array('class' => 'my&class'),
  82. ), $vars));
  83. if (!isset($vars['id'])) {
  84. $xpath = trim($xpath).'
  85. [@id="my&id"]';
  86. }
  87. if (!isset($vars['attr']['class'])) {
  88. $xpath .= '
  89. [@class="my&class"]';
  90. }
  91. $this->assertMatchesXpath($html, $xpath);
  92. }
  93. abstract protected function renderForm(FormView $view, array $vars = array());
  94. protected function renderEnctype(FormView $view)
  95. {
  96. $this->markTestSkipped(sprintf('Legacy %s::renderEnctype() is not implemented.', \get_class($this)));
  97. }
  98. abstract protected function renderLabel(FormView $view, $label = null, array $vars = array());
  99. abstract protected function renderErrors(FormView $view);
  100. abstract protected function renderWidget(FormView $view, array $vars = array());
  101. abstract protected function renderRow(FormView $view, array $vars = array());
  102. abstract protected function renderRest(FormView $view, array $vars = array());
  103. abstract protected function renderStart(FormView $view, array $vars = array());
  104. abstract protected function renderEnd(FormView $view, array $vars = array());
  105. abstract protected function setTheme(FormView $view, array $themes);
  106. /**
  107. * @group legacy
  108. */
  109. public function testEnctype()
  110. {
  111. $form = $this->factory->createNamedBuilder('name', 'Symfony\Component\Form\Extension\Core\Type\FormType')
  112. ->add('file', 'Symfony\Component\Form\Extension\Core\Type\FileType')
  113. ->getForm();
  114. $this->assertEquals('enctype="multipart/form-data"', $this->renderEnctype($form->createView()));
  115. }
  116. /**
  117. * @group legacy
  118. */
  119. public function testNoEnctype()
  120. {
  121. $form = $this->factory->createNamedBuilder('name', 'Symfony\Component\Form\Extension\Core\Type\FormType')
  122. ->add('text', 'Symfony\Component\Form\Extension\Core\Type\TextType')
  123. ->getForm();
  124. $this->assertEquals('', $this->renderEnctype($form->createView()));
  125. }
  126. public function testLabel()
  127. {
  128. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TextType');
  129. $view = $form->createView();
  130. $this->renderWidget($view, array('label' => 'foo'));
  131. $html = $this->renderLabel($view);
  132. $this->assertMatchesXpath($html,
  133. '/label
  134. [@for="name"]
  135. [.="[trans]Name[/trans]"]
  136. '
  137. );
  138. }
  139. public function testLabelWithoutTranslation()
  140. {
  141. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TextType', null, array(
  142. 'translation_domain' => false,
  143. ));
  144. $this->assertMatchesXpath($this->renderLabel($form->createView()),
  145. '/label
  146. [@for="name"]
  147. [.="Name"]
  148. '
  149. );
  150. }
  151. public function testLabelOnForm()
  152. {
  153. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\DateType');
  154. $view = $form->createView();
  155. $this->renderWidget($view, array('label' => 'foo'));
  156. $html = $this->renderLabel($view);
  157. $this->assertMatchesXpath($html,
  158. '/label
  159. [@class="required"]
  160. [.="[trans]Name[/trans]"]
  161. '
  162. );
  163. }
  164. public function testLabelWithCustomTextPassedAsOption()
  165. {
  166. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TextType', null, array(
  167. 'label' => 'Custom label',
  168. ));
  169. $html = $this->renderLabel($form->createView());
  170. $this->assertMatchesXpath($html,
  171. '/label
  172. [@for="name"]
  173. [.="[trans]Custom label[/trans]"]
  174. '
  175. );
  176. }
  177. public function testLabelWithCustomTextPassedDirectly()
  178. {
  179. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TextType');
  180. $html = $this->renderLabel($form->createView(), 'Custom label');
  181. $this->assertMatchesXpath($html,
  182. '/label
  183. [@for="name"]
  184. [.="[trans]Custom label[/trans]"]
  185. '
  186. );
  187. }
  188. public function testLabelWithCustomTextPassedAsOptionAndDirectly()
  189. {
  190. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TextType', null, array(
  191. 'label' => 'Custom label',
  192. ));
  193. $html = $this->renderLabel($form->createView(), 'Overridden label');
  194. $this->assertMatchesXpath($html,
  195. '/label
  196. [@for="name"]
  197. [.="[trans]Overridden label[/trans]"]
  198. '
  199. );
  200. }
  201. public function testLabelDoesNotRenderFieldAttributes()
  202. {
  203. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TextType');
  204. $html = $this->renderLabel($form->createView(), null, array(
  205. 'attr' => array(
  206. 'class' => 'my&class',
  207. ),
  208. ));
  209. $this->assertMatchesXpath($html,
  210. '/label
  211. [@for="name"]
  212. [@class="required"]
  213. '
  214. );
  215. }
  216. public function testLabelWithCustomAttributesPassedDirectly()
  217. {
  218. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TextType');
  219. $html = $this->renderLabel($form->createView(), null, array(
  220. 'label_attr' => array(
  221. 'class' => 'my&class',
  222. ),
  223. ));
  224. $this->assertMatchesXpath($html,
  225. '/label
  226. [@for="name"]
  227. [@class="my&class required"]
  228. '
  229. );
  230. }
  231. public function testLabelWithCustomTextAndCustomAttributesPassedDirectly()
  232. {
  233. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TextType');
  234. $html = $this->renderLabel($form->createView(), 'Custom label', array(
  235. 'label_attr' => array(
  236. 'class' => 'my&class',
  237. ),
  238. ));
  239. $this->assertMatchesXpath($html,
  240. '/label
  241. [@for="name"]
  242. [@class="my&class required"]
  243. [.="[trans]Custom label[/trans]"]
  244. '
  245. );
  246. }
  247. // https://github.com/symfony/symfony/issues/5029
  248. public function testLabelWithCustomTextAsOptionAndCustomAttributesPassedDirectly()
  249. {
  250. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TextType', null, array(
  251. 'label' => 'Custom label',
  252. ));
  253. $html = $this->renderLabel($form->createView(), null, array(
  254. 'label_attr' => array(
  255. 'class' => 'my&class',
  256. ),
  257. ));
  258. $this->assertMatchesXpath($html,
  259. '/label
  260. [@for="name"]
  261. [@class="my&class required"]
  262. [.="[trans]Custom label[/trans]"]
  263. '
  264. );
  265. }
  266. public function testLabelFormatName()
  267. {
  268. $form = $this->factory->createNamedBuilder('myform')
  269. ->add('myfield', 'Symfony\Component\Form\Extension\Core\Type\TextType')
  270. ->getForm();
  271. $view = $form->get('myfield')->createView();
  272. $html = $this->renderLabel($view, null, array('label_format' => 'form.%name%'));
  273. $this->assertMatchesXpath($html,
  274. '/label
  275. [@for="myform_myfield"]
  276. [.="[trans]form.myfield[/trans]"]
  277. '
  278. );
  279. }
  280. public function testLabelFormatId()
  281. {
  282. $form = $this->factory->createNamedBuilder('myform')
  283. ->add('myfield', 'Symfony\Component\Form\Extension\Core\Type\TextType')
  284. ->getForm();
  285. $view = $form->get('myfield')->createView();
  286. $html = $this->renderLabel($view, null, array('label_format' => 'form.%id%'));
  287. $this->assertMatchesXpath($html,
  288. '/label
  289. [@for="myform_myfield"]
  290. [.="[trans]form.myform_myfield[/trans]"]
  291. '
  292. );
  293. }
  294. public function testLabelFormatAsFormOption()
  295. {
  296. $options = array('label_format' => 'form.%name%');
  297. $form = $this->factory->createNamedBuilder('myform', 'Symfony\Component\Form\Extension\Core\Type\FormType', null, $options)
  298. ->add('myfield', 'Symfony\Component\Form\Extension\Core\Type\TextType')
  299. ->getForm();
  300. $view = $form->get('myfield')->createView();
  301. $html = $this->renderLabel($view);
  302. $this->assertMatchesXpath($html,
  303. '/label
  304. [@for="myform_myfield"]
  305. [.="[trans]form.myfield[/trans]"]
  306. '
  307. );
  308. }
  309. public function testLabelFormatOverriddenOption()
  310. {
  311. $options = array('label_format' => 'form.%name%');
  312. $form = $this->factory->createNamedBuilder('myform', 'Symfony\Component\Form\Extension\Core\Type\FormType', null, $options)
  313. ->add('myfield', 'Symfony\Component\Form\Extension\Core\Type\TextType', array('label_format' => 'field.%name%'))
  314. ->getForm();
  315. $view = $form->get('myfield')->createView();
  316. $html = $this->renderLabel($view);
  317. $this->assertMatchesXpath($html,
  318. '/label
  319. [@for="myform_myfield"]
  320. [.="[trans]field.myfield[/trans]"]
  321. '
  322. );
  323. }
  324. public function testLabelWithoutTranslationOnButton()
  325. {
  326. $form = $this->factory->createNamedBuilder('myform', 'Symfony\Component\Form\Extension\Core\Type\FormType', null, array(
  327. 'translation_domain' => false,
  328. ))
  329. ->add('mybutton', 'Symfony\Component\Form\Extension\Core\Type\ButtonType')
  330. ->getForm();
  331. $view = $form->get('mybutton')->createView();
  332. $html = $this->renderWidget($view);
  333. $this->assertMatchesXpath($html,
  334. '/button
  335. [@type="button"]
  336. [@name="myform[mybutton]"]
  337. [.="Mybutton"]
  338. '
  339. );
  340. }
  341. public function testLabelFormatOnButton()
  342. {
  343. $form = $this->factory->createNamedBuilder('myform')
  344. ->add('mybutton', 'Symfony\Component\Form\Extension\Core\Type\ButtonType')
  345. ->getForm();
  346. $view = $form->get('mybutton')->createView();
  347. $html = $this->renderWidget($view, array('label_format' => 'form.%name%'));
  348. $this->assertMatchesXpath($html,
  349. '/button
  350. [@type="button"]
  351. [@name="myform[mybutton]"]
  352. [.="[trans]form.mybutton[/trans]"]
  353. '
  354. );
  355. }
  356. public function testLabelFormatOnButtonId()
  357. {
  358. $form = $this->factory->createNamedBuilder('myform')
  359. ->add('mybutton', 'Symfony\Component\Form\Extension\Core\Type\ButtonType')
  360. ->getForm();
  361. $view = $form->get('mybutton')->createView();
  362. $html = $this->renderWidget($view, array('label_format' => 'form.%id%'));
  363. $this->assertMatchesXpath($html,
  364. '/button
  365. [@type="button"]
  366. [@name="myform[mybutton]"]
  367. [.="[trans]form.myform_mybutton[/trans]"]
  368. '
  369. );
  370. }
  371. public function testErrors()
  372. {
  373. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TextType');
  374. $form->addError(new FormError('[trans]Error 1[/trans]'));
  375. $form->addError(new FormError('[trans]Error 2[/trans]'));
  376. $view = $form->createView();
  377. $html = $this->renderErrors($view);
  378. $this->assertMatchesXpath($html,
  379. '/ul
  380. [
  381. ./li[.="[trans]Error 1[/trans]"]
  382. /following-sibling::li[.="[trans]Error 2[/trans]"]
  383. ]
  384. [count(./li)=2]
  385. '
  386. );
  387. }
  388. public function testOverrideWidgetBlock()
  389. {
  390. // see custom_widgets.html.twig
  391. $form = $this->factory->createNamed('text_id', 'Symfony\Component\Form\Extension\Core\Type\TextType');
  392. $html = $this->renderWidget($form->createView());
  393. $this->assertMatchesXpath($html,
  394. '/div
  395. [
  396. ./input
  397. [@type="text"]
  398. [@id="text_id"]
  399. ]
  400. [@id="container"]
  401. '
  402. );
  403. }
  404. public function testCheckedCheckbox()
  405. {
  406. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\CheckboxType', true);
  407. $this->assertWidgetMatchesXpath($form->createView(), array(),
  408. '/input
  409. [@type="checkbox"]
  410. [@name="name"]
  411. [@checked="checked"]
  412. [@value="1"]
  413. '
  414. );
  415. }
  416. public function testUncheckedCheckbox()
  417. {
  418. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\CheckboxType', false);
  419. $this->assertWidgetMatchesXpath($form->createView(), array(),
  420. '/input
  421. [@type="checkbox"]
  422. [@name="name"]
  423. [not(@checked)]
  424. '
  425. );
  426. }
  427. public function testCheckboxWithValue()
  428. {
  429. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\CheckboxType', false, array(
  430. 'value' => 'foo&bar',
  431. ));
  432. $this->assertWidgetMatchesXpath($form->createView(), array(),
  433. '/input
  434. [@type="checkbox"]
  435. [@name="name"]
  436. [@value="foo&bar"]
  437. '
  438. );
  439. }
  440. public function testSingleChoice()
  441. {
  442. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array(
  443. 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'),
  444. 'choices_as_values' => true,
  445. 'multiple' => false,
  446. 'expanded' => false,
  447. ));
  448. // If the field is collapsed, has no "multiple" attribute, is required but
  449. // has *no* empty value, the "required" must not be added, otherwise
  450. // the resulting HTML is invalid.
  451. // https://github.com/symfony/symfony/issues/8942
  452. // HTML 5 spec
  453. // http://www.w3.org/html/wg/drafts/html/master/forms.html#placeholder-label-option
  454. // "If a select element has a required attribute specified, does not
  455. // have a multiple attribute specified, and has a display size of 1,
  456. // then the select element must have a placeholder label option."
  457. $this->assertWidgetMatchesXpath($form->createView(), array(),
  458. '/select
  459. [@name="name"]
  460. [not(@required)]
  461. [
  462. ./option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"]
  463. /following-sibling::option[@value="&b"][not(@selected)][.="[trans]Choice&B[/trans]"]
  464. ]
  465. [count(./option)=2]
  466. '
  467. );
  468. }
  469. public function testSelectWithSizeBiggerThanOneCanBeRequired()
  470. {
  471. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array(
  472. 'choices' => array('a', 'b'),
  473. 'choices_as_values' => true,
  474. 'multiple' => false,
  475. 'expanded' => false,
  476. 'attr' => array('size' => 2),
  477. ));
  478. $this->assertWidgetMatchesXpath($form->createView(), array(),
  479. '/select
  480. [@name="name"]
  481. [@required="required"]
  482. [@size="2"]
  483. [count(./option)=2]
  484. '
  485. );
  486. }
  487. public function testSingleChoiceWithoutTranslation()
  488. {
  489. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array(
  490. 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'),
  491. 'choices_as_values' => true,
  492. 'multiple' => false,
  493. 'expanded' => false,
  494. 'choice_translation_domain' => false,
  495. ));
  496. $this->assertWidgetMatchesXpath($form->createView(), array(),
  497. '/select
  498. [@name="name"]
  499. [not(@required)]
  500. [
  501. ./option[@value="&a"][@selected="selected"][.="Choice&A"]
  502. /following-sibling::option[@value="&b"][not(@selected)][.="Choice&B"]
  503. ]
  504. [count(./option)=2]
  505. '
  506. );
  507. }
  508. public function testSingleChoiceWithPlaceholderWithoutTranslation()
  509. {
  510. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array(
  511. 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'),
  512. 'choices_as_values' => true,
  513. 'multiple' => false,
  514. 'expanded' => false,
  515. 'required' => false,
  516. 'translation_domain' => false,
  517. 'placeholder' => 'Placeholder&Not&Translated',
  518. ));
  519. $this->assertWidgetMatchesXpath($form->createView(), array(),
  520. '/select
  521. [@name="name"]
  522. [not(@required)]
  523. [
  524. ./option[@value=""][not(@selected)][not(@disabled)][.="Placeholder&Not&Translated"]
  525. /following-sibling::option[@value="&a"][@selected="selected"][.="Choice&A"]
  526. /following-sibling::option[@value="&b"][not(@selected)][.="Choice&B"]
  527. ]
  528. [count(./option)=3]
  529. '
  530. );
  531. }
  532. public function testSingleChoiceAttributes()
  533. {
  534. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array(
  535. 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'),
  536. 'choices_as_values' => true,
  537. 'choice_attr' => array('Choice&B' => array('class' => 'foo&bar')),
  538. 'multiple' => false,
  539. 'expanded' => false,
  540. ));
  541. $classPart = \in_array('choice_attr', $this->testableFeatures) ? '[@class="foo&bar"]' : '';
  542. $this->assertWidgetMatchesXpath($form->createView(), array(),
  543. '/select
  544. [@name="name"]
  545. [not(@required)]
  546. [
  547. ./option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"]
  548. /following-sibling::option[@value="&b"]'.$classPart.'[not(@selected)][.="[trans]Choice&B[/trans]"]
  549. ]
  550. [count(./option)=2]
  551. '
  552. );
  553. }
  554. public function testSingleChoiceAttributesWithMainAttributes()
  555. {
  556. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array(
  557. 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'),
  558. 'choices_as_values' => true,
  559. 'multiple' => false,
  560. 'expanded' => false,
  561. 'attr' => array('class' => 'bar&baz'),
  562. ));
  563. $this->assertWidgetMatchesXpath($form->createView(), array('attr' => array('class' => 'bar&baz')),
  564. '/select
  565. [@name="name"]
  566. [@class="bar&baz"]
  567. [not(@required)]
  568. [
  569. ./option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"][not(@id)][not(@name)]
  570. /following-sibling::option[@value="&b"][not(@class)][not(@selected)][.="[trans]Choice&B[/trans]"][not(@id)][not(@name)]
  571. ]
  572. [count(./option)=2]
  573. '
  574. );
  575. }
  576. public function testSingleExpandedChoiceAttributesWithMainAttributes()
  577. {
  578. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array(
  579. 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'),
  580. 'choices_as_values' => true,
  581. 'multiple' => false,
  582. 'expanded' => true,
  583. 'attr' => array('class' => 'bar&baz'),
  584. ));
  585. $this->assertWidgetMatchesXpath($form->createView(), array('attr' => array('class' => 'bar&baz')),
  586. '/div
  587. [@class="bar&baz"]
  588. [
  589. ./input[@type="radio"][@name="name"][@id="name_0"][@value="&a"][@checked]
  590. /following-sibling::label[@for="name_0"][.="[trans]Choice&A[/trans]"]
  591. /following-sibling::input[@type="radio"][@name="name"][@id="name_1"][@value="&b"][not(@checked)]
  592. /following-sibling::label[@for="name_1"][.="[trans]Choice&B[/trans]"]
  593. /following-sibling::input[@type="hidden"][@id="name__token"]
  594. ]
  595. [count(./input)=3]
  596. '
  597. );
  598. }
  599. public function testSingleChoiceWithPreferred()
  600. {
  601. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array(
  602. 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'),
  603. 'choices_as_values' => true,
  604. 'preferred_choices' => array('&b'),
  605. 'multiple' => false,
  606. 'expanded' => false,
  607. ));
  608. $this->assertWidgetMatchesXpath($form->createView(), array('separator' => '-- sep --'),
  609. '/select
  610. [@name="name"]
  611. [not(@required)]
  612. [
  613. ./option[@value="&b"][not(@selected)][.="[trans]Choice&B[/trans]"]
  614. /following-sibling::option[@disabled="disabled"][not(@selected)][.="-- sep --"]
  615. /following-sibling::option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"]
  616. ]
  617. [count(./option)=3]
  618. '
  619. );
  620. }
  621. public function testSingleChoiceWithPreferredAndNoSeparator()
  622. {
  623. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array(
  624. 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'),
  625. 'choices_as_values' => true,
  626. 'preferred_choices' => array('&b'),
  627. 'multiple' => false,
  628. 'expanded' => false,
  629. ));
  630. $this->assertWidgetMatchesXpath($form->createView(), array('separator' => null),
  631. '/select
  632. [@name="name"]
  633. [not(@required)]
  634. [
  635. ./option[@value="&b"][not(@selected)][.="[trans]Choice&B[/trans]"]
  636. /following-sibling::option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"]
  637. ]
  638. [count(./option)=2]
  639. '
  640. );
  641. }
  642. public function testSingleChoiceWithPreferredAndBlankSeparator()
  643. {
  644. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array(
  645. 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'),
  646. 'choices_as_values' => true,
  647. 'preferred_choices' => array('&b'),
  648. 'multiple' => false,
  649. 'expanded' => false,
  650. ));
  651. $this->assertWidgetMatchesXpath($form->createView(), array('separator' => ''),
  652. '/select
  653. [@name="name"]
  654. [not(@required)]
  655. [
  656. ./option[@value="&b"][not(@selected)][.="[trans]Choice&B[/trans]"]
  657. /following-sibling::option[@disabled="disabled"][not(@selected)][.=""]
  658. /following-sibling::option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"]
  659. ]
  660. [count(./option)=3]
  661. '
  662. );
  663. }
  664. public function testChoiceWithOnlyPreferred()
  665. {
  666. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array(
  667. 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'),
  668. 'choices_as_values' => true,
  669. 'preferred_choices' => array('&a', '&b'),
  670. 'multiple' => false,
  671. 'expanded' => false,
  672. ));
  673. $this->assertWidgetMatchesXpath($form->createView(), array(),
  674. '/select
  675. [count(./option)=2]
  676. '
  677. );
  678. }
  679. public function testSingleChoiceNonRequired()
  680. {
  681. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array(
  682. 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'),
  683. 'choices_as_values' => true,
  684. 'required' => false,
  685. 'multiple' => false,
  686. 'expanded' => false,
  687. ));
  688. $this->assertWidgetMatchesXpath($form->createView(), array(),
  689. '/select
  690. [@name="name"]
  691. [not(@required)]
  692. [
  693. ./option[@value=""][.=""]
  694. /following-sibling::option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"]
  695. /following-sibling::option[@value="&b"][not(@selected)][.="[trans]Choice&B[/trans]"]
  696. ]
  697. [count(./option)=3]
  698. '
  699. );
  700. }
  701. public function testSingleChoiceNonRequiredNoneSelected()
  702. {
  703. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array(
  704. 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'),
  705. 'choices_as_values' => true,
  706. 'required' => false,
  707. 'multiple' => false,
  708. 'expanded' => false,
  709. ));
  710. $this->assertWidgetMatchesXpath($form->createView(), array(),
  711. '/select
  712. [@name="name"]
  713. [not(@required)]
  714. [
  715. ./option[@value=""][.=""]
  716. /following-sibling::option[@value="&a"][not(@selected)][.="[trans]Choice&A[/trans]"]
  717. /following-sibling::option[@value="&b"][not(@selected)][.="[trans]Choice&B[/trans]"]
  718. ]
  719. [count(./option)=3]
  720. '
  721. );
  722. }
  723. public function testSingleChoiceNonRequiredWithPlaceholder()
  724. {
  725. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array(
  726. 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'),
  727. 'choices_as_values' => true,
  728. 'multiple' => false,
  729. 'expanded' => false,
  730. 'required' => false,
  731. 'placeholder' => 'Select&Anything&Not&Me',
  732. ));
  733. $this->assertWidgetMatchesXpath($form->createView(), array(),
  734. '/select
  735. [@name="name"]
  736. [not(@required)]
  737. [
  738. ./option[@value=""][not(@selected)][not(@disabled)][.="[trans]Select&Anything&Not&Me[/trans]"]
  739. /following-sibling::option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"]
  740. /following-sibling::option[@value="&b"][not(@selected)][.="[trans]Choice&B[/trans]"]
  741. ]
  742. [count(./option)=3]
  743. '
  744. );
  745. }
  746. public function testSingleChoiceRequiredWithPlaceholder()
  747. {
  748. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array(
  749. 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'),
  750. 'choices_as_values' => true,
  751. 'required' => true,
  752. 'multiple' => false,
  753. 'expanded' => false,
  754. 'placeholder' => 'Test&Me',
  755. ));
  756. // The "disabled" attribute was removed again due to a bug in the
  757. // BlackBerry 10 browser.
  758. // See https://github.com/symfony/symfony/pull/7678
  759. $this->assertWidgetMatchesXpath($form->createView(), array(),
  760. '/select
  761. [@name="name"]
  762. [@required="required"]
  763. [
  764. ./option[@value=""][not(@selected)][not(@disabled)][.="[trans]Test&Me[/trans]"]
  765. /following-sibling::option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"]
  766. /following-sibling::option[@value="&b"][not(@selected)][.="[trans]Choice&B[/trans]"]
  767. ]
  768. [count(./option)=3]
  769. '
  770. );
  771. }
  772. public function testSingleChoiceRequiredWithPlaceholderViaView()
  773. {
  774. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array(
  775. 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'),
  776. 'choices_as_values' => true,
  777. 'required' => true,
  778. 'multiple' => false,
  779. 'expanded' => false,
  780. ));
  781. // The "disabled" attribute was removed again due to a bug in the
  782. // BlackBerry 10 browser.
  783. // See https://github.com/symfony/symfony/pull/7678
  784. $this->assertWidgetMatchesXpath($form->createView(), array('placeholder' => ''),
  785. '/select
  786. [@name="name"]
  787. [@required="required"]
  788. [
  789. ./option[@value=""][not(@selected)][not(@disabled)][.=""]
  790. /following-sibling::option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"]
  791. /following-sibling::option[@value="&b"][not(@selected)][.="[trans]Choice&B[/trans]"]
  792. ]
  793. [count(./option)=3]
  794. '
  795. );
  796. }
  797. public function testSingleChoiceGrouped()
  798. {
  799. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array(
  800. 'choices' => array(
  801. 'Group&1' => array('Choice&A' => '&a', 'Choice&B' => '&b'),
  802. 'Group&2' => array('Choice&C' => '&c'),
  803. ),
  804. 'choices_as_values' => true,
  805. 'multiple' => false,
  806. 'expanded' => false,
  807. ));
  808. $this->assertWidgetMatchesXpath($form->createView(), array(),
  809. '/select
  810. [@name="name"]
  811. [./optgroup[@label="[trans]Group&1[/trans]"]
  812. [
  813. ./option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"]
  814. /following-sibling::option[@value="&b"][not(@selected)][.="[trans]Choice&B[/trans]"]
  815. ]
  816. [count(./option)=2]
  817. ]
  818. [./optgroup[@label="[trans]Group&2[/trans]"]
  819. [./option[@value="&c"][not(@selected)][.="[trans]Choice&C[/trans]"]]
  820. [count(./option)=1]
  821. ]
  822. [count(./optgroup)=2]
  823. '
  824. );
  825. }
  826. public function testMultipleChoice()
  827. {
  828. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', array('&a'), array(
  829. 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'),
  830. 'choices_as_values' => true,
  831. 'required' => true,
  832. 'multiple' => true,
  833. 'expanded' => false,
  834. ));
  835. $this->assertWidgetMatchesXpath($form->createView(), array(),
  836. '/select
  837. [@name="name[]"]
  838. [@required="required"]
  839. [@multiple="multiple"]
  840. [
  841. ./option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"]
  842. /following-sibling::option[@value="&b"][not(@selected)][.="[trans]Choice&B[/trans]"]
  843. ]
  844. [count(./option)=2]
  845. '
  846. );
  847. }
  848. public function testMultipleChoiceAttributes()
  849. {
  850. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', array('&a'), array(
  851. 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'),
  852. 'choices_as_values' => true,
  853. 'choice_attr' => array('Choice&B' => array('class' => 'foo&bar')),
  854. 'required' => true,
  855. 'multiple' => true,
  856. 'expanded' => false,
  857. ));
  858. $classPart = \in_array('choice_attr', $this->testableFeatures) ? '[@class="foo&bar"]' : '';
  859. $this->assertWidgetMatchesXpath($form->createView(), array(),
  860. '/select
  861. [@name="name[]"]
  862. [@required="required"]
  863. [@multiple="multiple"]
  864. [
  865. ./option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"]
  866. /following-sibling::option[@value="&b"]'.$classPart.'[not(@selected)][.="[trans]Choice&B[/trans]"]
  867. ]
  868. [count(./option)=2]
  869. '
  870. );
  871. }
  872. public function testMultipleChoiceSkipsPlaceholder()
  873. {
  874. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', array('&a'), array(
  875. 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'),
  876. 'choices_as_values' => true,
  877. 'multiple' => true,
  878. 'expanded' => false,
  879. 'placeholder' => 'Test&Me',
  880. ));
  881. $this->assertWidgetMatchesXpath($form->createView(), array(),
  882. '/select
  883. [@name="name[]"]
  884. [@multiple="multiple"]
  885. [
  886. ./option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"]
  887. /following-sibling::option[@value="&b"][not(@selected)][.="[trans]Choice&B[/trans]"]
  888. ]
  889. [count(./option)=2]
  890. '
  891. );
  892. }
  893. public function testMultipleChoiceNonRequired()
  894. {
  895. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', array('&a'), array(
  896. 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'),
  897. 'choices_as_values' => true,
  898. 'required' => false,
  899. 'multiple' => true,
  900. 'expanded' => false,
  901. ));
  902. $this->assertWidgetMatchesXpath($form->createView(), array(),
  903. '/select
  904. [@name="name[]"]
  905. [@multiple="multiple"]
  906. [
  907. ./option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"]
  908. /following-sibling::option[@value="&b"][not(@selected)][.="[trans]Choice&B[/trans]"]
  909. ]
  910. [count(./option)=2]
  911. '
  912. );
  913. }
  914. public function testSingleChoiceExpanded()
  915. {
  916. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array(
  917. 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'),
  918. 'choices_as_values' => true,
  919. 'multiple' => false,
  920. 'expanded' => true,
  921. ));
  922. $this->assertWidgetMatchesXpath($form->createView(), array(),
  923. '/div
  924. [
  925. ./input[@type="radio"][@name="name"][@id="name_0"][@value="&a"][@checked]
  926. /following-sibling::label[@for="name_0"][.="[trans]Choice&A[/trans]"]
  927. /following-sibling::input[@type="radio"][@name="name"][@id="name_1"][@value="&b"][not(@checked)]
  928. /following-sibling::label[@for="name_1"][.="[trans]Choice&B[/trans]"]
  929. /following-sibling::input[@type="hidden"][@id="name__token"]
  930. ]
  931. [count(./input)=3]
  932. '
  933. );
  934. }
  935. public function testSingleChoiceExpandedWithoutTranslation()
  936. {
  937. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array(
  938. 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'),
  939. 'choices_as_values' => true,
  940. 'multiple' => false,
  941. 'expanded' => true,
  942. 'choice_translation_domain' => false,
  943. 'placeholder' => 'Placeholder&Not&Translated',
  944. ));
  945. $this->assertWidgetMatchesXpath($form->createView(), array(),
  946. '/div
  947. [
  948. ./input[@type="radio"][@name="name"][@id="name_0"][@value="&a"][@checked]
  949. /following-sibling::label[@for="name_0"][.="Choice&A"]
  950. /following-sibling::input[@type="radio"][@name="name"][@id="name_1"][@value="&b"][not(@checked)]
  951. /following-sibling::label[@for="name_1"][.="Choice&B"]
  952. /following-sibling::input[@type="hidden"][@id="name__token"]
  953. ]
  954. [count(./input)=3]
  955. '
  956. );
  957. }
  958. public function testSingleChoiceExpandedAttributes()
  959. {
  960. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array(
  961. 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'),
  962. 'choices_as_values' => true,
  963. 'choice_attr' => array('Choice&B' => array('class' => 'foo&bar')),
  964. 'multiple' => false,
  965. 'expanded' => true,
  966. ));
  967. $classPart = \in_array('choice_attr', $this->testableFeatures) ? '[@class="foo&bar"]' : '';
  968. $this->assertWidgetMatchesXpath($form->createView(), array(),
  969. '/div
  970. [
  971. ./input[@type="radio"][@name="name"][@id="name_0"][@value="&a"][@checked]
  972. /following-sibling::label[@for="name_0"][.="[trans]Choice&A[/trans]"]
  973. /following-sibling::input[@type="radio"][@name="name"][@id="name_1"][@value="&b"]'.$classPart.'[not(@checked)]
  974. /following-sibling::label[@for="name_1"][.="[trans]Choice&B[/trans]"]
  975. /following-sibling::input[@type="hidden"][@id="name__token"]
  976. ]
  977. [count(./input)=3]
  978. '
  979. );
  980. }
  981. public function testSingleChoiceExpandedWithPlaceholder()
  982. {
  983. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array(
  984. 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'),
  985. 'choices_as_values' => true,
  986. 'multiple' => false,
  987. 'expanded' => true,
  988. 'placeholder' => 'Test&Me',
  989. 'required' => false,
  990. ));
  991. $this->assertWidgetMatchesXpath($form->createView(), array(),
  992. '/div
  993. [
  994. ./input[@type="radio"][@name="name"][@id="name_placeholder"][not(@checked)]
  995. /following-sibling::label[@for="name_placeholder"][.="[trans]Test&Me[/trans]"]
  996. /following-sibling::input[@type="radio"][@name="name"][@id="name_0"][@checked]
  997. /following-sibling::label[@for="name_0"][.="[trans]Choice&A[/trans]"]
  998. /following-sibling::input[@type="radio"][@name="name"][@id="name_1"][not(@checked)]
  999. /following-sibling::label[@for="name_1"][.="[trans]Choice&B[/trans]"]
  1000. /following-sibling::input[@type="hidden"][@id="name__token"]
  1001. ]
  1002. [count(./input)=4]
  1003. '
  1004. );
  1005. }
  1006. public function testSingleChoiceExpandedWithPlaceholderWithoutTranslation()
  1007. {
  1008. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array(
  1009. 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'),
  1010. 'choices_as_values' => true,
  1011. 'multiple' => false,
  1012. 'expanded' => true,
  1013. 'required' => false,
  1014. 'choice_translation_domain' => false,
  1015. 'placeholder' => 'Placeholder&Not&Translated',
  1016. ));
  1017. $this->assertWidgetMatchesXpath($form->createView(), array(),
  1018. '/div
  1019. [
  1020. ./input[@type="radio"][@name="name"][@id="name_placeholder"][not(@checked)]
  1021. /following-sibling::label[@for="name_placeholder"][.="Placeholder&Not&Translated"]
  1022. /following-sibling::input[@type="radio"][@name="name"][@id="name_0"][@checked]
  1023. /following-sibling::label[@for="name_0"][.="Choice&A"]
  1024. /following-sibling::input[@type="radio"][@name="name"][@id="name_1"][not(@checked)]
  1025. /following-sibling::label[@for="name_1"][.="Choice&B"]
  1026. /following-sibling::input[@type="hidden"][@id="name__token"]
  1027. ]
  1028. [count(./input)=4]
  1029. '
  1030. );
  1031. }
  1032. public function testSingleChoiceExpandedWithBooleanValue()
  1033. {
  1034. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', true, array(
  1035. 'choices' => array('Choice&A' => '1', 'Choice&B' => '0'),
  1036. 'choices_as_values' => true,
  1037. 'multiple' => false,
  1038. 'expanded' => true,
  1039. ));
  1040. $this->assertWidgetMatchesXpath($form->createView(), array(),
  1041. '/div
  1042. [
  1043. ./input[@type="radio"][@name="name"][@id="name_0"][@checked]
  1044. /following-sibling::label[@for="name_0"][.="[trans]Choice&A[/trans]"]
  1045. /following-sibling::input[@type="radio"][@name="name"][@id="name_1"][not(@checked)]
  1046. /following-sibling::label[@for="name_1"][.="[trans]Choice&B[/trans]"]
  1047. /following-sibling::input[@type="hidden"][@id="name__token"]
  1048. ]
  1049. [count(./input)=3]
  1050. '
  1051. );
  1052. }
  1053. public function testMultipleChoiceExpanded()
  1054. {
  1055. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', array('&a', '&c'), array(
  1056. 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b', 'Choice&C' => '&c'),
  1057. 'choices_as_values' => true,
  1058. 'multiple' => true,
  1059. 'expanded' => true,
  1060. 'required' => true,
  1061. ));
  1062. $this->assertWidgetMatchesXpath($form->createView(), array(),
  1063. '/div
  1064. [
  1065. ./input[@type="checkbox"][@name="name[]"][@id="name_0"][@checked][not(@required)]
  1066. /following-sibling::label[@for="name_0"][.="[trans]Choice&A[/trans]"]
  1067. /following-sibling::input[@type="checkbox"][@name="name[]"][@id="name_1"][not(@checked)][not(@required)]
  1068. /following-sibling::label[@for="name_1"][.="[trans]Choice&B[/trans]"]
  1069. /following-sibling::input[@type="checkbox"][@name="name[]"][@id="name_2"][@checked][not(@required)]
  1070. /following-sibling::label[@for="name_2"][.="[trans]Choice&C[/trans]"]
  1071. /following-sibling::input[@type="hidden"][@id="name__token"]
  1072. ]
  1073. [count(./input)=4]
  1074. '
  1075. );
  1076. }
  1077. public function testMultipleChoiceExpandedWithoutTranslation()
  1078. {
  1079. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', array('&a', '&c'), array(
  1080. 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b', 'Choice&C' => '&c'),
  1081. 'choices_as_values' => true,
  1082. 'multiple' => true,
  1083. 'expanded' => true,
  1084. 'required' => true,
  1085. 'choice_translation_domain' => false,
  1086. ));
  1087. $this->assertWidgetMatchesXpath($form->createView(), array(),
  1088. '/div
  1089. [
  1090. ./input[@type="checkbox"][@name="name[]"][@id="name_0"][@checked][not(@required)]
  1091. /following-sibling::label[@for="name_0"][.="Choice&A"]
  1092. /following-sibling::input[@type="checkbox"][@name="name[]"][@id="name_1"][not(@checked)][not(@required)]
  1093. /following-sibling::label[@for="name_1"][.="Choice&B"]
  1094. /following-sibling::input[@type="checkbox"][@name="name[]"][@id="name_2"][@checked][not(@required)]
  1095. /following-sibling::label[@for="name_2"][.="Choice&C"]
  1096. /following-sibling::input[@type="hidden"][@id="name__token"]
  1097. ]
  1098. [count(./input)=4]
  1099. '
  1100. );
  1101. }
  1102. public function testMultipleChoiceExpandedAttributes()
  1103. {
  1104. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', array('&a', '&c'), array(
  1105. 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b', 'Choice&C' => '&c'),
  1106. 'choices_as_values' => true,
  1107. 'choice_attr' => array('Choice&B' => array('class' => 'foo&bar')),
  1108. 'multiple' => true,
  1109. 'expanded' => true,
  1110. 'required' => true,
  1111. ));
  1112. $classPart = \in_array('choice_attr', $this->testableFeatures) ? '[@class="foo&bar"]' : '';
  1113. $this->assertWidgetMatchesXpath($form->createView(), array(),
  1114. '/div
  1115. [
  1116. ./input[@type="checkbox"][@name="name[]"][@id="name_0"][@checked][not(@required)]
  1117. /following-sibling::label[@for="name_0"][.="[trans]Choice&A[/trans]"]
  1118. /following-sibling::input[@type="checkbox"][@name="name[]"][@id="name_1"]'.$classPart.'[not(@checked)][not(@required)]
  1119. /following-sibling::label[@for="name_1"][.="[trans]Choice&B[/trans]"]
  1120. /following-sibling::input[@type="checkbox"][@name="name[]"][@id="name_2"][@checked][not(@required)]
  1121. /following-sibling::label[@for="name_2"][.="[trans]Choice&C[/trans]"]
  1122. /following-sibling::input[@type="hidden"][@id="name__token"]
  1123. ]
  1124. [count(./input)=4]
  1125. '
  1126. );
  1127. }
  1128. public function testCountry()
  1129. {
  1130. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\CountryType', 'AT');
  1131. $this->assertWidgetMatchesXpath($form->createView(), array(),
  1132. '/select
  1133. [@name="name"]
  1134. [./option[@value="AT"][@selected="selected"][.="Austria"]]
  1135. [count(./option)>200]
  1136. '
  1137. );
  1138. }
  1139. public function testCountryWithPlaceholder()
  1140. {
  1141. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\CountryType', 'AT', array(
  1142. 'placeholder' => 'Select&Country',
  1143. 'required' => false,
  1144. ));
  1145. $this->assertWidgetMatchesXpath($form->createView(), array(),
  1146. '/select
  1147. [@name="name"]
  1148. [./option[@value=""][not(@selected)][not(@disabled)][.="[trans]Select&Country[/trans]"]]
  1149. [./option[@value="AT"][@selected="selected"][.="Austria"]]
  1150. [count(./option)>201]
  1151. '
  1152. );
  1153. }
  1154. public function testDateTime()
  1155. {
  1156. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\DateTimeType', date('Y').'-02-03 04:05:06', array(
  1157. 'input' => 'string',
  1158. 'with_seconds' => false,
  1159. ));
  1160. $this->assertWidgetMatchesXpath($form->createView(), array(),
  1161. '/div
  1162. [
  1163. ./div
  1164. [@id="name_date"]
  1165. [
  1166. ./select
  1167. [@id="name_date_month"]
  1168. [./option[@value="2"][@selected="selected"]]
  1169. /following-sibling::select
  1170. [@id="name_date_day"]
  1171. [./option[@value="3"][@selected="selected"]]
  1172. /following-sibling::select
  1173. [@id="name_date_year"]
  1174. [./option[@value="'.date('Y').'"][@selected="selected"]]
  1175. ]
  1176. /following-sibling::div
  1177. [@id="name_time"]
  1178. [
  1179. ./select
  1180. [@id="name_time_hour"]
  1181. [./option[@value="4"][@selected="selected"]]
  1182. /following-sibling::select
  1183. [@id="name_time_minute"]
  1184. [./option[@value="5"][@selected="selected"]]
  1185. ]
  1186. ]
  1187. [count(.//select)=5]
  1188. '
  1189. );
  1190. }
  1191. public function testDateTimeWithPlaceholderGlobal()
  1192. {
  1193. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\DateTimeType', null, array(
  1194. 'input' => 'string',
  1195. 'placeholder' => 'Change&Me',
  1196. 'required' => false,
  1197. ));
  1198. $this->assertWidgetMatchesXpath($form->createView(), array(),
  1199. '/div
  1200. [
  1201. ./div
  1202. [@id="name_date"]
  1203. [
  1204. ./select
  1205. [@id="name_date_month"]
  1206. [./option[@value=""][not(@selected)][not(@disabled)][.="[trans]Change&Me[/trans]"]]
  1207. /following-sibling::select
  1208. [@id="name_date_day"]
  1209. [./option[@value=""][not(@selected)][not(@disabled)][.="[trans]Change&Me[/trans]"]]
  1210. /following-sibling::select
  1211. [@id="name_date_year"]
  1212. [./option[@value=""][not(@selected)][not(@disabled)][.="[trans]Change&Me[/trans]"]]
  1213. ]
  1214. /following-sibling::div
  1215. [@id="name_time"]
  1216. [
  1217. ./select
  1218. [@id="name_time_hour"]
  1219. [./option[@value=""][.="[trans]Change&Me[/trans]"]]
  1220. /following-sibling::select
  1221. [@id="name_time_minute"]
  1222. [./option[@value=""][.="[trans]Change&Me[/trans]"]]
  1223. ]
  1224. ]
  1225. [count(.//select)=5]
  1226. '
  1227. );
  1228. }
  1229. public function testDateTimeWithHourAndMinute()
  1230. {
  1231. $data = array('year' => date('Y'), 'month' => '2', 'day' => '3', 'hour' => '4', 'minute' => '5');
  1232. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\DateTimeType', $data, array(
  1233. 'input' => 'array',
  1234. 'required' => false,
  1235. ));
  1236. $this->assertWidgetMatchesXpath($form->createView(), array(),
  1237. '/div
  1238. [
  1239. ./div
  1240. [@id="name_date"]
  1241. [
  1242. ./select
  1243. [@id="name_date_month"]
  1244. [./option[@value="2"][@selected="selected"]]
  1245. /following-sibling::select
  1246. [@id="name_date_day"]
  1247. [./option[@value="3"][@selected="selected"]]
  1248. /following-sibling::select
  1249. [@id="name_date_year"]
  1250. [./option[@value="'.date('Y').'"][@selected="selected"]]
  1251. ]
  1252. /following-sibling::div
  1253. [@id="name_time"]
  1254. [
  1255. ./select
  1256. [@id="name_time_hour"]
  1257. [./option[@value="4"][@selected="selected"]]
  1258. /following-sibling::select
  1259. [@id="name_time_minute"]
  1260. [./option[@value="5"][@selected="selected"]]
  1261. ]
  1262. ]
  1263. [count(.//select)=5]
  1264. '
  1265. );
  1266. }
  1267. public function testDateTimeWithSeconds()
  1268. {
  1269. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\DateTimeType', date('Y').'-02-03 04:05:06', array(
  1270. 'input' => 'string',
  1271. 'with_seconds' => true,
  1272. ));
  1273. $this->assertWidgetMatchesXpath($form->createView(), array(),
  1274. '/div
  1275. [
  1276. ./div
  1277. [@id="name_date"]
  1278. [
  1279. ./select
  1280. [@id="name_date_month"]
  1281. [./option[@value="2"][@selected="selected"]]
  1282. /following-sibling::select
  1283. [@id="name_date_day"]
  1284. [./option[@value="3"][@selected="selected"]]
  1285. /following-sibling::select
  1286. [@id="name_date_year"]
  1287. [./option[@value="'.date('Y').'"][@selected="selected"]]
  1288. ]
  1289. /following-sibling::div
  1290. [@id="name_time"]
  1291. [
  1292. ./select
  1293. [@id="name_time_hour"]
  1294. [./option[@value="4"][@selected="selected"]]
  1295. /following-sibling::select
  1296. [@id="name_time_minute"]
  1297. [./option[@value="5"][@selected="selected"]]
  1298. /following-sibling::select
  1299. [@id="name_time_second"]
  1300. [./option[@value="6"][@selected="selected"]]
  1301. ]
  1302. ]
  1303. [count(.//select)=6]
  1304. '
  1305. );
  1306. }
  1307. public function testDateTimeSingleText()
  1308. {
  1309. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\DateTimeType', '2011-02-03 04:05:06', array(
  1310. 'input' => 'string',
  1311. 'date_widget' => 'single_text',
  1312. 'time_widget' => 'single_text',
  1313. ));
  1314. $this->assertWidgetMatchesXpath($form->createView(), array(),
  1315. '/div
  1316. [
  1317. ./input
  1318. [@type="date"]
  1319. [@id="name_date"]
  1320. [@name="name[date]"]
  1321. [@value="2011-02-03"]
  1322. /following-sibling::input
  1323. [@type="time"]
  1324. [@id="name_time"]
  1325. [@name="name[time]"]
  1326. [@value="04:05"]
  1327. ]
  1328. '
  1329. );
  1330. }
  1331. public function testDateTimeWithWidgetSingleText()
  1332. {
  1333. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\DateTimeType', '2011-02-03 04:05:06', array(
  1334. 'input' => 'string',
  1335. 'widget' => 'single_text',
  1336. 'model_timezone' => 'UTC',
  1337. 'view_timezone' => 'UTC',
  1338. ));
  1339. $this->assertWidgetMatchesXpath($form->createView(), array(),
  1340. '/input
  1341. [@type="datetime-local"]
  1342. [@name="name"]
  1343. [@value="2011-02-03T04:05:06"]
  1344. '
  1345. );
  1346. }
  1347. public function testDateTimeWithWidgetSingleTextIgnoreDateAndTimeWidgets()
  1348. {
  1349. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\DateTimeType', '2011-02-03 04:05:06', array(
  1350. 'input' => 'string',
  1351. 'date_widget' => 'choice',
  1352. 'time_widget' => 'choice',
  1353. 'widget' => 'single_text',
  1354. 'model_timezone' => 'UTC',
  1355. 'view_timezone' => 'UTC',
  1356. ));
  1357. $this->assertWidgetMatchesXpath($form->createView(), array(),
  1358. '/input
  1359. [@type="datetime-local"]
  1360. [@name="name"]
  1361. [@value="2011-02-03T04:05:06"]
  1362. '
  1363. );
  1364. }
  1365. public function testDateChoice()
  1366. {
  1367. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\DateType', date('Y').'-02-03', array(
  1368. 'input' => 'string',
  1369. 'widget' => 'choice',
  1370. ));
  1371. $this->assertWidgetMatchesXpath($form->createView(), array(),
  1372. '/div
  1373. [
  1374. ./select
  1375. [@id="name_month"]
  1376. [./option[@value="2"][@selected="selected"]]
  1377. /following-sibling::select
  1378. [@id="name_day"]
  1379. [./option[@value="3"][@selected="selected"]]
  1380. /following-sibling::select
  1381. [@id="name_year"]
  1382. [./option[@value="'.date('Y').'"][@selected="selected"]]
  1383. ]
  1384. [count(./select)=3]
  1385. '
  1386. );
  1387. }
  1388. public function testDateChoiceWithPlaceholderGlobal()
  1389. {
  1390. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\DateType', null, array(
  1391. 'input' => 'string',
  1392. 'widget' => 'choice',
  1393. 'placeholder' => 'Change&Me',
  1394. 'required' => false,
  1395. ));
  1396. $this->assertWidgetMatchesXpath($form->createView(), array(),
  1397. '/div
  1398. [
  1399. ./select
  1400. [@id="name_month"]
  1401. [./option[@value=""][not(@selected)][not(@disabled)][.="[trans]Change&Me[/trans]"]]
  1402. /following-sibling::select
  1403. [@id="name_day"]
  1404. [./option[@value=""][not(@selected)][not(@disabled)][.="[trans]Change&Me[/trans]"]]
  1405. /following-sibling::select
  1406. [@id="name_year"]
  1407. [./option[@value=""][not(@selected)][not(@disabled)][.="[trans]Change&Me[/trans]"]]
  1408. ]
  1409. [count(./select)=3]
  1410. '
  1411. );
  1412. }
  1413. public function testDateChoiceWithPlaceholderOnYear()
  1414. {
  1415. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\DateType', null, array(
  1416. 'input' => 'string',
  1417. 'widget' => 'choice',
  1418. 'required' => false,
  1419. 'placeholder' => array('year' => 'Change&Me'),
  1420. ));
  1421. $this->assertWidgetMatchesXpath($form->createView(), array(),
  1422. '/div
  1423. [
  1424. ./select
  1425. [@id="name_month"]
  1426. [./option[@value="1"]]
  1427. /following-sibling::select
  1428. [@id="name_day"]
  1429. [./option[@value="1"]]
  1430. /following-sibling::select
  1431. [@id="name_year"]
  1432. [./option[@value=""][not(@selected)][not(@disabled)][.="[trans]Change&Me[/trans]"]]
  1433. ]
  1434. [count(./select)=3]
  1435. '
  1436. );
  1437. }
  1438. public function testDateText()
  1439. {
  1440. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\DateType', '2011-02-03', array(
  1441. 'input' => 'string',
  1442. 'widget' => 'text',
  1443. ));
  1444. $this->assertWidgetMatchesXpath($form->createView(), array(),
  1445. '/div
  1446. [
  1447. ./input
  1448. [@id="name_month"]
  1449. [@type="text"]
  1450. [@value="2"]
  1451. /following-sibling::input
  1452. [@id="name_day"]
  1453. [@type="text"]
  1454. [@value="3"]
  1455. /following-sibling::input
  1456. [@id="name_year"]
  1457. [@type="text"]
  1458. [@value="2011"]
  1459. ]
  1460. [count(./input)=3]
  1461. '
  1462. );
  1463. }
  1464. public function testDateSingleText()
  1465. {
  1466. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\DateType', '2011-02-03', array(
  1467. 'input' => 'string',
  1468. 'widget' => 'single_text',
  1469. ));
  1470. $this->assertWidgetMatchesXpath($form->createView(), array(),
  1471. '/input
  1472. [@type="date"]
  1473. [@name="name"]
  1474. [@value="2011-02-03"]
  1475. '
  1476. );
  1477. }
  1478. public function testDateErrorBubbling()
  1479. {
  1480. $form = $this->factory->createNamedBuilder('form', 'Symfony\Component\Form\Extension\Core\Type\FormType')
  1481. ->add('date', 'Symfony\Component\Form\Extension\Core\Type\DateType')
  1482. ->getForm();
  1483. $form->get('date')->addError(new FormError('[trans]Error![/trans]'));
  1484. $view = $form->createView();
  1485. $this->assertEmpty($this->renderErrors($view));
  1486. $this->assertNotEmpty($this->renderErrors($view['date']));
  1487. }
  1488. public function testBirthDay()
  1489. {
  1490. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\BirthdayType', '2000-02-03', array(
  1491. 'input' => 'string',
  1492. ));
  1493. $this->assertWidgetMatchesXpath($form->createView(), array(),
  1494. '/div
  1495. [
  1496. ./select
  1497. [@id="name_month"]
  1498. [./option[@value="2"][@selected="selected"]]
  1499. /following-sibling::select
  1500. [@id="name_day"]
  1501. [./option[@value="3"][@selected="selected"]]
  1502. /following-sibling::select
  1503. [@id="name_year"]
  1504. [./option[@value="2000"][@selected="selected"]]
  1505. ]
  1506. [count(./select)=3]
  1507. '
  1508. );
  1509. }
  1510. public function testBirthDayWithPlaceholder()
  1511. {
  1512. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\BirthdayType', '1950-01-01', array(
  1513. 'input' => 'string',
  1514. 'placeholder' => '',
  1515. 'required' => false,
  1516. ));
  1517. $this->assertWidgetMatchesXpath($form->createView(), array(),
  1518. '/div
  1519. [
  1520. ./select
  1521. [@id="name_month"]
  1522. [./option[@value=""][not(@selected)][not(@disabled)][.=""]]
  1523. [./option[@value="1"][@selected="selected"]]
  1524. /following-sibling::select
  1525. [@id="name_day"]
  1526. [./option[@value=""][not(@selected)][not(@disabled)][.=""]]
  1527. [./option[@value="1"][@selected="selected"]]
  1528. /following-sibling::select
  1529. [@id="name_year"]
  1530. [./option[@value=""][not(@selected)][not(@disabled)][.=""]]
  1531. [./option[@value="1950"][@selected="selected"]]
  1532. ]
  1533. [count(./select)=3]
  1534. '
  1535. );
  1536. }
  1537. public function testEmail()
  1538. {
  1539. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\EmailType', 'foo&bar');
  1540. $this->assertWidgetMatchesXpath($form->createView(), array(),
  1541. '/input
  1542. [@type="email"]
  1543. [@name="name"]
  1544. [@value="foo&bar"]
  1545. [not(@maxlength)]
  1546. '
  1547. );
  1548. }
  1549. public function testEmailWithMaxLength()
  1550. {
  1551. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\EmailType', 'foo&bar', array(
  1552. 'attr' => array('maxlength' => 123),
  1553. ));
  1554. $this->assertWidgetMatchesXpath($form->createView(), array(),
  1555. '/input
  1556. [@type="email"]
  1557. [@name="name"]
  1558. [@value="foo&bar"]
  1559. [@maxlength="123"]
  1560. '
  1561. );
  1562. }
  1563. public function testFile()
  1564. {
  1565. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\FileType');
  1566. $this->assertWidgetMatchesXpath($form->createView(), array(),
  1567. '/input
  1568. [@type="file"]
  1569. '
  1570. );
  1571. }
  1572. public function testHidden()
  1573. {
  1574. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\HiddenType', 'foo&bar');
  1575. $this->assertWidgetMatchesXpath($form->createView(), array(),
  1576. '/input
  1577. [@type="hidden"]
  1578. [@name="name"]
  1579. [@value="foo&bar"]
  1580. '
  1581. );
  1582. }
  1583. /**
  1584. * @group legacy
  1585. */
  1586. public function testLegacyReadOnly()
  1587. {
  1588. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TextType', null, array(
  1589. 'read_only' => true,
  1590. ));
  1591. $this->assertWidgetMatchesXpath($form->createView(), array(),
  1592. '/input
  1593. [@type="text"]
  1594. [@name="name"]
  1595. [@readonly="readonly"]
  1596. '
  1597. );
  1598. }
  1599. public function testDisabled()
  1600. {
  1601. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TextType', null, array(
  1602. 'disabled' => true,
  1603. ));
  1604. $this->assertWidgetMatchesXpath($form->createView(), array(),
  1605. '/input
  1606. [@type="text"]
  1607. [@name="name"]
  1608. [@disabled="disabled"]
  1609. '
  1610. );
  1611. }
  1612. public function testInteger()
  1613. {
  1614. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\IntegerType', 123);
  1615. $this->assertWidgetMatchesXpath($form->createView(), array(),
  1616. '/input
  1617. [@type="number"]
  1618. [@name="name"]
  1619. [@value="123"]
  1620. '
  1621. );
  1622. }
  1623. public function testLanguage()
  1624. {
  1625. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\LanguageType', 'de');
  1626. $this->assertWidgetMatchesXpath($form->createView(), array(),
  1627. '/select
  1628. [@name="name"]
  1629. [./option[@value="de"][@selected="selected"][.="German"]]
  1630. [count(./option)>200]
  1631. '
  1632. );
  1633. }
  1634. public function testLocale()
  1635. {
  1636. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\LocaleType', 'de_AT');
  1637. $this->assertWidgetMatchesXpath($form->createView(), array(),
  1638. '/select
  1639. [@name="name"]
  1640. [./option[@value="de_AT"][@selected="selected"][.="German (Austria)"]]
  1641. [count(./option)>200]
  1642. '
  1643. );
  1644. }
  1645. public function testMoney()
  1646. {
  1647. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\MoneyType', 1234.56, array(
  1648. 'currency' => 'EUR',
  1649. ));
  1650. $this->assertWidgetMatchesXpath($form->createView(), array(),
  1651. '/input
  1652. [@type="text"]
  1653. [@name="name"]
  1654. [@value="1234.56"]
  1655. [contains(.., "€")]
  1656. '
  1657. );
  1658. }
  1659. public function testNumber()
  1660. {
  1661. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\NumberType', 1234.56);
  1662. $this->assertWidgetMatchesXpath($form->createView(), array(),
  1663. '/input
  1664. [@type="text"]
  1665. [@name="name"]
  1666. [@value="1234.56"]
  1667. '
  1668. );
  1669. }
  1670. public function testPassword()
  1671. {
  1672. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\PasswordType', 'foo&bar');
  1673. $this->assertWidgetMatchesXpath($form->createView(), array(),
  1674. '/input
  1675. [@type="password"]
  1676. [@name="name"]
  1677. '
  1678. );
  1679. }
  1680. public function testPasswordSubmittedWithNotAlwaysEmpty()
  1681. {
  1682. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\PasswordType', null, array(
  1683. 'always_empty' => false,
  1684. ));
  1685. $form->submit('foo&bar');
  1686. $this->assertWidgetMatchesXpath($form->createView(), array(),
  1687. '/input
  1688. [@type="password"]
  1689. [@name="name"]
  1690. [@value="foo&bar"]
  1691. '
  1692. );
  1693. }
  1694. public function testPasswordWithMaxLength()
  1695. {
  1696. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\PasswordType', 'foo&bar', array(
  1697. 'attr' => array('maxlength' => 123),
  1698. ));
  1699. $this->assertWidgetMatchesXpath($form->createView(), array(),
  1700. '/input
  1701. [@type="password"]
  1702. [@name="name"]
  1703. [@maxlength="123"]
  1704. '
  1705. );
  1706. }
  1707. public function testPercent()
  1708. {
  1709. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\PercentType', 0.1);
  1710. $this->assertWidgetMatchesXpath($form->createView(), array(),
  1711. '/input
  1712. [@type="text"]
  1713. [@name="name"]
  1714. [@value="10"]
  1715. [contains(.., "%")]
  1716. '
  1717. );
  1718. }
  1719. public function testCheckedRadio()
  1720. {
  1721. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\RadioType', true);
  1722. $this->assertWidgetMatchesXpath($form->createView(), array(),
  1723. '/input
  1724. [@type="radio"]
  1725. [@name="name"]
  1726. [@checked="checked"]
  1727. [@value="1"]
  1728. '
  1729. );
  1730. }
  1731. public function testUncheckedRadio()
  1732. {
  1733. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\RadioType', false);
  1734. $this->assertWidgetMatchesXpath($form->createView(), array(),
  1735. '/input
  1736. [@type="radio"]
  1737. [@name="name"]
  1738. [not(@checked)]
  1739. '
  1740. );
  1741. }
  1742. public function testRadioWithValue()
  1743. {
  1744. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\RadioType', false, array(
  1745. 'value' => 'foo&bar',
  1746. ));
  1747. $this->assertWidgetMatchesXpath($form->createView(), array(),
  1748. '/input
  1749. [@type="radio"]
  1750. [@name="name"]
  1751. [@value="foo&bar"]
  1752. '
  1753. );
  1754. }
  1755. public function testRange()
  1756. {
  1757. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\RangeType', 42, array('attr' => array('min' => 5)));
  1758. $this->assertWidgetMatchesXpath($form->createView(), array(),
  1759. '/input
  1760. [@type="range"]
  1761. [@name="name"]
  1762. [@value="42"]
  1763. [@min="5"]
  1764. '
  1765. );
  1766. }
  1767. public function testRangeWithMinMaxValues()
  1768. {
  1769. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\RangeType', 42, array('attr' => array('min' => 5, 'max' => 57)));
  1770. $this->assertWidgetMatchesXpath($form->createView(), array(),
  1771. '/input
  1772. [@type="range"]
  1773. [@name="name"]
  1774. [@value="42"]
  1775. [@min="5"]
  1776. [@max="57"]
  1777. '
  1778. );
  1779. }
  1780. public function testTextarea()
  1781. {
  1782. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TextareaType', 'foo&bar', array(
  1783. 'attr' => array('pattern' => 'foo'),
  1784. ));
  1785. $this->assertWidgetMatchesXpath($form->createView(), array(),
  1786. '/textarea
  1787. [@name="name"]
  1788. [@pattern="foo"]
  1789. [.="foo&bar"]
  1790. '
  1791. );
  1792. }
  1793. public function testText()
  1794. {
  1795. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TextType', 'foo&bar');
  1796. $this->assertWidgetMatchesXpath($form->createView(), array(),
  1797. '/input
  1798. [@type="text"]
  1799. [@name="name"]
  1800. [@value="foo&bar"]
  1801. [not(@maxlength)]
  1802. '
  1803. );
  1804. }
  1805. public function testTextWithMaxLength()
  1806. {
  1807. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TextType', 'foo&bar', array(
  1808. 'attr' => array('maxlength' => 123),
  1809. ));
  1810. $this->assertWidgetMatchesXpath($form->createView(), array(),
  1811. '/input
  1812. [@type="text"]
  1813. [@name="name"]
  1814. [@value="foo&bar"]
  1815. [@maxlength="123"]
  1816. '
  1817. );
  1818. }
  1819. public function testSearch()
  1820. {
  1821. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\SearchType', 'foo&bar');
  1822. $this->assertWidgetMatchesXpath($form->createView(), array(),
  1823. '/input
  1824. [@type="search"]
  1825. [@name="name"]
  1826. [@value="foo&bar"]
  1827. [not(@maxlength)]
  1828. '
  1829. );
  1830. }
  1831. public function testTime()
  1832. {
  1833. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TimeType', '04:05:06', array(
  1834. 'input' => 'string',
  1835. 'with_seconds' => false,
  1836. ));
  1837. $this->assertWidgetMatchesXpath($form->createView(), array(),
  1838. '/div
  1839. [
  1840. ./select
  1841. [@id="name_hour"]
  1842. [not(@size)]
  1843. [./option[@value="4"][@selected="selected"]]
  1844. /following-sibling::select
  1845. [@id="name_minute"]
  1846. [not(@size)]
  1847. [./option[@value="5"][@selected="selected"]]
  1848. ]
  1849. [count(./select)=2]
  1850. '
  1851. );
  1852. }
  1853. public function testTimeWithSeconds()
  1854. {
  1855. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TimeType', '04:05:06', array(
  1856. 'input' => 'string',
  1857. 'with_seconds' => true,
  1858. ));
  1859. $this->assertWidgetMatchesXpath($form->createView(), array(),
  1860. '/div
  1861. [
  1862. ./select
  1863. [@id="name_hour"]
  1864. [not(@size)]
  1865. [./option[@value="4"][@selected="selected"]]
  1866. [count(./option)>23]
  1867. /following-sibling::select
  1868. [@id="name_minute"]
  1869. [not(@size)]
  1870. [./option[@value="5"][@selected="selected"]]
  1871. [count(./option)>59]
  1872. /following-sibling::select
  1873. [@id="name_second"]
  1874. [not(@size)]
  1875. [./option[@value="6"][@selected="selected"]]
  1876. [count(./option)>59]
  1877. ]
  1878. [count(./select)=3]
  1879. '
  1880. );
  1881. }
  1882. public function testTimeText()
  1883. {
  1884. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TimeType', '04:05:06', array(
  1885. 'input' => 'string',
  1886. 'widget' => 'text',
  1887. ));
  1888. $this->assertWidgetMatchesXpath($form->createView(), array(),
  1889. '/div
  1890. [
  1891. ./input
  1892. [@type="text"]
  1893. [@id="name_hour"]
  1894. [@name="name[hour]"]
  1895. [@value="04"]
  1896. [@size="1"]
  1897. [@required="required"]
  1898. /following-sibling::input
  1899. [@type="text"]
  1900. [@id="name_minute"]
  1901. [@name="name[minute]"]
  1902. [@value="05"]
  1903. [@size="1"]
  1904. [@required="required"]
  1905. ]
  1906. [count(./input)=2]
  1907. '
  1908. );
  1909. }
  1910. public function testTimeSingleText()
  1911. {
  1912. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TimeType', '04:05:06', array(
  1913. 'input' => 'string',
  1914. 'widget' => 'single_text',
  1915. ));
  1916. $this->assertWidgetMatchesXpath($form->createView(), array(),
  1917. '/input
  1918. [@type="time"]
  1919. [@name="name"]
  1920. [@value="04:05"]
  1921. [not(@size)]
  1922. '
  1923. );
  1924. }
  1925. public function testTimeWithPlaceholderGlobal()
  1926. {
  1927. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TimeType', null, array(
  1928. 'input' => 'string',
  1929. 'placeholder' => 'Change&Me',
  1930. 'required' => false,
  1931. ));
  1932. $this->assertWidgetMatchesXpath($form->createView(), array(),
  1933. '/div
  1934. [
  1935. ./select
  1936. [@id="name_hour"]
  1937. [./option[@value=""][not(@selected)][not(@disabled)][.="[trans]Change&Me[/trans]"]]
  1938. [count(./option)>24]
  1939. /following-sibling::select
  1940. [@id="name_minute"]
  1941. [./option[@value=""][not(@selected)][not(@disabled)][.="[trans]Change&Me[/trans]"]]
  1942. [count(./option)>60]
  1943. ]
  1944. [count(./select)=2]
  1945. '
  1946. );
  1947. }
  1948. public function testTimeWithPlaceholderOnYear()
  1949. {
  1950. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TimeType', null, array(
  1951. 'input' => 'string',
  1952. 'required' => false,
  1953. 'placeholder' => array('hour' => 'Change&Me'),
  1954. ));
  1955. $this->assertWidgetMatchesXpath($form->createView(), array(),
  1956. '/div
  1957. [
  1958. ./select
  1959. [@id="name_hour"]
  1960. [./option[@value=""][not(@selected)][not(@disabled)][.="[trans]Change&Me[/trans]"]]
  1961. [count(./option)>24]
  1962. /following-sibling::select
  1963. [@id="name_minute"]
  1964. [./option[@value="1"]]
  1965. [count(./option)>59]
  1966. ]
  1967. [count(./select)=2]
  1968. '
  1969. );
  1970. }
  1971. public function testTimeErrorBubbling()
  1972. {
  1973. $form = $this->factory->createNamedBuilder('form', 'Symfony\Component\Form\Extension\Core\Type\FormType')
  1974. ->add('time', 'Symfony\Component\Form\Extension\Core\Type\TimeType')
  1975. ->getForm();
  1976. $form->get('time')->addError(new FormError('[trans]Error![/trans]'));
  1977. $view = $form->createView();
  1978. $this->assertEmpty($this->renderErrors($view));
  1979. $this->assertNotEmpty($this->renderErrors($view['time']));
  1980. }
  1981. public function testTimezone()
  1982. {
  1983. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TimezoneType', 'Europe/Vienna');
  1984. $this->assertWidgetMatchesXpath($form->createView(), array(),
  1985. '/select
  1986. [@name="name"]
  1987. [not(@required)]
  1988. [./optgroup
  1989. [@label="Europe"]
  1990. [./option[@value="Europe/Vienna"][@selected="selected"][.="Vienna"]]
  1991. ]
  1992. [count(./optgroup)>10]
  1993. [count(.//option)>200]
  1994. '
  1995. );
  1996. }
  1997. public function testTimezoneWithPlaceholder()
  1998. {
  1999. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TimezoneType', null, array(
  2000. 'placeholder' => 'Select&Timezone',
  2001. 'required' => false,
  2002. ));
  2003. $this->assertWidgetMatchesXpath($form->createView(), array(),
  2004. '/select
  2005. [./option[@value=""][not(@selected)][not(@disabled)][.="[trans]Select&Timezone[/trans]"]]
  2006. [count(./optgroup)>10]
  2007. [count(.//option)>201]
  2008. '
  2009. );
  2010. }
  2011. public function testUrl()
  2012. {
  2013. $url = 'http://www.google.com?foo1=bar1&foo2=bar2';
  2014. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\UrlType', $url);
  2015. $this->assertWidgetMatchesXpath($form->createView(), array(),
  2016. '/input
  2017. [@type="url"]
  2018. [@name="name"]
  2019. [@value="http://www.google.com?foo1=bar1&foo2=bar2"]
  2020. '
  2021. );
  2022. }
  2023. public function testCollectionPrototype()
  2024. {
  2025. $form = $this->factory->createNamedBuilder('name', 'Symfony\Component\Form\Extension\Core\Type\FormType', array('items' => array('one', 'two', 'three')))
  2026. ->add('items', 'Symfony\Component\Form\Extension\Core\Type\CollectionType', array('allow_add' => true))
  2027. ->getForm()
  2028. ->createView();
  2029. $html = $this->renderWidget($form);
  2030. $this->assertMatchesXpath($html,
  2031. '//div[@id="name_items"][@data-prototype]
  2032. |
  2033. //table[@id="name_items"][@data-prototype]'
  2034. );
  2035. }
  2036. public function testEmptyRootFormName()
  2037. {
  2038. $form = $this->factory->createNamedBuilder('', 'Symfony\Component\Form\Extension\Core\Type\FormType')
  2039. ->add('child', 'Symfony\Component\Form\Extension\Core\Type\TextType')
  2040. ->getForm();
  2041. $this->assertMatchesXpath($this->renderWidget($form->createView()),
  2042. '//input[@type="hidden"][@id="_token"][@name="_token"]
  2043. |
  2044. //input[@type="text"][@id="child"][@name="child"]', 2);
  2045. }
  2046. public function testButton()
  2047. {
  2048. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ButtonType');
  2049. $this->assertWidgetMatchesXpath($form->createView(), array(),
  2050. '/button[@type="button"][@name="name"][.="[trans]Name[/trans]"]'
  2051. );
  2052. }
  2053. public function testButtonLabelIsEmpty()
  2054. {
  2055. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ButtonType');
  2056. $this->assertSame('', $this->renderLabel($form->createView()));
  2057. }
  2058. public function testButtonlabelWithoutTranslation()
  2059. {
  2060. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ButtonType', null, array(
  2061. 'translation_domain' => false,
  2062. ));
  2063. $this->assertWidgetMatchesXpath($form->createView(), array(),
  2064. '/button[@type="button"][@name="name"][.="Name"]'
  2065. );
  2066. }
  2067. public function testSubmit()
  2068. {
  2069. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\SubmitType');
  2070. $this->assertWidgetMatchesXpath($form->createView(), array(),
  2071. '/button[@type="submit"][@name="name"]'
  2072. );
  2073. }
  2074. public function testReset()
  2075. {
  2076. $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ResetType');
  2077. $this->assertWidgetMatchesXpath($form->createView(), array(),
  2078. '/button[@type="reset"][@name="name"]'
  2079. );
  2080. }
  2081. public function testStartTag()
  2082. {
  2083. $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\FormType', null, array(
  2084. 'method' => 'get',
  2085. 'action' => 'http://example.com/directory',
  2086. ));
  2087. $html = $this->renderStart($form->createView());
  2088. $this->assertSame('<form name="form" method="get" action="http://example.com/directory">', $html);
  2089. }
  2090. public function testStartTagForPutRequest()
  2091. {
  2092. $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\FormType', null, array(
  2093. 'method' => 'put',
  2094. 'action' => 'http://example.com/directory',
  2095. ));
  2096. $html = $this->renderStart($form->createView());
  2097. $this->assertMatchesXpath($html.'</form>',
  2098. '/form
  2099. [./input[@type="hidden"][@name="_method"][@value="PUT"]]
  2100. [@method="post"]
  2101. [@action="http://example.com/directory"]'
  2102. );
  2103. }
  2104. public function testStartTagWithOverriddenVars()
  2105. {
  2106. $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\FormType', null, array(
  2107. 'method' => 'put',
  2108. 'action' => 'http://example.com/directory',
  2109. ));
  2110. $html = $this->renderStart($form->createView(), array(
  2111. 'method' => 'post',
  2112. 'action' => 'http://foo.com/directory',
  2113. ));
  2114. $this->assertSame('<form name="form" method="post" action="http://foo.com/directory">', $html);
  2115. }
  2116. public function testStartTagForMultipartForm()
  2117. {
  2118. $form = $this->factory->createBuilder('Symfony\Component\Form\Extension\Core\Type\FormType', null, array(
  2119. 'method' => 'get',
  2120. 'action' => 'http://example.com/directory',
  2121. ))
  2122. ->add('file', 'Symfony\Component\Form\Extension\Core\Type\FileType')
  2123. ->getForm();
  2124. $html = $this->renderStart($form->createView());
  2125. $this->assertSame('<form name="form" method="get" action="http://example.com/directory" enctype="multipart/form-data">', $html);
  2126. }
  2127. public function testStartTagWithExtraAttributes()
  2128. {
  2129. $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\FormType', null, array(
  2130. 'method' => 'get',
  2131. 'action' => 'http://example.com/directory',
  2132. ));
  2133. $html = $this->renderStart($form->createView(), array(
  2134. 'attr' => array('class' => 'foobar'),
  2135. ));
  2136. $this->assertSame('<form name="form" method="get" action="http://example.com/directory" class="foobar">', $html);
  2137. }
  2138. public function testWidgetAttributes()
  2139. {
  2140. $form = $this->factory->createNamed('text', 'Symfony\Component\Form\Extension\Core\Type\TextType', 'value', array(
  2141. 'required' => true,
  2142. 'disabled' => true,
  2143. 'attr' => array('readonly' => true, 'maxlength' => 10, 'pattern' => '\d+', 'class' => 'foobar', 'data-foo' => 'bar'),
  2144. ));
  2145. $html = $this->renderWidget($form->createView());
  2146. // compare plain HTML to check the whitespace
  2147. try {
  2148. $this->assertSame('<input type="text" id="text" name="text" readonly="readonly" disabled="disabled" required="required" maxlength="10" pattern="\d+" class="foobar" data-foo="bar" value="value" />', $html);
  2149. } catch (\PHPUnit\Framework\AssertionFailedError $e) {
  2150. $this->assertSame('<input type="text" id="text" name="text" disabled="disabled" required="required" readonly="readonly" maxlength="10" pattern="\d+" class="foobar" data-foo="bar" value="value" />', $html);
  2151. } catch (\PHPUnit_Framework_AssertionFailedError $e) {
  2152. $this->assertSame('<input type="text" id="text" name="text" disabled="disabled" required="required" readonly="readonly" maxlength="10" pattern="\d+" class="foobar" data-foo="bar" value="value" />', $html);
  2153. }
  2154. }
  2155. public function testWidgetAttributeNameRepeatedIfTrue()
  2156. {
  2157. $form = $this->factory->createNamed('text', 'Symfony\Component\Form\Extension\Core\Type\TextType', 'value', array(
  2158. 'attr' => array('foo' => true),
  2159. ));
  2160. $html = $this->renderWidget($form->createView());
  2161. // foo="foo"
  2162. $this->assertSame('<input type="text" id="text" name="text" required="required" foo="foo" value="value" />', $html);
  2163. }
  2164. public function testWidgetAttributeHiddenIfFalse()
  2165. {
  2166. $form = $this->factory->createNamed('text', 'Symfony\Component\Form\Extension\Core\Type\TextType', 'value', array(
  2167. 'attr' => array('foo' => false),
  2168. ));
  2169. $html = $this->renderWidget($form->createView());
  2170. $this->assertNotContains('foo="', $html);
  2171. }
  2172. public function testButtonAttributes()
  2173. {
  2174. $form = $this->factory->createNamed('button', 'Symfony\Component\Form\Extension\Core\Type\ButtonType', null, array(
  2175. 'disabled' => true,
  2176. 'attr' => array('class' => 'foobar', 'data-foo' => 'bar'),
  2177. ));
  2178. $html = $this->renderWidget($form->createView());
  2179. // compare plain HTML to check the whitespace
  2180. $this->assertSame('<button type="button" id="button" name="button" disabled="disabled" class="foobar" data-foo="bar">[trans]Button[/trans]</button>', $html);
  2181. }
  2182. public function testButtonAttributeNameRepeatedIfTrue()
  2183. {
  2184. $form = $this->factory->createNamed('button', 'Symfony\Component\Form\Extension\Core\Type\ButtonType', null, array(
  2185. 'attr' => array('foo' => true),
  2186. ));
  2187. $html = $this->renderWidget($form->createView());
  2188. // foo="foo"
  2189. $this->assertSame('<button type="button" id="button" name="button" foo="foo">[trans]Button[/trans]</button>', $html);
  2190. }
  2191. public function testButtonAttributeHiddenIfFalse()
  2192. {
  2193. $form = $this->factory->createNamed('button', 'Symfony\Component\Form\Extension\Core\Type\ButtonType', null, array(
  2194. 'attr' => array('foo' => false),
  2195. ));
  2196. $html = $this->renderWidget($form->createView());
  2197. $this->assertNotContains('foo="', $html);
  2198. }
  2199. public function testTextareaWithWhitespaceOnlyContentRetainsValue()
  2200. {
  2201. $form = $this->factory->createNamed('textarea', 'Symfony\Component\Form\Extension\Core\Type\TextareaType', ' ');
  2202. $html = $this->renderWidget($form->createView());
  2203. $this->assertContains('> </textarea>', $html);
  2204. }
  2205. public function testTextareaWithWhitespaceOnlyContentRetainsValueWhenRenderingForm()
  2206. {
  2207. $form = $this->factory->createBuilder('Symfony\Component\Form\Extension\Core\Type\FormType', array('textarea' => ' '))
  2208. ->add('textarea', 'Symfony\Component\Form\Extension\Core\Type\TextareaType')
  2209. ->getForm();
  2210. $html = $this->renderForm($form->createView());
  2211. $this->assertContains('> </textarea>', $html);
  2212. }
  2213. public function testWidgetContainerAttributeHiddenIfFalse()
  2214. {
  2215. $form = $this->factory->createNamed('form', 'Symfony\Component\Form\Extension\Core\Type\FormType', null, array(
  2216. 'attr' => array('foo' => false),
  2217. ));
  2218. $html = $this->renderWidget($form->createView());
  2219. // no foo
  2220. $this->assertNotContains('foo="', $html);
  2221. }
  2222. public function testTranslatedAttributes()
  2223. {
  2224. $view = $this->factory->createNamedBuilder('name', 'Symfony\Component\Form\Extension\Core\Type\FormType')
  2225. ->add('firstName', 'Symfony\Component\Form\Extension\Core\Type\TextType', array('attr' => array('title' => 'Foo')))
  2226. ->add('lastName', 'Symfony\Component\Form\Extension\Core\Type\TextType', array('attr' => array('placeholder' => 'Bar')))
  2227. ->getForm()
  2228. ->createView();
  2229. $html = $this->renderForm($view);
  2230. $this->assertMatchesXpath($html, '/form//input[@title="[trans]Foo[/trans]"]');
  2231. $this->assertMatchesXpath($html, '/form//input[@placeholder="[trans]Bar[/trans]"]');
  2232. }
  2233. public function testAttributesNotTranslatedWhenTranslationDomainIsFalse()
  2234. {
  2235. $view = $this->factory->createNamedBuilder('name', 'Symfony\Component\Form\Extension\Core\Type\FormType', null, array(
  2236. 'translation_domain' => false,
  2237. ))
  2238. ->add('firstName', 'Symfony\Component\Form\Extension\Core\Type\TextType', array('attr' => array('title' => 'Foo')))
  2239. ->add('lastName', 'Symfony\Component\Form\Extension\Core\Type\TextType', array('attr' => array('placeholder' => 'Bar')))
  2240. ->getForm()
  2241. ->createView();
  2242. $html = $this->renderForm($view);
  2243. $this->assertMatchesXpath($html, '/form//input[@title="Foo"]');
  2244. $this->assertMatchesXpath($html, '/form//input[@placeholder="Bar"]');
  2245. }
  2246. }