AbstractLayoutTest.php 30 KB

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