AbstractLayoutTest.php 33 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210
  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\Tests\Component\Form;
  11. use Symfony\Component\Form\FormError;
  12. use Symfony\Component\Form\FormView;
  13. use Symfony\Component\Form\FormFactory;
  14. use Symfony\Component\Form\Extension\Core\CoreExtension;
  15. use Symfony\Component\Form\Extension\Csrf\CsrfExtension;
  16. use Symfony\Component\EventDispatcher\EventDispatcher;
  17. abstract class AbstractLayoutTest extends \PHPUnit_Framework_TestCase
  18. {
  19. protected $csrfProvider;
  20. protected $factory;
  21. protected function setUp()
  22. {
  23. \Locale::setDefault('en');
  24. $this->csrfProvider = $this->getMock('Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface');
  25. $this->factory = new FormFactory(array(
  26. new CoreExtension(),
  27. new CsrfExtension($this->csrfProvider),
  28. ));
  29. }
  30. protected function tearDown()
  31. {
  32. $this->csrfProvider = null;
  33. $this->factory = null;
  34. }
  35. protected function assertXpathNodeValue(\DomElement $element, $expression, $nodeValue)
  36. {
  37. $xpath = new \DOMXPath($element->ownerDocument);
  38. $nodeList = $xpath->evaluate($expression);
  39. $this->assertEquals(1, $nodeList->length);
  40. $this->assertEquals($nodeValue, $nodeList->item(0)->nodeValue);
  41. }
  42. protected function assertMatchesXpath($html, $expression, $count = 1)
  43. {
  44. $dom = new \DomDocument('UTF-8');
  45. try {
  46. // Wrap in <root> node so we can load HTML with multiple tags at
  47. // the top level
  48. $dom->loadXml('<root>'.$html.'</root>');
  49. } catch (\Exception $e) {
  50. return $this->fail(sprintf(
  51. "Failed loading HTML:\n\n%s\n\nError: %s",
  52. $html,
  53. $e->getMessage()
  54. ));
  55. }
  56. $xpath = new \DOMXPath($dom);
  57. $nodeList = $xpath->evaluate('/root'.$expression);
  58. if ($nodeList->length != $count) {
  59. $dom->formatOutput = true;
  60. $this->fail(sprintf(
  61. "Failed asserting that \n\n%s\n\nmatches exactly %s. Matches %s in \n\n%s",
  62. $expression,
  63. $count == 1 ? 'once' : $count . ' times',
  64. $nodeList->length == 1 ? 'once' : $nodeList->length . ' times',
  65. // strip away <root> and </root>
  66. substr($dom->saveHTML(), 6, -8)
  67. ));
  68. }
  69. }
  70. protected function assertWidgetMatchesXpath(FormView $view, array $vars, $xpath)
  71. {
  72. // include ampersands everywhere to validate escaping
  73. $html = $this->renderWidget($view, array_merge(array(
  74. 'id' => 'my&id',
  75. 'attr' => array('class' => 'my&class'),
  76. ), $vars));
  77. $xpath = trim($xpath).'
  78. [@id="my&id"]
  79. [@class="my&class"]';
  80. $this->assertMatchesXpath($html, $xpath);
  81. }
  82. abstract protected function renderEnctype(FormView $view);
  83. abstract protected function renderLabel(FormView $view, $label = null, array $vars = array());
  84. abstract protected function renderErrors(FormView $view);
  85. abstract protected function renderWidget(FormView $view, array $vars = array());
  86. abstract protected function renderRow(FormView $view, array $vars = array());
  87. abstract protected function renderRest(FormView $view, array $vars = array());
  88. public function testEnctype()
  89. {
  90. $form = $this->factory->createNamedBuilder('form', 'na&me', null, array(
  91. 'property_path' => 'name',
  92. ))
  93. ->add('file', 'file')
  94. ->getForm();
  95. $this->assertEquals('enctype="multipart/form-data"', $this->renderEnctype($form->createView()));
  96. }
  97. public function testNoEnctype()
  98. {
  99. $form = $this->factory->createNamedBuilder('form', 'na&me', null, array(
  100. 'property_path' => 'name',
  101. ))
  102. ->add('text', 'text')
  103. ->getForm();
  104. $this->assertEquals('', $this->renderEnctype($form->createView()));
  105. }
  106. public function testLabel()
  107. {
  108. $form = $this->factory->createNamed('text', 'na&me', null, array(
  109. 'property_path' => 'name',
  110. ));
  111. $view = $form->createView();
  112. $this->renderWidget($view, array('label' => 'foo'));
  113. $html = $this->renderLabel($view);
  114. $this->assertMatchesXpath($html,
  115. '/label
  116. [@for="na&me"]
  117. [.="[trans]Na&me[/trans]"]
  118. '
  119. );
  120. }
  121. public function testLabelWithCustomTextPassedAsOption()
  122. {
  123. $form = $this->factory->createNamed('text', 'na&me', null, array(
  124. 'property_path' => 'name',
  125. 'label' => 'Custom label',
  126. ));
  127. $html = $this->renderLabel($form->createView());
  128. $this->assertMatchesXpath($html,
  129. '/label
  130. [@for="na&me"]
  131. [.="[trans]Custom label[/trans]"]
  132. '
  133. );
  134. }
  135. public function testLabelWithCustomTextPassedDirectly()
  136. {
  137. $form = $this->factory->createNamed('text', 'na&me', null, array(
  138. 'property_path' => 'name',
  139. ));
  140. $html = $this->renderLabel($form->createView(), 'Custom label');
  141. $this->assertMatchesXpath($html,
  142. '/label
  143. [@for="na&me"]
  144. [.="[trans]Custom label[/trans]"]
  145. '
  146. );
  147. }
  148. public function testLabelWithCustomTextPassedAsOptionAndDirectly()
  149. {
  150. $form = $this->factory->createNamed('text', 'na&me', null, array(
  151. 'property_path' => 'name',
  152. 'label' => 'Custom label',
  153. ));
  154. $html = $this->renderLabel($form->createView(), 'Overridden label');
  155. $this->assertMatchesXpath($html,
  156. '/label
  157. [@for="na&me"]
  158. [.="[trans]Overridden label[/trans]"]
  159. '
  160. );
  161. }
  162. public function testLabelWithCustomOptionsPassedDirectly()
  163. {
  164. $form = $this->factory->createNamed('text', 'na&me', null, array(
  165. 'property_path' => 'name',
  166. ));
  167. $html = $this->renderLabel($form->createView(), null, array(
  168. 'attr' => array(
  169. 'class' => 'my&class'
  170. ),
  171. ));
  172. $this->assertMatchesXpath($html,
  173. '/label
  174. [@for="na&me"]
  175. [@class="my&class"]
  176. '
  177. );
  178. }
  179. public function testLabelWithCustomTextAndCustomOptionsPassedDirectly()
  180. {
  181. $form = $this->factory->createNamed('text', 'na&me', null, array(
  182. 'property_path' => 'name',
  183. ));
  184. $html = $this->renderLabel($form->createView(), 'Custom label', array(
  185. 'attr' => array(
  186. 'class' => 'my&class'
  187. ),
  188. ));
  189. $this->assertMatchesXpath($html,
  190. '/label
  191. [@for="na&me"]
  192. [@class="my&class"]
  193. [.="[trans]Custom label[/trans]"]
  194. '
  195. );
  196. }
  197. public function testErrors()
  198. {
  199. $form = $this->factory->createNamed('text', 'na&me', null, array(
  200. 'property_path' => 'name',
  201. ));
  202. $form->addError(new FormError('Error 1'));
  203. $form->addError(new FormError('Error 2'));
  204. $view = $form->createView();
  205. $html = $this->renderErrors($view);
  206. $this->assertMatchesXpath($html,
  207. '/ul
  208. [
  209. ./li[.="[trans]Error 1[/trans]"]
  210. /following-sibling::li[.="[trans]Error 2[/trans]"]
  211. ]
  212. [count(./li)=2]
  213. '
  214. );
  215. }
  216. public function testWidgetById()
  217. {
  218. $form = $this->factory->createNamed('text', 'text_id');
  219. $html = $this->renderWidget($form->createView());
  220. $this->assertMatchesXpath($html,
  221. '/div
  222. [
  223. ./input
  224. [@type="text"]
  225. [@id="text_id"]
  226. ]
  227. [@id="container"]
  228. '
  229. );
  230. }
  231. public function testCheckedCheckbox()
  232. {
  233. $form = $this->factory->createNamed('checkbox', 'na&me', true, array(
  234. 'property_path' => 'name',
  235. ));
  236. $this->assertWidgetMatchesXpath($form->createView(), array(),
  237. '/input
  238. [@type="checkbox"]
  239. [@name="na&me"]
  240. [@checked="checked"]
  241. [@value="1"]
  242. '
  243. );
  244. }
  245. public function testCheckedCheckboxWithValue()
  246. {
  247. $form = $this->factory->createNamed('checkbox', 'na&me', true, array(
  248. 'property_path' => 'name',
  249. 'value' => 'foo&bar',
  250. ));
  251. $this->assertWidgetMatchesXpath($form->createView(), array(),
  252. '/input
  253. [@type="checkbox"]
  254. [@name="na&me"]
  255. [@checked="checked"]
  256. [@value="foo&bar"]
  257. '
  258. );
  259. }
  260. public function testUncheckedCheckbox()
  261. {
  262. $form = $this->factory->createNamed('checkbox', 'na&me', false, array(
  263. 'property_path' => 'name',
  264. ));
  265. $this->assertWidgetMatchesXpath($form->createView(), array(),
  266. '/input
  267. [@type="checkbox"]
  268. [@name="na&me"]
  269. [not(@checked)]
  270. '
  271. );
  272. }
  273. public function testSingleChoice()
  274. {
  275. $form = $this->factory->createNamed('choice', 'na&me', '&a', array(
  276. 'property_path' => 'name',
  277. 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B'),
  278. 'multiple' => false,
  279. 'expanded' => false,
  280. ));
  281. $this->assertWidgetMatchesXpath($form->createView(), array(),
  282. '/select
  283. [@name="na&me"]
  284. [
  285. ./option[@value="&a"][@selected="selected"][.="Choice&A"]
  286. /following-sibling::option[@value="&b"][not(@selected)][.="Choice&B"]
  287. ]
  288. [count(./option)=2]
  289. '
  290. );
  291. }
  292. public function testSingleChoiceWithPreferred()
  293. {
  294. $form = $this->factory->createNamed('choice', 'na&me', '&a', array(
  295. 'property_path' => 'name',
  296. 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B'),
  297. 'preferred_choices' => array('&b'),
  298. 'multiple' => false,
  299. 'expanded' => false,
  300. ));
  301. $this->assertWidgetMatchesXpath($form->createView(), array('separator' => '-- sep --'),
  302. '/select
  303. [@name="na&me"]
  304. [
  305. ./option[@value="&b"][not(@selected)][.="Choice&B"]
  306. /following-sibling::option[@disabled="disabled"][not(@selected)][.="-- sep --"]
  307. /following-sibling::option[@value="&a"][@selected="selected"][.="Choice&A"]
  308. ]
  309. [count(./option)=3]
  310. '
  311. );
  312. }
  313. public function testSingleChoiceNonRequired()
  314. {
  315. $form = $this->factory->createNamed('choice', 'na&me', '&a', array(
  316. 'property_path' => 'name',
  317. 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B'),
  318. 'required' => false,
  319. 'multiple' => false,
  320. 'expanded' => false,
  321. ));
  322. $this->assertWidgetMatchesXpath($form->createView(), array(),
  323. '/select
  324. [@name="na&me"]
  325. [
  326. ./option[@value=""][.="[trans][/trans]"]
  327. /following-sibling::option[@value="&a"][@selected="selected"][.="Choice&A"]
  328. /following-sibling::option[@value="&b"][not(@selected)][.="Choice&B"]
  329. ]
  330. [count(./option)=3]
  331. '
  332. );
  333. }
  334. public function testSingleChoiceNonRequiredNoneSelected()
  335. {
  336. $form = $this->factory->createNamed('choice', 'na&me', null, array(
  337. 'property_path' => 'name',
  338. 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B'),
  339. 'required' => false,
  340. 'multiple' => false,
  341. 'expanded' => false,
  342. ));
  343. $this->assertWidgetMatchesXpath($form->createView(), array(),
  344. '/select
  345. [@name="na&me"]
  346. [
  347. ./option[@value=""][not(@selected)][.="[trans][/trans]"]
  348. /following-sibling::option[@value="&a"][not(@selected)][.="Choice&A"]
  349. /following-sibling::option[@value="&b"][not(@selected)][.="Choice&B"]
  350. ]
  351. [count(./option)=3]
  352. '
  353. );
  354. }
  355. public function testSingleChoiceWithNonRequiredEmptyValue()
  356. {
  357. $form = $this->factory->createNamed('choice', 'na&me', '&a', array(
  358. 'property_path' => 'name',
  359. 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B'),
  360. 'multiple' => false,
  361. 'expanded' => false,
  362. 'required' => false,
  363. 'empty_value' => 'Select&Anything&Not&Me',
  364. ));
  365. $this->assertWidgetMatchesXpath($form->createView(), array(),
  366. '/select
  367. [@name="na&me"]
  368. [
  369. ./option[@value=""][not(@selected)][.="[trans]Select&Anything&Not&Me[/trans]"]
  370. /following-sibling::option[@value="&a"][@selected="selected"][.="Choice&A"]
  371. /following-sibling::option[@value="&b"][not(@selected)][.="Choice&B"]
  372. ]
  373. [count(./option)=3]
  374. '
  375. );
  376. }
  377. public function testSingleChoiceRequiredWithEmptyValueViaView()
  378. {
  379. $form = $this->factory->createNamed('choice', 'na&me', '&a', array(
  380. 'property_path' => 'name',
  381. 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B'),
  382. 'required' => true,
  383. 'multiple' => false,
  384. 'expanded' => false,
  385. ));
  386. $this->assertWidgetMatchesXpath($form->createView(), array('empty_value' => ''),
  387. '/select
  388. [@name="na&me"]
  389. [
  390. ./option[@value=""][.="[trans][/trans]"]
  391. /following-sibling::option[@value="&a"][@selected="selected"][.="Choice&A"]
  392. /following-sibling::option[@value="&b"][not(@selected)][.="Choice&B"]
  393. ]
  394. [count(./option)=3]
  395. '
  396. );
  397. }
  398. public function testSingleChoiceGrouped()
  399. {
  400. $form = $this->factory->createNamed('choice', 'na&me', '&a', array(
  401. 'property_path' => 'name',
  402. 'choices' => array(
  403. 'Group&1' => array('&a' => 'Choice&A', '&b' => 'Choice&B'),
  404. 'Group&2' => array('&c' => 'Choice&C'),
  405. ),
  406. 'multiple' => false,
  407. 'expanded' => false,
  408. ));
  409. $this->assertWidgetMatchesXpath($form->createView(), array(),
  410. '/select
  411. [@name="na&me"]
  412. [./optgroup[@label="Group&1"]
  413. [
  414. ./option[@value="&a"][@selected="selected"][.="Choice&A"]
  415. /following-sibling::option[@value="&b"][not(@selected)][.="Choice&B"]
  416. ]
  417. [count(./option)=2]
  418. ]
  419. [./optgroup[@label="Group&2"]
  420. [./option[@value="&c"][not(@selected)][.="Choice&C"]]
  421. [count(./option)=1]
  422. ]
  423. [count(./optgroup)=2]
  424. '
  425. );
  426. }
  427. public function testMultipleChoice()
  428. {
  429. $form = $this->factory->createNamed('choice', 'na&me', array('&a'), array(
  430. 'property_path' => 'name',
  431. 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B'),
  432. 'multiple' => true,
  433. 'expanded' => false,
  434. ));
  435. $this->assertWidgetMatchesXpath($form->createView(), array(),
  436. '/select
  437. [@name="na&me[]"]
  438. [@multiple="multiple"]
  439. [
  440. ./option[@value="&a"][@selected="selected"][.="Choice&A"]
  441. /following-sibling::option[@value="&b"][not(@selected)][.="Choice&B"]
  442. ]
  443. [count(./option)=2]
  444. '
  445. );
  446. }
  447. public function testMultipleChoiceNonRequired()
  448. {
  449. $form = $this->factory->createNamed('choice', 'na&me', array('&a'), array(
  450. 'property_path' => 'name',
  451. 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B'),
  452. 'required' => false,
  453. 'multiple' => true,
  454. 'expanded' => false,
  455. ));
  456. $this->assertWidgetMatchesXpath($form->createView(), array(),
  457. '/select
  458. [@name="na&me[]"]
  459. [@multiple="multiple"]
  460. [
  461. ./option[@value="&a"][@selected="selected"][.="Choice&A"]
  462. /following-sibling::option[@value="&b"][not(@selected)][.="Choice&B"]
  463. ]
  464. [count(./option)=2]
  465. '
  466. );
  467. }
  468. public function testSingleChoiceExpanded()
  469. {
  470. $form = $this->factory->createNamed('choice', 'na&me', '&a', array(
  471. 'property_path' => 'name',
  472. 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B'),
  473. 'multiple' => false,
  474. 'expanded' => true,
  475. ));
  476. $this->assertWidgetMatchesXpath($form->createView(), array(),
  477. '/div
  478. [
  479. ./input[@type="radio"][@name="na&me"][@id="na&me_&a"][@checked]
  480. /following-sibling::label[@for="na&me_&a"][.="[trans]Choice&A[/trans]"]
  481. /following-sibling::input[@type="radio"][@name="na&me"][@id="na&me_&b"][not(@checked)]
  482. /following-sibling::label[@for="na&me_&b"][.="[trans]Choice&B[/trans]"]
  483. ]
  484. [count(./input)=2]
  485. '
  486. );
  487. }
  488. public function testSingleChoiceExpandedWithBooleanValue()
  489. {
  490. $form = $this->factory->createNamed('choice', 'na&me', true, array(
  491. 'property_path' => 'name',
  492. 'choices' => array('1' => 'Choice&A', '0' => 'Choice&B'),
  493. 'multiple' => false,
  494. 'expanded' => true,
  495. ));
  496. $this->assertWidgetMatchesXpath($form->createView(), array(),
  497. '/div
  498. [
  499. ./input[@type="radio"][@name="na&me"][@id="na&me_1"][@checked]
  500. /following-sibling::label[@for="na&me_1"][.="[trans]Choice&A[/trans]"]
  501. /following-sibling::input[@type="radio"][@name="na&me"][@id="na&me_0"][not(@checked)]
  502. /following-sibling::label[@for="na&me_0"][.="[trans]Choice&B[/trans]"]
  503. ]
  504. [count(./input)=2]
  505. '
  506. );
  507. }
  508. public function testMultipleChoiceExpanded()
  509. {
  510. $form = $this->factory->createNamed('choice', 'na&me', array('&a', '&c'), array(
  511. 'property_path' => 'name',
  512. 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B', '&c' => 'Choice&C'),
  513. 'multiple' => true,
  514. 'expanded' => true,
  515. 'required' => true,
  516. ));
  517. $this->assertWidgetMatchesXpath($form->createView(), array(),
  518. '/div
  519. [
  520. ./input[@type="checkbox"][@name="na&me[&a]"][@id="na&me_&a"][@checked][not(@required)]
  521. /following-sibling::label[@for="na&me_&a"][.="[trans]Choice&A[/trans]"]
  522. /following-sibling::input[@type="checkbox"][@name="na&me[&b]"][@id="na&me_&b"][not(@checked)][not(@required)]
  523. /following-sibling::label[@for="na&me_&b"][.="[trans]Choice&B[/trans]"]
  524. /following-sibling::input[@type="checkbox"][@name="na&me[&c]"][@id="na&me_&c"][@checked][not(@required)]
  525. /following-sibling::label[@for="na&me_&c"][.="[trans]Choice&C[/trans]"]
  526. ]
  527. [count(./input)=3]
  528. '
  529. );
  530. }
  531. public function testCountry()
  532. {
  533. $form = $this->factory->createNamed('country', 'na&me', 'AT', array(
  534. 'property_path' => 'name',
  535. ));
  536. $this->assertWidgetMatchesXpath($form->createView(), array(),
  537. '/select
  538. [@name="na&me"]
  539. [./option[@value="AT"][@selected="selected"][.="Austria"]]
  540. [count(./option)>200]
  541. '
  542. );
  543. }
  544. public function testCsrf()
  545. {
  546. $this->csrfProvider->expects($this->any())
  547. ->method('generateCsrfToken')
  548. ->will($this->returnValue('foo&bar'));
  549. $form = $this->factory->createNamed('csrf', 'na&me', null, array(
  550. 'property_path' => 'name',
  551. ));
  552. $this->assertWidgetMatchesXpath($form->createView(), array(),
  553. '/input
  554. [@type="hidden"]
  555. [@value="foo&bar"]
  556. '
  557. );
  558. }
  559. public function testDateTime()
  560. {
  561. $form = $this->factory->createNamed('datetime', 'na&me', '2011-02-03 04:05:06', array(
  562. 'property_path' => 'name',
  563. 'input' => 'string',
  564. 'with_seconds' => false,
  565. ));
  566. $this->assertWidgetMatchesXpath($form->createView(), array(),
  567. '/div
  568. [
  569. ./div
  570. [@id="na&me_date"]
  571. [
  572. ./select
  573. [@id="na&me_date_month"]
  574. [./option[@value="2"][@selected="selected"]]
  575. /following-sibling::select
  576. [@id="na&me_date_day"]
  577. [./option[@value="3"][@selected="selected"]]
  578. /following-sibling::select
  579. [@id="na&me_date_year"]
  580. [./option[@value="2011"][@selected="selected"]]
  581. ]
  582. /following-sibling::div
  583. [@id="na&me_time"]
  584. [
  585. ./select
  586. [@id="na&me_time_hour"]
  587. [./option[@value="4"][@selected="selected"]]
  588. /following-sibling::select
  589. [@id="na&me_time_minute"]
  590. [./option[@value="5"][@selected="selected"]]
  591. ]
  592. ]
  593. [count(.//select)=5]
  594. '
  595. );
  596. }
  597. public function testDateTimeWithSeconds()
  598. {
  599. $form = $this->factory->createNamed('datetime', 'na&me', '2011-02-03 04:05:06', array(
  600. 'property_path' => 'name',
  601. 'input' => 'string',
  602. 'with_seconds' => true,
  603. ));
  604. $this->assertWidgetMatchesXpath($form->createView(), array(),
  605. '/div
  606. [
  607. ./div
  608. [@id="na&me_date"]
  609. [
  610. ./select
  611. [@id="na&me_date_month"]
  612. [./option[@value="2"][@selected="selected"]]
  613. /following-sibling::select
  614. [@id="na&me_date_day"]
  615. [./option[@value="3"][@selected="selected"]]
  616. /following-sibling::select
  617. [@id="na&me_date_year"]
  618. [./option[@value="2011"][@selected="selected"]]
  619. ]
  620. /following-sibling::div
  621. [@id="na&me_time"]
  622. [
  623. ./select
  624. [@id="na&me_time_hour"]
  625. [./option[@value="4"][@selected="selected"]]
  626. /following-sibling::select
  627. [@id="na&me_time_minute"]
  628. [./option[@value="5"][@selected="selected"]]
  629. /following-sibling::select
  630. [@id="na&me_time_second"]
  631. [./option[@value="6"][@selected="selected"]]
  632. ]
  633. ]
  634. [count(.//select)=6]
  635. '
  636. );
  637. }
  638. public function testDateChoice()
  639. {
  640. $form = $this->factory->createNamed('date', 'na&me', '2011-02-03', array(
  641. 'property_path' => 'name',
  642. 'input' => 'string',
  643. 'widget' => 'choice',
  644. ));
  645. $this->assertWidgetMatchesXpath($form->createView(), array(),
  646. '/div
  647. [
  648. ./select
  649. [@id="na&me_month"]
  650. [./option[@value="2"][@selected="selected"]]
  651. /following-sibling::select
  652. [@id="na&me_day"]
  653. [./option[@value="3"][@selected="selected"]]
  654. /following-sibling::select
  655. [@id="na&me_year"]
  656. [./option[@value="2011"][@selected="selected"]]
  657. ]
  658. [count(./select)=3]
  659. '
  660. );
  661. }
  662. public function testDateText()
  663. {
  664. $form = $this->factory->createNamed('date', 'na&me', '2011-02-03', array(
  665. 'property_path' => 'name',
  666. 'input' => 'string',
  667. 'widget' => 'text',
  668. ));
  669. $this->assertWidgetMatchesXpath($form->createView(), array(),
  670. '/div
  671. [
  672. ./input
  673. [@id="na&me_month"]
  674. [@type="text"]
  675. [@value="2"]
  676. /following-sibling::input
  677. [@id="na&me_day"]
  678. [@type="text"]
  679. [@value="3"]
  680. /following-sibling::input
  681. [@id="na&me_year"]
  682. [@type="text"]
  683. [@value="2011"]
  684. ]
  685. [count(./input)=3]
  686. '
  687. );
  688. }
  689. public function testDateSingleText()
  690. {
  691. $form = $this->factory->createNamed('date', 'na&me', '2011-02-03', array(
  692. 'property_path' => 'name',
  693. 'input' => 'string',
  694. 'widget' => 'single_text',
  695. ));
  696. $this->assertWidgetMatchesXpath($form->createView(), array(),
  697. '/input
  698. [@type="text"]
  699. [@name="na&me"]
  700. [@value="Feb 3, 2011"]
  701. '
  702. );
  703. }
  704. public function testEmail()
  705. {
  706. $form = $this->factory->createNamed('email', 'na&me', 'foo&bar', array(
  707. 'property_path' => 'name',
  708. ));
  709. $this->assertWidgetMatchesXpath($form->createView(), array(),
  710. '/input
  711. [@type="email"]
  712. [@name="na&me"]
  713. [@value="foo&bar"]
  714. [not(@maxlength)]
  715. '
  716. );
  717. }
  718. public function testEmailWithMaxLength()
  719. {
  720. $form = $this->factory->createNamed('email', 'na&me', 'foo&bar', array(
  721. 'property_path' => 'name',
  722. 'max_length' => 123,
  723. ));
  724. $this->assertWidgetMatchesXpath($form->createView(), array(),
  725. '/input
  726. [@type="email"]
  727. [@name="na&me"]
  728. [@value="foo&bar"]
  729. [@maxlength="123"]
  730. '
  731. );
  732. }
  733. public function testFile()
  734. {
  735. $form = $this->factory->createNamed('file', 'na&me', null, array(
  736. 'property_path' => 'name',
  737. ));
  738. $this->assertWidgetMatchesXpath($form->createView(), array(),
  739. '/input
  740. [@type="file"]
  741. '
  742. );
  743. }
  744. public function testHidden()
  745. {
  746. $form = $this->factory->createNamed('hidden', 'na&me', 'foo&bar', array(
  747. 'property_path' => 'name',
  748. ));
  749. $this->assertWidgetMatchesXpath($form->createView(), array(),
  750. '/input
  751. [@type="hidden"]
  752. [@name="na&me"]
  753. [@value="foo&bar"]
  754. '
  755. );
  756. }
  757. public function testInteger()
  758. {
  759. $form = $this->factory->createNamed('integer', 'na&me', 123, array(
  760. 'property_path' => 'name',
  761. ));
  762. $this->assertWidgetMatchesXpath($form->createView(), array(),
  763. '/input
  764. [@type="number"]
  765. [@name="na&me"]
  766. [@value="123"]
  767. '
  768. );
  769. }
  770. public function testLanguage()
  771. {
  772. $form = $this->factory->createNamed('language', 'na&me', 'de', array(
  773. 'property_path' => 'name',
  774. ));
  775. $this->assertWidgetMatchesXpath($form->createView(), array(),
  776. '/select
  777. [@name="na&me"]
  778. [./option[@value="de"][@selected="selected"][.="German"]]
  779. [count(./option)>200]
  780. '
  781. );
  782. }
  783. public function testLocale()
  784. {
  785. $form = $this->factory->createNamed('locale', 'na&me', 'de_AT', array(
  786. 'property_path' => 'name',
  787. ));
  788. $this->assertWidgetMatchesXpath($form->createView(), array(),
  789. '/select
  790. [@name="na&me"]
  791. [./option[@value="de_AT"][@selected="selected"][.="German (Austria)"]]
  792. [count(./option)>200]
  793. '
  794. );
  795. }
  796. public function testMoney()
  797. {
  798. $form = $this->factory->createNamed('money', 'na&me', 1234.56, array(
  799. 'property_path' => 'name',
  800. 'currency' => 'EUR',
  801. ));
  802. $this->assertWidgetMatchesXpath($form->createView(), array(),
  803. '/input
  804. [@type="text"]
  805. [@name="na&me"]
  806. [@value="1234.56"]
  807. [contains(.., "€")]
  808. '
  809. );
  810. }
  811. public function testNumber()
  812. {
  813. $form = $this->factory->createNamed('number', 'na&me', 1234.56, array(
  814. 'property_path' => 'name',
  815. ));
  816. $this->assertWidgetMatchesXpath($form->createView(), array(),
  817. '/input
  818. [@type="text"]
  819. [@name="na&me"]
  820. [@value="1234.56"]
  821. '
  822. );
  823. }
  824. public function testPassword()
  825. {
  826. $form = $this->factory->createNamed('password', 'na&me', 'foo&bar', array(
  827. 'property_path' => 'name',
  828. ));
  829. $this->assertWidgetMatchesXpath($form->createView(), array(),
  830. '/input
  831. [@type="password"]
  832. [@name="na&me"]
  833. [@value=""]
  834. '
  835. );
  836. }
  837. public function testPasswordBoundNotAlwaysEmpty()
  838. {
  839. $form = $this->factory->createNamed('password', 'na&me', null, array(
  840. 'property_path' => 'name',
  841. 'always_empty' => false,
  842. ));
  843. $form->bind('foo&bar');
  844. $this->assertWidgetMatchesXpath($form->createView(), array(),
  845. '/input
  846. [@type="password"]
  847. [@name="na&me"]
  848. [@value="foo&bar"]
  849. '
  850. );
  851. }
  852. public function testPasswordWithMaxLength()
  853. {
  854. $form = $this->factory->createNamed('password', 'na&me', 'foo&bar', array(
  855. 'property_path' => 'name',
  856. 'max_length' => 123,
  857. ));
  858. $this->assertWidgetMatchesXpath($form->createView(), array(),
  859. '/input
  860. [@type="password"]
  861. [@name="na&me"]
  862. [@value=""]
  863. [@maxlength="123"]
  864. '
  865. );
  866. }
  867. public function testPercent()
  868. {
  869. $form = $this->factory->createNamed('percent', 'na&me', 0.1, array(
  870. 'property_path' => 'name',
  871. ));
  872. $this->assertWidgetMatchesXpath($form->createView(), array(),
  873. '/input
  874. [@type="text"]
  875. [@name="na&me"]
  876. [@value="10"]
  877. [contains(.., "%")]
  878. '
  879. );
  880. }
  881. public function testCheckedRadio()
  882. {
  883. $form = $this->factory->createNamed('radio', 'na&me', true, array(
  884. 'property_path' => 'name',
  885. ));
  886. $this->assertWidgetMatchesXpath($form->createView(), array(),
  887. '/input
  888. [@type="radio"]
  889. [@name="na&me"]
  890. [@checked="checked"]
  891. [@value=""]
  892. '
  893. );
  894. }
  895. public function testCheckedRadioWithValue()
  896. {
  897. $form = $this->factory->createNamed('radio', 'na&me', true, array(
  898. 'property_path' => 'name',
  899. 'value' => 'foo&bar',
  900. ));
  901. $this->assertWidgetMatchesXpath($form->createView(), array(),
  902. '/input
  903. [@type="radio"]
  904. [@name="na&me"]
  905. [@checked="checked"]
  906. [@value="foo&bar"]
  907. '
  908. );
  909. }
  910. public function testUncheckedRadio()
  911. {
  912. $form = $this->factory->createNamed('radio', 'na&me', false, array(
  913. 'property_path' => 'name',
  914. ));
  915. $this->assertWidgetMatchesXpath($form->createView(), array(),
  916. '/input
  917. [@type="radio"]
  918. [@name="na&me"]
  919. [not(@checked)]
  920. '
  921. );
  922. }
  923. public function testTextarea()
  924. {
  925. $form = $this->factory->createNamed('textarea', 'na&me', 'foo&bar', array(
  926. 'property_path' => 'name',
  927. ));
  928. $this->assertWidgetMatchesXpath($form->createView(), array(),
  929. '/textarea
  930. [@name="na&me"]
  931. [.="foo&bar"]
  932. '
  933. );
  934. }
  935. public function testText()
  936. {
  937. $form = $this->factory->createNamed('text', 'na&me', 'foo&bar', array(
  938. 'property_path' => 'name',
  939. ));
  940. $this->assertWidgetMatchesXpath($form->createView(), array(),
  941. '/input
  942. [@type="text"]
  943. [@name="na&me"]
  944. [@value="foo&bar"]
  945. [not(@maxlength)]
  946. '
  947. );
  948. }
  949. public function testTextWithMaxLength()
  950. {
  951. $form = $this->factory->createNamed('text', 'na&me', 'foo&bar', array(
  952. 'property_path' => 'name',
  953. 'max_length' => 123,
  954. ));
  955. $this->assertWidgetMatchesXpath($form->createView(), array(),
  956. '/input
  957. [@type="text"]
  958. [@name="na&me"]
  959. [@value="foo&bar"]
  960. [@maxlength="123"]
  961. '
  962. );
  963. }
  964. public function testSearch()
  965. {
  966. $form = $this->factory->createNamed('search', 'na&me', 'foo&bar', array(
  967. 'property_path' => 'name',
  968. ));
  969. $this->assertWidgetMatchesXpath($form->createView(), array(),
  970. '/input
  971. [@type="search"]
  972. [@name="na&me"]
  973. [@value="foo&bar"]
  974. [not(@maxlength)]
  975. '
  976. );
  977. }
  978. public function testTime()
  979. {
  980. $form = $this->factory->createNamed('time', 'na&me', '04:05:06', array(
  981. 'property_path' => 'name',
  982. 'input' => 'string',
  983. 'with_seconds' => false,
  984. ));
  985. $this->assertWidgetMatchesXpath($form->createView(), array(),
  986. '/div
  987. [
  988. ./select
  989. [@id="na&me_hour"]
  990. [@size="1"]
  991. [./option[@value="4"][@selected="selected"]]
  992. /following-sibling::select
  993. [@id="na&me_minute"]
  994. [@size="1"]
  995. [./option[@value="5"][@selected="selected"]]
  996. ]
  997. [count(./select)=2]
  998. '
  999. );
  1000. }
  1001. public function testTimeWithSeconds()
  1002. {
  1003. $form = $this->factory->createNamed('time', 'na&me', '04:05:06', array(
  1004. 'property_path' => 'name',
  1005. 'input' => 'string',
  1006. 'with_seconds' => true,
  1007. ));
  1008. $this->assertWidgetMatchesXpath($form->createView(), array(),
  1009. '/div
  1010. [
  1011. ./select
  1012. [@id="na&me_hour"]
  1013. [@size="1"]
  1014. [./option[@value="4"][@selected="selected"]]
  1015. /following-sibling::select
  1016. [@id="na&me_minute"]
  1017. [@size="1"]
  1018. [./option[@value="5"][@selected="selected"]]
  1019. /following-sibling::select
  1020. [@id="na&me_second"]
  1021. [@size="1"]
  1022. [./option[@value="6"][@selected="selected"]]
  1023. ]
  1024. [count(./select)=3]
  1025. '
  1026. );
  1027. }
  1028. public function testTimezone()
  1029. {
  1030. $form = $this->factory->createNamed('timezone', 'na&me', 'Europe/Vienna', array(
  1031. 'property_path' => 'name',
  1032. ));
  1033. $this->assertWidgetMatchesXpath($form->createView(), array(),
  1034. '/select
  1035. [@name="na&me"]
  1036. [./optgroup
  1037. [@label="Europe"]
  1038. [./option[@value="Europe/Vienna"][@selected="selected"][.="Vienna"]]
  1039. ]
  1040. [count(./optgroup)>10]
  1041. [count(.//option)>200]
  1042. '
  1043. );
  1044. }
  1045. public function testUrl()
  1046. {
  1047. $url = 'http://www.google.com?foo1=bar1&foo2=bar2';
  1048. $form = $this->factory->createNamed('url', 'na&me', $url, array(
  1049. 'property_path' => 'name',
  1050. ));
  1051. $this->assertWidgetMatchesXpath($form->createView(), array(),
  1052. '/input
  1053. [@type="url"]
  1054. [@name="na&me"]
  1055. [@value="http://www.google.com?foo1=bar1&foo2=bar2"]
  1056. '
  1057. );
  1058. }
  1059. public function testCollectionPrototype()
  1060. {
  1061. $form = $this->factory->createNamedBuilder('form', 'na&me', array('items' => array('one', 'two', 'three')))
  1062. ->add('items', 'collection', array('allow_add' => true))
  1063. ->getForm()
  1064. ->createView();
  1065. $html = $this->renderWidget($form);
  1066. $this->assertMatchesXpath($html,
  1067. '//script
  1068. [@id="na&me_items_prototype"]
  1069. [@type="text/html"]
  1070. '
  1071. );
  1072. }
  1073. }