SimpleFormTest.php 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140
  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\Component\Form\Tests;
  11. use Symfony\Component\EventDispatcher\EventDispatcher;
  12. use Symfony\Component\Form\Exception\TransformationFailedException;
  13. use Symfony\Component\Form\Form;
  14. use Symfony\Component\Form\FormConfigBuilder;
  15. use Symfony\Component\Form\FormError;
  16. use Symfony\Component\Form\FormEvent;
  17. use Symfony\Component\Form\FormEvents;
  18. use Symfony\Component\Form\Tests\Fixtures\FixedDataTransformer;
  19. use Symfony\Component\Form\Tests\Fixtures\FixedFilterListener;
  20. use Symfony\Component\PropertyAccess\PropertyPath;
  21. class SimpleFormTest_Countable implements \Countable
  22. {
  23. private $count;
  24. public function __construct($count)
  25. {
  26. $this->count = $count;
  27. }
  28. public function count()
  29. {
  30. return $this->count;
  31. }
  32. }
  33. class SimpleFormTest_Traversable implements \IteratorAggregate
  34. {
  35. private $iterator;
  36. public function __construct($count)
  37. {
  38. $this->iterator = new \ArrayIterator($count > 0 ? array_fill(0, $count, 'Foo') : array());
  39. }
  40. public function getIterator()
  41. {
  42. return $this->iterator;
  43. }
  44. }
  45. class SimpleFormTest extends AbstractFormTest
  46. {
  47. public function testDataIsInitializedToConfiguredValue()
  48. {
  49. $model = new FixedDataTransformer(array(
  50. 'default' => 'foo',
  51. ));
  52. $view = new FixedDataTransformer(array(
  53. 'foo' => 'bar',
  54. ));
  55. $config = new FormConfigBuilder('name', null, $this->dispatcher);
  56. $config->addViewTransformer($view);
  57. $config->addModelTransformer($model);
  58. $config->setData('default');
  59. $form = new Form($config);
  60. $this->assertSame('default', $form->getData());
  61. $this->assertSame('foo', $form->getNormData());
  62. $this->assertSame('bar', $form->getViewData());
  63. }
  64. /**
  65. * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException
  66. * @expectedExceptionMessage Unable to transform value for property path "name": No mapping for value "arg"
  67. */
  68. public function testDataTransformationFailure()
  69. {
  70. $model = new FixedDataTransformer(array(
  71. 'default' => 'foo',
  72. ));
  73. $view = new FixedDataTransformer(array(
  74. 'foo' => 'bar',
  75. ));
  76. $config = new FormConfigBuilder('name', null, $this->dispatcher);
  77. $config->addViewTransformer($view);
  78. $config->addModelTransformer($model);
  79. $config->setData('arg');
  80. $form = new Form($config);
  81. $form->getData();
  82. }
  83. // https://github.com/symfony/symfony/commit/d4f4038f6daf7cf88ca7c7ab089473cce5ebf7d8#commitcomment-1632879
  84. public function testDataIsInitializedFromSubmit()
  85. {
  86. $mock = $this->getMockBuilder('\stdClass')
  87. ->setMethods(array('preSetData', 'preSubmit'))
  88. ->getMock();
  89. $mock->expects($this->at(0))
  90. ->method('preSetData');
  91. $mock->expects($this->at(1))
  92. ->method('preSubmit');
  93. $config = new FormConfigBuilder('name', null, $this->dispatcher);
  94. $config->addEventListener(FormEvents::PRE_SET_DATA, array($mock, 'preSetData'));
  95. $config->addEventListener(FormEvents::PRE_SUBMIT, array($mock, 'preSubmit'));
  96. $form = new Form($config);
  97. // no call to setData() or similar where the object would be
  98. // initialized otherwise
  99. $form->submit('foobar');
  100. }
  101. // https://github.com/symfony/symfony/pull/7789
  102. public function testFalseIsConvertedToNull()
  103. {
  104. $mock = $this->getMockBuilder('\stdClass')
  105. ->setMethods(array('preSubmit'))
  106. ->getMock();
  107. $mock->expects($this->once())
  108. ->method('preSubmit')
  109. ->with($this->callback(function ($event) {
  110. return null === $event->getData();
  111. }));
  112. $config = new FormConfigBuilder('name', null, $this->dispatcher);
  113. $config->addEventListener(FormEvents::PRE_SUBMIT, array($mock, 'preSubmit'));
  114. $form = new Form($config);
  115. $form->submit(false);
  116. $this->assertTrue($form->isValid());
  117. $this->assertNull($form->getData());
  118. }
  119. /**
  120. * @expectedException \Symfony\Component\Form\Exception\AlreadySubmittedException
  121. */
  122. public function testSubmitThrowsExceptionIfAlreadySubmitted()
  123. {
  124. $this->form->submit(array());
  125. $this->form->submit(array());
  126. }
  127. public function testSubmitIsIgnoredIfDisabled()
  128. {
  129. $form = $this->getBuilder()
  130. ->setDisabled(true)
  131. ->setData('initial')
  132. ->getForm();
  133. $form->submit('new');
  134. $this->assertEquals('initial', $form->getData());
  135. $this->assertTrue($form->isSubmitted());
  136. }
  137. public function testNeverRequiredIfParentNotRequired()
  138. {
  139. $parent = $this->getBuilder()->setRequired(false)->getForm();
  140. $child = $this->getBuilder()->setRequired(true)->getForm();
  141. $child->setParent($parent);
  142. $this->assertFalse($child->isRequired());
  143. }
  144. public function testRequired()
  145. {
  146. $parent = $this->getBuilder()->setRequired(true)->getForm();
  147. $child = $this->getBuilder()->setRequired(true)->getForm();
  148. $child->setParent($parent);
  149. $this->assertTrue($child->isRequired());
  150. }
  151. public function testNotRequired()
  152. {
  153. $parent = $this->getBuilder()->setRequired(true)->getForm();
  154. $child = $this->getBuilder()->setRequired(false)->getForm();
  155. $child->setParent($parent);
  156. $this->assertFalse($child->isRequired());
  157. }
  158. /**
  159. * @dataProvider getDisabledStates
  160. */
  161. public function testAlwaysDisabledIfParentDisabled($parentDisabled, $disabled, $result)
  162. {
  163. $parent = $this->getBuilder()->setDisabled($parentDisabled)->getForm();
  164. $child = $this->getBuilder()->setDisabled($disabled)->getForm();
  165. $child->setParent($parent);
  166. $this->assertSame($result, $child->isDisabled());
  167. }
  168. public function getDisabledStates()
  169. {
  170. return array(
  171. // parent, button, result
  172. array(true, true, true),
  173. array(true, false, true),
  174. array(false, true, true),
  175. array(false, false, false),
  176. );
  177. }
  178. public function testGetRootReturnsRootOfParent()
  179. {
  180. $parent = $this->getMockForm();
  181. $parent->expects($this->once())
  182. ->method('getRoot')
  183. ->will($this->returnValue('ROOT'));
  184. $this->form->setParent($parent);
  185. $this->assertEquals('ROOT', $this->form->getRoot());
  186. }
  187. public function testGetRootReturnsSelfIfNoParent()
  188. {
  189. $this->assertSame($this->form, $this->form->getRoot());
  190. }
  191. public function testEmptyIfEmptyArray()
  192. {
  193. $this->form->setData(array());
  194. $this->assertTrue($this->form->isEmpty());
  195. }
  196. public function testEmptyIfEmptyCountable()
  197. {
  198. $this->form = new Form(new FormConfigBuilder('name', __NAMESPACE__.'\SimpleFormTest_Countable', $this->dispatcher));
  199. $this->form->setData(new SimpleFormTest_Countable(0));
  200. $this->assertTrue($this->form->isEmpty());
  201. }
  202. public function testNotEmptyIfFilledCountable()
  203. {
  204. $this->form = new Form(new FormConfigBuilder('name', __NAMESPACE__.'\SimpleFormTest_Countable', $this->dispatcher));
  205. $this->form->setData(new SimpleFormTest_Countable(1));
  206. $this->assertFalse($this->form->isEmpty());
  207. }
  208. public function testEmptyIfEmptyTraversable()
  209. {
  210. $this->form = new Form(new FormConfigBuilder('name', __NAMESPACE__.'\SimpleFormTest_Traversable', $this->dispatcher));
  211. $this->form->setData(new SimpleFormTest_Traversable(0));
  212. $this->assertTrue($this->form->isEmpty());
  213. }
  214. public function testNotEmptyIfFilledTraversable()
  215. {
  216. $this->form = new Form(new FormConfigBuilder('name', __NAMESPACE__.'\SimpleFormTest_Traversable', $this->dispatcher));
  217. $this->form->setData(new SimpleFormTest_Traversable(1));
  218. $this->assertFalse($this->form->isEmpty());
  219. }
  220. public function testEmptyIfNull()
  221. {
  222. $this->form->setData(null);
  223. $this->assertTrue($this->form->isEmpty());
  224. }
  225. public function testEmptyIfEmptyString()
  226. {
  227. $this->form->setData('');
  228. $this->assertTrue($this->form->isEmpty());
  229. }
  230. public function testNotEmptyIfText()
  231. {
  232. $this->form->setData('foobar');
  233. $this->assertFalse($this->form->isEmpty());
  234. }
  235. public function testValidIfSubmitted()
  236. {
  237. $form = $this->getBuilder()->getForm();
  238. $form->submit('foobar');
  239. $this->assertTrue($form->isValid());
  240. }
  241. public function testValidIfSubmittedAndDisabled()
  242. {
  243. $form = $this->getBuilder()->setDisabled(true)->getForm();
  244. $form->submit('foobar');
  245. $this->assertTrue($form->isValid());
  246. }
  247. public function testNotValidIfNotSubmitted()
  248. {
  249. $this->assertFalse($this->form->isValid());
  250. }
  251. public function testNotValidIfErrors()
  252. {
  253. $form = $this->getBuilder()->getForm();
  254. $form->submit('foobar');
  255. $form->addError(new FormError('Error!'));
  256. $this->assertFalse($form->isValid());
  257. }
  258. public function testHasErrors()
  259. {
  260. $this->form->addError(new FormError('Error!'));
  261. $this->assertCount(1, $this->form->getErrors());
  262. }
  263. public function testHasNoErrors()
  264. {
  265. $this->assertCount(0, $this->form->getErrors());
  266. }
  267. /**
  268. * @expectedException \Symfony\Component\Form\Exception\AlreadySubmittedException
  269. */
  270. public function testSetParentThrowsExceptionIfAlreadySubmitted()
  271. {
  272. $this->form->submit(array());
  273. $this->form->setParent($this->getBuilder('parent')->getForm());
  274. }
  275. public function testSubmitted()
  276. {
  277. $form = $this->getBuilder()->getForm();
  278. $form->submit('foobar');
  279. $this->assertTrue($form->isSubmitted());
  280. }
  281. public function testNotSubmitted()
  282. {
  283. $this->assertFalse($this->form->isSubmitted());
  284. }
  285. /**
  286. * @expectedException \Symfony\Component\Form\Exception\AlreadySubmittedException
  287. */
  288. public function testSetDataThrowsExceptionIfAlreadySubmitted()
  289. {
  290. $this->form->submit(array());
  291. $this->form->setData(null);
  292. }
  293. public function testSetDataClonesObjectIfNotByReference()
  294. {
  295. $data = new \stdClass();
  296. $form = $this->getBuilder('name', null, '\stdClass')->setByReference(false)->getForm();
  297. $form->setData($data);
  298. $this->assertNotSame($data, $form->getData());
  299. $this->assertEquals($data, $form->getData());
  300. }
  301. public function testSetDataDoesNotCloneObjectIfByReference()
  302. {
  303. $data = new \stdClass();
  304. $form = $this->getBuilder('name', null, '\stdClass')->setByReference(true)->getForm();
  305. $form->setData($data);
  306. $this->assertSame($data, $form->getData());
  307. }
  308. public function testSetDataExecutesTransformationChain()
  309. {
  310. // use real event dispatcher now
  311. $form = $this->getBuilder('name', new EventDispatcher())
  312. ->addEventSubscriber(new FixedFilterListener(array(
  313. 'preSetData' => array(
  314. 'app' => 'filtered',
  315. ),
  316. )))
  317. ->addModelTransformer(new FixedDataTransformer(array(
  318. '' => '',
  319. 'filtered' => 'norm',
  320. )))
  321. ->addViewTransformer(new FixedDataTransformer(array(
  322. '' => '',
  323. 'norm' => 'client',
  324. )))
  325. ->getForm();
  326. $form->setData('app');
  327. $this->assertEquals('filtered', $form->getData());
  328. $this->assertEquals('norm', $form->getNormData());
  329. $this->assertEquals('client', $form->getViewData());
  330. }
  331. public function testSetDataExecutesViewTransformersInOrder()
  332. {
  333. $form = $this->getBuilder()
  334. ->addViewTransformer(new FixedDataTransformer(array(
  335. '' => '',
  336. 'first' => 'second',
  337. )))
  338. ->addViewTransformer(new FixedDataTransformer(array(
  339. '' => '',
  340. 'second' => 'third',
  341. )))
  342. ->getForm();
  343. $form->setData('first');
  344. $this->assertEquals('third', $form->getViewData());
  345. }
  346. public function testSetDataExecutesModelTransformersInReverseOrder()
  347. {
  348. $form = $this->getBuilder()
  349. ->addModelTransformer(new FixedDataTransformer(array(
  350. '' => '',
  351. 'second' => 'third',
  352. )))
  353. ->addModelTransformer(new FixedDataTransformer(array(
  354. '' => '',
  355. 'first' => 'second',
  356. )))
  357. ->getForm();
  358. $form->setData('first');
  359. $this->assertEquals('third', $form->getNormData());
  360. }
  361. /*
  362. * When there is no data transformer, the data must have the same format
  363. * in all three representations
  364. */
  365. public function testSetDataConvertsScalarToStringIfNoTransformer()
  366. {
  367. $form = $this->getBuilder()->getForm();
  368. $form->setData(1);
  369. $this->assertSame('1', $form->getData());
  370. $this->assertSame('1', $form->getNormData());
  371. $this->assertSame('1', $form->getViewData());
  372. }
  373. /*
  374. * Data in client format should, if possible, always be a string to
  375. * facilitate differentiation between '0' and ''
  376. */
  377. public function testSetDataConvertsScalarToStringIfOnlyModelTransformer()
  378. {
  379. $form = $this->getBuilder()
  380. ->addModelTransformer(new FixedDataTransformer(array(
  381. '' => '',
  382. 1 => 23,
  383. )))
  384. ->getForm();
  385. $form->setData(1);
  386. $this->assertSame(1, $form->getData());
  387. $this->assertSame(23, $form->getNormData());
  388. $this->assertSame('23', $form->getViewData());
  389. }
  390. /*
  391. * NULL remains NULL in app and norm format to remove the need to treat
  392. * empty values and NULL explicitly in the application
  393. */
  394. public function testSetDataConvertsNullToStringIfNoTransformer()
  395. {
  396. $form = $this->getBuilder()->getForm();
  397. $form->setData(null);
  398. $this->assertNull($form->getData());
  399. $this->assertNull($form->getNormData());
  400. $this->assertSame('', $form->getViewData());
  401. }
  402. public function testSetDataIsIgnoredIfDataIsLocked()
  403. {
  404. $form = $this->getBuilder()
  405. ->setData('default')
  406. ->setDataLocked(true)
  407. ->getForm();
  408. $form->setData('foobar');
  409. $this->assertSame('default', $form->getData());
  410. }
  411. public function testPreSetDataChangesDataIfDataIsLocked()
  412. {
  413. $config = new FormConfigBuilder('name', null, $this->dispatcher);
  414. $config
  415. ->setData('default')
  416. ->setDataLocked(true)
  417. ->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) {
  418. $event->setData('foobar');
  419. });
  420. $form = new Form($config);
  421. $this->assertSame('foobar', $form->getData());
  422. $this->assertSame('foobar', $form->getNormData());
  423. $this->assertSame('foobar', $form->getViewData());
  424. }
  425. public function testSubmitConvertsEmptyToNullIfNoTransformer()
  426. {
  427. $form = $this->getBuilder()->getForm();
  428. $form->submit('');
  429. $this->assertNull($form->getData());
  430. $this->assertNull($form->getNormData());
  431. $this->assertSame('', $form->getViewData());
  432. }
  433. public function testSubmitExecutesTransformationChain()
  434. {
  435. // use real event dispatcher now
  436. $form = $this->getBuilder('name', new EventDispatcher())
  437. ->addEventSubscriber(new FixedFilterListener(array(
  438. 'preSubmit' => array(
  439. 'client' => 'filteredclient',
  440. ),
  441. 'onSubmit' => array(
  442. 'norm' => 'filterednorm',
  443. ),
  444. )))
  445. ->addViewTransformer(new FixedDataTransformer(array(
  446. '' => '',
  447. // direction is reversed!
  448. 'norm' => 'filteredclient',
  449. 'filterednorm' => 'cleanedclient',
  450. )))
  451. ->addModelTransformer(new FixedDataTransformer(array(
  452. '' => '',
  453. // direction is reversed!
  454. 'app' => 'filterednorm',
  455. )))
  456. ->getForm();
  457. $form->submit('client');
  458. $this->assertEquals('app', $form->getData());
  459. $this->assertEquals('filterednorm', $form->getNormData());
  460. $this->assertEquals('cleanedclient', $form->getViewData());
  461. }
  462. public function testSubmitExecutesViewTransformersInReverseOrder()
  463. {
  464. $form = $this->getBuilder()
  465. ->addViewTransformer(new FixedDataTransformer(array(
  466. '' => '',
  467. 'third' => 'second',
  468. )))
  469. ->addViewTransformer(new FixedDataTransformer(array(
  470. '' => '',
  471. 'second' => 'first',
  472. )))
  473. ->getForm();
  474. $form->submit('first');
  475. $this->assertEquals('third', $form->getNormData());
  476. }
  477. public function testSubmitExecutesModelTransformersInOrder()
  478. {
  479. $form = $this->getBuilder()
  480. ->addModelTransformer(new FixedDataTransformer(array(
  481. '' => '',
  482. 'second' => 'first',
  483. )))
  484. ->addModelTransformer(new FixedDataTransformer(array(
  485. '' => '',
  486. 'third' => 'second',
  487. )))
  488. ->getForm();
  489. $form->submit('first');
  490. $this->assertEquals('third', $form->getData());
  491. }
  492. public function testSynchronizedByDefault()
  493. {
  494. $this->assertTrue($this->form->isSynchronized());
  495. }
  496. public function testSynchronizedAfterSubmission()
  497. {
  498. $this->form->submit('foobar');
  499. $this->assertTrue($this->form->isSynchronized());
  500. }
  501. public function testNotSynchronizedIfViewReverseTransformationFailed()
  502. {
  503. $transformer = $this->getDataTransformer();
  504. $transformer->expects($this->once())
  505. ->method('reverseTransform')
  506. ->will($this->throwException(new TransformationFailedException()));
  507. $form = $this->getBuilder()
  508. ->addViewTransformer($transformer)
  509. ->getForm();
  510. $form->submit('foobar');
  511. $this->assertFalse($form->isSynchronized());
  512. }
  513. public function testNotSynchronizedIfModelReverseTransformationFailed()
  514. {
  515. $transformer = $this->getDataTransformer();
  516. $transformer->expects($this->once())
  517. ->method('reverseTransform')
  518. ->will($this->throwException(new TransformationFailedException()));
  519. $form = $this->getBuilder()
  520. ->addModelTransformer($transformer)
  521. ->getForm();
  522. $form->submit('foobar');
  523. $this->assertFalse($form->isSynchronized());
  524. }
  525. public function testEmptyDataCreatedBeforeTransforming()
  526. {
  527. $form = $this->getBuilder()
  528. ->setEmptyData('foo')
  529. ->addViewTransformer(new FixedDataTransformer(array(
  530. '' => '',
  531. // direction is reversed!
  532. 'bar' => 'foo',
  533. )))
  534. ->getForm();
  535. $form->submit('');
  536. $this->assertEquals('bar', $form->getData());
  537. }
  538. public function testEmptyDataFromClosure()
  539. {
  540. $test = $this;
  541. $form = $this->getBuilder()
  542. ->setEmptyData(function ($form) use ($test) {
  543. // the form instance is passed to the closure to allow use
  544. // of form data when creating the empty value
  545. $test->assertInstanceOf('Symfony\Component\Form\FormInterface', $form);
  546. return 'foo';
  547. })
  548. ->addViewTransformer(new FixedDataTransformer(array(
  549. '' => '',
  550. // direction is reversed!
  551. 'bar' => 'foo',
  552. )))
  553. ->getForm();
  554. $form->submit('');
  555. $this->assertEquals('bar', $form->getData());
  556. }
  557. public function testSubmitResetsErrors()
  558. {
  559. $this->form->addError(new FormError('Error!'));
  560. $this->form->submit('foobar');
  561. $this->assertCount(0, $this->form->getErrors());
  562. }
  563. public function testCreateView()
  564. {
  565. $type = $this->getMockBuilder('Symfony\Component\Form\ResolvedFormTypeInterface')->getMock();
  566. $view = $this->getMockBuilder('Symfony\Component\Form\FormView')->getMock();
  567. $form = $this->getBuilder()->setType($type)->getForm();
  568. $type->expects($this->once())
  569. ->method('createView')
  570. ->with($form)
  571. ->will($this->returnValue($view));
  572. $this->assertSame($view, $form->createView());
  573. }
  574. public function testCreateViewWithParent()
  575. {
  576. $type = $this->getMockBuilder('Symfony\Component\Form\ResolvedFormTypeInterface')->getMock();
  577. $view = $this->getMockBuilder('Symfony\Component\Form\FormView')->getMock();
  578. $parentForm = $this->getMockBuilder('Symfony\Component\Form\Test\FormInterface')->getMock();
  579. $parentView = $this->getMockBuilder('Symfony\Component\Form\FormView')->getMock();
  580. $form = $this->getBuilder()->setType($type)->getForm();
  581. $form->setParent($parentForm);
  582. $parentForm->expects($this->once())
  583. ->method('createView')
  584. ->will($this->returnValue($parentView));
  585. $type->expects($this->once())
  586. ->method('createView')
  587. ->with($form, $parentView)
  588. ->will($this->returnValue($view));
  589. $this->assertSame($view, $form->createView());
  590. }
  591. public function testCreateViewWithExplicitParent()
  592. {
  593. $type = $this->getMockBuilder('Symfony\Component\Form\ResolvedFormTypeInterface')->getMock();
  594. $view = $this->getMockBuilder('Symfony\Component\Form\FormView')->getMock();
  595. $parentView = $this->getMockBuilder('Symfony\Component\Form\FormView')->getMock();
  596. $form = $this->getBuilder()->setType($type)->getForm();
  597. $type->expects($this->once())
  598. ->method('createView')
  599. ->with($form, $parentView)
  600. ->will($this->returnValue($view));
  601. $this->assertSame($view, $form->createView($parentView));
  602. }
  603. /**
  604. * @group legacy
  605. */
  606. public function testGetErrorsAsString()
  607. {
  608. $this->form->addError(new FormError('Error!'));
  609. $this->assertEquals("ERROR: Error!\n", $this->form->getErrorsAsString());
  610. }
  611. public function testFormCanHaveEmptyName()
  612. {
  613. $form = $this->getBuilder('')->getForm();
  614. $this->assertEquals('', $form->getName());
  615. }
  616. public function testSetNullParentWorksWithEmptyName()
  617. {
  618. $form = $this->getBuilder('')->getForm();
  619. $form->setParent(null);
  620. $this->assertNull($form->getParent());
  621. }
  622. /**
  623. * @expectedException \Symfony\Component\Form\Exception\LogicException
  624. * @expectedExceptionMessage A form with an empty name cannot have a parent form.
  625. */
  626. public function testFormCannotHaveEmptyNameNotInRootLevel()
  627. {
  628. $this->getBuilder()
  629. ->setCompound(true)
  630. ->setDataMapper($this->getDataMapper())
  631. ->add($this->getBuilder(''))
  632. ->getForm();
  633. }
  634. public function testGetPropertyPathReturnsConfiguredPath()
  635. {
  636. $form = $this->getBuilder()->setPropertyPath('address.street')->getForm();
  637. $this->assertEquals(new PropertyPath('address.street'), $form->getPropertyPath());
  638. }
  639. // see https://github.com/symfony/symfony/issues/3903
  640. public function testGetPropertyPathDefaultsToNameIfParentHasDataClass()
  641. {
  642. $parent = $this->getBuilder(null, null, 'stdClass')
  643. ->setCompound(true)
  644. ->setDataMapper($this->getDataMapper())
  645. ->getForm();
  646. $form = $this->getBuilder('name')->getForm();
  647. $parent->add($form);
  648. $this->assertEquals(new PropertyPath('name'), $form->getPropertyPath());
  649. }
  650. // see https://github.com/symfony/symfony/issues/3903
  651. public function testGetPropertyPathDefaultsToIndexedNameIfParentDataClassIsNull()
  652. {
  653. $parent = $this->getBuilder()
  654. ->setCompound(true)
  655. ->setDataMapper($this->getDataMapper())
  656. ->getForm();
  657. $form = $this->getBuilder('name')->getForm();
  658. $parent->add($form);
  659. $this->assertEquals(new PropertyPath('[name]'), $form->getPropertyPath());
  660. }
  661. public function testGetPropertyPathDefaultsToNameIfFirstParentWithoutInheritDataHasDataClass()
  662. {
  663. $grandParent = $this->getBuilder(null, null, 'stdClass')
  664. ->setCompound(true)
  665. ->setDataMapper($this->getDataMapper())
  666. ->getForm();
  667. $parent = $this->getBuilder()
  668. ->setCompound(true)
  669. ->setDataMapper($this->getDataMapper())
  670. ->setInheritData(true)
  671. ->getForm();
  672. $form = $this->getBuilder('name')->getForm();
  673. $grandParent->add($parent);
  674. $parent->add($form);
  675. $this->assertEquals(new PropertyPath('name'), $form->getPropertyPath());
  676. }
  677. public function testGetPropertyPathDefaultsToIndexedNameIfDataClassOfFirstParentWithoutInheritDataIsNull()
  678. {
  679. $grandParent = $this->getBuilder()
  680. ->setCompound(true)
  681. ->setDataMapper($this->getDataMapper())
  682. ->getForm();
  683. $parent = $this->getBuilder()
  684. ->setCompound(true)
  685. ->setDataMapper($this->getDataMapper())
  686. ->setInheritData(true)
  687. ->getForm();
  688. $form = $this->getBuilder('name')->getForm();
  689. $grandParent->add($parent);
  690. $parent->add($form);
  691. $this->assertEquals(new PropertyPath('[name]'), $form->getPropertyPath());
  692. }
  693. public function testViewDataMayBeObjectIfDataClassIsNull()
  694. {
  695. $object = new \stdClass();
  696. $config = new FormConfigBuilder('name', null, $this->dispatcher);
  697. $config->addViewTransformer(new FixedDataTransformer(array(
  698. '' => '',
  699. 'foo' => $object,
  700. )));
  701. $form = new Form($config);
  702. $form->setData('foo');
  703. $this->assertSame($object, $form->getViewData());
  704. }
  705. public function testViewDataMayBeArrayAccessIfDataClassIsNull()
  706. {
  707. $arrayAccess = $this->getMockBuilder('\ArrayAccess')->getMock();
  708. $config = new FormConfigBuilder('name', null, $this->dispatcher);
  709. $config->addViewTransformer(new FixedDataTransformer(array(
  710. '' => '',
  711. 'foo' => $arrayAccess,
  712. )));
  713. $form = new Form($config);
  714. $form->setData('foo');
  715. $this->assertSame($arrayAccess, $form->getViewData());
  716. }
  717. /**
  718. * @expectedException \Symfony\Component\Form\Exception\LogicException
  719. */
  720. public function testViewDataMustBeObjectIfDataClassIsSet()
  721. {
  722. $config = new FormConfigBuilder('name', 'stdClass', $this->dispatcher);
  723. $config->addViewTransformer(new FixedDataTransformer(array(
  724. '' => '',
  725. 'foo' => array('bar' => 'baz'),
  726. )));
  727. $form = new Form($config);
  728. $form->setData('foo');
  729. }
  730. /**
  731. * @expectedException \Symfony\Component\Form\Exception\RuntimeException
  732. * @expectedExceptionMessage A cycle was detected. Listeners to the PRE_SET_DATA event must not call setData(). You should call setData() on the FormEvent object instead.
  733. */
  734. public function testSetDataCannotInvokeItself()
  735. {
  736. // Cycle detection to prevent endless loops
  737. $config = new FormConfigBuilder('name', 'stdClass', $this->dispatcher);
  738. $config->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) {
  739. $event->getForm()->setData('bar');
  740. });
  741. $form = new Form($config);
  742. $form->setData('foo');
  743. }
  744. public function testSubmittingWrongDataIsIgnored()
  745. {
  746. $called = 0;
  747. $child = $this->getBuilder('child', $this->dispatcher);
  748. $child->addEventListener(FormEvents::PRE_SUBMIT, function () use (&$called) {
  749. ++$called;
  750. });
  751. $parent = $this->getBuilder('parent', new EventDispatcher())
  752. ->setCompound(true)
  753. ->setDataMapper($this->getDataMapper())
  754. ->add($child)
  755. ->getForm();
  756. $parent->submit('not-an-array');
  757. $this->assertSame(0, $called, 'PRE_SUBMIT event listeners are not called for wrong data');
  758. }
  759. public function testHandleRequestForwardsToRequestHandler()
  760. {
  761. $handler = $this->getMockBuilder('Symfony\Component\Form\RequestHandlerInterface')->getMock();
  762. $form = $this->getBuilder()
  763. ->setRequestHandler($handler)
  764. ->getForm();
  765. $handler->expects($this->once())
  766. ->method('handleRequest')
  767. ->with($this->identicalTo($form), 'REQUEST');
  768. $this->assertSame($form, $form->handleRequest('REQUEST'));
  769. }
  770. public function testFormInheritsParentData()
  771. {
  772. $child = $this->getBuilder('child')
  773. ->setInheritData(true);
  774. $parent = $this->getBuilder('parent')
  775. ->setCompound(true)
  776. ->setDataMapper($this->getDataMapper())
  777. ->setData('foo')
  778. ->addModelTransformer(new FixedDataTransformer(array(
  779. 'foo' => 'norm[foo]',
  780. )))
  781. ->addViewTransformer(new FixedDataTransformer(array(
  782. 'norm[foo]' => 'view[foo]',
  783. )))
  784. ->add($child)
  785. ->getForm();
  786. $this->assertSame('foo', $parent->get('child')->getData());
  787. $this->assertSame('norm[foo]', $parent->get('child')->getNormData());
  788. $this->assertSame('view[foo]', $parent->get('child')->getViewData());
  789. }
  790. /**
  791. * @expectedException \Symfony\Component\Form\Exception\RuntimeException
  792. */
  793. public function testInheritDataDisallowsSetData()
  794. {
  795. $form = $this->getBuilder()
  796. ->setInheritData(true)
  797. ->getForm();
  798. $form->setData('foo');
  799. }
  800. /**
  801. * @expectedException \Symfony\Component\Form\Exception\RuntimeException
  802. */
  803. public function testGetDataRequiresParentToBeSetIfInheritData()
  804. {
  805. $form = $this->getBuilder()
  806. ->setInheritData(true)
  807. ->getForm();
  808. $form->getData();
  809. }
  810. /**
  811. * @expectedException \Symfony\Component\Form\Exception\RuntimeException
  812. */
  813. public function testGetNormDataRequiresParentToBeSetIfInheritData()
  814. {
  815. $form = $this->getBuilder()
  816. ->setInheritData(true)
  817. ->getForm();
  818. $form->getNormData();
  819. }
  820. /**
  821. * @expectedException \Symfony\Component\Form\Exception\RuntimeException
  822. */
  823. public function testGetViewDataRequiresParentToBeSetIfInheritData()
  824. {
  825. $form = $this->getBuilder()
  826. ->setInheritData(true)
  827. ->getForm();
  828. $form->getViewData();
  829. }
  830. public function testPostSubmitDataIsNullIfInheritData()
  831. {
  832. $test = $this;
  833. $form = $this->getBuilder()
  834. ->addEventListener(FormEvents::POST_SUBMIT, function (FormEvent $event) use ($test) {
  835. $test->assertNull($event->getData());
  836. })
  837. ->setInheritData(true)
  838. ->getForm();
  839. $form->submit('foo');
  840. }
  841. public function testSubmitIsNeverFiredIfInheritData()
  842. {
  843. $called = 0;
  844. $form = $this->getBuilder()
  845. ->addEventListener(FormEvents::SUBMIT, function () use (&$called) {
  846. ++$called;
  847. })
  848. ->setInheritData(true)
  849. ->getForm();
  850. $form->submit('foo');
  851. $this->assertSame(0, $called, 'The SUBMIT event is not fired when data are inherited from the parent form');
  852. }
  853. public function testInitializeSetsDefaultData()
  854. {
  855. $config = $this->getBuilder()->setData('DEFAULT')->getFormConfig();
  856. $form = $this->getMockBuilder('Symfony\Component\Form\Form')->setMethods(array('setData'))->setConstructorArgs(array($config))->getMock();
  857. $form->expects($this->once())
  858. ->method('setData')
  859. ->with($this->identicalTo('DEFAULT'));
  860. /* @var Form $form */
  861. $form->initialize();
  862. }
  863. /**
  864. * @expectedException \Symfony\Component\Form\Exception\RuntimeException
  865. */
  866. public function testInitializeFailsIfParent()
  867. {
  868. $parent = $this->getBuilder()->setRequired(false)->getForm();
  869. $child = $this->getBuilder()->setRequired(true)->getForm();
  870. $child->setParent($parent);
  871. $child->initialize();
  872. }
  873. /**
  874. * @expectedException \InvalidArgumentException
  875. * @expectedExceptionMessage Custom resolver "Symfony\Component\Form\Tests\Fixtures\CustomOptionsResolver" must extend "Symfony\Component\OptionsResolver\OptionsResolver".
  876. */
  877. public function testCustomOptionsResolver()
  878. {
  879. $fooType = new Fixtures\LegacyFooType();
  880. $resolver = new Fixtures\CustomOptionsResolver();
  881. $fooType->setDefaultOptions($resolver);
  882. }
  883. /**
  884. * @expectedException \Symfony\Component\Form\Exception\RuntimeException
  885. * @expectedExceptionMessage A cycle was detected. Listeners to the PRE_SET_DATA event must not call getData() if the form data has not already been set. You should call getData() on the FormEvent object instead.
  886. */
  887. public function testCannotCallGetDataInPreSetDataListenerIfDataHasNotAlreadyBeenSet()
  888. {
  889. $config = new FormConfigBuilder('name', 'stdClass', $this->dispatcher);
  890. $config->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) {
  891. $event->getForm()->getData();
  892. });
  893. $form = new Form($config);
  894. $form->setData('foo');
  895. }
  896. /**
  897. * @expectedException \Symfony\Component\Form\Exception\RuntimeException
  898. * @expectedExceptionMessage A cycle was detected. Listeners to the PRE_SET_DATA event must not call getNormData() if the form data has not already been set.
  899. */
  900. public function testCannotCallGetNormDataInPreSetDataListener()
  901. {
  902. $config = new FormConfigBuilder('name', 'stdClass', $this->dispatcher);
  903. $config->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) {
  904. $event->getForm()->getNormData();
  905. });
  906. $form = new Form($config);
  907. $form->setData('foo');
  908. }
  909. /**
  910. * @expectedException \Symfony\Component\Form\Exception\RuntimeException
  911. * @expectedExceptionMessage A cycle was detected. Listeners to the PRE_SET_DATA event must not call getViewData() if the form data has not already been set.
  912. */
  913. public function testCannotCallGetViewDataInPreSetDataListener()
  914. {
  915. $config = new FormConfigBuilder('name', 'stdClass', $this->dispatcher);
  916. $config->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) {
  917. $event->getForm()->getViewData();
  918. });
  919. $form = new Form($config);
  920. $form->setData('foo');
  921. }
  922. protected function createForm()
  923. {
  924. return $this->getBuilder()->getForm();
  925. }
  926. }