FormTest.php 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien.potencier@symfony-project.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. require_once __DIR__ . '/Fixtures/Author.php';
  12. require_once __DIR__ . '/Fixtures/TestField.php';
  13. require_once __DIR__ . '/Fixtures/TestForm.php';
  14. use Symfony\Component\Form\Form;
  15. use Symfony\Component\Form\FormContext;
  16. use Symfony\Component\Form\Field;
  17. use Symfony\Component\Form\FieldError;
  18. use Symfony\Component\Form\HiddenField;
  19. use Symfony\Component\Form\PropertyPath;
  20. use Symfony\Component\HttpFoundation\Request;
  21. use Symfony\Component\HttpFoundation\File\UploadedFile;
  22. use Symfony\Component\Validator\ConstraintViolation;
  23. use Symfony\Component\Validator\ConstraintViolationList;
  24. use Symfony\Tests\Component\Form\Fixtures\Author;
  25. use Symfony\Tests\Component\Form\Fixtures\TestField;
  26. use Symfony\Tests\Component\Form\Fixtures\TestForm;
  27. class FormTest_PreconfiguredForm extends Form
  28. {
  29. protected function configure()
  30. {
  31. $this->add(new Field('firstName'));
  32. parent::configure();
  33. }
  34. }
  35. class TestSetDataBeforeConfigureForm extends Form
  36. {
  37. protected $testCase;
  38. protected $object;
  39. public function __construct($testCase, $name, $object, $validator)
  40. {
  41. $this->testCase = $testCase;
  42. $this->object = $object;
  43. parent::__construct($name, $object, $validator);
  44. }
  45. protected function configure()
  46. {
  47. $this->testCase->assertEquals($this->object, $this->getData());
  48. parent::configure();
  49. }
  50. }
  51. class FormTest extends \PHPUnit_Framework_TestCase
  52. {
  53. protected $validator;
  54. protected $form;
  55. public static function setUpBeforeClass()
  56. {
  57. @session_start();
  58. }
  59. protected function setUp()
  60. {
  61. $this->validator = $this->createMockValidator();
  62. $this->form = new Form('author', array('validator' => $this->validator));
  63. }
  64. public function testNoCsrfProtectionByDefault()
  65. {
  66. $form = new Form('author');
  67. $this->assertFalse($form->isCsrfProtected());
  68. }
  69. public function testCsrfProtectionCanBeEnabled()
  70. {
  71. $form = new Form('author', array(
  72. 'csrf_provider' => $this->createMockCsrfProvider(),
  73. ));
  74. $this->assertTrue($form->isCsrfProtected());
  75. }
  76. public function testCsrfFieldNameCanBeSet()
  77. {
  78. $form = new Form('author', array(
  79. 'csrf_provider' => $this->createMockCsrfProvider(),
  80. 'csrf_field_name' => 'foobar',
  81. ));
  82. $this->assertEquals('foobar', $form->getCsrfFieldName());
  83. }
  84. public function testCsrfProtectedFormsHaveExtraField()
  85. {
  86. $provider = $this->createMockCsrfProvider();
  87. $provider->expects($this->once())
  88. ->method('generateCsrfToken')
  89. ->with($this->equalTo('Symfony\Component\Form\Form'))
  90. ->will($this->returnValue('ABCDEF'));
  91. $form = new Form('author', array(
  92. 'csrf_provider' => $provider,
  93. ));
  94. $this->assertTrue($form->has($this->form->getCsrfFieldName()));
  95. $field = $form->get($form->getCsrfFieldName());
  96. $this->assertTrue($field instanceof HiddenField);
  97. $this->assertEquals('ABCDEF', $field->getDisplayedData());
  98. }
  99. public function testIsCsrfTokenValidPassesIfCsrfProtectionIsDisabled()
  100. {
  101. $this->form->bind(array());
  102. $this->assertTrue($this->form->isCsrfTokenValid());
  103. }
  104. public function testIsCsrfTokenValidPasses()
  105. {
  106. $provider = $this->createMockCsrfProvider();
  107. $provider->expects($this->once())
  108. ->method('isCsrfTokenValid')
  109. ->with($this->equalTo('Symfony\Component\Form\Form'), $this->equalTo('ABCDEF'))
  110. ->will($this->returnValue(true));
  111. $form = new Form('author', array(
  112. 'csrf_provider' => $provider,
  113. 'validator' => $this->validator,
  114. ));
  115. $field = $form->getCsrfFieldName();
  116. $form->bind(array($field => 'ABCDEF'));
  117. $this->assertTrue($form->isCsrfTokenValid());
  118. }
  119. public function testIsCsrfTokenValidFails()
  120. {
  121. $provider = $this->createMockCsrfProvider();
  122. $provider->expects($this->once())
  123. ->method('isCsrfTokenValid')
  124. ->with($this->equalTo('Symfony\Component\Form\Form'), $this->equalTo('ABCDEF'))
  125. ->will($this->returnValue(false));
  126. $form = new Form('author', array(
  127. 'csrf_provider' => $provider,
  128. 'validator' => $this->validator,
  129. ));
  130. $field = $form->getCsrfFieldName();
  131. $form->bind(array($field => 'ABCDEF'));
  132. $this->assertFalse($form->isCsrfTokenValid());
  133. }
  134. public function testValidationGroupNullByDefault()
  135. {
  136. $this->assertNull($this->form->getValidationGroups());
  137. }
  138. public function testValidationGroupsCanBeSetToString()
  139. {
  140. $form = new Form('author', array(
  141. 'validation_groups' => 'group',
  142. ));
  143. $this->assertEquals(array('group'), $form->getValidationGroups());
  144. }
  145. public function testValidationGroupsCanBeSetToArray()
  146. {
  147. $form = new Form('author', array(
  148. 'validation_groups' => array('group1', 'group2'),
  149. ));
  150. $this->assertEquals(array('group1', 'group2'), $form->getValidationGroups());
  151. }
  152. public function testBindUsesValidationGroups()
  153. {
  154. $field = $this->createMockField('firstName');
  155. $form = new Form('author', array(
  156. 'validation_groups' => 'group',
  157. 'validator' => $this->validator,
  158. ));
  159. $form->add($field);
  160. $this->validator->expects($this->once())
  161. ->method('validate')
  162. ->with($this->equalTo($form), $this->equalTo(array('group')));
  163. $form->bind(array()); // irrelevant
  164. }
  165. // public function testBindThrowsExceptionIfNoValidatorIsSet()
  166. // {
  167. // $field = $this->createMockField('firstName');
  168. // $form = new Form('author');
  169. // $form->add($field);
  170. //
  171. // $this->setExpectedException('Symfony\Component\Form\Exception\FormException');
  172. //
  173. // $form->bind(array()); // irrelevant
  174. // }
  175. public function testBindRequest()
  176. {
  177. $values = array(
  178. 'author' => array(
  179. 'name' => 'Bernhard',
  180. 'image' => array('filename' => 'foobar.png'),
  181. ),
  182. );
  183. $files = array(
  184. 'author' => array(
  185. 'error' => array('image' => array('file' => UPLOAD_ERR_OK)),
  186. 'name' => array('image' => array('file' => 'upload.png')),
  187. 'size' => array('image' => array('file' => 123)),
  188. 'tmp_name' => array('image' => array('file' => 'abcdef.png')),
  189. 'type' => array('image' => array('file' => 'image/png')),
  190. ),
  191. );
  192. $request = new Request(array(), $values, array(), array(), $files);
  193. $form = new Form('author', array('validator' => $this->validator));
  194. $form->add(new TestField('name'));
  195. $imageForm = new Form('image');
  196. $imageForm->add(new TestField('file'));
  197. $imageForm->add(new TestField('filename'));
  198. $form->add($imageForm);
  199. $form->bindRequest($request);
  200. $file = new UploadedFile('abcdef.png', 'upload.png', 'image/png', 123, UPLOAD_ERR_OK);
  201. $this->assertEquals('Bernhard', $form['name']->getData());
  202. $this->assertEquals('foobar.png', $form['image']['filename']->getData());
  203. $this->assertEquals($file, $form['image']['file']->getData());
  204. }
  205. public function testBindGlobals()
  206. {
  207. $_POST = array(
  208. 'author' => array(
  209. 'name' => 'Bernhard',
  210. 'image' => array('filename' => 'foobar.png'),
  211. ),
  212. );
  213. $_FILES = array(
  214. 'author' => array(
  215. 'error' => array('image' => array('file' => UPLOAD_ERR_OK)),
  216. 'name' => array('image' => array('file' => 'upload.png')),
  217. 'size' => array('image' => array('file' => 123)),
  218. 'tmp_name' => array('image' => array('file' => 'abcdef.png')),
  219. 'type' => array('image' => array('file' => 'image/png')),
  220. ),
  221. );
  222. $form = new Form('author', array('validator' => $this->validator));
  223. $form->add(new TestField('name'));
  224. $imageForm = new Form('image');
  225. $imageForm->add(new TestField('file'));
  226. $imageForm->add(new TestField('filename'));
  227. $form->add($imageForm);
  228. $form->bindGlobals();
  229. $file = new UploadedFile('abcdef.png', 'upload.png', 'image/png', 123, UPLOAD_ERR_OK);
  230. $this->assertEquals('Bernhard', $form['name']->getData());
  231. $this->assertEquals('foobar.png', $form['image']['filename']->getData());
  232. $this->assertEquals($file, $form['image']['file']->getData());
  233. }
  234. public function testUpdateFromPropertyIsIgnoredIfPropertyPathIsNull()
  235. {
  236. $author = new Author();
  237. $author->child = new Author();
  238. $standaloneChild = new Author();
  239. $form = new Form('child');
  240. $form->setData($standaloneChild);
  241. $form->setPropertyPath(null);
  242. $form->updateFromProperty($author);
  243. // should not be $author->child!!
  244. $this->assertSame($standaloneChild, $form->getData());
  245. }
  246. public function testUpdatePropertyIsIgnoredIfPropertyPathIsNull()
  247. {
  248. $author = new Author();
  249. $author->child = $child = new Author();
  250. $standaloneChild = new Author();
  251. $form = new Form('child');
  252. $form->setData($standaloneChild);
  253. $form->setPropertyPath(null);
  254. $form->updateProperty($author);
  255. // $author->child was not modified
  256. $this->assertSame($child, $author->child);
  257. }
  258. public function testSupportsArrayAccess()
  259. {
  260. $form = new Form('author');
  261. $form->add($this->createMockField('firstName'));
  262. $this->assertEquals($form->get('firstName'), $form['firstName']);
  263. $this->assertTrue(isset($form['firstName']));
  264. }
  265. public function testSupportsUnset()
  266. {
  267. $form = new Form('author');
  268. $form->add($this->createMockField('firstName'));
  269. unset($form['firstName']);
  270. $this->assertFalse(isset($form['firstName']));
  271. }
  272. public function testDoesNotSupportAddingFields()
  273. {
  274. $form = new Form('author');
  275. $this->setExpectedException('LogicException');
  276. $form[] = $this->createMockField('lastName');
  277. }
  278. public function testSupportsCountable()
  279. {
  280. $form = new Form('group');
  281. $form->add($this->createMockField('firstName'));
  282. $form->add($this->createMockField('lastName'));
  283. $this->assertEquals(2, count($form));
  284. $form->add($this->createMockField('australian'));
  285. $this->assertEquals(3, count($form));
  286. }
  287. public function testSupportsIterable()
  288. {
  289. $form = new Form('group');
  290. $form->add($field1 = $this->createMockField('field1'));
  291. $form->add($field2 = $this->createMockField('field2'));
  292. $form->add($field3 = $this->createMockField('field3'));
  293. $expected = array(
  294. 'field1' => $field1,
  295. 'field2' => $field2,
  296. 'field3' => $field3,
  297. );
  298. $this->assertEquals($expected, iterator_to_array($form));
  299. }
  300. public function testIsBound()
  301. {
  302. $form = new Form('author', array('validator' => $this->validator));
  303. $this->assertFalse($form->isBound());
  304. $form->bind(array('firstName' => 'Bernhard'));
  305. $this->assertTrue($form->isBound());
  306. }
  307. public function testValidIfAllFieldsAreValid()
  308. {
  309. $form = new Form('author', array('validator' => $this->validator));
  310. $form->add($this->createValidMockField('firstName'));
  311. $form->add($this->createValidMockField('lastName'));
  312. $form->bind(array('firstName' => 'Bernhard', 'lastName' => 'Potencier'));
  313. $this->assertTrue($form->isValid());
  314. }
  315. public function testInvalidIfFieldIsInvalid()
  316. {
  317. $form = new Form('author', array('validator' => $this->validator));
  318. $form->add($this->createInvalidMockField('firstName'));
  319. $form->add($this->createValidMockField('lastName'));
  320. $form->bind(array('firstName' => 'Bernhard', 'lastName' => 'Potencier'));
  321. $this->assertFalse($form->isValid());
  322. }
  323. public function testInvalidIfBoundWithExtraFields()
  324. {
  325. $form = new Form('author', array('validator' => $this->validator));
  326. $form->add($this->createValidMockField('firstName'));
  327. $form->add($this->createValidMockField('lastName'));
  328. $form->bind(array('foo' => 'bar', 'firstName' => 'Bernhard', 'lastName' => 'Potencier'));
  329. $this->assertTrue($form->isBoundWithExtraFields());
  330. }
  331. public function testHasNoErrorsIfOnlyFieldHasErrors()
  332. {
  333. $form = new Form('author', array('validator' => $this->validator));
  334. $form->add($this->createInvalidMockField('firstName'));
  335. $form->bind(array('firstName' => 'Bernhard'));
  336. $this->assertFalse($form->hasErrors());
  337. }
  338. public function testBindForwardsPreprocessedData()
  339. {
  340. $field = $this->createMockField('firstName');
  341. $form = $this->getMock(
  342. 'Symfony\Component\Form\Form',
  343. array('preprocessData'), // only mock preprocessData()
  344. array('author', array('validator' => $this->validator))
  345. );
  346. // The data array is prepared directly after binding
  347. $form->expects($this->once())
  348. ->method('preprocessData')
  349. ->with($this->equalTo(array('firstName' => 'Bernhard')))
  350. ->will($this->returnValue(array('firstName' => 'preprocessed[Bernhard]')));
  351. $form->add($field);
  352. // The preprocessed data is then forwarded to the fields
  353. $field->expects($this->once())
  354. ->method('bind')
  355. ->with($this->equalTo('preprocessed[Bernhard]'));
  356. $form->bind(array('firstName' => 'Bernhard'));
  357. }
  358. public function testBindForwardsNullIfValueIsMissing()
  359. {
  360. $field = $this->createMockField('firstName');
  361. $field->expects($this->once())
  362. ->method('bind')
  363. ->with($this->equalTo(null));
  364. $form = new Form('author', array('validator' => $this->validator));
  365. $form->add($field);
  366. $form->bind(array());
  367. }
  368. public function testAddErrorMapsFieldValidationErrorsOntoFields()
  369. {
  370. $error = new FieldError('Message');
  371. $field = $this->createMockField('firstName');
  372. $field->expects($this->once())
  373. ->method('addError')
  374. ->with($this->equalTo($error));
  375. $form = new Form('author');
  376. $form->add($field);
  377. $path = new PropertyPath('fields[firstName].data');
  378. $form->addError($error, $path->getIterator(), Form::FIELD_ERROR);
  379. }
  380. public function testAddErrorMapsFieldValidationErrorsOntoFieldsWithinNestedForms()
  381. {
  382. $error = new FieldError('Message');
  383. $field = $this->createMockField('firstName');
  384. $field->expects($this->once())
  385. ->method('addError')
  386. ->with($this->equalTo($error));
  387. $form = new Form('author');
  388. $innerGroup = new Form('names');
  389. $innerGroup->add($field);
  390. $form->add($innerGroup);
  391. $path = new PropertyPath('fields[names].fields[firstName].data');
  392. $form->addError($error, $path->getIterator(), Form::FIELD_ERROR);
  393. }
  394. public function testAddErrorKeepsFieldValidationErrorsIfFieldNotFound()
  395. {
  396. $error = new FieldError('Message');
  397. $field = $this->createMockField('foo');
  398. $field->expects($this->never())
  399. ->method('addError');
  400. $form = new Form('author');
  401. $form->add($field);
  402. $path = new PropertyPath('fields[bar].data');
  403. $form->addError($error, $path->getIterator(), Form::FIELD_ERROR);
  404. $this->assertEquals(array($error), $form->getErrors());
  405. }
  406. public function testAddErrorKeepsFieldValidationErrorsIfFieldIsHidden()
  407. {
  408. $error = new FieldError('Message');
  409. $field = $this->createMockField('firstName');
  410. $field->expects($this->any())
  411. ->method('isHidden')
  412. ->will($this->returnValue(true));
  413. $field->expects($this->never())
  414. ->method('addError');
  415. $form = new Form('author');
  416. $form->add($field);
  417. $path = new PropertyPath('fields[firstName].data');
  418. $form->addError($error, $path->getIterator(), Form::FIELD_ERROR);
  419. $this->assertEquals(array($error), $form->getErrors());
  420. }
  421. public function testAddErrorMapsDataValidationErrorsOntoFields()
  422. {
  423. $error = new FieldError('Message');
  424. // path is expected to point at "firstName"
  425. $expectedPath = new PropertyPath('firstName');
  426. $expectedPathIterator = $expectedPath->getIterator();
  427. $field = $this->createMockField('firstName');
  428. $field->expects($this->any())
  429. ->method('getPropertyPath')
  430. ->will($this->returnValue(new PropertyPath('firstName')));
  431. $field->expects($this->once())
  432. ->method('addError')
  433. ->with($this->equalTo($error), $this->equalTo($expectedPathIterator), $this->equalTo(Form::DATA_ERROR));
  434. $form = new Form('author');
  435. $form->add($field);
  436. $path = new PropertyPath('firstName');
  437. $form->addError($error, $path->getIterator(), Form::DATA_ERROR);
  438. }
  439. public function testAddErrorKeepsDataValidationErrorsIfFieldNotFound()
  440. {
  441. $error = new FieldError('Message');
  442. $field = $this->createMockField('foo');
  443. $field->expects($this->any())
  444. ->method('getPropertyPath')
  445. ->will($this->returnValue(new PropertyPath('foo')));
  446. $field->expects($this->never())
  447. ->method('addError');
  448. $form = new Form('author');
  449. $form->add($field);
  450. $path = new PropertyPath('bar');
  451. $form->addError($error, $path->getIterator(), Form::DATA_ERROR);
  452. }
  453. public function testAddErrorKeepsDataValidationErrorsIfFieldIsHidden()
  454. {
  455. $error = new FieldError('Message');
  456. $field = $this->createMockField('firstName');
  457. $field->expects($this->any())
  458. ->method('isHidden')
  459. ->will($this->returnValue(true));
  460. $field->expects($this->any())
  461. ->method('getPropertyPath')
  462. ->will($this->returnValue(new PropertyPath('firstName')));
  463. $field->expects($this->never())
  464. ->method('addError');
  465. $form = new Form('author');
  466. $form->add($field);
  467. $path = new PropertyPath('firstName');
  468. $form->addError($error, $path->getIterator(), Form::DATA_ERROR);
  469. }
  470. public function testAddErrorMapsDataValidationErrorsOntoNestedFields()
  471. {
  472. $error = new FieldError('Message');
  473. // path is expected to point at "street"
  474. $expectedPath = new PropertyPath('address.street');
  475. $expectedPathIterator = $expectedPath->getIterator();
  476. $expectedPathIterator->next();
  477. $field = $this->createMockField('address');
  478. $field->expects($this->any())
  479. ->method('getPropertyPath')
  480. ->will($this->returnValue(new PropertyPath('address')));
  481. $field->expects($this->once())
  482. ->method('addError')
  483. ->with($this->equalTo($error), $this->equalTo($expectedPathIterator), $this->equalTo(Form::DATA_ERROR));
  484. $form = new Form('author');
  485. $form->add($field);
  486. $path = new PropertyPath('address.street');
  487. $form->addError($error, $path->getIterator(), Form::DATA_ERROR);
  488. }
  489. public function testAddErrorMapsErrorsOntoFieldsInVirtualGroups()
  490. {
  491. $error = new FieldError('Message');
  492. // path is expected to point at "address"
  493. $expectedPath = new PropertyPath('address');
  494. $expectedPathIterator = $expectedPath->getIterator();
  495. $field = $this->createMockField('address');
  496. $field->expects($this->any())
  497. ->method('getPropertyPath')
  498. ->will($this->returnValue(new PropertyPath('address')));
  499. $field->expects($this->once())
  500. ->method('addError')
  501. ->with($this->equalTo($error), $this->equalTo($expectedPathIterator), $this->equalTo(Form::DATA_ERROR));
  502. $form = new Form('author');
  503. $nestedForm = new Form('nested', array('virtual' => true));
  504. $nestedForm->add($field);
  505. $form->add($nestedForm);
  506. $path = new PropertyPath('address');
  507. $form->addError($error, $path->getIterator(), Form::DATA_ERROR);
  508. }
  509. public function testAddThrowsExceptionIfAlreadyBound()
  510. {
  511. $form = new Form('author', array('validator' => $this->validator));
  512. $form->add($this->createMockField('firstName'));
  513. $form->bind(array('firstName' => 'Bernhard'));
  514. $this->setExpectedException('Symfony\Component\Form\Exception\AlreadyBoundException');
  515. $form->add($this->createMockField('lastName'));
  516. }
  517. public function testAddSetsFieldParent()
  518. {
  519. $form = new Form('author');
  520. $field = $this->createMockField('firstName');
  521. $field->expects($this->once())
  522. ->method('setParent');
  523. // PHPUnit fails to compare infinitely recursive objects
  524. //->with($this->equalTo($form));
  525. $form->add($field);
  526. }
  527. public function testRemoveUnsetsFieldParent()
  528. {
  529. $form = new Form('author');
  530. $field = $this->createMockField('firstName');
  531. $field->expects($this->exactly(2))
  532. ->method('setParent');
  533. // PHPUnit fails to compare subsequent method calls with different arguments
  534. $form->add($field);
  535. $form->remove('firstName');
  536. }
  537. public function testAddUpdatesFieldFromTransformedData()
  538. {
  539. $originalAuthor = new Author();
  540. $transformedAuthor = new Author();
  541. // the authors should differ to make sure the test works
  542. $transformedAuthor->firstName = 'Foo';
  543. $form = new TestForm('author');
  544. $transformer = $this->createMockTransformer();
  545. $transformer->expects($this->once())
  546. ->method('transform')
  547. ->with($this->equalTo($originalAuthor))
  548. ->will($this->returnValue($transformedAuthor));
  549. $form->setValueTransformer($transformer);
  550. $form->setData($originalAuthor);
  551. $field = $this->createMockField('firstName');
  552. $field->expects($this->any())
  553. ->method('getPropertyPath')
  554. ->will($this->returnValue(new PropertyPath('firstName')));
  555. $field->expects($this->once())
  556. ->method('updateFromProperty')
  557. ->with($this->equalTo($transformedAuthor));
  558. $form->add($field);
  559. }
  560. public function testAddDoesNotUpdateFieldIfTransformedDataIsEmpty()
  561. {
  562. $originalAuthor = new Author();
  563. $form = new TestForm('author');
  564. $transformer = $this->createMockTransformer();
  565. $transformer->expects($this->once())
  566. ->method('transform')
  567. ->with($this->equalTo($originalAuthor))
  568. ->will($this->returnValue(''));
  569. $form->setValueTransformer($transformer);
  570. $form->setData($originalAuthor);
  571. $field = $this->createMockField('firstName');
  572. $field->expects($this->never())
  573. ->method('updateFromProperty');
  574. $form->add($field);
  575. }
  576. /**
  577. * @expectedException Symfony\Component\Form\Exception\UnexpectedTypeException
  578. */
  579. public function testAddThrowsExceptionIfNoFieldOrString()
  580. {
  581. $form = new Form('author');
  582. $form->add(1234);
  583. }
  584. /**
  585. * @expectedException Symfony\Component\Form\Exception\FormException
  586. */
  587. public function testAddThrowsExceptionIfAnonymousField()
  588. {
  589. $form = new Form('author');
  590. $field = $this->createMockField('');
  591. $form->add($field);
  592. }
  593. public function testAddThrowsExceptionIfStringButNoFieldFactory()
  594. {
  595. $rootForm = $this->createMockForm();
  596. $rootForm->expects($this->once())
  597. ->method('getFieldFactory')
  598. ->will($this->returnValue(null));
  599. $form = new Form('author');
  600. $form->setParent($rootForm);
  601. $this->setExpectedException('\LogicException');
  602. $form->add('firstName');
  603. }
  604. public function testAddUsesFieldFromFactoryIfStringIsGiven()
  605. {
  606. $author = new \stdClass();
  607. $field = $this->createMockField('firstName');
  608. $factory = $this->getMock('Symfony\Component\Form\FieldFactory\FieldFactoryInterface');
  609. $factory->expects($this->once())
  610. ->method('getInstance')
  611. ->with($this->equalTo($author), $this->equalTo('firstName'), $this->equalTo(array('foo' => 'bar')))
  612. ->will($this->returnValue($field));
  613. $rootForm = $this->createMockForm();
  614. $rootForm->expects($this->once())
  615. ->method('getFieldFactory')
  616. ->will($this->returnValue($factory));
  617. $form = new Form('author');
  618. $form->setParent($rootForm);
  619. $form->setData($author);
  620. $form->add('firstName', array('foo' => 'bar'));
  621. $this->assertSame($field, $form['firstName']);
  622. }
  623. public function testSetDataUpdatesAllFieldsFromTransformedData()
  624. {
  625. $originalAuthor = new Author();
  626. $transformedAuthor = new Author();
  627. // the authors should differ to make sure the test works
  628. $transformedAuthor->firstName = 'Foo';
  629. $form = new TestForm('author');
  630. $transformer = $this->createMockTransformer();
  631. $transformer->expects($this->once())
  632. ->method('transform')
  633. ->with($this->equalTo($originalAuthor))
  634. ->will($this->returnValue($transformedAuthor));
  635. $form->setValueTransformer($transformer);
  636. $field = $this->createMockField('firstName');
  637. $field->expects($this->once())
  638. ->method('updateFromProperty')
  639. ->with($this->equalTo($transformedAuthor));
  640. $form->add($field);
  641. $field = $this->createMockField('lastName');
  642. $field->expects($this->once())
  643. ->method('updateFromProperty')
  644. ->with($this->equalTo($transformedAuthor));
  645. $form->add($field);
  646. $form->setData($originalAuthor);
  647. }
  648. /**
  649. * The use case for this test are groups whose fields should be mapped
  650. * directly onto properties of the form's object.
  651. *
  652. * Example:
  653. *
  654. * <code>
  655. * $dateRangeField = new Form('dateRange');
  656. * $dateRangeField->add(new DateField('startDate'));
  657. * $dateRangeField->add(new DateField('endDate'));
  658. * $form->add($dateRangeField);
  659. * </code>
  660. *
  661. * If $dateRangeField is not virtual, the property "dateRange" must be
  662. * present on the form's object. In this property, an object or array
  663. * with the properties "startDate" and "endDate" is expected.
  664. *
  665. * If $dateRangeField is virtual though, it's children are mapped directly
  666. * onto the properties "startDate" and "endDate" of the form's object.
  667. */
  668. public function testSetDataSkipsVirtualForms()
  669. {
  670. $author = new Author();
  671. $author->firstName = 'Foo';
  672. $form = new Form('author');
  673. $nestedForm = new Form('personal_data', array(
  674. 'virtual' => true,
  675. ));
  676. // both fields are in the nested group but receive the object of the
  677. // top-level group because the nested group is virtual
  678. $field = $this->createMockField('firstName');
  679. $field->expects($this->once())
  680. ->method('updateFromProperty')
  681. ->with($this->equalTo($author));
  682. $nestedForm->add($field);
  683. $field = $this->createMockField('lastName');
  684. $field->expects($this->once())
  685. ->method('updateFromProperty')
  686. ->with($this->equalTo($author));
  687. $nestedForm->add($field);
  688. $form->add($nestedForm);
  689. $form->setData($author);
  690. }
  691. public function testSetDataThrowsAnExceptionIfArgumentIsNotObjectOrArray()
  692. {
  693. $form = new Form('author');
  694. $this->setExpectedException('InvalidArgumentException');
  695. $form->setData('foobar');
  696. }
  697. public function testBindUpdatesTransformedDataFromAllFields()
  698. {
  699. $originalAuthor = new Author();
  700. $transformedAuthor = new Author();
  701. // the authors should differ to make sure the test works
  702. $transformedAuthor->firstName = 'Foo';
  703. $form = new TestForm('author', array('validator' => $this->validator));
  704. $transformer = $this->createMockTransformer();
  705. $transformer->expects($this->exactly(2))
  706. ->method('transform')
  707. // the method is first called with NULL, then
  708. // with $originalAuthor -> not testable by PHPUnit
  709. // ->with($this->equalTo(null))
  710. // ->with($this->equalTo($originalAuthor))
  711. ->will($this->returnValue($transformedAuthor));
  712. $form->setValueTransformer($transformer);
  713. $form->setData($originalAuthor);
  714. $field = $this->createMockField('firstName');
  715. $field->expects($this->once())
  716. ->method('updateProperty')
  717. ->with($this->equalTo($transformedAuthor));
  718. $form->add($field);
  719. $field = $this->createMockField('lastName');
  720. $field->expects($this->once())
  721. ->method('updateProperty')
  722. ->with($this->equalTo($transformedAuthor));
  723. $form->add($field);
  724. $form->bind(array()); // irrelevant
  725. }
  726. public function testGetDataReturnsObject()
  727. {
  728. $form = new Form('author');
  729. $object = new \stdClass();
  730. $form->setData($object);
  731. $this->assertEquals($object, $form->getData());
  732. }
  733. public function testGetDisplayedDataForwardsCall()
  734. {
  735. $field = $this->createValidMockField('firstName');
  736. $field->expects($this->atLeastOnce())
  737. ->method('getDisplayedData')
  738. ->will($this->returnValue('Bernhard'));
  739. $form = new Form('author');
  740. $form->add($field);
  741. $this->assertEquals(array('firstName' => 'Bernhard'), $form->getDisplayedData());
  742. }
  743. public function testIsMultipartIfAnyFieldIsMultipart()
  744. {
  745. $form = new Form('author');
  746. $form->add($this->createMultipartMockField('firstName'));
  747. $form->add($this->createNonMultipartMockField('lastName'));
  748. $this->assertTrue($form->isMultipart());
  749. }
  750. public function testIsNotMultipartIfNoFieldIsMultipart()
  751. {
  752. $form = new Form('author');
  753. $form->add($this->createNonMultipartMockField('firstName'));
  754. $form->add($this->createNonMultipartMockField('lastName'));
  755. $this->assertFalse($form->isMultipart());
  756. }
  757. public function testSupportsClone()
  758. {
  759. $form = new Form('author');
  760. $form->add($this->createMockField('firstName'));
  761. $clone = clone $form;
  762. $this->assertNotSame($clone['firstName'], $form['firstName']);
  763. }
  764. public function testBindWithoutPriorSetData()
  765. {
  766. return; // TODO
  767. $field = $this->createMockField('firstName');
  768. $field->expects($this->any())
  769. ->method('getData')
  770. ->will($this->returnValue('Bernhard'));
  771. $form = new Form('author');
  772. $form->add($field);
  773. $form->bind(array('firstName' => 'Bernhard'));
  774. $this->assertEquals(array('firstName' => 'Bernhard'), $form->getData());
  775. }
  776. public function testGetHiddenFieldsReturnsOnlyHiddenFields()
  777. {
  778. $form = $this->getGroupWithBothVisibleAndHiddenField();
  779. $hiddenFields = $form->getHiddenFields(true, false);
  780. $this->assertSame(array($form['hiddenField']), $hiddenFields);
  781. }
  782. public function testGetVisibleFieldsReturnsOnlyVisibleFields()
  783. {
  784. $form = $this->getGroupWithBothVisibleAndHiddenField();
  785. $visibleFields = $form->getVisibleFields(true, false);
  786. $this->assertSame(array($form['visibleField']), $visibleFields);
  787. }
  788. /**
  789. * Create a group containing two fields, "visibleField" and "hiddenField"
  790. *
  791. * @return Form
  792. */
  793. protected function getGroupWithBothVisibleAndHiddenField()
  794. {
  795. $form = new Form('testGroup');
  796. // add a visible field
  797. $visibleField = $this->createMockField('visibleField');
  798. $visibleField->expects($this->once())
  799. ->method('isHidden')
  800. ->will($this->returnValue(false));
  801. $form->add($visibleField);
  802. // add a hidden field
  803. $hiddenField = $this->createMockField('hiddenField');
  804. $hiddenField->expects($this->once())
  805. ->method('isHidden')
  806. ->will($this->returnValue(true));
  807. $form->add($hiddenField);
  808. return $form;
  809. }
  810. protected function createMockField($key)
  811. {
  812. $field = $this->getMock(
  813. 'Symfony\Component\Form\FieldInterface',
  814. array(),
  815. array(),
  816. '',
  817. false, // don't use constructor
  818. false // don't call parent::__clone
  819. );
  820. $field->expects($this->any())
  821. ->method('getKey')
  822. ->will($this->returnValue($key));
  823. return $field;
  824. }
  825. protected function createMockForm()
  826. {
  827. $form = $this->getMock(
  828. 'Symfony\Component\Form\Form',
  829. array(),
  830. array(),
  831. '',
  832. false, // don't use constructor
  833. false // don't call parent::__clone)
  834. );
  835. $form->expects($this->any())
  836. ->method('getRoot')
  837. ->will($this->returnValue($form));
  838. return $form;
  839. }
  840. protected function createInvalidMockField($key)
  841. {
  842. $field = $this->createMockField($key);
  843. $field->expects($this->any())
  844. ->method('isValid')
  845. ->will($this->returnValue(false));
  846. return $field;
  847. }
  848. protected function createValidMockField($key)
  849. {
  850. $field = $this->createMockField($key);
  851. $field->expects($this->any())
  852. ->method('isValid')
  853. ->will($this->returnValue(true));
  854. return $field;
  855. }
  856. protected function createNonMultipartMockField($key)
  857. {
  858. $field = $this->createMockField($key);
  859. $field->expects($this->any())
  860. ->method('isMultipart')
  861. ->will($this->returnValue(false));
  862. return $field;
  863. }
  864. protected function createMultipartMockField($key)
  865. {
  866. $field = $this->createMockField($key);
  867. $field->expects($this->any())
  868. ->method('isMultipart')
  869. ->will($this->returnValue(true));
  870. return $field;
  871. }
  872. protected function createMockTransformer()
  873. {
  874. return $this->getMock('Symfony\Component\Form\ValueTransformer\ValueTransformerInterface', array(), array(), '', false, false);
  875. }
  876. protected function createMockValidator()
  877. {
  878. return $this->getMock('Symfony\Component\Validator\ValidatorInterface');
  879. }
  880. protected function createMockCsrfProvider()
  881. {
  882. return $this->getMock('Symfony\Component\Form\CsrfProvider\CsrfProviderInterface');
  883. }
  884. }