]> git.immae.eu Git - github/wallabag/wallabag.git/blob - vendor/symfony/form/Symfony/Component/Form/Tests/AbstractDivLayoutTest.php
twig implementation
[github/wallabag/wallabag.git] / vendor / symfony / form / Symfony / Component / Form / Tests / AbstractDivLayoutTest.php
1 <?php
2
3 /*
4 * This file is part of the Symfony package.
5 *
6 * (c) Fabien Potencier <fabien@symfony.com>
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12 namespace Symfony\Component\Form\Tests;
13
14 use Symfony\Component\Form\FormError;
15 use Symfony\Component\Form\Tests\Fixtures\AlternatingRowType;
16
17 abstract class AbstractDivLayoutTest extends AbstractLayoutTest
18 {
19 public function testRow()
20 {
21 $form = $this->factory->createNamed('name', 'text');
22 $form->addError(new FormError('[trans]Error![/trans]'));
23 $view = $form->createView();
24 $html = $this->renderRow($view);
25
26 $this->assertMatchesXpath($html,
27 '/div
28 [
29 ./label[@for="name"]
30 /following-sibling::ul
31 [./li[.="[trans]Error![/trans]"]]
32 [count(./li)=1]
33 /following-sibling::input[@id="name"]
34 ]
35 '
36 );
37 }
38
39 public function testRowOverrideVariables()
40 {
41 $view = $this->factory->createNamed('name', 'text')->createView();
42 $html = $this->renderRow($view, array(
43 'attr' => array('class' => 'my&class'),
44 'label' => 'foo&bar',
45 'label_attr' => array('class' => 'my&label&class'),
46 ));
47
48 $this->assertMatchesXpath($html,
49 '/div
50 [
51 ./label[@for="name"][@class="my&label&class required"][.="[trans]foo&bar[/trans]"]
52 /following-sibling::input[@id="name"][@class="my&class"]
53 ]
54 '
55 );
56 }
57
58 public function testRepeatedRow()
59 {
60 $form = $this->factory->createNamed('name', 'repeated');
61 $form->addError(new FormError('[trans]Error![/trans]'));
62 $view = $form->createView();
63 $html = $this->renderRow($view);
64
65 // The errors of the form are not rendered by intention!
66 // In practice, repeated fields cannot have errors as all errors
67 // on them are mapped to the first child.
68 // (see RepeatedTypeValidatorExtension)
69
70 $this->assertMatchesXpath($html,
71 '/div
72 [
73 ./label[@for="name_first"]
74 /following-sibling::input[@id="name_first"]
75 ]
76 /following-sibling::div
77 [
78 ./label[@for="name_second"]
79 /following-sibling::input[@id="name_second"]
80 ]
81 '
82 );
83 }
84
85 public function testButtonRow()
86 {
87 $form = $this->factory->createNamed('name', 'button');
88 $view = $form->createView();
89 $html = $this->renderRow($view);
90
91 $this->assertMatchesXpath($html,
92 '/div
93 [
94 ./button[@type="button"][@name="name"]
95 ]
96 [count(//label)=0]
97 '
98 );
99 }
100
101 public function testRest()
102 {
103 $view = $this->factory->createNamedBuilder('name', 'form')
104 ->add('field1', 'text')
105 ->add('field2', 'repeated')
106 ->add('field3', 'text')
107 ->add('field4', 'text')
108 ->getForm()
109 ->createView();
110
111 // Render field2 row -> does not implicitly call renderWidget because
112 // it is a repeated field!
113 $this->renderRow($view['field2']);
114
115 // Render field3 widget
116 $this->renderWidget($view['field3']);
117
118 // Rest should only contain field1 and field4
119 $html = $this->renderRest($view);
120
121 $this->assertMatchesXpath($html,
122 '/div
123 [
124 ./label[@for="name_field1"]
125 /following-sibling::input[@type="text"][@id="name_field1"]
126 ]
127 /following-sibling::div
128 [
129 ./label[@for="name_field4"]
130 /following-sibling::input[@type="text"][@id="name_field4"]
131 ]
132 [count(../div)=2]
133 [count(..//label)=2]
134 [count(..//input)=3]
135 /following-sibling::input
136 [@type="hidden"]
137 [@id="name__token"]
138 '
139 );
140 }
141
142 public function testRestWithChildrenForms()
143 {
144 $child1 = $this->factory->createNamedBuilder('child1', 'form')
145 ->add('field1', 'text')
146 ->add('field2', 'text');
147
148 $child2 = $this->factory->createNamedBuilder('child2', 'form')
149 ->add('field1', 'text')
150 ->add('field2', 'text');
151
152 $view = $this->factory->createNamedBuilder('parent', 'form')
153 ->add($child1)
154 ->add($child2)
155 ->getForm()
156 ->createView();
157
158 // Render child1.field1 row
159 $this->renderRow($view['child1']['field1']);
160
161 // Render child2.field2 widget (remember that widget don't render label)
162 $this->renderWidget($view['child2']['field2']);
163
164 // Rest should only contain child1.field2 and child2.field1
165 $html = $this->renderRest($view);
166
167 $this->assertMatchesXpath($html,
168 '/div
169 [
170 ./label[not(@for)]
171 /following-sibling::div[@id="parent_child1"]
172 [
173 ./div
174 [
175 ./label[@for="parent_child1_field2"]
176 /following-sibling::input[@id="parent_child1_field2"]
177 ]
178 ]
179 ]
180
181 /following-sibling::div
182 [
183 ./label[not(@for)]
184 /following-sibling::div[@id="parent_child2"]
185 [
186 ./div
187 [
188 ./label[@for="parent_child2_field1"]
189 /following-sibling::input[@id="parent_child2_field1"]
190 ]
191 ]
192 ]
193 [count(//label)=4]
194 [count(//input[@type="text"])=2]
195 /following-sibling::input[@type="hidden"][@id="parent__token"]
196 '
197 );
198 }
199
200 public function testRestAndRepeatedWithRow()
201 {
202 $view = $this->factory->createNamedBuilder('name', 'form')
203 ->add('first', 'text')
204 ->add('password', 'repeated')
205 ->getForm()
206 ->createView();
207
208 $this->renderRow($view['password']);
209
210 $html = $this->renderRest($view);
211
212 $this->assertMatchesXpath($html,
213 '/div
214 [
215 ./label[@for="name_first"]
216 /following-sibling::input[@type="text"][@id="name_first"]
217 ]
218 [count(.//input)=1]
219 /following-sibling::input
220 [@type="hidden"]
221 [@id="name__token"]
222 '
223 );
224 }
225
226 public function testRestAndRepeatedWithRowPerChild()
227 {
228 $view = $this->factory->createNamedBuilder('name', 'form')
229 ->add('first', 'text')
230 ->add('password', 'repeated')
231 ->getForm()
232 ->createView();
233
234 $this->renderRow($view['password']['first']);
235 $this->renderRow($view['password']['second']);
236
237 $html = $this->renderRest($view);
238
239 $this->assertMatchesXpath($html,
240 '/div
241 [
242 ./label[@for="name_first"]
243 /following-sibling::input[@type="text"][@id="name_first"]
244 ]
245 [count(.//input)=1]
246 [count(.//label)=1]
247 /following-sibling::input
248 [@type="hidden"]
249 [@id="name__token"]
250 '
251 );
252 }
253
254 public function testRestAndRepeatedWithWidgetPerChild()
255 {
256 $view = $this->factory->createNamedBuilder('name', 'form')
257 ->add('first', 'text')
258 ->add('password', 'repeated')
259 ->getForm()
260 ->createView();
261
262 // The password form is considered as rendered as all its children
263 // are rendered
264 $this->renderWidget($view['password']['first']);
265 $this->renderWidget($view['password']['second']);
266
267 $html = $this->renderRest($view);
268
269 $this->assertMatchesXpath($html,
270 '/div
271 [
272 ./label[@for="name_first"]
273 /following-sibling::input[@type="text"][@id="name_first"]
274 ]
275 [count(//input)=2]
276 [count(//label)=1]
277 /following-sibling::input
278 [@type="hidden"]
279 [@id="name__token"]
280 '
281 );
282 }
283
284 public function testCollection()
285 {
286 $form = $this->factory->createNamed('name', 'collection', array('a', 'b'), array(
287 'type' => 'text',
288 ));
289
290 $this->assertWidgetMatchesXpath($form->createView(), array(),
291 '/div
292 [
293 ./div[./input[@type="text"][@value="a"]]
294 /following-sibling::div[./input[@type="text"][@value="b"]]
295 ]
296 [count(./div[./input])=2]
297 '
298 );
299 }
300
301 // https://github.com/symfony/symfony/issues/5038
302 public function testCollectionWithAlternatingRowTypes()
303 {
304 $data = array(
305 array('title' => 'a'),
306 array('title' => 'b'),
307 );
308 $form = $this->factory->createNamed('name', 'collection', $data, array(
309 'type' => new AlternatingRowType(),
310 ));
311
312 $this->assertWidgetMatchesXpath($form->createView(), array(),
313 '/div
314 [
315 ./div[./div/div/input[@type="text"][@value="a"]]
316 /following-sibling::div[./div/div/textarea[.="b"]]
317 ]
318 [count(./div[./div/div/input])=1]
319 [count(./div[./div/div/textarea])=1]
320 '
321 );
322 }
323
324 public function testEmptyCollection()
325 {
326 $form = $this->factory->createNamed('name', 'collection', array(), array(
327 'type' => 'text',
328 ));
329
330 $this->assertWidgetMatchesXpath($form->createView(), array(),
331 '/div
332 [./input[@type="hidden"][@id="name__token"]]
333 [count(./div)=0]
334 '
335 );
336 }
337
338 public function testCollectionRow()
339 {
340 $collection = $this->factory->createNamedBuilder(
341 'collection',
342 'collection',
343 array('a', 'b'),
344 array('type' => 'text')
345 );
346
347 $form = $this->factory->createNamedBuilder('form', 'form')
348 ->add($collection)
349 ->getForm();
350
351 $this->assertWidgetMatchesXpath($form->createView(), array(),
352 '/div
353 [
354 ./div
355 [
356 ./label[not(@for)]
357 /following-sibling::div
358 [
359 ./div
360 [
361 ./label[@for="form_collection_0"]
362 /following-sibling::input[@type="text"][@value="a"]
363 ]
364 /following-sibling::div
365 [
366 ./label[@for="form_collection_1"]
367 /following-sibling::input[@type="text"][@value="b"]
368 ]
369 ]
370 ]
371 /following-sibling::input[@type="hidden"][@id="form__token"]
372 ]
373 [count(.//input)=3]
374 '
375 );
376 }
377
378 public function testForm()
379 {
380 $form = $this->factory->createNamedBuilder('name', 'form')
381 ->setMethod('PUT')
382 ->setAction('http://example.com')
383 ->add('firstName', 'text')
384 ->add('lastName', 'text')
385 ->getForm();
386
387 // include ampersands everywhere to validate escaping
388 $html = $this->renderForm($form->createView(), array(
389 'id' => 'my&id',
390 'attr' => array('class' => 'my&class'),
391 ));
392
393 $this->assertMatchesXpath($html,
394 '/form
395 [
396 ./input[@type="hidden"][@name="_method"][@value="PUT"]
397 /following-sibling::div
398 [
399 ./div
400 [
401 ./label[@for="name_firstName"]
402 /following-sibling::input[@type="text"][@id="name_firstName"]
403 ]
404 /following-sibling::div
405 [
406 ./label[@for="name_lastName"]
407 /following-sibling::input[@type="text"][@id="name_lastName"]
408 ]
409 /following-sibling::input[@type="hidden"][@id="name__token"]
410 ]
411 [count(.//input)=3]
412 [@id="my&id"]
413 [@class="my&class"]
414 ]
415 [@method="post"]
416 [@action="http://example.com"]
417 [@class="my&class"]
418 '
419 );
420 }
421
422 public function testFormWidget()
423 {
424 $form = $this->factory->createNamedBuilder('name', 'form')
425 ->add('firstName', 'text')
426 ->add('lastName', 'text')
427 ->getForm();
428
429 $this->assertWidgetMatchesXpath($form->createView(), array(),
430 '/div
431 [
432 ./div
433 [
434 ./label[@for="name_firstName"]
435 /following-sibling::input[@type="text"][@id="name_firstName"]
436 ]
437 /following-sibling::div
438 [
439 ./label[@for="name_lastName"]
440 /following-sibling::input[@type="text"][@id="name_lastName"]
441 ]
442 /following-sibling::input[@type="hidden"][@id="name__token"]
443 ]
444 [count(.//input)=3]
445 '
446 );
447 }
448
449 // https://github.com/symfony/symfony/issues/2308
450 public function testNestedFormError()
451 {
452 $form = $this->factory->createNamedBuilder('name', 'form')
453 ->add($this->factory
454 ->createNamedBuilder('child', 'form', null, array('error_bubbling' => false))
455 ->add('grandChild', 'form')
456 )
457 ->getForm();
458
459 $form->get('child')->addError(new FormError('[trans]Error![/trans]'));
460
461 $this->assertWidgetMatchesXpath($form->createView(), array(),
462 '/div
463 [
464 ./div/label
465 /following-sibling::ul[./li[.="[trans]Error![/trans]"]]
466 ]
467 [count(.//li[.="[trans]Error![/trans]"])=1]
468 '
469 );
470 }
471
472 public function testCsrf()
473 {
474 $this->csrfProvider->expects($this->any())
475 ->method('generateCsrfToken')
476 ->will($this->returnValue('foo&bar'));
477
478 $form = $this->factory->createNamedBuilder('name', 'form')
479 ->add($this->factory
480 // No CSRF protection on nested forms
481 ->createNamedBuilder('child', 'form')
482 ->add($this->factory->createNamedBuilder('grandchild', 'text'))
483 )
484 ->getForm();
485
486 $this->assertWidgetMatchesXpath($form->createView(), array(),
487 '/div
488 [
489 ./div
490 /following-sibling::input[@type="hidden"][@id="name__token"][@value="foo&bar"]
491 ]
492 [count(.//input[@type="hidden"])=1]
493 '
494 );
495 }
496
497 public function testRepeated()
498 {
499 $form = $this->factory->createNamed('name', 'repeated', 'foobar', array(
500 'type' => 'text',
501 ));
502
503 $this->assertWidgetMatchesXpath($form->createView(), array(),
504 '/div
505 [
506 ./div
507 [
508 ./label[@for="name_first"]
509 /following-sibling::input[@type="text"][@id="name_first"]
510 ]
511 /following-sibling::div
512 [
513 ./label[@for="name_second"]
514 /following-sibling::input[@type="text"][@id="name_second"]
515 ]
516 /following-sibling::input[@type="hidden"][@id="name__token"]
517 ]
518 [count(.//input)=3]
519 '
520 );
521 }
522
523 public function testRepeatedWithCustomOptions()
524 {
525 $form = $this->factory->createNamed('name', 'repeated', null, array(
526 // the global required value cannot be overridden
527 'first_options' => array('label' => 'Test', 'required' => false),
528 'second_options' => array('label' => 'Test2')
529 ));
530
531 $this->assertWidgetMatchesXpath($form->createView(), array(),
532 '/div
533 [
534 ./div
535 [
536 ./label[@for="name_first"][.="[trans]Test[/trans]"]
537 /following-sibling::input[@type="text"][@id="name_first"][@required="required"]
538 ]
539 /following-sibling::div
540 [
541 ./label[@for="name_second"][.="[trans]Test2[/trans]"]
542 /following-sibling::input[@type="text"][@id="name_second"][@required="required"]
543 ]
544 /following-sibling::input[@type="hidden"][@id="name__token"]
545 ]
546 [count(.//input)=3]
547 '
548 );
549 }
550
551 public function testSearchInputName()
552 {
553 $form = $this->factory->createNamedBuilder('full', 'form')
554 ->add('name', 'search')
555 ->getForm();
556
557 $this->assertWidgetMatchesXpath($form->createView(), array(),
558 '/div
559 [
560 ./div
561 [
562 ./label[@for="full_name"]
563 /following-sibling::input[@type="search"][@id="full_name"][@name="full[name]"]
564 ]
565 /following-sibling::input[@type="hidden"][@id="full__token"]
566 ]
567 [count(//input)=2]
568 '
569 );
570 }
571
572 public function testLabelHasNoId()
573 {
574 $form = $this->factory->createNamed('name', 'text');
575 $html = $this->renderRow($form->createView());
576
577 $this->assertMatchesXpath($html,
578 '/div
579 [
580 ./label[@for="name"][not(@id)]
581 /following-sibling::input[@id="name"]
582 ]
583 '
584 );
585 }
586
587 public function testLabelIsNotRenderedWhenSetToFalse()
588 {
589 $form = $this->factory->createNamed('name', 'text', null, array(
590 'label' => false
591 ));
592 $html = $this->renderRow($form->createView());
593
594 $this->assertMatchesXpath($html,
595 '/div
596 [
597 ./input[@id="name"]
598 ]
599 [count(//label)=0]
600 '
601 );
602 }
603
604 /**
605 * @dataProvider themeBlockInheritanceProvider
606 */
607 public function testThemeBlockInheritance($theme)
608 {
609 $view = $this->factory
610 ->createNamed('name', 'email')
611 ->createView()
612 ;
613
614 $this->setTheme($view, $theme);
615
616 $this->assertMatchesXpath(
617 $this->renderWidget($view),
618 '/input[@type="email"][@rel="theme"]'
619 );
620 }
621
622 /**
623 * @dataProvider themeInheritanceProvider
624 */
625 public function testThemeInheritance($parentTheme, $childTheme)
626 {
627 $child = $this->factory->createNamedBuilder('child', 'form')
628 ->add('field', 'text');
629
630 $view = $this->factory->createNamedBuilder('parent', 'form')
631 ->add('field', 'text')
632 ->add($child)
633 ->getForm()
634 ->createView()
635 ;
636
637 $this->setTheme($view, $parentTheme);
638 $this->setTheme($view['child'], $childTheme);
639
640 $this->assertWidgetMatchesXpath($view, array(),
641 '/div
642 [
643 ./div
644 [
645 ./label[.="parent"]
646 /following-sibling::input[@type="text"]
647 ]
648 /following-sibling::div
649 [
650 ./label[.="child"]
651 /following-sibling::div
652 [
653 ./div
654 [
655 ./label[.="child"]
656 /following-sibling::input[@type="text"]
657 ]
658 ]
659 ]
660 /following-sibling::input[@type="hidden"]
661 ]
662 '
663 );
664 }
665
666 /**
667 * The block "_name_child_label" should be overridden in the theme of the
668 * implemented driver.
669 */
670 public function testCollectionRowWithCustomBlock()
671 {
672 $collection = array('one', 'two', 'three');
673 $form = $this->factory->createNamedBuilder('name', 'collection', $collection)
674 ->getForm();
675
676 $this->assertWidgetMatchesXpath($form->createView(), array(),
677 '/div
678 [
679 ./div[./label[.="Custom label: [trans]0[/trans]"]]
680 /following-sibling::div[./label[.="Custom label: [trans]1[/trans]"]]
681 /following-sibling::div[./label[.="Custom label: [trans]2[/trans]"]]
682 ]
683 '
684 );
685 }
686
687 public function testFormEndWithRest()
688 {
689 $view = $this->factory->createNamedBuilder('name', 'form')
690 ->add('field1', 'text')
691 ->add('field2', 'text')
692 ->getForm()
693 ->createView();
694
695 $this->renderWidget($view['field1']);
696
697 // Rest should only contain field2
698 $html = $this->renderEnd($view);
699
700 // Insert the start tag, the end tag should be rendered by the helper
701 $this->assertMatchesXpath('<form>' . $html,
702 '/form
703 [
704 ./div
705 [
706 ./label[@for="name_field2"]
707 /following-sibling::input[@type="text"][@id="name_field2"]
708 ]
709 /following-sibling::input
710 [@type="hidden"]
711 [@id="name__token"]
712 ]
713 '
714 );
715 }
716
717 public function testFormEndWithoutRest()
718 {
719 $view = $this->factory->createNamedBuilder('name', 'form')
720 ->add('field1', 'text')
721 ->add('field2', 'text')
722 ->getForm()
723 ->createView();
724
725 $this->renderWidget($view['field1']);
726
727 // Rest should only contain field2, but isn't rendered
728 $html = $this->renderEnd($view, array('render_rest' => false));
729
730 $this->assertEquals('</form>', $html);
731 }
732 }