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
;
14 use Symfony\Component\Form\FormError
;
15 use Symfony\Component\Form\Tests\Fixtures\AlternatingRowType
;
17 abstract class AbstractDivLayoutTest
extends AbstractLayoutTest
19 public function testRow()
21 $form = $this->factory
->createNamed('name', 'text');
22 $form->addError(new FormError('[trans]Error![/trans]'));
23 $view = $form->createView();
24 $html = $this->renderRow($view);
26 $this->assertMatchesXpath($html,
30 /following-sibling::ul
31 [./li[.="[trans]Error![/trans]"]]
33 /following-sibling::input[@id="name"]
39 public function testRowOverrideVariables()
41 $view = $this->factory
->createNamed('name', 'text')->createView();
42 $html = $this->renderRow($view, array(
43 'attr' => array('class' => 'my&class'),
45 'label_attr' => array('class' => 'my&label&class'),
48 $this->assertMatchesXpath($html,
51 ./label[@for="name"][@class="my&label&class required"][.="[trans]foo&bar[/trans]"]
52 /following-sibling::input[@id="name"][@class="my&class"]
58 public function testRepeatedRow()
60 $form = $this->factory
->createNamed('name', 'repeated');
61 $form->addError(new FormError('[trans]Error![/trans]'));
62 $view = $form->createView();
63 $html = $this->renderRow($view);
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)
70 $this->assertMatchesXpath($html,
73 ./label[@for="name_first"]
74 /following-sibling::input[@id="name_first"]
76 /following-sibling::div
78 ./label[@for="name_second"]
79 /following-sibling::input[@id="name_second"]
85 public function testButtonRow()
87 $form = $this->factory
->createNamed('name', 'button');
88 $view = $form->createView();
89 $html = $this->renderRow($view);
91 $this->assertMatchesXpath($html,
94 ./button[@type="button"][@name="name"]
101 public function testRest()
103 $view = $this->factory
->createNamedBuilder('name', 'form')
104 ->add('field1', 'text')
105 ->add('field2', 'repeated')
106 ->add('field3', 'text')
107 ->add('field4', 'text')
111 // Render field2 row -> does not implicitly call renderWidget because
112 // it is a repeated field!
113 $this->renderRow($view['field2']);
115 // Render field3 widget
116 $this->renderWidget($view['field3']);
118 // Rest should only contain field1 and field4
119 $html = $this->renderRest($view);
121 $this->assertMatchesXpath($html,
124 ./label[@for="name_field1"]
125 /following-sibling::input[@type="text"][@id="name_field1"]
127 /following-sibling::div
129 ./label[@for="name_field4"]
130 /following-sibling::input[@type="text"][@id="name_field4"]
135 /following-sibling::input
142 public function testRestWithChildrenForms()
144 $child1 = $this->factory
->createNamedBuilder('child1', 'form')
145 ->add('field1', 'text')
146 ->add('field2', 'text');
148 $child2 = $this->factory
->createNamedBuilder('child2', 'form')
149 ->add('field1', 'text')
150 ->add('field2', 'text');
152 $view = $this->factory
->createNamedBuilder('parent', 'form')
158 // Render child1.field1 row
159 $this->renderRow($view['child1']['field1']);
161 // Render child2.field2 widget (remember that widget don't render label)
162 $this->renderWidget($view['child2']['field2']);
164 // Rest should only contain child1.field2 and child2.field1
165 $html = $this->renderRest($view);
167 $this->assertMatchesXpath($html,
171 /following-sibling::div[@id="parent_child1"]
175 ./label[@for="parent_child1_field2"]
176 /following-sibling::input[@id="parent_child1_field2"]
181 /following-sibling::div
184 /following-sibling::div[@id="parent_child2"]
188 ./label[@for="parent_child2_field1"]
189 /following-sibling::input[@id="parent_child2_field1"]
194 [count(//input[@type="text"])=2]
195 /following-sibling::input[@type="hidden"][@id="parent__token"]
200 public function testRestAndRepeatedWithRow()
202 $view = $this->factory
->createNamedBuilder('name', 'form')
203 ->add('first', 'text')
204 ->add('password', 'repeated')
208 $this->renderRow($view['password']);
210 $html = $this->renderRest($view);
212 $this->assertMatchesXpath($html,
215 ./label[@for="name_first"]
216 /following-sibling::input[@type="text"][@id="name_first"]
219 /following-sibling::input
226 public function testRestAndRepeatedWithRowPerChild()
228 $view = $this->factory
->createNamedBuilder('name', 'form')
229 ->add('first', 'text')
230 ->add('password', 'repeated')
234 $this->renderRow($view['password']['first']);
235 $this->renderRow($view['password']['second']);
237 $html = $this->renderRest($view);
239 $this->assertMatchesXpath($html,
242 ./label[@for="name_first"]
243 /following-sibling::input[@type="text"][@id="name_first"]
247 /following-sibling::input
254 public function testRestAndRepeatedWithWidgetPerChild()
256 $view = $this->factory
->createNamedBuilder('name', 'form')
257 ->add('first', 'text')
258 ->add('password', 'repeated')
262 // The password form is considered as rendered as all its children
264 $this->renderWidget($view['password']['first']);
265 $this->renderWidget($view['password']['second']);
267 $html = $this->renderRest($view);
269 $this->assertMatchesXpath($html,
272 ./label[@for="name_first"]
273 /following-sibling::input[@type="text"][@id="name_first"]
277 /following-sibling::input
284 public function testCollection()
286 $form = $this->factory
->createNamed('name', 'collection', array('a', 'b'), array(
290 $this->assertWidgetMatchesXpath($form->createView(), array(),
293 ./div[./input[@type="text"][@value="a"]]
294 /following-sibling::div[./input[@type="text"][@value="b"]]
296 [count(./div[./input])=2]
301 // https://github.com/symfony/symfony/issues/5038
302 public function testCollectionWithAlternatingRowTypes()
305 array('title' => 'a'),
306 array('title' => 'b'),
308 $form = $this->factory
->createNamed('name', 'collection', $data, array(
309 'type' => new AlternatingRowType(),
312 $this->assertWidgetMatchesXpath($form->createView(), array(),
315 ./div[./div/div/input[@type="text"][@value="a"]]
316 /following-sibling::div[./div/div/textarea[.="b"]]
318 [count(./div[./div/div/input])=1]
319 [count(./div[./div/div/textarea])=1]
324 public function testEmptyCollection()
326 $form = $this->factory
->createNamed('name', 'collection', array(), array(
330 $this->assertWidgetMatchesXpath($form->createView(), array(),
332 [./input[@type="hidden"][@id="name__token"]]
338 public function testCollectionRow()
340 $collection = $this->factory
->createNamedBuilder(
344 array('type' => 'text')
347 $form = $this->factory
->createNamedBuilder('form', 'form')
351 $this->assertWidgetMatchesXpath($form->createView(), array(),
357 /following-sibling::div
361 ./label[@for="form_collection_0"]
362 /following-sibling::input[@type="text"][@value="a"]
364 /following-sibling::div
366 ./label[@for="form_collection_1"]
367 /following-sibling::input[@type="text"][@value="b"]
371 /following-sibling::input[@type="hidden"][@id="form__token"]
378 public function testForm()
380 $form = $this->factory
->createNamedBuilder('name', 'form')
382 ->setAction('http://example.com')
383 ->add('firstName', 'text')
384 ->add('lastName', 'text')
387 // include ampersands everywhere to validate escaping
388 $html = $this->renderForm($form->createView(), array(
390 'attr' => array('class' => 'my&class'),
393 $this->assertMatchesXpath($html,
396 ./input[@type="hidden"][@name="_method"][@value="PUT"]
397 /following-sibling::div
401 ./label[@for="name_firstName"]
402 /following-sibling::input[@type="text"][@id="name_firstName"]
404 /following-sibling::div
406 ./label[@for="name_lastName"]
407 /following-sibling::input[@type="text"][@id="name_lastName"]
409 /following-sibling::input[@type="hidden"][@id="name__token"]
416 [@action="http://example.com"]
422 public function testFormWidget()
424 $form = $this->factory
->createNamedBuilder('name', 'form')
425 ->add('firstName', 'text')
426 ->add('lastName', 'text')
429 $this->assertWidgetMatchesXpath($form->createView(), array(),
434 ./label[@for="name_firstName"]
435 /following-sibling::input[@type="text"][@id="name_firstName"]
437 /following-sibling::div
439 ./label[@for="name_lastName"]
440 /following-sibling::input[@type="text"][@id="name_lastName"]
442 /following-sibling::input[@type="hidden"][@id="name__token"]
449 // https://github.com/symfony/symfony/issues/2308
450 public function testNestedFormError()
452 $form = $this->factory
->createNamedBuilder('name', 'form')
454 ->createNamedBuilder('child', 'form', null, array('error_bubbling' => false))
455 ->add('grandChild', 'form')
459 $form->get('child')->addError(new FormError('[trans]Error![/trans]'));
461 $this->assertWidgetMatchesXpath($form->createView(), array(),
465 /following-sibling::ul[./li[.="[trans]Error![/trans]"]]
467 [count(.//li[.="[trans]Error![/trans]"])=1]
472 public function testCsrf()
474 $this->csrfProvider
->expects($this->any())
475 ->method('generateCsrfToken')
476 ->will($this->returnValue('foo&bar'));
478 $form = $this->factory
->createNamedBuilder('name', 'form')
480 // No CSRF protection on nested forms
481 ->createNamedBuilder('child', 'form')
482 ->add($this->factory
->createNamedBuilder('grandchild', 'text'))
486 $this->assertWidgetMatchesXpath($form->createView(), array(),
490 /following-sibling::input[@type="hidden"][@id="name__token"][@value="foo&bar"]
492 [count(.//input[@type="hidden"])=1]
497 public function testRepeated()
499 $form = $this->factory
->createNamed('name', 'repeated', 'foobar', array(
503 $this->assertWidgetMatchesXpath($form->createView(), array(),
508 ./label[@for="name_first"]
509 /following-sibling::input[@type="text"][@id="name_first"]
511 /following-sibling::div
513 ./label[@for="name_second"]
514 /following-sibling::input[@type="text"][@id="name_second"]
516 /following-sibling::input[@type="hidden"][@id="name__token"]
523 public function testRepeatedWithCustomOptions()
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')
531 $this->assertWidgetMatchesXpath($form->createView(), array(),
536 ./label[@for="name_first"][.="[trans]Test[/trans]"]
537 /following-sibling::input[@type="text"][@id="name_first"][@required="required"]
539 /following-sibling::div
541 ./label[@for="name_second"][.="[trans]Test2[/trans]"]
542 /following-sibling::input[@type="text"][@id="name_second"][@required="required"]
544 /following-sibling::input[@type="hidden"][@id="name__token"]
551 public function testSearchInputName()
553 $form = $this->factory
->createNamedBuilder('full', 'form')
554 ->add('name', 'search')
557 $this->assertWidgetMatchesXpath($form->createView(), array(),
562 ./label[@for="full_name"]
563 /following-sibling::input[@type="search"][@id="full_name"][@name="full[name]"]
565 /following-sibling::input[@type="hidden"][@id="full__token"]
572 public function testLabelHasNoId()
574 $form = $this->factory
->createNamed('name', 'text');
575 $html = $this->renderRow($form->createView());
577 $this->assertMatchesXpath($html,
580 ./label[@for="name"][not(@id)]
581 /following-sibling::input[@id="name"]
587 public function testLabelIsNotRenderedWhenSetToFalse()
589 $form = $this->factory
->createNamed('name', 'text', null, array(
592 $html = $this->renderRow($form->createView());
594 $this->assertMatchesXpath($html,
605 * @dataProvider themeBlockInheritanceProvider
607 public function testThemeBlockInheritance($theme)
609 $view = $this->factory
610 ->createNamed('name', 'email')
614 $this->setTheme($view, $theme);
616 $this->assertMatchesXpath(
617 $this->renderWidget($view),
618 '/input[@type="email"][@rel="theme"]'
623 * @dataProvider themeInheritanceProvider
625 public function testThemeInheritance($parentTheme, $childTheme)
627 $child = $this->factory
->createNamedBuilder('child', 'form')
628 ->add('field', 'text');
630 $view = $this->factory
->createNamedBuilder('parent', 'form')
631 ->add('field', 'text')
637 $this->setTheme($view, $parentTheme);
638 $this->setTheme($view['child'], $childTheme);
640 $this->assertWidgetMatchesXpath($view, array(),
646 /following-sibling::input[@type="text"]
648 /following-sibling::div
651 /following-sibling::div
656 /following-sibling::input[@type="text"]
660 /following-sibling::input[@type="hidden"]
667 * The block "_name_child_label" should be overridden in the theme of the
668 * implemented driver.
670 public function testCollectionRowWithCustomBlock()
672 $collection = array('one', 'two', 'three');
673 $form = $this->factory
->createNamedBuilder('name', 'collection', $collection)
676 $this->assertWidgetMatchesXpath($form->createView(), array(),
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]"]]
687 public function testFormEndWithRest()
689 $view = $this->factory
->createNamedBuilder('name', 'form')
690 ->add('field1', 'text')
691 ->add('field2', 'text')
695 $this->renderWidget($view['field1']);
697 // Rest should only contain field2
698 $html = $this->renderEnd($view);
700 // Insert the start tag, the end tag should be rendered by the helper
701 $this->assertMatchesXpath('<form>' . $html,
706 ./label[@for="name_field2"]
707 /following-sibling::input[@type="text"][@id="name_field2"]
709 /following-sibling::input
717 public function testFormEndWithoutRest()
719 $view = $this->factory
->createNamedBuilder('name', 'form')
720 ->add('field1', 'text')
721 ->add('field2', 'text')
725 $this->renderWidget($view['field1']);
727 // Rest should only contain field2, but isn't rendered
728 $html = $this->renderEnd($view, array('render_rest' => false));
730 $this->assertEquals('</form>', $html);