AbstractLayoutTest.php 31 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163
  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 testSingleChoiceRequiredWithEmptyValue()
  335. {
  336. $form = $this->factory->createNamed('choice', 'na&me', '&a', array(
  337. 'property_path' => 'name',
  338. 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B'),
  339. 'required' => true,
  340. 'multiple' => false,
  341. 'expanded' => false,
  342. ));
  343. $this->assertWidgetMatchesXpath($form->createView(), array('empty_value' => ''),
  344. '/select
  345. [@name="na&me"]
  346. [
  347. ./option[@value=""][.="[trans][/trans]"]
  348. /following-sibling::option[@value="&a"][@selected="selected"][.="Choice&A"]
  349. /following-sibling::option[@value="&b"][not(@selected)][.="Choice&B"]
  350. ]
  351. [count(./option)=3]
  352. '
  353. );
  354. }
  355. public function testSingleChoiceGrouped()
  356. {
  357. $form = $this->factory->createNamed('choice', 'na&me', '&a', array(
  358. 'property_path' => 'name',
  359. 'choices' => array(
  360. 'Group&1' => array('&a' => 'Choice&A', '&b' => 'Choice&B'),
  361. 'Group&2' => array('&c' => 'Choice&C'),
  362. ),
  363. 'multiple' => false,
  364. 'expanded' => false,
  365. ));
  366. $this->assertWidgetMatchesXpath($form->createView(), array(),
  367. '/select
  368. [@name="na&me"]
  369. [./optgroup[@label="Group&1"]
  370. [
  371. ./option[@value="&a"][@selected="selected"][.="Choice&A"]
  372. /following-sibling::option[@value="&b"][not(@selected)][.="Choice&B"]
  373. ]
  374. [count(./option)=2]
  375. ]
  376. [./optgroup[@label="Group&2"]
  377. [./option[@value="&c"][not(@selected)][.="Choice&C"]]
  378. [count(./option)=1]
  379. ]
  380. [count(./optgroup)=2]
  381. '
  382. );
  383. }
  384. public function testMultipleChoice()
  385. {
  386. $form = $this->factory->createNamed('choice', 'na&me', array('&a'), array(
  387. 'property_path' => 'name',
  388. 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B'),
  389. 'multiple' => true,
  390. 'expanded' => false,
  391. ));
  392. $this->assertWidgetMatchesXpath($form->createView(), array(),
  393. '/select
  394. [@name="na&me[]"]
  395. [@multiple="multiple"]
  396. [
  397. ./option[@value="&a"][@selected="selected"][.="Choice&A"]
  398. /following-sibling::option[@value="&b"][not(@selected)][.="Choice&B"]
  399. ]
  400. [count(./option)=2]
  401. '
  402. );
  403. }
  404. public function testMultipleChoiceNonRequired()
  405. {
  406. $form = $this->factory->createNamed('choice', 'na&me', array('&a'), array(
  407. 'property_path' => 'name',
  408. 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B'),
  409. 'required' => false,
  410. 'multiple' => true,
  411. 'expanded' => false,
  412. ));
  413. $this->assertWidgetMatchesXpath($form->createView(), array(),
  414. '/select
  415. [@name="na&me[]"]
  416. [@multiple="multiple"]
  417. [
  418. ./option[@value="&a"][@selected="selected"][.="Choice&A"]
  419. /following-sibling::option[@value="&b"][not(@selected)][.="Choice&B"]
  420. ]
  421. [count(./option)=2]
  422. '
  423. );
  424. }
  425. public function testSingleChoiceExpanded()
  426. {
  427. $form = $this->factory->createNamed('choice', 'na&me', '&a', array(
  428. 'property_path' => 'name',
  429. 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B'),
  430. 'multiple' => false,
  431. 'expanded' => true,
  432. ));
  433. $this->assertWidgetMatchesXpath($form->createView(), array(),
  434. '/div
  435. [
  436. ./input[@type="radio"][@name="na&me"][@id="na&me_&a"][@checked]
  437. /following-sibling::label[@for="na&me_&a"][.="[trans]Choice&A[/trans]"]
  438. /following-sibling::input[@type="radio"][@name="na&me"][@id="na&me_&b"][not(@checked)]
  439. /following-sibling::label[@for="na&me_&b"][.="[trans]Choice&B[/trans]"]
  440. ]
  441. [count(./input)=2]
  442. '
  443. );
  444. }
  445. public function testSingleChoiceExpandedWithBooleanValue()
  446. {
  447. $form = $this->factory->createNamed('choice', 'na&me', true, array(
  448. 'property_path' => 'name',
  449. 'choices' => array('1' => 'Choice&A', '0' => 'Choice&B'),
  450. 'multiple' => false,
  451. 'expanded' => true,
  452. ));
  453. $this->assertWidgetMatchesXpath($form->createView(), array(),
  454. '/div
  455. [
  456. ./input[@type="radio"][@name="na&me"][@id="na&me_1"][@checked]
  457. /following-sibling::label[@for="na&me_1"][.="[trans]Choice&A[/trans]"]
  458. /following-sibling::input[@type="radio"][@name="na&me"][@id="na&me_0"][not(@checked)]
  459. /following-sibling::label[@for="na&me_0"][.="[trans]Choice&B[/trans]"]
  460. ]
  461. [count(./input)=2]
  462. '
  463. );
  464. }
  465. public function testMultipleChoiceExpanded()
  466. {
  467. $form = $this->factory->createNamed('choice', 'na&me', array('&a', '&c'), array(
  468. 'property_path' => 'name',
  469. 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B', '&c' => 'Choice&C'),
  470. 'multiple' => true,
  471. 'expanded' => true,
  472. 'required' => true,
  473. ));
  474. $this->assertWidgetMatchesXpath($form->createView(), array(),
  475. '/div
  476. [
  477. ./input[@type="checkbox"][@name="na&me[&a]"][@id="na&me_&a"][@checked][not(@required)]
  478. /following-sibling::label[@for="na&me_&a"][.="[trans]Choice&A[/trans]"]
  479. /following-sibling::input[@type="checkbox"][@name="na&me[&b]"][@id="na&me_&b"][not(@checked)][not(@required)]
  480. /following-sibling::label[@for="na&me_&b"][.="[trans]Choice&B[/trans]"]
  481. /following-sibling::input[@type="checkbox"][@name="na&me[&c]"][@id="na&me_&c"][@checked][not(@required)]
  482. /following-sibling::label[@for="na&me_&c"][.="[trans]Choice&C[/trans]"]
  483. ]
  484. [count(./input)=3]
  485. '
  486. );
  487. }
  488. public function testCountry()
  489. {
  490. $form = $this->factory->createNamed('country', 'na&me', 'AT', array(
  491. 'property_path' => 'name',
  492. ));
  493. $this->assertWidgetMatchesXpath($form->createView(), array(),
  494. '/select
  495. [@name="na&me"]
  496. [./option[@value="AT"][@selected="selected"][.="Austria"]]
  497. [count(./option)>200]
  498. '
  499. );
  500. }
  501. public function testCsrf()
  502. {
  503. $this->csrfProvider->expects($this->any())
  504. ->method('generateCsrfToken')
  505. ->will($this->returnValue('foo&bar'));
  506. $form = $this->factory->createNamed('csrf', 'na&me', null, array(
  507. 'property_path' => 'name',
  508. ));
  509. $this->assertWidgetMatchesXpath($form->createView(), array(),
  510. '/input
  511. [@type="hidden"]
  512. [@value="foo&bar"]
  513. '
  514. );
  515. }
  516. public function testDateTime()
  517. {
  518. $form = $this->factory->createNamed('datetime', 'na&me', '2011-02-03 04:05:06', array(
  519. 'property_path' => 'name',
  520. 'input' => 'string',
  521. 'with_seconds' => false,
  522. ));
  523. $this->assertWidgetMatchesXpath($form->createView(), array(),
  524. '/div
  525. [
  526. ./div
  527. [@id="na&me_date"]
  528. [
  529. ./select
  530. [@id="na&me_date_month"]
  531. [./option[@value="2"][@selected="selected"]]
  532. /following-sibling::select
  533. [@id="na&me_date_day"]
  534. [./option[@value="3"][@selected="selected"]]
  535. /following-sibling::select
  536. [@id="na&me_date_year"]
  537. [./option[@value="2011"][@selected="selected"]]
  538. ]
  539. /following-sibling::div
  540. [@id="na&me_time"]
  541. [
  542. ./select
  543. [@id="na&me_time_hour"]
  544. [./option[@value="4"][@selected="selected"]]
  545. /following-sibling::select
  546. [@id="na&me_time_minute"]
  547. [./option[@value="5"][@selected="selected"]]
  548. ]
  549. ]
  550. [count(.//select)=5]
  551. '
  552. );
  553. }
  554. public function testDateTimeWithSeconds()
  555. {
  556. $form = $this->factory->createNamed('datetime', 'na&me', '2011-02-03 04:05:06', array(
  557. 'property_path' => 'name',
  558. 'input' => 'string',
  559. 'with_seconds' => true,
  560. ));
  561. $this->assertWidgetMatchesXpath($form->createView(), array(),
  562. '/div
  563. [
  564. ./div
  565. [@id="na&me_date"]
  566. [
  567. ./select
  568. [@id="na&me_date_month"]
  569. [./option[@value="2"][@selected="selected"]]
  570. /following-sibling::select
  571. [@id="na&me_date_day"]
  572. [./option[@value="3"][@selected="selected"]]
  573. /following-sibling::select
  574. [@id="na&me_date_year"]
  575. [./option[@value="2011"][@selected="selected"]]
  576. ]
  577. /following-sibling::div
  578. [@id="na&me_time"]
  579. [
  580. ./select
  581. [@id="na&me_time_hour"]
  582. [./option[@value="4"][@selected="selected"]]
  583. /following-sibling::select
  584. [@id="na&me_time_minute"]
  585. [./option[@value="5"][@selected="selected"]]
  586. /following-sibling::select
  587. [@id="na&me_time_second"]
  588. [./option[@value="6"][@selected="selected"]]
  589. ]
  590. ]
  591. [count(.//select)=6]
  592. '
  593. );
  594. }
  595. public function testDateChoice()
  596. {
  597. $form = $this->factory->createNamed('date', 'na&me', '2011-02-03', array(
  598. 'property_path' => 'name',
  599. 'input' => 'string',
  600. 'widget' => 'choice',
  601. ));
  602. $this->assertWidgetMatchesXpath($form->createView(), array(),
  603. '/div
  604. [
  605. ./select
  606. [@id="na&me_month"]
  607. [./option[@value="2"][@selected="selected"]]
  608. /following-sibling::select
  609. [@id="na&me_day"]
  610. [./option[@value="3"][@selected="selected"]]
  611. /following-sibling::select
  612. [@id="na&me_year"]
  613. [./option[@value="2011"][@selected="selected"]]
  614. ]
  615. [count(./select)=3]
  616. '
  617. );
  618. }
  619. public function testDateText()
  620. {
  621. $form = $this->factory->createNamed('date', 'na&me', '2011-02-03', array(
  622. 'property_path' => 'name',
  623. 'input' => 'string',
  624. 'widget' => 'text',
  625. ));
  626. $this->assertWidgetMatchesXpath($form->createView(), array(),
  627. '/div
  628. [
  629. ./input
  630. [@id="na&me_month"]
  631. [@type="text"]
  632. [@value="2"]
  633. /following-sibling::input
  634. [@id="na&me_day"]
  635. [@type="text"]
  636. [@value="3"]
  637. /following-sibling::input
  638. [@id="na&me_year"]
  639. [@type="text"]
  640. [@value="2011"]
  641. ]
  642. [count(./input)=3]
  643. '
  644. );
  645. }
  646. public function testDateSingleText()
  647. {
  648. $form = $this->factory->createNamed('date', 'na&me', '2011-02-03', array(
  649. 'property_path' => 'name',
  650. 'input' => 'string',
  651. 'widget' => 'single_text',
  652. ));
  653. $this->assertWidgetMatchesXpath($form->createView(), array(),
  654. '/input
  655. [@type="text"]
  656. [@name="na&me"]
  657. [@value="Feb 3, 2011"]
  658. '
  659. );
  660. }
  661. public function testEmail()
  662. {
  663. $form = $this->factory->createNamed('email', 'na&me', 'foo&bar', array(
  664. 'property_path' => 'name',
  665. ));
  666. $this->assertWidgetMatchesXpath($form->createView(), array(),
  667. '/input
  668. [@type="email"]
  669. [@name="na&me"]
  670. [@value="foo&bar"]
  671. [not(@maxlength)]
  672. '
  673. );
  674. }
  675. public function testEmailWithMaxLength()
  676. {
  677. $form = $this->factory->createNamed('email', 'na&me', 'foo&bar', array(
  678. 'property_path' => 'name',
  679. 'max_length' => 123,
  680. ));
  681. $this->assertWidgetMatchesXpath($form->createView(), array(),
  682. '/input
  683. [@type="email"]
  684. [@name="na&me"]
  685. [@value="foo&bar"]
  686. [@maxlength="123"]
  687. '
  688. );
  689. }
  690. public function testFile()
  691. {
  692. $form = $this->factory->createNamed('file', 'na&me', null, array(
  693. 'property_path' => 'name',
  694. ));
  695. $this->assertWidgetMatchesXpath($form->createView(), array(),
  696. '/input
  697. [@type="file"]
  698. '
  699. );
  700. }
  701. public function testHidden()
  702. {
  703. $form = $this->factory->createNamed('hidden', 'na&me', 'foo&bar', array(
  704. 'property_path' => 'name',
  705. ));
  706. $this->assertWidgetMatchesXpath($form->createView(), array(),
  707. '/input
  708. [@type="hidden"]
  709. [@name="na&me"]
  710. [@value="foo&bar"]
  711. '
  712. );
  713. }
  714. public function testInteger()
  715. {
  716. $form = $this->factory->createNamed('integer', 'na&me', 123, array(
  717. 'property_path' => 'name',
  718. ));
  719. $this->assertWidgetMatchesXpath($form->createView(), array(),
  720. '/input
  721. [@type="number"]
  722. [@name="na&me"]
  723. [@value="123"]
  724. '
  725. );
  726. }
  727. public function testLanguage()
  728. {
  729. $form = $this->factory->createNamed('language', 'na&me', 'de', array(
  730. 'property_path' => 'name',
  731. ));
  732. $this->assertWidgetMatchesXpath($form->createView(), array(),
  733. '/select
  734. [@name="na&me"]
  735. [./option[@value="de"][@selected="selected"][.="German"]]
  736. [count(./option)>200]
  737. '
  738. );
  739. }
  740. public function testLocale()
  741. {
  742. $form = $this->factory->createNamed('locale', 'na&me', 'de_AT', array(
  743. 'property_path' => 'name',
  744. ));
  745. $this->assertWidgetMatchesXpath($form->createView(), array(),
  746. '/select
  747. [@name="na&me"]
  748. [./option[@value="de_AT"][@selected="selected"][.="German (Austria)"]]
  749. [count(./option)>200]
  750. '
  751. );
  752. }
  753. public function testMoney()
  754. {
  755. $form = $this->factory->createNamed('money', 'na&me', 1234.56, array(
  756. 'property_path' => 'name',
  757. 'currency' => 'EUR',
  758. ));
  759. $this->assertWidgetMatchesXpath($form->createView(), array(),
  760. '/input
  761. [@type="text"]
  762. [@name="na&me"]
  763. [@value="1234.56"]
  764. [contains(.., "€")]
  765. '
  766. );
  767. }
  768. public function testNumber()
  769. {
  770. $form = $this->factory->createNamed('number', 'na&me', 1234.56, array(
  771. 'property_path' => 'name',
  772. ));
  773. $this->assertWidgetMatchesXpath($form->createView(), array(),
  774. '/input
  775. [@type="text"]
  776. [@name="na&me"]
  777. [@value="1234.56"]
  778. '
  779. );
  780. }
  781. public function testPassword()
  782. {
  783. $form = $this->factory->createNamed('password', 'na&me', 'foo&bar', array(
  784. 'property_path' => 'name',
  785. ));
  786. $this->assertWidgetMatchesXpath($form->createView(), array(),
  787. '/input
  788. [@type="password"]
  789. [@name="na&me"]
  790. [@value=""]
  791. '
  792. );
  793. }
  794. public function testPasswordBoundNotAlwaysEmpty()
  795. {
  796. $form = $this->factory->createNamed('password', 'na&me', null, array(
  797. 'property_path' => 'name',
  798. 'always_empty' => false,
  799. ));
  800. $form->bind('foo&bar');
  801. $this->assertWidgetMatchesXpath($form->createView(), array(),
  802. '/input
  803. [@type="password"]
  804. [@name="na&me"]
  805. [@value="foo&bar"]
  806. '
  807. );
  808. }
  809. public function testPasswordWithMaxLength()
  810. {
  811. $form = $this->factory->createNamed('password', 'na&me', 'foo&bar', array(
  812. 'property_path' => 'name',
  813. 'max_length' => 123,
  814. ));
  815. $this->assertWidgetMatchesXpath($form->createView(), array(),
  816. '/input
  817. [@type="password"]
  818. [@name="na&me"]
  819. [@value=""]
  820. [@maxlength="123"]
  821. '
  822. );
  823. }
  824. public function testPercent()
  825. {
  826. $form = $this->factory->createNamed('percent', 'na&me', 0.1, array(
  827. 'property_path' => 'name',
  828. ));
  829. $this->assertWidgetMatchesXpath($form->createView(), array(),
  830. '/input
  831. [@type="text"]
  832. [@name="na&me"]
  833. [@value="10"]
  834. [contains(.., "%")]
  835. '
  836. );
  837. }
  838. public function testCheckedRadio()
  839. {
  840. $form = $this->factory->createNamed('radio', 'na&me', true, array(
  841. 'property_path' => 'name',
  842. ));
  843. $this->assertWidgetMatchesXpath($form->createView(), array(),
  844. '/input
  845. [@type="radio"]
  846. [@name="na&me"]
  847. [@checked="checked"]
  848. [@value=""]
  849. '
  850. );
  851. }
  852. public function testCheckedRadioWithValue()
  853. {
  854. $form = $this->factory->createNamed('radio', 'na&me', true, array(
  855. 'property_path' => 'name',
  856. 'value' => 'foo&bar',
  857. ));
  858. $this->assertWidgetMatchesXpath($form->createView(), array(),
  859. '/input
  860. [@type="radio"]
  861. [@name="na&me"]
  862. [@checked="checked"]
  863. [@value="foo&bar"]
  864. '
  865. );
  866. }
  867. public function testUncheckedRadio()
  868. {
  869. $form = $this->factory->createNamed('radio', 'na&me', false, array(
  870. 'property_path' => 'name',
  871. ));
  872. $this->assertWidgetMatchesXpath($form->createView(), array(),
  873. '/input
  874. [@type="radio"]
  875. [@name="na&me"]
  876. [not(@checked)]
  877. '
  878. );
  879. }
  880. public function testTextarea()
  881. {
  882. $form = $this->factory->createNamed('textarea', 'na&me', 'foo&bar', array(
  883. 'property_path' => 'name',
  884. ));
  885. $this->assertWidgetMatchesXpath($form->createView(), array(),
  886. '/textarea
  887. [@name="na&me"]
  888. [.="foo&bar"]
  889. '
  890. );
  891. }
  892. public function testText()
  893. {
  894. $form = $this->factory->createNamed('text', 'na&me', 'foo&bar', array(
  895. 'property_path' => 'name',
  896. ));
  897. $this->assertWidgetMatchesXpath($form->createView(), array(),
  898. '/input
  899. [@type="text"]
  900. [@name="na&me"]
  901. [@value="foo&bar"]
  902. [not(@maxlength)]
  903. '
  904. );
  905. }
  906. public function testTextWithMaxLength()
  907. {
  908. $form = $this->factory->createNamed('text', 'na&me', 'foo&bar', array(
  909. 'property_path' => 'name',
  910. 'max_length' => 123,
  911. ));
  912. $this->assertWidgetMatchesXpath($form->createView(), array(),
  913. '/input
  914. [@type="text"]
  915. [@name="na&me"]
  916. [@value="foo&bar"]
  917. [@maxlength="123"]
  918. '
  919. );
  920. }
  921. public function testSearch()
  922. {
  923. $form = $this->factory->createNamed('search', 'na&me', 'foo&bar', array(
  924. 'property_path' => 'name',
  925. ));
  926. $this->assertWidgetMatchesXpath($form->createView(), array(),
  927. '/input
  928. [@type="search"]
  929. [@name="na&me"]
  930. [@value="foo&bar"]
  931. [not(@maxlength)]
  932. '
  933. );
  934. }
  935. public function testTime()
  936. {
  937. $form = $this->factory->createNamed('time', 'na&me', '04:05:06', array(
  938. 'property_path' => 'name',
  939. 'input' => 'string',
  940. 'with_seconds' => false,
  941. ));
  942. $this->assertWidgetMatchesXpath($form->createView(), array(),
  943. '/div
  944. [
  945. ./select
  946. [@id="na&me_hour"]
  947. [@size="1"]
  948. [./option[@value="4"][@selected="selected"]]
  949. /following-sibling::select
  950. [@id="na&me_minute"]
  951. [@size="1"]
  952. [./option[@value="5"][@selected="selected"]]
  953. ]
  954. [count(./select)=2]
  955. '
  956. );
  957. }
  958. public function testTimeWithSeconds()
  959. {
  960. $form = $this->factory->createNamed('time', 'na&me', '04:05:06', array(
  961. 'property_path' => 'name',
  962. 'input' => 'string',
  963. 'with_seconds' => true,
  964. ));
  965. $this->assertWidgetMatchesXpath($form->createView(), array(),
  966. '/div
  967. [
  968. ./select
  969. [@id="na&me_hour"]
  970. [@size="1"]
  971. [./option[@value="4"][@selected="selected"]]
  972. /following-sibling::select
  973. [@id="na&me_minute"]
  974. [@size="1"]
  975. [./option[@value="5"][@selected="selected"]]
  976. /following-sibling::select
  977. [@id="na&me_second"]
  978. [@size="1"]
  979. [./option[@value="6"][@selected="selected"]]
  980. ]
  981. [count(./select)=3]
  982. '
  983. );
  984. }
  985. public function testTimezone()
  986. {
  987. $form = $this->factory->createNamed('timezone', 'na&me', 'Europe/Vienna', array(
  988. 'property_path' => 'name',
  989. ));
  990. $this->assertWidgetMatchesXpath($form->createView(), array(),
  991. '/select
  992. [@name="na&me"]
  993. [./optgroup
  994. [@label="Europe"]
  995. [./option[@value="Europe/Vienna"][@selected="selected"][.="Vienna"]]
  996. ]
  997. [count(./optgroup)>10]
  998. [count(.//option)>200]
  999. '
  1000. );
  1001. }
  1002. public function testUrl()
  1003. {
  1004. $url = 'http://www.google.com?foo1=bar1&foo2=bar2';
  1005. $form = $this->factory->createNamed('url', 'na&me', $url, array(
  1006. 'property_path' => 'name',
  1007. ));
  1008. $this->assertWidgetMatchesXpath($form->createView(), array(),
  1009. '/input
  1010. [@type="url"]
  1011. [@name="na&me"]
  1012. [@value="http://www.google.com?foo1=bar1&foo2=bar2"]
  1013. '
  1014. );
  1015. }
  1016. public function testCollectionPrototype()
  1017. {
  1018. $form = $this->factory->createNamedBuilder('form', 'na&me', array('items' => array('one', 'two', 'three')))
  1019. ->add('items', 'collection', array('allow_add' => true))
  1020. ->getForm()
  1021. ->createView();
  1022. $html = $this->renderWidget($form);
  1023. $this->assertMatchesXpath($html,
  1024. '//script
  1025. [@id="na&me_items_prototype"]
  1026. [@type="text/html"]
  1027. '
  1028. );
  1029. }
  1030. }