1<?php 2 3declare(strict_types=1); 4 5namespace JMS\Serializer\Tests\Handler; 6 7use JMS\Serializer\GraphNavigatorInterface; 8use JMS\Serializer\Handler\FormErrorHandler; 9use JMS\Serializer\JsonSerializationVisitor; 10use JMS\Serializer\SerializationContext; 11use JMS\Serializer\Visitor\Factory\JsonSerializationVisitorFactory; 12use PHPUnit\Framework\TestCase; 13use Symfony\Component\EventDispatcher\EventDispatcher; 14use Symfony\Component\EventDispatcher\EventDispatcherInterface; 15use Symfony\Component\Form\Extension\Core\Type\FormType; 16use Symfony\Component\Form\Extension\Core\Type\TextType; 17use Symfony\Component\Form\Extension\Validator\ValidatorExtension; 18use Symfony\Component\Form\FormBuilder; 19use Symfony\Component\Form\FormError; 20use Symfony\Component\Form\FormFactoryInterface; 21use Symfony\Component\Form\Forms; 22use Symfony\Component\Translation\Translator; 23use Symfony\Component\Validator\Constraints\Length; 24use Symfony\Component\Validator\Validation; 25 26class FormErrorHandlerTest extends TestCase 27{ 28 /** 29 * @var FormErrorHandler 30 */ 31 protected $handler; 32 33 /** 34 * @var JsonSerializationVisitor 35 */ 36 protected $visitor; 37 38 /** 39 * @var EventDispatcherInterface 40 */ 41 protected $dispatcher; 42 43 /** 44 * @var FormFactoryInterface 45 */ 46 protected $factory; 47 48 public function setUp() 49 { 50 $this->handler = new FormErrorHandler(new Translator('en')); 51 $navigator = $this->getMockBuilder(GraphNavigatorInterface::class)->getMock(); 52 $context = SerializationContext::create(); 53 $this->visitor = (new JsonSerializationVisitorFactory())->getVisitor($navigator, $context); 54 $this->dispatcher = new EventDispatcher(); 55 $this->factory = $this->getMockBuilder('Symfony\Component\Form\FormFactoryInterface')->getMock(); 56 } 57 58 protected function tearDown() 59 { 60 $this->handler = null; 61 $this->visitor = null; 62 $this->dispatcher = null; 63 $this->factory = null; 64 } 65 66 public function testSerializeEmptyFormError() 67 { 68 $form = $this->createForm(); 69 $json = json_encode($this->handler->serializeFormToJson($this->visitor, $form, [])); 70 71 self::assertSame('{}', $json); 72 } 73 74 public function testErrorHandlerWithoutTranslator() 75 { 76 $this->handler = new FormErrorHandler(); 77 $form = $this->createForm(); 78 $form->addError(new FormError('error!')); 79 $json = json_encode($this->handler->serializeFormToJson($this->visitor, $form, [])); 80 81 self::assertSame(json_encode([ 82 'errors' => ['error!'], 83 ]), $json); 84 } 85 86 public function testSerializeHasFormError() 87 { 88 $form = $this->createForm(); 89 $form->addError(new FormError('error!')); 90 $json = json_encode($this->handler->serializeFormToJson($this->visitor, $form, [])); 91 92 self::assertSame(json_encode([ 93 'errors' => ['error!'], 94 ]), $json); 95 } 96 97 public function testSerializeFormWithData() 98 { 99 $formFactoryBuilder = Forms::createFormFactoryBuilder(); 100 $formFactoryBuilder->addExtension(new ValidatorExtension(Validation::createValidator())); 101 102 $formFactory = $formFactoryBuilder->getFormFactory(); 103 $builer = $formFactory->createNamedBuilder('foo', FormType::class); 104 105 $builer->add('url', TextType::class); 106 $builer->add('txt', TextType::class, [ 107 'constraints' => [ 108 new Length(['min' => 10]), 109 ], 110 ]); 111 112 $form = $builer->getForm(); 113 114 $form->submit([ 115 'url' => 'hi', 116 'txt' => 'hello', 117 ]); 118 119 $data = json_encode($this->handler->serializeFormToJson($this->visitor, $form, [])); 120 self::assertSame('{"children":{"url":{},"txt":{"errors":["This value is too short. It should have 10 characters or more."]}}}', $data); 121 } 122 123 public function testSerializeChildElements() 124 { 125 $formFactory = Forms::createFormFactory(); 126 $form = $formFactory->createBuilder() 127 ->add('child') 128 ->add('date') 129 ->getForm(); 130 131 $form->addError(new FormError('error!')); 132 $form->get('date')->addError(new FormError('child-error')); 133 134 $json = json_encode($this->handler->serializeFormToJson($this->visitor, $form, [])); 135 136 self::assertSame(json_encode([ 137 'errors' => ['error!'], 138 'children' => [ 139 'child' => new \stdClass(), 140 'date' => ['errors' => ['child-error']], 141 ], 142 ]), $json); 143 } 144 145 public function testDefaultTranslationDomain() 146 { 147 /** @var Translator|\PHPUnit_Framework_MockObject_MockObject $translator */ 148 $translator = $this->getMockBuilder('Symfony\Component\Translation\TranslatorInterface')->getMock(); 149 150 $handler = new FormErrorHandler($translator); 151 152 $translator->expects($this->once()) 153 ->method('trans') 154 ->with( 155 $this->equalTo('error!'), 156 $this->equalTo([]), 157 $this->equalTo('validators') 158 ); 159 160 $formError = $this->getMockBuilder('Symfony\Component\Form\FormError')->disableOriginalConstructor()->getMock(); 161 $formError->expects($this->once())->method('getMessageTemplate')->willReturn('error!'); 162 $formError->expects($this->once())->method('getMessagePluralization')->willReturn(null); 163 $formError->expects($this->once())->method('getMessageParameters')->willReturn([]); 164 165 $this->invokeMethod($handler, 'getErrorMessage', [$formError]); 166 } 167 168 public function testDefaultTranslationDomainWithPluralTranslation() 169 { 170 /** @var Translator|\PHPUnit_Framework_MockObject_MockObject $translator */ 171 $translator = $this->getMockBuilder('Symfony\Component\Translation\TranslatorInterface')->getMock(); 172 173 $handler = new FormErrorHandler($translator); 174 175 $translator->expects($this->once()) 176 ->method('transChoice') 177 ->with( 178 $this->equalTo('error!'), 179 $this->equalTo(0), 180 $this->equalTo([]), 181 $this->equalTo('validators') 182 ); 183 184 $formError = $this->getMockBuilder('Symfony\Component\Form\FormError')->disableOriginalConstructor()->getMock(); 185 $formError->expects($this->once())->method('getMessageTemplate')->willReturn('error!'); 186 $formError->expects($this->exactly(2))->method('getMessagePluralization')->willReturn(0); 187 $formError->expects($this->once())->method('getMessageParameters')->willReturn([]); 188 189 $this->invokeMethod($handler, 'getErrorMessage', [$formError]); 190 } 191 192 public function testCustomTranslationDomain() 193 { 194 /** @var Translator|\PHPUnit_Framework_MockObject_MockObject $translator */ 195 $translator = $this->getMockBuilder('Symfony\Component\Translation\TranslatorInterface')->getMock(); 196 197 $handler = new FormErrorHandler($translator, 'custom_domain'); 198 199 $translator->expects($this->once()) 200 ->method('trans') 201 ->with( 202 $this->equalTo('error!'), 203 $this->equalTo([]), 204 $this->equalTo('custom_domain') 205 ); 206 207 $formError = $this->getMockBuilder('Symfony\Component\Form\FormError')->disableOriginalConstructor()->getMock(); 208 $formError->expects($this->once())->method('getMessageTemplate')->willReturn('error!'); 209 $formError->expects($this->once())->method('getMessagePluralization')->willReturn(null); 210 $formError->expects($this->once())->method('getMessageParameters')->willReturn([]); 211 212 $this->invokeMethod($handler, 'getErrorMessage', [$formError]); 213 } 214 215 public function testCustomTranslationDomainWithPluralTranslation() 216 { 217 /** @var Translator|\PHPUnit_Framework_MockObject_MockObject $translator */ 218 $translator = $this->getMockBuilder('Symfony\Component\Translation\TranslatorInterface')->getMock(); 219 220 $handler = new FormErrorHandler($translator, 'custom_domain'); 221 222 $translator->expects($this->once()) 223 ->method('transChoice') 224 ->with( 225 $this->equalTo('error!'), 226 $this->equalTo(0), 227 $this->equalTo([]), 228 $this->equalTo('custom_domain') 229 ); 230 231 $formError = $this->getMockBuilder('Symfony\Component\Form\FormError')->disableOriginalConstructor()->getMock(); 232 $formError->expects($this->once())->method('getMessageTemplate')->willReturn('error!'); 233 $formError->expects($this->exactly(2))->method('getMessagePluralization')->willReturn(0); 234 $formError->expects($this->once())->method('getMessageParameters')->willReturn([]); 235 236 $this->invokeMethod($handler, 'getErrorMessage', [$formError]); 237 } 238 239 /** 240 * @param string $name 241 * @param EventDispatcherInterface $dispatcher 242 * @param string $dataClass 243 * 244 * @return FormBuilder 245 */ 246 protected function getBuilder($name = 'name', ?EventDispatcherInterface $dispatcher = null, $dataClass = null) 247 { 248 return new FormBuilder($name, $dataClass, $dispatcher ?: $this->dispatcher, $this->factory); 249 } 250 251 /** 252 * @param string $name 253 * 254 * @return \PHPUnit_Framework_MockObject_MockObject 255 */ 256 protected function getMockForm($name = 'name') 257 { 258 $form = $this->getMockBuilder('Symfony\Component\Form\Test\FormInterface')->getMock(); 259 $config = $this->getMockBuilder('Symfony\Component\Form\FormConfigInterface')->getMock(); 260 261 $form->expects($this->any()) 262 ->method('getName') 263 ->will($this->returnValue($name)); 264 $form->expects($this->any()) 265 ->method('getConfig') 266 ->will($this->returnValue($config)); 267 268 return $form; 269 } 270 271 protected function createForm() 272 { 273 return $this->getBuilder()->getForm(); 274 } 275 276 protected function invokeMethod($object, $method, array $args = []) 277 { 278 $reflectionMethod = new \ReflectionMethod($object, $method); 279 $reflectionMethod->setAccessible(true); 280 281 return $reflectionMethod->invokeArgs($object, $args); 282 } 283} 284