AbstractLayoutTest.php 30 KB

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