FieldTest.php 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498
  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\Tests\Component\Form\Fixtures\Author;
  12. use Symfony\Tests\Component\Form\Fixtures\TestField;
  13. use Symfony\Tests\Component\Form\Fixtures\InvalidField;
  14. use Symfony\Tests\Component\Form\Fixtures\RequiredOptionsField;
  15. class FieldTest extends \PHPUnit_Framework_TestCase
  16. {
  17. protected $field;
  18. protected function setUp()
  19. {
  20. $this->field = new TestField('title');
  21. }
  22. public function testGetPropertyPath_defaultPath()
  23. {
  24. $field = new TestField('title');
  25. $this->assertEquals(new PropertyPath('title'), $field->getPropertyPath());
  26. }
  27. public function testGetPropertyPath_pathIsZero()
  28. {
  29. $field = new TestField('title', array('property_path' => '0'));
  30. $this->assertEquals(new PropertyPath('0'), $field->getPropertyPath());
  31. }
  32. public function testGetPropertyPath_pathIsEmpty()
  33. {
  34. $field = new TestField('title', array('property_path' => ''));
  35. $this->assertEquals(null, $field->getPropertyPath());
  36. }
  37. public function testGetPropertyPath_pathIsNull()
  38. {
  39. $field = new TestField('title', array('property_path' => null));
  40. $this->assertEquals(null, $field->getPropertyPath());
  41. }
  42. public function testPassRequiredAsOption()
  43. {
  44. $field = new TestField('title', array('required' => false));
  45. $this->assertFalse($field->isRequired());
  46. $field = new TestField('title', array('required' => true));
  47. $this->assertTrue($field->isRequired());
  48. }
  49. public function testPassDisabledAsOption()
  50. {
  51. $field = new TestField('title', array('disabled' => false));
  52. $this->assertFalse($field->isDisabled());
  53. $field = new TestField('title', array('disabled' => true));
  54. $this->assertTrue($field->isDisabled());
  55. }
  56. public function testFieldIsDisabledIfParentIsDisabled()
  57. {
  58. $field = new TestField('title', array('disabled' => false));
  59. $field->setParent(new TestField('title', array('disabled' => true)));
  60. $this->assertTrue($field->isDisabled());
  61. }
  62. public function testFieldWithNoErrorsIsValid()
  63. {
  64. $this->field->bind('data');
  65. $this->assertTrue($this->field->isValid());
  66. }
  67. public function testFieldWithErrorsIsInvalid()
  68. {
  69. $this->field->bind('data');
  70. $this->field->addError(new FieldError('Some error'));
  71. $this->assertFalse($this->field->isValid());
  72. }
  73. public function testBindResetsErrors()
  74. {
  75. $this->field->addError(new FieldError('Some error'));
  76. $this->field->bind('data');
  77. $this->assertTrue($this->field->isValid());
  78. }
  79. public function testUnboundFieldIsInvalid()
  80. {
  81. $this->assertFalse($this->field->isValid());
  82. }
  83. public function testGetNameReturnsKey()
  84. {
  85. $this->assertEquals('title', $this->field->getName());
  86. }
  87. public function testGetNameIncludesParent()
  88. {
  89. $this->field->setParent($this->createMockGroupWithName('news[article]'));
  90. $this->assertEquals('news[article][title]', $this->field->getName());
  91. }
  92. public function testGetIdReturnsKey()
  93. {
  94. $this->assertEquals('title', $this->field->getId());
  95. }
  96. public function testGetIdIncludesParent()
  97. {
  98. $this->field->setParent($this->createMockGroupWithId('news_article'));
  99. $this->assertEquals('news_article_title', $this->field->getId());
  100. }
  101. public function testLocaleIsPassedToValueTransformer()
  102. {
  103. FormConfiguration::setDefaultLocale('de_DE');
  104. $transformer = $this->getMock('Symfony\Component\Form\ValueTransformer\ValueTransformerInterface');
  105. $transformer->expects($this->exactly(1))
  106. ->method('setLocale')
  107. ->with($this->equalTo('de_DE'));
  108. $field = new TestField('title', array(
  109. 'value_transformer' => $transformer,
  110. ));
  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 testIsBound()
  152. {
  153. $this->assertFalse($this->field->isBound());
  154. $this->field->bind('symfony');
  155. $this->assertTrue($this->field->isBound());
  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 testBoundValuesAreTransformedCorrectly()
  170. {
  171. $valueTransformer = $this->createMockTransformer();
  172. $normTransformer = $this->createMockTransformer();
  173. $field = $this->getMock(
  174. 'Symfony\Tests\Component\Form\Fixtures\TestField',
  175. array('processData'), // only mock processData()
  176. array('title', array(
  177. 'value_transformer' => $valueTransformer,
  178. 'normalization_transformer' => $normTransformer,
  179. ))
  180. );
  181. // 1a. The value is converted to a string and passed to the value transformer
  182. $valueTransformer->expects($this->once())
  183. ->method('reverseTransform')
  184. ->with($this->identicalTo('0'))
  185. ->will($this->returnValue('reverse[0]'));
  186. // 2. The output of the reverse transformation is passed to processData()
  187. // The processed data is accessible through getNormalizedData()
  188. $field->expects($this->once())
  189. ->method('processData')
  190. ->with($this->equalTo('reverse[0]'))
  191. ->will($this->returnValue('processed[reverse[0]]'));
  192. // 3. The processed data is denormalized and then accessible through
  193. // getData()
  194. $normTransformer->expects($this->once())
  195. ->method('reverseTransform')
  196. ->with($this->identicalTo('processed[reverse[0]]'))
  197. ->will($this->returnValue('denorm[processed[reverse[0]]]'));
  198. // 4. The processed data is transformed again and then accessible
  199. // through getDisplayedData()
  200. $valueTransformer->expects($this->once())
  201. ->method('transform')
  202. ->with($this->equalTo('processed[reverse[0]]'))
  203. ->will($this->returnValue('transform[processed[reverse[0]]]'));
  204. $field->bind(0);
  205. $this->assertEquals('denorm[processed[reverse[0]]]', $field->getData());
  206. $this->assertEquals('processed[reverse[0]]', $field->getNormalizedData());
  207. $this->assertEquals('transform[processed[reverse[0]]]', $field->getDisplayedData());
  208. }
  209. public function testBoundValuesAreTransformedCorrectlyIfEmpty_processDataReturnsValue()
  210. {
  211. $transformer = $this->createMockTransformer();
  212. $field = $this->getMock(
  213. 'Symfony\Tests\Component\Form\Fixtures\TestField',
  214. array('processData'), // only mock processData()
  215. array('title', array(
  216. 'value_transformer' => $transformer,
  217. ))
  218. );
  219. // 1. Empty values are converted to NULL by convention
  220. $transformer->expects($this->once())
  221. ->method('reverseTransform')
  222. ->with($this->identicalTo(''))
  223. ->will($this->returnValue(null));
  224. // 2. NULL is passed to processData()
  225. $field->expects($this->once())
  226. ->method('processData')
  227. ->with($this->identicalTo(null))
  228. ->will($this->returnValue('processed'));
  229. // 3. The processed data is transformed (for displayed data)
  230. $transformer->expects($this->once())
  231. ->method('transform')
  232. ->with($this->equalTo('processed'))
  233. ->will($this->returnValue('transform[processed]'));
  234. $field->bind('');
  235. $this->assertSame('processed', $field->getData());
  236. $this->assertEquals('transform[processed]', $field->getDisplayedData());
  237. }
  238. public function testBoundValuesAreTransformedCorrectlyIfEmpty_processDataReturnsNull()
  239. {
  240. $transformer = $this->createMockTransformer();
  241. $field = new TestField('title', array(
  242. 'value_transformer' => $transformer,
  243. ));
  244. // 1. Empty values are converted to NULL by convention
  245. $transformer->expects($this->once())
  246. ->method('reverseTransform')
  247. ->with($this->identicalTo(''))
  248. ->will($this->returnValue(null));
  249. // 2. The processed data is NULL and therefore transformed to an empty
  250. // string by convention
  251. $transformer->expects($this->once())
  252. ->method('transform')
  253. ->with($this->identicalTo(null))
  254. ->will($this->returnValue(''));
  255. $field->bind('');
  256. $this->assertSame(null, $field->getData());
  257. $this->assertEquals('', $field->getDisplayedData());
  258. }
  259. public function testBoundValuesAreTransformedCorrectlyIfEmpty_processDataReturnsNull_noValueTransformer()
  260. {
  261. $this->field->bind('');
  262. $this->assertSame(null, $this->field->getData());
  263. $this->assertEquals('', $this->field->getDisplayedData());
  264. }
  265. public function testValuesAreTransformedCorrectly()
  266. {
  267. // The value is first passed to the normalization transformer...
  268. $normTransformer = $this->createMockTransformer();
  269. $normTransformer->expects($this->exactly(2))
  270. ->method('transform')
  271. // Impossible to test with PHPUnit because called twice
  272. // ->with($this->identicalTo(0))
  273. ->will($this->returnValue('norm[0]'));
  274. // ...and then to the value transformer
  275. $valueTransformer = $this->createMockTransformer();
  276. $valueTransformer->expects($this->exactly(2))
  277. ->method('transform')
  278. // Impossible to test with PHPUnit because called twice
  279. // ->with($this->identicalTo('norm[0]'))
  280. ->will($this->returnValue('transform[norm[0]]'));
  281. $field = new TestField('title', array(
  282. 'value_transformer' => $valueTransformer,
  283. 'normalization_transformer' => $normTransformer,
  284. ));
  285. $field->setData(0);
  286. $this->assertEquals(0, $field->getData());
  287. $this->assertEquals('norm[0]', $field->getNormalizedData());
  288. $this->assertEquals('transform[norm[0]]', $field->getDisplayedData());
  289. }
  290. public function testBoundValuesAreTrimmedBeforeTransforming()
  291. {
  292. // The value is passed to the value transformer
  293. $transformer = $this->createMockTransformer();
  294. $transformer->expects($this->once())
  295. ->method('reverseTransform')
  296. ->with($this->identicalTo('a'))
  297. ->will($this->returnValue('reverse[a]'));
  298. $transformer->expects($this->exactly(2))
  299. ->method('transform')
  300. // Impossible to test with PHPUnit because called twice
  301. // ->with($this->identicalTo('reverse[a]'))
  302. ->will($this->returnValue('a'));
  303. $field = new TestField('title', array(
  304. 'value_transformer' => $transformer,
  305. ));
  306. $field->bind(' a ');
  307. $this->assertEquals('a', $field->getDisplayedData());
  308. $this->assertEquals('reverse[a]', $field->getData());
  309. }
  310. public function testBoundValuesAreNotTrimmedBeforeTransformingIfDisabled()
  311. {
  312. // The value is passed to the value transformer
  313. $transformer = $this->createMockTransformer();
  314. $transformer->expects($this->once())
  315. ->method('reverseTransform')
  316. ->with($this->identicalTo(' a '))
  317. ->will($this->returnValue('reverse[ a ]'));
  318. $transformer->expects($this->exactly(2))
  319. ->method('transform')
  320. // Impossible to test with PHPUnit because called twice
  321. // ->with($this->identicalTo('reverse[ a ]'))
  322. ->will($this->returnValue(' a '));
  323. $field = new TestField('title', array(
  324. 'trim' => false,
  325. 'value_transformer' => $transformer,
  326. ));
  327. $field->bind(' a ');
  328. $this->assertEquals(' a ', $field->getDisplayedData());
  329. $this->assertEquals('reverse[ a ]', $field->getData());
  330. }
  331. /*
  332. * This is important so that bind() can work even if setData() was not called
  333. * before
  334. */
  335. public function testUpdatePropertyTreatsEmptyValuesAsArrays()
  336. {
  337. $array = null;
  338. $field = new TestField('firstName');
  339. $field->bind('Bernhard');
  340. $field->updateProperty($array);
  341. $this->assertEquals(array('firstName' => 'Bernhard'), $array);
  342. }
  343. public function testUpdatePropertyDoesNotUpdatePropertyIfPropertyPathIsEmpty()
  344. {
  345. $object = new Author();
  346. $field = new TestField('firstName', array('property_path' => null));
  347. $field->bind('Bernhard');
  348. $field->updateProperty($object);
  349. $this->assertEquals(null, $object->firstName);
  350. }
  351. protected function createMockTransformer()
  352. {
  353. return $this->getMock('Symfony\Component\Form\ValueTransformer\ValueTransformerInterface', array(), array(), '', false, false);
  354. }
  355. protected function createMockTransformerTransformingTo($value)
  356. {
  357. $transformer = $this->createMockTransformer();
  358. $transformer->expects($this->any())
  359. ->method('reverseTransform')
  360. ->will($this->returnValue($value));
  361. return $transformer;
  362. }
  363. protected function createMockGroup()
  364. {
  365. return $this->getMock(
  366. 'Symfony\Component\Form\FieldGroup',
  367. array(),
  368. array(),
  369. '',
  370. false // don't call constructor
  371. );
  372. }
  373. protected function createMockGroupWithName($name)
  374. {
  375. $group = $this->createMockGroup();
  376. $group->expects($this->any())
  377. ->method('getName')
  378. ->will($this->returnValue($name));
  379. return $group;
  380. }
  381. protected function createMockGroupWithId($id)
  382. {
  383. $group = $this->createMockGroup();
  384. $group->expects($this->any())
  385. ->method('getId')
  386. ->will($this->returnValue($id));
  387. return $group;
  388. }
  389. }