AbstractLayoutTest.php 27 KB

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