FieldTest.php 19 KB

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