AbstractLayoutTest.php 31 KB

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