FormTest.php 36 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141
  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->submit(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->submit(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->submit(array($field => 'ABCDEF'));
  132. $this->assertFalse($form->isCsrfTokenValid());
  133. }
  134. public function testGetValidator()
  135. {
  136. $this->assertSame($this->validator, $this->form->getValidator());
  137. }
  138. public function testValidationGroupNullByDefault()
  139. {
  140. $this->assertNull($this->form->getValidationGroups());
  141. }
  142. public function testValidationGroupsCanBeSetToString()
  143. {
  144. $form = new Form('author', array(
  145. 'validation_groups' => 'group',
  146. ));
  147. $this->assertEquals(array('group'), $form->getValidationGroups());
  148. }
  149. public function testValidationGroupsCanBeSetToArray()
  150. {
  151. $form = new Form('author', array(
  152. 'validation_groups' => array('group1', 'group2'),
  153. ));
  154. $this->assertEquals(array('group1', 'group2'), $form->getValidationGroups());
  155. }
  156. public function testBindUsesValidationGroups()
  157. {
  158. $field = $this->createMockField('firstName');
  159. $form = new Form('author', array(
  160. 'validation_groups' => 'group',
  161. 'validator' => $this->validator,
  162. ));
  163. $form->add($field);
  164. $this->validator->expects($this->once())
  165. ->method('validate')
  166. ->with($this->equalTo($form), $this->equalTo(array('group')));
  167. // data is irrelevant
  168. $form->bind($this->createPostRequest());
  169. }
  170. public function testBindThrowsExceptionIfNoValidatorIsSet()
  171. {
  172. $field = $this->createMockField('firstName');
  173. $form = new Form('author');
  174. $form->add($field);
  175. $this->setExpectedException('Symfony\Component\Form\Exception\MissingOptionsException');
  176. // data is irrelevant
  177. $form->bind($this->createPostRequest());
  178. }
  179. public function testBindReadsRequestData()
  180. {
  181. $values = array(
  182. 'author' => array(
  183. 'name' => 'Bernhard',
  184. 'image' => array('filename' => 'foobar.png'),
  185. ),
  186. );
  187. $files = array(
  188. 'author' => array(
  189. 'error' => array('image' => array('file' => UPLOAD_ERR_OK)),
  190. 'name' => array('image' => array('file' => 'upload.png')),
  191. 'size' => array('image' => array('file' => 123)),
  192. 'tmp_name' => array('image' => array('file' => 'abcdef.png')),
  193. 'type' => array('image' => array('file' => 'image/png')),
  194. ),
  195. );
  196. $form = new Form('author', array('validator' => $this->validator));
  197. $form->add(new TestField('name'));
  198. $imageForm = new Form('image');
  199. $imageForm->add(new TestField('file'));
  200. $imageForm->add(new TestField('filename'));
  201. $form->add($imageForm);
  202. $form->bind($this->createPostRequest($values, $files));
  203. $file = new UploadedFile('abcdef.png', 'upload.png', 'image/png', 123, UPLOAD_ERR_OK);
  204. $this->assertEquals('Bernhard', $form['name']->getData());
  205. $this->assertEquals('foobar.png', $form['image']['filename']->getData());
  206. $this->assertEquals($file, $form['image']['file']->getData());
  207. }
  208. public function testBindAcceptsObject()
  209. {
  210. $object = new \stdClass();
  211. $form = new Form('author', array('validator' => $this->validator));
  212. $form->bind(new Request(), $object);
  213. $this->assertSame($object, $form->getData());
  214. }
  215. public function testReadPropertyIsIgnoredIfPropertyPathIsNull()
  216. {
  217. $author = new Author();
  218. $author->child = new Author();
  219. $standaloneChild = new Author();
  220. $form = new Form('child');
  221. $form->setData($standaloneChild);
  222. $form->setPropertyPath(null);
  223. $form->readProperty($author);
  224. // should not be $author->child!!
  225. $this->assertSame($standaloneChild, $form->getData());
  226. }
  227. public function testWritePropertyIsIgnoredIfPropertyPathIsNull()
  228. {
  229. $author = new Author();
  230. $author->child = $child = new Author();
  231. $standaloneChild = new Author();
  232. $form = new Form('child');
  233. $form->setData($standaloneChild);
  234. $form->setPropertyPath(null);
  235. $form->writeProperty($author);
  236. // $author->child was not modified
  237. $this->assertSame($child, $author->child);
  238. }
  239. public function testSupportsArrayAccess()
  240. {
  241. $form = new Form('author');
  242. $form->add($this->createMockField('firstName'));
  243. $this->assertEquals($form->get('firstName'), $form['firstName']);
  244. $this->assertTrue(isset($form['firstName']));
  245. }
  246. public function testSupportsUnset()
  247. {
  248. $form = new Form('author');
  249. $form->add($this->createMockField('firstName'));
  250. unset($form['firstName']);
  251. $this->assertFalse(isset($form['firstName']));
  252. }
  253. public function testDoesNotSupportAddingFields()
  254. {
  255. $form = new Form('author');
  256. $this->setExpectedException('LogicException');
  257. $form[] = $this->createMockField('lastName');
  258. }
  259. public function testSupportsCountable()
  260. {
  261. $form = new Form('group');
  262. $form->add($this->createMockField('firstName'));
  263. $form->add($this->createMockField('lastName'));
  264. $this->assertEquals(2, count($form));
  265. $form->add($this->createMockField('australian'));
  266. $this->assertEquals(3, count($form));
  267. }
  268. public function testSupportsIterable()
  269. {
  270. $form = new Form('group');
  271. $form->add($field1 = $this->createMockField('field1'));
  272. $form->add($field2 = $this->createMockField('field2'));
  273. $form->add($field3 = $this->createMockField('field3'));
  274. $expected = array(
  275. 'field1' => $field1,
  276. 'field2' => $field2,
  277. 'field3' => $field3,
  278. );
  279. $this->assertEquals($expected, iterator_to_array($form));
  280. }
  281. public function testIsSubmitted()
  282. {
  283. $form = new Form('author', array('validator' => $this->validator));
  284. $this->assertFalse($form->isSubmitted());
  285. $form->submit(array('firstName' => 'Bernhard'));
  286. $this->assertTrue($form->isSubmitted());
  287. }
  288. public function testValidIfAllFieldsAreValid()
  289. {
  290. $form = new Form('author', array('validator' => $this->validator));
  291. $form->add($this->createValidMockField('firstName'));
  292. $form->add($this->createValidMockField('lastName'));
  293. $form->submit(array('firstName' => 'Bernhard', 'lastName' => 'Potencier'));
  294. $this->assertTrue($form->isValid());
  295. }
  296. public function testInvalidIfFieldIsInvalid()
  297. {
  298. $form = new Form('author', array('validator' => $this->validator));
  299. $form->add($this->createInvalidMockField('firstName'));
  300. $form->add($this->createValidMockField('lastName'));
  301. $form->submit(array('firstName' => 'Bernhard', 'lastName' => 'Potencier'));
  302. $this->assertFalse($form->isValid());
  303. }
  304. public function testInvalidIfSubmittedWithExtraFields()
  305. {
  306. $form = new Form('author', array('validator' => $this->validator));
  307. $form->add($this->createValidMockField('firstName'));
  308. $form->add($this->createValidMockField('lastName'));
  309. $form->submit(array('foo' => 'bar', 'firstName' => 'Bernhard', 'lastName' => 'Potencier'));
  310. $this->assertTrue($form->isSubmittedWithExtraFields());
  311. }
  312. public function testHasNoErrorsIfOnlyFieldHasErrors()
  313. {
  314. $form = new Form('author', array('validator' => $this->validator));
  315. $form->add($this->createInvalidMockField('firstName'));
  316. $form->submit(array('firstName' => 'Bernhard'));
  317. $this->assertFalse($form->hasErrors());
  318. }
  319. public function testSubmitForwardsPreprocessedData()
  320. {
  321. $field = $this->createMockField('firstName');
  322. $form = $this->getMock(
  323. 'Symfony\Component\Form\Form',
  324. array('preprocessData'), // only mock preprocessData()
  325. array('author', array('validator' => $this->validator))
  326. );
  327. // The data array is prepared directly after binding
  328. $form->expects($this->once())
  329. ->method('preprocessData')
  330. ->with($this->equalTo(array('firstName' => 'Bernhard')))
  331. ->will($this->returnValue(array('firstName' => 'preprocessed[Bernhard]')));
  332. $form->add($field);
  333. // The preprocessed data is then forwarded to the fields
  334. $field->expects($this->once())
  335. ->method('submit')
  336. ->with($this->equalTo('preprocessed[Bernhard]'));
  337. $form->submit(array('firstName' => 'Bernhard'));
  338. }
  339. public function testSubmitForwardsNullIfValueIsMissing()
  340. {
  341. $field = $this->createMockField('firstName');
  342. $field->expects($this->once())
  343. ->method('submit')
  344. ->with($this->equalTo(null));
  345. $form = new Form('author', array('validator' => $this->validator));
  346. $form->add($field);
  347. $form->submit(array());
  348. }
  349. public function testAddErrorMapsFieldValidationErrorsOntoFields()
  350. {
  351. $error = new FieldError('Message');
  352. $field = $this->createMockField('firstName');
  353. $field->expects($this->once())
  354. ->method('addError')
  355. ->with($this->equalTo($error));
  356. $form = new Form('author');
  357. $form->add($field);
  358. $path = new PropertyPath('fields[firstName].data');
  359. $form->addError($error, $path->getIterator(), Form::FIELD_ERROR);
  360. }
  361. public function testAddErrorMapsFieldValidationErrorsOntoFieldsWithinNestedForms()
  362. {
  363. $error = new FieldError('Message');
  364. $field = $this->createMockField('firstName');
  365. $field->expects($this->once())
  366. ->method('addError')
  367. ->with($this->equalTo($error));
  368. $form = new Form('author');
  369. $innerGroup = new Form('names');
  370. $innerGroup->add($field);
  371. $form->add($innerGroup);
  372. $path = new PropertyPath('fields[names].fields[firstName].data');
  373. $form->addError($error, $path->getIterator(), Form::FIELD_ERROR);
  374. }
  375. public function testAddErrorKeepsFieldValidationErrorsIfFieldNotFound()
  376. {
  377. $error = new FieldError('Message');
  378. $field = $this->createMockField('foo');
  379. $field->expects($this->never())
  380. ->method('addError');
  381. $form = new Form('author');
  382. $form->add($field);
  383. $path = new PropertyPath('fields[bar].data');
  384. $form->addError($error, $path->getIterator(), Form::FIELD_ERROR);
  385. $this->assertEquals(array($error), $form->getErrors());
  386. }
  387. public function testAddErrorKeepsFieldValidationErrorsIfFieldIsHidden()
  388. {
  389. $error = new FieldError('Message');
  390. $field = $this->createMockField('firstName');
  391. $field->expects($this->any())
  392. ->method('isHidden')
  393. ->will($this->returnValue(true));
  394. $field->expects($this->never())
  395. ->method('addError');
  396. $form = new Form('author');
  397. $form->add($field);
  398. $path = new PropertyPath('fields[firstName].data');
  399. $form->addError($error, $path->getIterator(), Form::FIELD_ERROR);
  400. $this->assertEquals(array($error), $form->getErrors());
  401. }
  402. public function testAddErrorMapsDataValidationErrorsOntoFields()
  403. {
  404. $error = new FieldError('Message');
  405. // path is expected to point at "firstName"
  406. $expectedPath = new PropertyPath('firstName');
  407. $expectedPathIterator = $expectedPath->getIterator();
  408. $field = $this->createMockField('firstName');
  409. $field->expects($this->any())
  410. ->method('getPropertyPath')
  411. ->will($this->returnValue(new PropertyPath('firstName')));
  412. $field->expects($this->once())
  413. ->method('addError')
  414. ->with($this->equalTo($error), $this->equalTo($expectedPathIterator), $this->equalTo(Form::DATA_ERROR));
  415. $form = new Form('author');
  416. $form->add($field);
  417. $path = new PropertyPath('firstName');
  418. $form->addError($error, $path->getIterator(), Form::DATA_ERROR);
  419. }
  420. public function testAddErrorKeepsDataValidationErrorsIfFieldNotFound()
  421. {
  422. $error = new FieldError('Message');
  423. $field = $this->createMockField('foo');
  424. $field->expects($this->any())
  425. ->method('getPropertyPath')
  426. ->will($this->returnValue(new PropertyPath('foo')));
  427. $field->expects($this->never())
  428. ->method('addError');
  429. $form = new Form('author');
  430. $form->add($field);
  431. $path = new PropertyPath('bar');
  432. $form->addError($error, $path->getIterator(), Form::DATA_ERROR);
  433. }
  434. public function testAddErrorKeepsDataValidationErrorsIfFieldIsHidden()
  435. {
  436. $error = new FieldError('Message');
  437. $field = $this->createMockField('firstName');
  438. $field->expects($this->any())
  439. ->method('isHidden')
  440. ->will($this->returnValue(true));
  441. $field->expects($this->any())
  442. ->method('getPropertyPath')
  443. ->will($this->returnValue(new PropertyPath('firstName')));
  444. $field->expects($this->never())
  445. ->method('addError');
  446. $form = new Form('author');
  447. $form->add($field);
  448. $path = new PropertyPath('firstName');
  449. $form->addError($error, $path->getIterator(), Form::DATA_ERROR);
  450. }
  451. public function testAddErrorMapsDataValidationErrorsOntoNestedFields()
  452. {
  453. $error = new FieldError('Message');
  454. // path is expected to point at "street"
  455. $expectedPath = new PropertyPath('address.street');
  456. $expectedPathIterator = $expectedPath->getIterator();
  457. $expectedPathIterator->next();
  458. $field = $this->createMockField('address');
  459. $field->expects($this->any())
  460. ->method('getPropertyPath')
  461. ->will($this->returnValue(new PropertyPath('address')));
  462. $field->expects($this->once())
  463. ->method('addError')
  464. ->with($this->equalTo($error), $this->equalTo($expectedPathIterator), $this->equalTo(Form::DATA_ERROR));
  465. $form = new Form('author');
  466. $form->add($field);
  467. $path = new PropertyPath('address.street');
  468. $form->addError($error, $path->getIterator(), Form::DATA_ERROR);
  469. }
  470. public function testAddErrorMapsErrorsOntoFieldsInVirtualGroups()
  471. {
  472. $error = new FieldError('Message');
  473. // path is expected to point at "address"
  474. $expectedPath = new PropertyPath('address');
  475. $expectedPathIterator = $expectedPath->getIterator();
  476. $field = $this->createMockField('address');
  477. $field->expects($this->any())
  478. ->method('getPropertyPath')
  479. ->will($this->returnValue(new PropertyPath('address')));
  480. $field->expects($this->once())
  481. ->method('addError')
  482. ->with($this->equalTo($error), $this->equalTo($expectedPathIterator), $this->equalTo(Form::DATA_ERROR));
  483. $form = new Form('author');
  484. $nestedForm = new Form('nested', array('virtual' => true));
  485. $nestedForm->add($field);
  486. $form->add($nestedForm);
  487. $path = new PropertyPath('address');
  488. $form->addError($error, $path->getIterator(), Form::DATA_ERROR);
  489. }
  490. public function testAddThrowsExceptionIfAlreadySubmitted()
  491. {
  492. $form = new Form('author', array('validator' => $this->validator));
  493. $form->add($this->createMockField('firstName'));
  494. $form->submit(array());
  495. $this->setExpectedException('Symfony\Component\Form\Exception\AlreadySubmittedException');
  496. $form->add($this->createMockField('lastName'));
  497. }
  498. public function testAddSetsFieldParent()
  499. {
  500. $form = new Form('author');
  501. $field = $this->createMockField('firstName');
  502. $field->expects($this->once())
  503. ->method('setParent')
  504. ->with($this->equalTo($form));
  505. $form->add($field);
  506. }
  507. public function testRemoveUnsetsFieldParent()
  508. {
  509. $form = new Form('author');
  510. $field = $this->createMockField('firstName');
  511. $field->expects($this->exactly(2))
  512. ->method('setParent');
  513. // PHPUnit fails to compare subsequent method calls with different arguments
  514. $form->add($field);
  515. $form->remove('firstName');
  516. }
  517. public function testAddUpdatesFieldFromTransformedData()
  518. {
  519. $originalAuthor = new Author();
  520. $transformedAuthor = new Author();
  521. // the authors should differ to make sure the test works
  522. $transformedAuthor->firstName = 'Foo';
  523. $form = new TestForm('author');
  524. $transformer = $this->createMockTransformer();
  525. $transformer->expects($this->once())
  526. ->method('transform')
  527. ->with($this->equalTo($originalAuthor))
  528. ->will($this->returnValue($transformedAuthor));
  529. $form->setValueTransformer($transformer);
  530. $form->setData($originalAuthor);
  531. $field = $this->createMockField('firstName');
  532. $field->expects($this->any())
  533. ->method('getPropertyPath')
  534. ->will($this->returnValue(new PropertyPath('firstName')));
  535. $field->expects($this->once())
  536. ->method('readProperty')
  537. ->with($this->equalTo($transformedAuthor));
  538. $form->add($field);
  539. }
  540. public function testAddDoesNotUpdateFieldIfTransformedDataIsEmpty()
  541. {
  542. $originalAuthor = new Author();
  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(''));
  549. $form->setValueTransformer($transformer);
  550. $form->setData($originalAuthor);
  551. $field = $this->createMockField('firstName');
  552. $field->expects($this->never())
  553. ->method('readProperty');
  554. $form->add($field);
  555. }
  556. /**
  557. * @expectedException Symfony\Component\Form\Exception\UnexpectedTypeException
  558. */
  559. public function testAddThrowsExceptionIfNoFieldOrString()
  560. {
  561. $form = new Form('author');
  562. $form->add(1234);
  563. }
  564. /**
  565. * @expectedException Symfony\Component\Form\Exception\FieldDefinitionException
  566. */
  567. public function testAddThrowsExceptionIfAnonymousField()
  568. {
  569. $form = new Form('author');
  570. $field = $this->createMockField('');
  571. $form->add($field);
  572. }
  573. /**
  574. * @expectedException Symfony\Component\Form\Exception\FormException
  575. */
  576. public function testAddThrowsExceptionIfStringButNoFieldFactory()
  577. {
  578. $form = new Form('author', array('data_class' => 'Application\Entity'));
  579. $form->add('firstName');
  580. }
  581. /**
  582. * @expectedException Symfony\Component\Form\Exception\FormException
  583. */
  584. public function testAddThrowsExceptionIfStringButNoClass()
  585. {
  586. $form = new Form('author', array('field_factory' => new \stdClass()));
  587. $form->add('firstName');
  588. }
  589. public function testAddUsesFieldFromFactoryIfStringIsGiven()
  590. {
  591. $author = new \stdClass();
  592. $field = $this->createMockField('firstName');
  593. $factory = $this->getMock('Symfony\Component\Form\FieldFactory\FieldFactoryInterface');
  594. $factory->expects($this->once())
  595. ->method('getInstance')
  596. ->with($this->equalTo('stdClass'), $this->equalTo('firstName'), $this->equalTo(array('foo' => 'bar')))
  597. ->will($this->returnValue($field));
  598. $form = new Form('author', array(
  599. 'data' => $author,
  600. 'data_class' => 'stdClass',
  601. 'field_factory' => $factory,
  602. ));
  603. $form->add('firstName', array('foo' => 'bar'));
  604. $this->assertSame($field, $form['firstName']);
  605. }
  606. public function testSetDataUpdatesAllFieldsFromTransformedData()
  607. {
  608. $originalAuthor = new Author();
  609. $transformedAuthor = new Author();
  610. // the authors should differ to make sure the test works
  611. $transformedAuthor->firstName = 'Foo';
  612. $form = new TestForm('author');
  613. $transformer = $this->createMockTransformer();
  614. $transformer->expects($this->once())
  615. ->method('transform')
  616. ->with($this->equalTo($originalAuthor))
  617. ->will($this->returnValue($transformedAuthor));
  618. $form->setValueTransformer($transformer);
  619. $field = $this->createMockField('firstName');
  620. $field->expects($this->once())
  621. ->method('readProperty')
  622. ->with($this->equalTo($transformedAuthor));
  623. $form->add($field);
  624. $field = $this->createMockField('lastName');
  625. $field->expects($this->once())
  626. ->method('readProperty')
  627. ->with($this->equalTo($transformedAuthor));
  628. $form->add($field);
  629. $form->setData($originalAuthor);
  630. }
  631. /**
  632. * The use case for this test are groups whose fields should be mapped
  633. * directly onto properties of the form's object.
  634. *
  635. * Example:
  636. *
  637. * <code>
  638. * $dateRangeField = new Form('dateRange');
  639. * $dateRangeField->add(new DateField('startDate'));
  640. * $dateRangeField->add(new DateField('endDate'));
  641. * $form->add($dateRangeField);
  642. * </code>
  643. *
  644. * If $dateRangeField is not virtual, the property "dateRange" must be
  645. * present on the form's object. In this property, an object or array
  646. * with the properties "startDate" and "endDate" is expected.
  647. *
  648. * If $dateRangeField is virtual though, it's children are mapped directly
  649. * onto the properties "startDate" and "endDate" of the form's object.
  650. */
  651. public function testSetDataSkipsVirtualForms()
  652. {
  653. $author = new Author();
  654. $author->firstName = 'Foo';
  655. $form = new Form('author');
  656. $nestedForm = new Form('personal_data', array(
  657. 'virtual' => true,
  658. ));
  659. // both fields are in the nested group but receive the object of the
  660. // top-level group because the nested group is virtual
  661. $field = $this->createMockField('firstName');
  662. $field->expects($this->once())
  663. ->method('readProperty')
  664. ->with($this->equalTo($author));
  665. $nestedForm->add($field);
  666. $field = $this->createMockField('lastName');
  667. $field->expects($this->once())
  668. ->method('readProperty')
  669. ->with($this->equalTo($author));
  670. $nestedForm->add($field);
  671. $form->add($nestedForm);
  672. $form->setData($author);
  673. }
  674. public function testSetDataThrowsAnExceptionIfArgumentIsNotObjectOrArray()
  675. {
  676. $form = new Form('author');
  677. $this->setExpectedException('InvalidArgumentException');
  678. $form->setData('foobar');
  679. }
  680. /**
  681. * @expectedException Symfony\Component\Form\Exception\FormException
  682. */
  683. public function testSetDataMatchesAgainstDataClass_fails()
  684. {
  685. $form = new Form('author', array(
  686. 'data_class' => 'Symfony\Tests\Component\Form\Fixtures\Author',
  687. ));
  688. $form->setData(new \stdClass());
  689. }
  690. public function testSetDataMatchesAgainstDataClass_succeeds()
  691. {
  692. $form = new Form('author', array(
  693. 'data_class' => 'Symfony\Tests\Component\Form\Fixtures\Author',
  694. ));
  695. $form->setData(new Author());
  696. }
  697. public function testSubmitUpdatesTransformedDataFromAllFields()
  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('writeProperty')
  717. ->with($this->equalTo($transformedAuthor));
  718. $form->add($field);
  719. $field = $this->createMockField('lastName');
  720. $field->expects($this->once())
  721. ->method('writeProperty')
  722. ->with($this->equalTo($transformedAuthor));
  723. $form->add($field);
  724. $form->submit(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 testSubmitWithoutPriorSetData()
  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->submit(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. protected function createPostRequest(array $values = array(), array $files = array())
  885. {
  886. $server = array('REQUEST_METHOD' => 'POST');
  887. return new Request(array(), $values, array(), array(), $files, $server);
  888. }
  889. }