4 * This file is part of the Symfony package.
6 * (c) Fabien Potencier <fabien@symfony.com>
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
12 namespace Symfony\Component\Form\Tests\Extension\Validator\Constraints
;
14 use Symfony\Component\Form\FormBuilder
;
15 use Symfony\Component\Form\Exception\TransformationFailedException
;
16 use Symfony\Component\Form\CallbackTransformer
;
17 use Symfony\Component\Form\FormInterface
;
18 use Symfony\Component\Form\Extension\Validator\Constraints\Form
;
19 use Symfony\Component\Form\Extension\Validator\Constraints\FormValidator
;
20 use Symfony\Component\Form\SubmitButtonBuilder
;
21 use Symfony\Component\Validator\Constraint
;
22 use Symfony\Component\Validator\Constraints\NotNull
;
23 use Symfony\Component\Validator\Constraints\NotBlank
;
26 * @author Bernhard Schussek <bschussek@gmail.com>
28 class FormValidatorTest
extends \PHPUnit_Framework_TestCase
31 * @var \PHPUnit_Framework_MockObject_MockObject
36 * @var \PHPUnit_Framework_MockObject_MockObject
41 * @var \PHPUnit_Framework_MockObject_MockObject
43 private $serverParams;
50 protected function setUp()
52 if (!class_exists('Symfony\Component\EventDispatcher\Event')) {
53 $this->markTestSkipped('The "EventDispatcher" component is not available');
56 $this->dispatcher
= $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface');
57 $this->factory
= $this->getMock('Symfony\Component\Form\FormFactoryInterface');
58 $this->serverParams
= $this->getMock(
59 'Symfony\Component\Form\Extension\Validator\Util\ServerParams',
60 array('getNormalizedIniPostMaxSize', 'getContentLength')
62 $this->validator
= new FormValidator($this->serverParams
);
65 public function testValidate()
67 $context = $this->getMockExecutionContext();
68 $object = $this->getMock('\stdClass');
69 $options = array('validation_groups' => array('group1', 'group2'));
70 $form = $this->getBuilder('name', '\stdClass', $options)
74 $context->expects($this->at(0))
76 ->with($object, 'data', 'group1', true);
77 $context->expects($this->at(1))
79 ->with($object, 'data', 'group2', true);
81 $this->validator
->initialize($context);
82 $this->validator
->validate($form, new Form());
85 public function testValidateConstraints()
87 $context = $this->getMockExecutionContext();
88 $object = $this->getMock('\stdClass');
89 $constraint1 = new NotNull(array('groups' => array('group1', 'group2')));
90 $constraint2 = new NotBlank(array('groups' => 'group2'));
93 'validation_groups' => array('group1', 'group2'),
94 'constraints' => array($constraint1, $constraint2),
96 $form = $this->getBuilder('name', '\stdClass', $options)
100 // First default constraints
101 $context->expects($this->at(0))
103 ->with($object, 'data', 'group1', true);
104 $context->expects($this->at(1))
106 ->with($object, 'data', 'group2', true);
108 // Then custom constraints
109 $context->expects($this->at(2))
110 ->method('validateValue')
111 ->with($object, $constraint1, 'data', 'group1');
112 $context->expects($this->at(3))
113 ->method('validateValue')
114 ->with($object, $constraint2, 'data', 'group2');
116 $this->validator
->initialize($context);
117 $this->validator
->validate($form, new Form());
120 public function testDontValidateIfParentWithoutCascadeValidation()
122 $context = $this->getMockExecutionContext();
123 $object = $this->getMock('\stdClass');
125 $parent = $this->getBuilder('parent', null, array('cascade_validation' => false))
127 ->setDataMapper($this->getDataMapper())
129 $options = array('validation_groups' => array('group1', 'group2'));
130 $form = $this->getBuilder('name', '\stdClass', $options)->getForm();
133 $form->setData($object);
135 $context->expects($this->never())
136 ->method('validate');
138 $this->validator
->initialize($context);
139 $this->validator
->validate($form, new Form());
142 public function testValidateConstraintsEvenIfNoCascadeValidation()
144 $context = $this->getMockExecutionContext();
145 $object = $this->getMock('\stdClass');
146 $constraint1 = new NotNull(array('groups' => array('group1', 'group2')));
147 $constraint2 = new NotBlank(array('groups' => 'group2'));
149 $parent = $this->getBuilder('parent', null, array('cascade_validation' => false))
151 ->setDataMapper($this->getDataMapper())
154 'validation_groups' => array('group1', 'group2'),
155 'constraints' => array($constraint1, $constraint2),
157 $form = $this->getBuilder('name', '\stdClass', $options)
162 $context->expects($this->at(0))
163 ->method('validateValue')
164 ->with($object, $constraint1, 'data', 'group1');
165 $context->expects($this->at(1))
166 ->method('validateValue')
167 ->with($object, $constraint2, 'data', 'group2');
169 $this->validator
->initialize($context);
170 $this->validator
->validate($form, new Form());
173 public function testDontValidateIfNoValidationGroups()
175 $context = $this->getMockExecutionContext();
176 $object = $this->getMock('\stdClass');
178 $form = $this->getBuilder('name', '\stdClass', array(
179 'validation_groups' => array(),
184 $form->setData($object);
186 $context->expects($this->never())
187 ->method('validate');
189 $this->validator
->initialize($context);
190 $this->validator
->validate($form, new Form());
193 public function testDontValidateConstraintsIfNoValidationGroups()
195 $context = $this->getMockExecutionContext();
196 $object = $this->getMock('\stdClass');
197 $constraint1 = $this->getMock('Symfony\Component\Validator\Constraint');
198 $constraint2 = $this->getMock('Symfony\Component\Validator\Constraint');
201 'validation_groups' => array(),
202 'constraints' => array($constraint1, $constraint2),
204 $form = $this->getBuilder('name', '\stdClass', $options)
208 // Launch transformer
209 $form->submit(array());
211 $context->expects($this->never())
212 ->method('validate');
214 $this->validator
->initialize($context);
215 $this->validator
->validate($form, new Form());
218 public function testDontValidateIfNotSynchronized()
220 $context = $this->getMockExecutionContext();
221 $object = $this->getMock('\stdClass');
223 $form = $this->getBuilder('name', '\stdClass', array(
224 'invalid_message' => 'invalid_message_key',
225 // Invalid message parameters must be supported, because the
226 // invalid message can be a translation key
227 // see https://github.com/symfony/symfony/issues/5144
228 'invalid_message_parameters' => array('{{ foo }}' => 'bar'),
231 ->addViewTransformer(new CallbackTransformer(
232 function ($data) { return $data
; },
233 function () { throw new TransformationFailedException(); }
237 // Launch transformer
238 $form->submit('foo');
240 $context->expects($this->never())
241 ->method('validate');
243 $context->expects($this->once())
244 ->method('addViolation')
246 'invalid_message_key',
247 array('{{ value }}' => 'foo', '{{ foo }}' => 'bar'),
250 $context->expects($this->never())
251 ->method('addViolationAt');
253 $this->validator
->initialize($context);
254 $this->validator
->validate($form, new Form());
257 public function testAddInvalidErrorEvenIfNoValidationGroups()
259 $context = $this->getMockExecutionContext();
260 $object = $this->getMock('\stdClass');
262 $form = $this->getBuilder('name', '\stdClass', array(
263 'invalid_message' => 'invalid_message_key',
264 // Invalid message parameters must be supported, because the
265 // invalid message can be a translation key
266 // see https://github.com/symfony/symfony/issues/5144
267 'invalid_message_parameters' => array('{{ foo }}' => 'bar'),
268 'validation_groups' => array(),
271 ->addViewTransformer(new CallbackTransformer(
272 function ($data) { return $data
; },
273 function () { throw new TransformationFailedException(); }
277 // Launch transformer
278 $form->submit('foo');
280 $context->expects($this->never())
281 ->method('validate');
283 $context->expects($this->once())
284 ->method('addViolation')
286 'invalid_message_key',
287 array('{{ value }}' => 'foo', '{{ foo }}' => 'bar'),
290 $context->expects($this->never())
291 ->method('addViolationAt');
293 $this->validator
->initialize($context);
294 $this->validator
->validate($form, new Form());
297 public function testDontValidateConstraintsIfNotSynchronized()
299 $context = $this->getMockExecutionContext();
300 $object = $this->getMock('\stdClass');
301 $constraint1 = $this->getMock('Symfony\Component\Validator\Constraint');
302 $constraint2 = $this->getMock('Symfony\Component\Validator\Constraint');
305 'validation_groups' => array('group1', 'group2'),
306 'constraints' => array($constraint1, $constraint2),
308 $form = $this->getBuilder('name', '\stdClass', $options)
310 ->addViewTransformer(new CallbackTransformer(
311 function ($data) { return $data
; },
312 function () { throw new TransformationFailedException(); }
316 // Launch transformer
317 $form->submit(array());
319 $context->expects($this->never())
320 ->method('validate');
322 $this->validator
->initialize($context);
323 $this->validator
->validate($form, new Form());
326 // https://github.com/symfony/symfony/issues/4359
327 public function testDontMarkInvalidIfAnyChildIsNotSynchronized()
329 $context = $this->getMockExecutionContext();
330 $object = $this->getMock('\stdClass');
332 $failingTransformer = new CallbackTransformer(
333 function ($data) { return $data
; },
334 function () { throw new TransformationFailedException(); }
337 $form = $this->getBuilder('name', '\stdClass')
339 ->addViewTransformer($failingTransformer)
341 ->setDataMapper($this->getDataMapper())
343 $this->getBuilder('child')
344 ->addViewTransformer($failingTransformer)
348 // Launch transformer
349 $form->submit(array('child' => 'foo'));
351 $context->expects($this->never())
352 ->method('addViolation');
353 $context->expects($this->never())
354 ->method('addViolationAt');
356 $this->validator
->initialize($context);
357 $this->validator
->validate($form, new Form());
360 public function testHandleCallbackValidationGroups()
362 $context = $this->getMockExecutionContext();
363 $object = $this->getMock('\stdClass');
364 $options = array('validation_groups' => array($this, 'getValidationGroups'));
365 $form = $this->getBuilder('name', '\stdClass', $options)
369 $context->expects($this->at(0))
371 ->with($object, 'data', 'group1', true);
372 $context->expects($this->at(1))
374 ->with($object, 'data', 'group2', true);
376 $this->validator
->initialize($context);
377 $this->validator
->validate($form, new Form());
380 public function testDontExecuteFunctionNames()
382 $context = $this->getMockExecutionContext();
383 $object = $this->getMock('\stdClass');
384 $options = array('validation_groups' => 'header');
385 $form = $this->getBuilder('name', '\stdClass', $options)
389 $context->expects($this->once())
391 ->with($object, 'data', 'header', true);
393 $this->validator
->initialize($context);
394 $this->validator
->validate($form, new Form());
397 public function testHandleClosureValidationGroups()
399 $context = $this->getMockExecutionContext();
400 $object = $this->getMock('\stdClass');
401 $options = array('validation_groups' => function(FormInterface
$form){
402 return array('group1', 'group2');
404 $form = $this->getBuilder('name', '\stdClass', $options)
408 $context->expects($this->at(0))
410 ->with($object, 'data', 'group1', true);
411 $context->expects($this->at(1))
413 ->with($object, 'data', 'group2', true);
415 $this->validator
->initialize($context);
416 $this->validator
->validate($form, new Form());
419 public function testUseValidationGroupOfClickedButton()
421 $context = $this->getMockExecutionContext();
422 $object = $this->getMock('\stdClass');
424 $parent = $this->getBuilder('parent', null, array('cascade_validation' => true))
426 ->setDataMapper($this->getDataMapper())
428 $form = $this->getForm('name', '\stdClass', array(
429 'validation_groups' => 'form_group',
433 $parent->add($this->getClickedSubmitButton('submit', array(
434 'validation_groups' => 'button_group',
437 $form->setData($object);
439 $context->expects($this->once())
441 ->with($object, 'data', 'button_group', true);
443 $this->validator
->initialize($context);
444 $this->validator
->validate($form, new Form());
447 public function testDontUseValidationGroupOfUnclickedButton()
449 $context = $this->getMockExecutionContext();
450 $object = $this->getMock('\stdClass');
452 $parent = $this->getBuilder('parent', null, array('cascade_validation' => true))
454 ->setDataMapper($this->getDataMapper())
456 $form = $this->getForm('name', '\stdClass', array(
457 'validation_groups' => 'form_group',
461 $parent->add($this->getSubmitButton('submit', array(
462 'validation_groups' => 'button_group',
465 $form->setData($object);
467 $context->expects($this->once())
469 ->with($object, 'data', 'form_group', true);
471 $this->validator
->initialize($context);
472 $this->validator
->validate($form, new Form());
475 public function testUseInheritedValidationGroup()
477 $context = $this->getMockExecutionContext();
478 $object = $this->getMock('\stdClass');
480 $parentOptions = array(
481 'validation_groups' => 'group',
482 'cascade_validation' => true,
484 $parent = $this->getBuilder('parent', null, $parentOptions)
486 ->setDataMapper($this->getDataMapper())
488 $form = $this->getBuilder('name', '\stdClass')->getForm();
491 $form->setData($object);
493 $context->expects($this->once())
495 ->with($object, 'data', 'group', true);
497 $this->validator
->initialize($context);
498 $this->validator
->validate($form, new Form());
501 public function testUseInheritedCallbackValidationGroup()
503 $context = $this->getMockExecutionContext();
504 $object = $this->getMock('\stdClass');
506 $parentOptions = array(
507 'validation_groups' => array($this, 'getValidationGroups'),
508 'cascade_validation' => true,
510 $parent = $this->getBuilder('parent', null, $parentOptions)
512 ->setDataMapper($this->getDataMapper())
514 $form = $this->getBuilder('name', '\stdClass')->getForm();
517 $form->setData($object);
519 $context->expects($this->at(0))
521 ->with($object, 'data', 'group1', true);
522 $context->expects($this->at(1))
524 ->with($object, 'data', 'group2', true);
526 $this->validator
->initialize($context);
527 $this->validator
->validate($form, new Form());
530 public function testUseInheritedClosureValidationGroup()
532 $context = $this->getMockExecutionContext();
533 $object = $this->getMock('\stdClass');
535 $parentOptions = array(
536 'validation_groups' => function(FormInterface
$form){
537 return array('group1', 'group2');
539 'cascade_validation' => true,
541 $parent = $this->getBuilder('parent', null, $parentOptions)
543 ->setDataMapper($this->getDataMapper())
545 $form = $this->getBuilder('name', '\stdClass')->getForm();
548 $form->setData($object);
550 $context->expects($this->at(0))
552 ->with($object, 'data', 'group1', true);
553 $context->expects($this->at(1))
555 ->with($object, 'data', 'group2', true);
557 $this->validator
->initialize($context);
558 $this->validator
->validate($form, new Form());
561 public function testAppendPropertyPath()
563 $context = $this->getMockExecutionContext();
564 $object = $this->getMock('\stdClass');
565 $form = $this->getBuilder('name', '\stdClass')
569 $context->expects($this->once())
571 ->with($object, 'data', 'Default', true);
573 $this->validator
->initialize($context);
574 $this->validator
->validate($form, new Form());
577 public function testDontWalkScalars()
579 $context = $this->getMockExecutionContext();
581 $form = $this->getBuilder()
585 $context->expects($this->never())
586 ->method('validate');
588 $this->validator
->initialize($context);
589 $this->validator
->validate($form, new Form());
592 public function testViolationIfExtraData()
594 $context = $this->getMockExecutionContext();
596 $form = $this->getBuilder('parent', null, array('extra_fields_message' => 'Extra!'))
598 ->setDataMapper($this->getDataMapper())
599 ->add($this->getBuilder('child'))
602 $form->submit(array('foo' => 'bar'));
604 $context->expects($this->once())
605 ->method('addViolation')
608 array('{{ extra_fields }}' => 'foo'),
609 array('foo' => 'bar')
611 $context->expects($this->never())
612 ->method('addViolationAt');
614 $this->validator
->initialize($context);
615 $this->validator
->validate($form, new Form());
619 * @dataProvider getPostMaxSizeFixtures
621 public function testPostMaxSizeViolation($contentLength, $iniMax, $nbViolation, array $params = array())
623 $this->serverParams
->expects($this->once())
624 ->method('getContentLength')
625 ->will($this->returnValue($contentLength));
626 $this->serverParams
->expects($this->any())
627 ->method('getNormalizedIniPostMaxSize')
628 ->will($this->returnValue($iniMax));
630 $context = $this->getMockExecutionContext();
631 $options = array('post_max_size_message' => 'Max {{ max }}!');
632 $form = $this->getBuilder('name', null, $options)->getForm();
634 for ($i = 0; $i < $nbViolation; ++
$i) {
635 if (0 === $i && count($params) > 0) {
636 $context->expects($this->at($i))
637 ->method('addViolation')
638 ->with($options['post_max_size_message'], $params);
640 $context->expects($this->at($i))
641 ->method('addViolation');
645 $context->expects($this->never())
646 ->method('addViolationAt');
648 $this->validator
->initialize($context);
649 $this->validator
->validate($form, new Form());
652 public function getPostMaxSizeFixtures()
655 array(pow(1024, 3) +
1, '1G', 1, array('{{ max }}' => '1G')),
656 array(pow(1024, 3), '1G', 0),
657 array(pow(1024, 2) +
1, '1M', 1, array('{{ max }}' => '1M')),
658 array(pow(1024, 2), '1M', 0),
659 array(1024 +
1, '1K', 1, array('{{ max }}' => '1K')),
660 array(1024, '1K', 0),
661 array(null, '1K', 0),
667 public function testNoViolationIfNotRoot()
669 $this->serverParams
->expects($this->once())
670 ->method('getContentLength')
671 ->will($this->returnValue(1025));
672 $this->serverParams
->expects($this->never())
673 ->method('getNormalizedIniPostMaxSize');
675 $context = $this->getMockExecutionContext();
676 $parent = $this->getBuilder()
678 ->setDataMapper($this->getDataMapper())
680 $form = $this->getForm();
683 $context->expects($this->never())
684 ->method('addViolation');
685 $context->expects($this->never())
686 ->method('addViolationAt');
688 $this->validator
->initialize($context);
689 $this->validator
->validate($form, new Form());
693 * Access has to be public, as this method is called via callback array
694 * in {@link testValidateFormDataCanHandleCallbackValidationGroups()}
695 * and {@link testValidateFormDataUsesInheritedCallbackValidationGroup()}
697 public function getValidationGroups(FormInterface
$form)
699 return array('group1', 'group2');
702 private function getMockExecutionContext()
704 return $this->getMock('Symfony\Component\Validator\ExecutionContextInterface');
708 * @param string $name
709 * @param string $dataClass
710 * @param array $options
712 * @return FormBuilder
714 private function getBuilder($name = 'name', $dataClass = null, array $options = array())
716 $options = array_replace(array(
717 'constraints' => array(),
718 'invalid_message_parameters' => array(),
721 return new FormBuilder($name, $dataClass, $this->dispatcher
, $this->factory
, $options);
724 private function getForm($name = 'name', $dataClass = null, array $options = array())
726 return $this->getBuilder($name, $dataClass, $options)->getForm();
729 private function getSubmitButton($name = 'name', array $options = array())
731 $builder = new SubmitButtonBuilder($name, $options);
733 return $builder->getForm();
736 private function getClickedSubmitButton($name = 'name', array $options = array())
738 return $this->getSubmitButton($name, $options)->submit('');
742 * @return \PHPUnit_Framework_MockObject_MockObject
744 private function getDataMapper()
746 return $this->getMock('Symfony\Component\Form\DataMapperInterface');