FieldTest.php 18 KB

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