FieldTest.php 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543
  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__ . '/TestCase.php';
  12. require_once __DIR__ . '/Fixtures/Author.php';
  13. require_once __DIR__ . '/Fixtures/InvalidField.php';
  14. use Symfony\Component\Form\ValueTransformer\ValueTransformerInterface;
  15. use Symfony\Component\Form\PropertyPath;
  16. use Symfony\Component\Form\FieldError;
  17. use Symfony\Component\Form\Form;
  18. use Symfony\Component\Form\ValueTransformer\TransformationFailedException;
  19. use Symfony\Tests\Component\Form\Fixtures\Author;
  20. use Symfony\Tests\Component\Form\Fixtures\InvalidField;
  21. class FieldTest extends TestCase
  22. {
  23. protected $field;
  24. protected function setUp()
  25. {
  26. parent::setUp();
  27. $this->field = $this->factory->getField('title');
  28. }
  29. public function testGetPropertyPath_defaultPath()
  30. {
  31. $field = $this->factory->getField('title');
  32. $this->assertEquals(new PropertyPath('title'), $field->getPropertyPath());
  33. }
  34. public function testGetPropertyPath_pathIsZero()
  35. {
  36. $field = $this->factory->getField('title', array('property_path' => '0'));
  37. $this->assertEquals(new PropertyPath('0'), $field->getPropertyPath());
  38. }
  39. public function testGetPropertyPath_pathIsEmpty()
  40. {
  41. $field = $this->factory->getField('title', array('property_path' => ''));
  42. $this->assertEquals(null, $field->getPropertyPath());
  43. }
  44. public function testGetPropertyPath_pathIsNull()
  45. {
  46. $field = $this->factory->getField('title', array('property_path' => null));
  47. $this->assertEquals(null, $field->getPropertyPath());
  48. }
  49. public function testPassRequiredAsOption()
  50. {
  51. $field = $this->factory->getField('title', array('required' => false));
  52. $this->assertFalse($field->isRequired());
  53. $field = $this->factory->getField('title', array('required' => true));
  54. $this->assertTrue($field->isRequired());
  55. }
  56. public function testPassDisabledAsOption()
  57. {
  58. $field = $this->factory->getField('title', array('disabled' => false));
  59. $this->assertFalse($field->isDisabled());
  60. $field = $this->factory->getField('title', array('disabled' => true));
  61. $this->assertTrue($field->isDisabled());
  62. }
  63. public function testFieldIsDisabledIfParentIsDisabled()
  64. {
  65. $field = $this->factory->getField('title', array('disabled' => false));
  66. $field->setParent($this->factory->getField('title', array('disabled' => true)));
  67. $this->assertTrue($field->isDisabled());
  68. }
  69. public function testFieldWithNoErrorsIsValid()
  70. {
  71. $this->field->submit('data');
  72. $this->assertTrue($this->field->isValid());
  73. }
  74. public function testFieldWithErrorsIsInvalid()
  75. {
  76. $this->field->submit('data');
  77. $this->field->addError(new FieldError('Some error'));
  78. $this->assertFalse($this->field->isValid());
  79. }
  80. public function testSubmitResetsErrors()
  81. {
  82. $this->field->addError(new FieldError('Some error'));
  83. $this->field->submit('data');
  84. $this->assertTrue($this->field->isValid());
  85. }
  86. public function testUnsubmittedFieldIsInvalid()
  87. {
  88. $this->assertFalse($this->field->isValid());
  89. }
  90. public function testIsRequiredReturnsOwnValueIfNoParent()
  91. {
  92. $this->field->setRequired(true);
  93. $this->assertTrue($this->field->isRequired());
  94. $this->field->setRequired(false);
  95. $this->assertFalse($this->field->isRequired());
  96. }
  97. public function testIsRequiredReturnsOwnValueIfParentIsRequired()
  98. {
  99. $group = $this->createMockGroup();
  100. $group->expects($this->any())
  101. ->method('isRequired')
  102. ->will($this->returnValue(true));
  103. $this->field->setParent($group);
  104. $this->field->setRequired(true);
  105. $this->assertTrue($this->field->isRequired());
  106. $this->field->setRequired(false);
  107. $this->assertFalse($this->field->isRequired());
  108. }
  109. public function testIsRequiredReturnsFalseIfParentIsNotRequired()
  110. {
  111. $group = $this->createMockGroup();
  112. $group->expects($this->any())
  113. ->method('isRequired')
  114. ->will($this->returnValue(false));
  115. $this->field->setParent($group);
  116. $this->field->setRequired(true);
  117. $this->assertFalse($this->field->isRequired());
  118. }
  119. public function testIsSubmitted()
  120. {
  121. $this->assertFalse($this->field->isSubmitted());
  122. $this->field->submit('symfony');
  123. $this->assertTrue($this->field->isSubmitted());
  124. }
  125. public function testDefaultValuesAreTransformedCorrectly()
  126. {
  127. $field = $this->factory->getField('name');
  128. $this->assertEquals(null, $this->field->getData());
  129. $this->assertEquals('', $this->field->getDisplayedData());
  130. }
  131. public function testValuesAreTransformedCorrectlyIfNull_noValueTransformer()
  132. {
  133. $this->field->setData(null);
  134. $this->assertSame(null, $this->field->getData());
  135. $this->assertSame('', $this->field->getDisplayedData());
  136. }
  137. public function testValuesAreTransformedCorrectlyIfNotNull_noValueTransformer()
  138. {
  139. $this->field->setData(123);
  140. $this->assertSame(123, $this->field->getData());
  141. $this->assertSame('123', $this->field->getDisplayedData());
  142. }
  143. public function testSubmittedValuesAreTransformedCorrectly()
  144. {
  145. $valueTransformer = $this->createMockTransformer();
  146. $normTransformer = $this->createMockTransformer();
  147. $field = $this->getMock(
  148. 'Symfony\Component\Form\Field',
  149. array('processData'), // only mock processData()
  150. array('title')
  151. );
  152. $field->setValueTransformer($valueTransformer);
  153. $field->setNormalizationTransformer($normTransformer);
  154. // 1a. The value is converted to a string and passed to the value transformer
  155. $valueTransformer->expects($this->once())
  156. ->method('reverseTransform')
  157. ->with($this->identicalTo('0'))
  158. ->will($this->returnValue('reverse[0]'));
  159. // 2. The output of the reverse transformation is passed to processData()
  160. // The processed data is accessible through getNormalizedData()
  161. $field->expects($this->once())
  162. ->method('processData')
  163. ->with($this->equalTo('reverse[0]'))
  164. ->will($this->returnValue('processed[reverse[0]]'));
  165. // 3. The processed data is denormalized and then accessible through
  166. // getData()
  167. $normTransformer->expects($this->once())
  168. ->method('reverseTransform')
  169. ->with($this->identicalTo('processed[reverse[0]]'))
  170. ->will($this->returnValue('denorm[processed[reverse[0]]]'));
  171. // 4. The processed data is transformed again and then accessible
  172. // through getDisplayedData()
  173. $valueTransformer->expects($this->once())
  174. ->method('transform')
  175. ->with($this->equalTo('processed[reverse[0]]'))
  176. ->will($this->returnValue('transform[processed[reverse[0]]]'));
  177. $field->submit(0);
  178. $this->assertEquals('denorm[processed[reverse[0]]]', $field->getData());
  179. $this->assertEquals('processed[reverse[0]]', $field->getNormalizedData());
  180. $this->assertEquals('transform[processed[reverse[0]]]', $field->getDisplayedData());
  181. }
  182. public function testSubmittedValuesAreTransformedCorrectlyIfEmpty_processDataReturnsValue()
  183. {
  184. $transformer = $this->createMockTransformer();
  185. $field = $this->getMock(
  186. 'Symfony\Component\Form\Field',
  187. array('processData'), // only mock processData()
  188. array('title')
  189. );
  190. $field->setValueTransformer($transformer);
  191. // 1. Empty values are converted to NULL by convention
  192. $transformer->expects($this->once())
  193. ->method('reverseTransform')
  194. ->with($this->identicalTo(''))
  195. ->will($this->returnValue(null));
  196. // 2. NULL is passed to processData()
  197. $field->expects($this->once())
  198. ->method('processData')
  199. ->with($this->identicalTo(null))
  200. ->will($this->returnValue('processed'));
  201. // 3. The processed data is transformed (for displayed data)
  202. $transformer->expects($this->once())
  203. ->method('transform')
  204. ->with($this->equalTo('processed'))
  205. ->will($this->returnValue('transform[processed]'));
  206. $field->submit('');
  207. $this->assertSame('processed', $field->getData());
  208. $this->assertEquals('transform[processed]', $field->getDisplayedData());
  209. }
  210. public function testSubmittedValuesAreTransformedCorrectlyIfEmpty_processDataReturnsNull()
  211. {
  212. $transformer = $this->createMockTransformer();
  213. $field = $this->factory->getField('title', array(
  214. 'value_transformer' => $transformer,
  215. ));
  216. // 1. Empty values are converted to NULL by convention
  217. $transformer->expects($this->once())
  218. ->method('reverseTransform')
  219. ->with($this->identicalTo(''))
  220. ->will($this->returnValue(null));
  221. // 2. The processed data is NULL and therefore transformed to an empty
  222. // string by convention
  223. $transformer->expects($this->once())
  224. ->method('transform')
  225. ->with($this->identicalTo(null))
  226. ->will($this->returnValue(''));
  227. $field->submit('');
  228. $this->assertSame(null, $field->getData());
  229. $this->assertEquals('', $field->getDisplayedData());
  230. }
  231. public function testSubmittedValuesAreTransformedCorrectlyIfEmpty_processDataReturnsNull_noValueTransformer()
  232. {
  233. $this->field->submit('');
  234. $this->assertSame(null, $this->field->getData());
  235. $this->assertEquals('', $this->field->getDisplayedData());
  236. }
  237. public function testValuesAreTransformedCorrectly()
  238. {
  239. // The value is first passed to the normalization transformer...
  240. $normTransformer = $this->createMockTransformer();
  241. $normTransformer->expects($this->once())
  242. ->method('transform')
  243. ->with($this->identicalTo(0))
  244. ->will($this->returnValue('norm[0]'));
  245. // ...and then to the value transformer
  246. $valueTransformer = $this->createMockTransformer();
  247. $valueTransformer->expects($this->once())
  248. ->method('transform')
  249. ->with($this->identicalTo('norm[0]'))
  250. ->will($this->returnValue('transform[norm[0]]'));
  251. $field = $this->factory->getField('title', array(
  252. 'value_transformer' => $valueTransformer,
  253. 'normalization_transformer' => $normTransformer,
  254. ));
  255. $field->setData(0);
  256. $this->assertEquals(0, $field->getData());
  257. $this->assertEquals('norm[0]', $field->getNormalizedData());
  258. $this->assertEquals('transform[norm[0]]', $field->getDisplayedData());
  259. }
  260. public function testSubmittedValuesAreTrimmedBeforeTransforming()
  261. {
  262. // The value is passed to the value transformer
  263. $transformer = $this->createMockTransformer();
  264. $transformer->expects($this->once())
  265. ->method('reverseTransform')
  266. ->with($this->identicalTo('a'))
  267. ->will($this->returnValue('reverse[a]'));
  268. $transformer->expects($this->once())
  269. ->method('transform')
  270. ->with($this->identicalTo('reverse[a]'))
  271. ->will($this->returnValue('a'));
  272. $field = $this->factory->getField('title', array(
  273. 'value_transformer' => $transformer,
  274. ));
  275. $field->submit(' a ');
  276. $this->assertEquals('a', $field->getDisplayedData());
  277. $this->assertEquals('reverse[a]', $field->getData());
  278. }
  279. public function testSubmittedValuesAreNotTrimmedBeforeTransformingIfDisabled()
  280. {
  281. // The value is passed to the value transformer
  282. $transformer = $this->createMockTransformer();
  283. $transformer->expects($this->once())
  284. ->method('reverseTransform')
  285. ->with($this->identicalTo(' a '))
  286. ->will($this->returnValue('reverse[ a ]'));
  287. $transformer->expects($this->once())
  288. ->method('transform')
  289. ->with($this->identicalTo('reverse[ a ]'))
  290. ->will($this->returnValue(' a '));
  291. $field = $this->factory->getField('title', array(
  292. 'trim' => false,
  293. 'value_transformer' => $transformer,
  294. ));
  295. $field->submit(' a ');
  296. $this->assertEquals(' a ', $field->getDisplayedData());
  297. $this->assertEquals('reverse[ a ]', $field->getData());
  298. }
  299. /*
  300. * This is important so that submit() can work even if setData() was not called
  301. * before
  302. */
  303. public function testWritePropertyTreatsEmptyValuesAsArrays()
  304. {
  305. $array = null;
  306. $field = $this->factory->getField('firstName');
  307. $field->submit('Bernhard');
  308. $field->writeProperty($array);
  309. $this->assertEquals(array('firstName' => 'Bernhard'), $array);
  310. }
  311. public function testWritePropertyDoesNotWritePropertyIfPropertyPathIsEmpty()
  312. {
  313. $object = new Author();
  314. $field = $this->factory->getField('firstName', array('property_path' => null));
  315. $field->submit('Bernhard');
  316. $field->writeProperty($object);
  317. $this->assertEquals(null, $object->firstName);
  318. }
  319. public function testIsTransformationSuccessfulReturnsTrueIfReverseTransformSucceeded()
  320. {
  321. $field = $this->factory->getField('title', array(
  322. 'trim' => false,
  323. ));
  324. $field->submit('a');
  325. $this->assertEquals('a', $field->getDisplayedData());
  326. $this->assertTrue($field->isTransformationSuccessful());
  327. }
  328. public function testIsTransformationSuccessfulReturnsFalseIfReverseTransformThrowsException()
  329. {
  330. // The value is passed to the value transformer
  331. $transformer = $this->createMockTransformer();
  332. $transformer->expects($this->once())
  333. ->method('reverseTransform')
  334. ->will($this->throwException(new TransformationFailedException()));
  335. $field = $this->factory->getField('title', array(
  336. 'trim' => false,
  337. 'value_transformer' => $transformer,
  338. ));
  339. $field->submit('a');
  340. $this->assertEquals('a', $field->getDisplayedData());
  341. $this->assertFalse($field->isTransformationSuccessful());
  342. }
  343. public function testGetRootReturnsRootOfParentIfSet()
  344. {
  345. $parent = $this->createMockGroup();
  346. $parent->expects($this->any())
  347. ->method('getRoot')
  348. ->will($this->returnValue('ROOT'));
  349. $this->field->setParent($parent);
  350. $this->assertEquals('ROOT', $this->field->getRoot());
  351. }
  352. public function testFieldsInitializedWithDataAreNotUpdatedWhenAddedToForms()
  353. {
  354. $author = new Author();
  355. $author->firstName = 'Bernhard';
  356. $field = $this->factory->getField('firstName', array(
  357. 'data' => 'foobar',
  358. ));
  359. $form = new Form('author', array(
  360. 'data' => $author,
  361. ));
  362. $form->add($field);
  363. $this->assertEquals('foobar', $field->getData());
  364. }
  365. public function testGetRootReturnsFieldIfNoParent()
  366. {
  367. $this->assertEquals($this->field, $this->field->getRoot());
  368. }
  369. public function testIsEmptyReturnsTrueIfNull()
  370. {
  371. $this->field->setData(null);
  372. $this->assertTrue($this->field->isEmpty());
  373. }
  374. public function testIsEmptyReturnsTrueIfEmptyString()
  375. {
  376. $this->field->setData('');
  377. $this->assertTrue($this->field->isEmpty());
  378. }
  379. public function testIsEmptyReturnsFalseIfZero()
  380. {
  381. $this->field->setData(0);
  382. $this->assertFalse($this->field->isEmpty());
  383. }
  384. protected function createMockTransformer()
  385. {
  386. return $this->getMock('Symfony\Component\Form\ValueTransformer\ValueTransformerInterface', array(), array(), '', false, false);
  387. }
  388. protected function createMockTransformerTransformingTo($value)
  389. {
  390. $transformer = $this->createMockTransformer();
  391. $transformer->expects($this->any())
  392. ->method('reverseTransform')
  393. ->will($this->returnValue($value));
  394. return $transformer;
  395. }
  396. protected function createMockGroup()
  397. {
  398. return $this->getMock(
  399. 'Symfony\Component\Form\Form',
  400. array(),
  401. array(),
  402. '',
  403. false // don't call constructor
  404. );
  405. }
  406. protected function createMockGroupWithName($name)
  407. {
  408. $group = $this->createMockGroup();
  409. $group->expects($this->any())
  410. ->method('getName')
  411. ->will($this->returnValue($name));
  412. return $group;
  413. }
  414. protected function createMockGroupWithId($id)
  415. {
  416. $group = $this->createMockGroup();
  417. $group->expects($this->any())
  418. ->method('getId')
  419. ->will($this->returnValue($id));
  420. return $group;
  421. }
  422. }