AbstractLayoutTest.php 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746
  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\FormInterface;
  12. use Symfony\Component\Form\FormError;
  13. use Symfony\Component\Form\TemplateContext;
  14. use Symfony\Component\Form\FormFactory;
  15. use Symfony\Component\Form\CsrfProvider\DefaultCsrfProvider;
  16. use Symfony\Component\Form\Type\Loader\DefaultTypeLoader;
  17. use Symfony\Component\EventDispatcher\EventDispatcher;
  18. abstract class AbstractLayoutTest extends \PHPUnit_Framework_TestCase
  19. {
  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. $csrfProvider = new DefaultCsrfProvider('foo');
  27. $storage = new \Symfony\Component\HttpFoundation\File\TemporaryStorage('foo', 1, \sys_get_temp_dir());
  28. $loader = new DefaultTypeLoader($validator, $csrfProvider , $storage);
  29. $this->factory = new FormFactory($loader);
  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(FormInterface $form, array $vars, $xpath)
  67. {
  68. $html = $this->renderWidget($form, array_merge(array(
  69. 'name' => 'my_name',
  70. 'id' => 'my_id',
  71. 'attr' => array('class' => 'my_class'),
  72. ), $vars));
  73. $xpath = trim($xpath).'
  74. [@id="my_id"]
  75. [@name="my_name"]
  76. [@class="my_class"]';
  77. $this->assertMatchesXpath($html, $xpath);
  78. }
  79. abstract protected function renderEnctype(FormInterface $form);
  80. abstract protected function renderLabel(FormInterface $form, $label = null);
  81. abstract protected function renderErrors(FormInterface $form);
  82. abstract protected function renderWidget(FormInterface $form, array $vars = array());
  83. abstract protected function renderRow(FormInterface $form, array $vars = array());
  84. abstract protected function renderRest(FormInterface $form, array $vars = array());
  85. public function testEnctype()
  86. {
  87. $form = $this->factory->createBuilder('form', 'name')
  88. ->add('file', 'file')
  89. ->getForm();
  90. $this->assertEquals('enctype="multipart/form-data"', $this->renderEnctype($form));
  91. }
  92. public function testNoEnctype()
  93. {
  94. $form = $this->factory->createBuilder('form', 'name')
  95. ->add('text', 'text')
  96. ->getForm();
  97. $this->assertEquals('', $this->renderEnctype($form));
  98. }
  99. public function testLabel()
  100. {
  101. $form = $this->factory->create('text', 'name');
  102. $html = $this->renderLabel($form);
  103. $this->assertMatchesXpath($html,
  104. '/label
  105. [@for="name"]
  106. [.="[trans]Name[/trans]"]
  107. '
  108. );
  109. }
  110. public function testLabelWithCustomText()
  111. {
  112. $form = $this->factory->create('text', 'name');
  113. $html = $this->renderLabel($form, 'Custom label');
  114. $this->assertMatchesXpath($html,
  115. '/label
  116. [@for="name"]
  117. [.="[trans]Custom label[/trans]"]
  118. '
  119. );
  120. }
  121. public function testErrors()
  122. {
  123. $form = $this->factory->create('text', 'name');
  124. $form->addError(new FormError('Error 1'));
  125. $form->addError(new FormError('Error 2'));
  126. $html = $this->renderErrors($form);
  127. $this->assertMatchesXpath($html,
  128. '/ul
  129. [
  130. ./li[.="[trans]Error 1[/trans]"]
  131. /following-sibling::li[.="[trans]Error 2[/trans]"]
  132. ]
  133. [count(./li)=2]
  134. '
  135. );
  136. }
  137. public function testCheckedCheckbox()
  138. {
  139. $form = $this->factory->create('checkbox', 'name', array(
  140. 'data' => true,
  141. ));
  142. $this->assertWidgetMatchesXpath($form, array(),
  143. '/input
  144. [@type="checkbox"]
  145. [@checked="checked"]
  146. [@value="1"]
  147. '
  148. );
  149. }
  150. public function testCheckedCheckboxWithValue()
  151. {
  152. $form = $this->factory->create('checkbox', 'name', array(
  153. 'value' => 'foobar',
  154. 'data' => true,
  155. ));
  156. $this->assertWidgetMatchesXpath($form, array(),
  157. '/input
  158. [@type="checkbox"]
  159. [@checked="checked"]
  160. [@value="foobar"]
  161. '
  162. );
  163. }
  164. public function testUncheckedCheckbox()
  165. {
  166. $form = $this->factory->create('checkbox', 'name', array(
  167. 'data' => false,
  168. ));
  169. $this->assertWidgetMatchesXpath($form, array(),
  170. '/input
  171. [@type="checkbox"]
  172. [not(@checked)]
  173. '
  174. );
  175. }
  176. public function testChoice()
  177. {
  178. $form = $this->factory->create('choice', 'name', array(
  179. 'choices' => array('a' => 'A', 'b' => 'B'),
  180. 'data' => 'a',
  181. ));
  182. $this->assertWidgetMatchesXpath($form, array(),
  183. '/select
  184. [
  185. ./option[@value="a"][@selected="selected"][.="A"]
  186. /following-sibling::option[@value="b"][not(@selected)][.="B"]
  187. ]
  188. [count(./option)=2]
  189. '
  190. );
  191. }
  192. public function testChoiceWithPreferred()
  193. {
  194. $form = $this->factory->create('choice', 'name', array(
  195. 'choices' => array('a' => 'A', 'b' => 'B'),
  196. 'preferred_choices' => array('b'),
  197. 'data' => 'a',
  198. ));
  199. $this->assertWidgetMatchesXpath($form, array('separator' => '-- sep --'),
  200. '/select
  201. [
  202. ./option[@value="b"][not(@selected)][.="B"]
  203. /following-sibling::option[@disabled="disabled"][not(@selected)][.="-- sep --"]
  204. /following-sibling::option[@value="a"][@selected="selected"][.="A"]
  205. ]
  206. [count(./option)=3]
  207. '
  208. );
  209. }
  210. public function testNonRequiredChoice()
  211. {
  212. $form = $this->factory->create('choice', 'name', array(
  213. 'choices' => array('a' => 'A', 'b' => 'B'),
  214. 'required' => false,
  215. 'data' => 'a',
  216. ));
  217. $this->assertWidgetMatchesXpath($form, array(),
  218. '/select
  219. [
  220. ./option[@value=""][.=""]
  221. /following-sibling::option[@value="a"][@selected="selected"][.="A"]
  222. /following-sibling::option[@value="b"][not(@selected)][.="B"]
  223. ]
  224. [count(./option)=3]
  225. '
  226. );
  227. }
  228. public function testGroupedChoice()
  229. {
  230. $form = $this->factory->create('choice', 'name', array(
  231. 'choices' => array(
  232. 'Group1' => array('a' => 'A', 'b' => 'B'),
  233. 'Group2' => array('c' => 'C'),
  234. ),
  235. 'data' => 'a',
  236. ));
  237. $this->assertWidgetMatchesXpath($form, array(),
  238. '/select
  239. [./optgroup[@label="Group1"]
  240. [
  241. ./option[@value="a"][@selected="selected"][.="A"]
  242. /following-sibling::option[@value="b"][not(@selected)][.="B"]
  243. ]
  244. [count(./option)=2]
  245. ]
  246. [./optgroup[@label="Group2"]
  247. [./option[@value="c"][not(@selected)][.="C"]]
  248. [count(./option)=1]
  249. ]
  250. [count(./optgroup)=2]
  251. '
  252. );
  253. }
  254. public function testCountry()
  255. {
  256. $form = $this->factory->create('country', 'name', array(
  257. 'data' => 'AT',
  258. ));
  259. $this->assertWidgetMatchesXpath($form, array(),
  260. '/select
  261. [./option[@value="AT"][@selected="selected"][.="Austria"]]
  262. [count(./option)>200]
  263. '
  264. );
  265. }
  266. public function testCsrf()
  267. {
  268. $form = $this->factory->create('csrf', 'name');
  269. $this->assertWidgetMatchesXpath($form, array(),
  270. '/input
  271. [@type="hidden"]
  272. [string-length(@value)>=40]
  273. '
  274. );
  275. }
  276. public function testDateTime()
  277. {
  278. $form = $this->factory->create('datetime', 'name', array(
  279. 'data' => '2011-02-03 04:05:06',
  280. 'input' => 'string',
  281. 'with_seconds' => false,
  282. ));
  283. $this->assertWidgetMatchesXpath($form, array(),
  284. '/div
  285. [
  286. ./div
  287. [@id="name_date"]
  288. [
  289. ./select
  290. [@id="name_date_month"]
  291. [./option[@value="2"][@selected="selected"]]
  292. /following-sibling::select
  293. [@id="name_date_day"]
  294. [./option[@value="3"][@selected="selected"]]
  295. /following-sibling::select
  296. [@id="name_date_year"]
  297. [./option[@value="2011"][@selected="selected"]]
  298. ]
  299. /following-sibling::div
  300. [@id="name_time"]
  301. [
  302. ./select
  303. [@id="name_time_hour"]
  304. [./option[@value="4"][@selected="selected"]]
  305. /following-sibling::select
  306. [@id="name_time_minute"]
  307. [./option[@value="5"][@selected="selected"]]
  308. ]
  309. ]
  310. [count(.//select)=5]
  311. '
  312. );
  313. }
  314. public function testDateTimeWithSeconds()
  315. {
  316. $form = $this->factory->create('datetime', 'name', array(
  317. 'data' => '2011-02-03 04:05:06',
  318. 'input' => 'string',
  319. 'with_seconds' => true,
  320. ));
  321. $this->assertWidgetMatchesXpath($form, array(),
  322. '/div
  323. [
  324. ./div
  325. [@id="name_date"]
  326. [
  327. ./select
  328. [@id="name_date_month"]
  329. [./option[@value="2"][@selected="selected"]]
  330. /following-sibling::select
  331. [@id="name_date_day"]
  332. [./option[@value="3"][@selected="selected"]]
  333. /following-sibling::select
  334. [@id="name_date_year"]
  335. [./option[@value="2011"][@selected="selected"]]
  336. ]
  337. /following-sibling::div
  338. [@id="name_time"]
  339. [
  340. ./select
  341. [@id="name_time_hour"]
  342. [./option[@value="4"][@selected="selected"]]
  343. /following-sibling::select
  344. [@id="name_time_minute"]
  345. [./option[@value="5"][@selected="selected"]]
  346. /following-sibling::select
  347. [@id="name_time_second"]
  348. [./option[@value="6"][@selected="selected"]]
  349. ]
  350. ]
  351. [count(.//select)=6]
  352. '
  353. );
  354. }
  355. public function testDateChoice()
  356. {
  357. $form = $this->factory->create('date', 'name', array(
  358. 'data' => '2011-02-03',
  359. 'input' => 'string',
  360. 'widget' => 'choice',
  361. ));
  362. $this->assertWidgetMatchesXpath($form, array(),
  363. '/div
  364. [./select
  365. [@id="name_month"]
  366. [./option[@value="2"][@selected="selected"]]
  367. /following-sibling::select
  368. [@id="name_day"]
  369. [./option[@value="3"][@selected="selected"]]
  370. /following-sibling::select
  371. [@id="name_year"]
  372. [./option[@value="2011"][@selected="selected"]]
  373. ]
  374. [count(./select)=3]
  375. '
  376. );
  377. }
  378. public function testDateText()
  379. {
  380. $form = $this->factory->create('date', 'name', array(
  381. 'data' => '2011-02-03',
  382. 'input' => 'string',
  383. 'widget' => 'text',
  384. ));
  385. $this->assertWidgetMatchesXpath($form, array(),
  386. '/input
  387. [@type="text"]
  388. [@value="Feb 3, 2011"]
  389. '
  390. );
  391. }
  392. public function testFile()
  393. {
  394. $form = $this->factory->create('file', 'name');
  395. $this->assertWidgetMatchesXpath($form, array(),
  396. '/div
  397. [
  398. ./input[@type="file"][@id="name_file"]
  399. /following-sibling::input[@type="hidden"][@id="name_token"]
  400. /following-sibling::input[@type="hidden"][@id="name_name"]
  401. ]
  402. [count(./input)=3]
  403. '
  404. );
  405. }
  406. public function testHidden()
  407. {
  408. $form = $this->factory->create('hidden', 'name', array(
  409. 'data' => 'foobar',
  410. ));
  411. $this->assertWidgetMatchesXpath($form, array(),
  412. '/input
  413. [@type="hidden"]
  414. [@value="foobar"]
  415. '
  416. );
  417. }
  418. public function testInteger()
  419. {
  420. $form = $this->factory->create('integer', 'name', array(
  421. 'data' => '123',
  422. ));
  423. $this->assertWidgetMatchesXpath($form, array(),
  424. '/input
  425. [@type="number"]
  426. [@value="123"]
  427. '
  428. );
  429. }
  430. public function testLanguage()
  431. {
  432. $form = $this->factory->create('language', 'name', array(
  433. 'data' => 'de',
  434. ));
  435. $this->assertWidgetMatchesXpath($form, array(),
  436. '/select
  437. [./option[@value="de"][@selected="selected"][.="German"]]
  438. [count(./option)>200]
  439. '
  440. );
  441. }
  442. public function testLocale()
  443. {
  444. $form = $this->factory->create('locale', 'name', array(
  445. 'data' => 'de_AT',
  446. ));
  447. $this->assertWidgetMatchesXpath($form, array(),
  448. '/select
  449. [./option[@value="de_AT"][@selected="selected"][.="German (Austria)"]]
  450. [count(./option)>200]
  451. '
  452. );
  453. }
  454. public function testMoney()
  455. {
  456. $form = $this->factory->create('money', 'name', array(
  457. 'data' => 1234.56,
  458. 'currency' => 'EUR',
  459. ));
  460. $this->assertWidgetMatchesXpath($form, array(),
  461. '/input
  462. [@type="text"]
  463. [@value="1234.56"]
  464. [contains(.., "€")]
  465. '
  466. );
  467. }
  468. public function testNumber()
  469. {
  470. $form = $this->factory->create('number', 'name', array(
  471. 'data' => 1234.56,
  472. ));
  473. $this->assertWidgetMatchesXpath($form, array(),
  474. '/input
  475. [@type="text"]
  476. [@value="1234.56"]
  477. '
  478. );
  479. }
  480. public function testPassword()
  481. {
  482. $form = $this->factory->create('password', 'name', array(
  483. 'data' => 'Pa$sW0rD',
  484. ));
  485. $this->assertWidgetMatchesXpath($form, array(),
  486. '/input
  487. [@type="password"]
  488. [@value=""]
  489. '
  490. );
  491. }
  492. public function testPercent()
  493. {
  494. $form = $this->factory->create('percent', 'name', array(
  495. 'data' => 0.1,
  496. ));
  497. $this->assertWidgetMatchesXpath($form, array(),
  498. '/input
  499. [@type="text"]
  500. [@value="10"]
  501. [contains(.., "%")]
  502. '
  503. );
  504. }
  505. public function testCheckedRadio()
  506. {
  507. $form = $this->factory->create('radio', 'name', array(
  508. 'data' => true,
  509. ));
  510. $this->assertWidgetMatchesXpath($form, array(),
  511. '/input
  512. [@type="radio"]
  513. [@checked="checked"]
  514. [@value=""]
  515. '
  516. );
  517. }
  518. public function testCheckedRadioWithValue()
  519. {
  520. $form = $this->factory->create('radio', 'name', array(
  521. 'data' => true,
  522. 'value' => 'foobar',
  523. ));
  524. $this->assertWidgetMatchesXpath($form, array(),
  525. '/input
  526. [@type="radio"]
  527. [@checked="checked"]
  528. [@value="foobar"]
  529. '
  530. );
  531. }
  532. public function testUncheckedRadio()
  533. {
  534. $form = $this->factory->create('radio', 'name', array(
  535. 'data' => false,
  536. ));
  537. $this->assertWidgetMatchesXpath($form, array(),
  538. '/input
  539. [@type="radio"]
  540. [not(@checked)]
  541. '
  542. );
  543. }
  544. public function testTextarea()
  545. {
  546. $form = $this->factory->create('textarea', 'name', array(
  547. 'data' => 'foobar',
  548. ));
  549. $this->assertWidgetMatchesXpath($form, array(),
  550. '/textarea
  551. [.="foobar"]
  552. '
  553. );
  554. }
  555. public function testText()
  556. {
  557. $form = $this->factory->create('text', 'name', array(
  558. 'data' => 'foobar',
  559. ));
  560. $this->assertWidgetMatchesXpath($form, array(),
  561. '/input
  562. [@type="text"]
  563. [@value="foobar"]
  564. [not(@maxlength)]
  565. '
  566. );
  567. }
  568. public function testTextWithMaxLength()
  569. {
  570. $form = $this->factory->create('text', 'name', array(
  571. 'data' => 'foobar',
  572. 'max_length' => 123,
  573. ));
  574. $this->assertWidgetMatchesXpath($form, array(),
  575. '/input
  576. [@type="text"]
  577. [@value="foobar"]
  578. [@maxlength="123"]
  579. '
  580. );
  581. }
  582. public function testTime()
  583. {
  584. $form = $this->factory->create('time', 'name', array(
  585. 'data' => '04:05:06',
  586. 'input' => 'string',
  587. 'with_seconds' => false,
  588. ));
  589. $this->assertWidgetMatchesXpath($form, array(),
  590. '/div
  591. [./select
  592. [@id="name_hour"]
  593. [./option[@value="4"][@selected="selected"]]
  594. /following-sibling::select
  595. [@id="name_minute"]
  596. [./option[@value="5"][@selected="selected"]]
  597. ]
  598. [count(./select)=2]
  599. '
  600. );
  601. }
  602. public function testTimeWithSeconds()
  603. {
  604. $form = $this->factory->create('time', 'name', array(
  605. 'data' => '04:05:06',
  606. 'input' => 'string',
  607. 'with_seconds' => true,
  608. ));
  609. $this->assertWidgetMatchesXpath($form, array(),
  610. '/div
  611. [./select
  612. [@id="name_hour"]
  613. [./option[@value="4"][@selected="selected"]]
  614. /following-sibling::select
  615. [@id="name_minute"]
  616. [./option[@value="5"][@selected="selected"]]
  617. /following-sibling::select
  618. [@id="name_second"]
  619. [./option[@value="6"][@selected="selected"]]
  620. ]
  621. [count(./select)=3]
  622. '
  623. );
  624. }
  625. public function testTimezone()
  626. {
  627. $form = $this->factory->create('timezone', 'name', array(
  628. 'data' => 'Europe/Vienna',
  629. ));
  630. $this->assertWidgetMatchesXpath($form, array(),
  631. '/select
  632. [./optgroup
  633. [@label="Europe"]
  634. [./option[@value="Europe/Vienna"][@selected="selected"][.="Vienna"]]
  635. ]
  636. [count(./optgroup)>10]
  637. [count(.//option)>200]
  638. '
  639. );
  640. }
  641. public function testUrl()
  642. {
  643. $form = $this->factory->create('url', 'name', array(
  644. 'data' => 'http://www.google.com',
  645. ));
  646. $this->assertWidgetMatchesXpath($form, array(),
  647. '/input
  648. [@type="url"]
  649. [@value="http://www.google.com"]
  650. '
  651. );
  652. }
  653. }