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
;
16 abstract class AbstractTableLayoutTest
extends AbstractLayoutTest
18 public function testRow()
20 $form = $this->factory
->createNamed('name', 'text');
21 $form->addError(new FormError('[trans]Error![/trans]'));
22 $view = $form->createView();
23 $html = $this->renderRow($view);
25 $this->assertMatchesXpath($html,
29 [./label[@for="name"]]
30 /following-sibling::td
33 [./li[.="[trans]Error![/trans]"]]
35 /following-sibling::input[@id="name"]
42 public function testLabelIsNotRenderedWhenSetToFalse()
44 $form = $this->factory
->createNamed('name', 'text', null, array(
47 $html = $this->renderRow($form->createView());
49 $this->assertMatchesXpath($html,
54 /following-sibling::td
61 public function testRepeatedRow()
63 $form = $this->factory
->createNamed('name', 'repeated');
64 $html = $this->renderRow($form->createView());
66 $this->assertMatchesXpath($html,
70 [./label[@for="name_first"]]
71 /following-sibling::td
72 [./input[@id="name_first"]]
74 /following-sibling::tr
77 [./label[@for="name_second"]]
78 /following-sibling::td
79 [./input[@id="name_second"]]
81 /following-sibling::tr[@style="display: none"]
82 [./td[@colspan="2"]/input
91 public function testRepeatedRowWithErrors()
93 $form = $this->factory
->createNamed('name', 'repeated');
94 $form->addError(new FormError('[trans]Error![/trans]'));
95 $view = $form->createView();
96 $html = $this->renderRow($view);
98 // The errors of the form are not rendered by intention!
99 // In practice, repeated fields cannot have errors as all errors
100 // on them are mapped to the first child.
101 // (see RepeatedTypeValidatorExtension)
103 $this->assertMatchesXpath($html,
107 [./label[@for="name_first"]]
108 /following-sibling::td
109 [./input[@id="name_first"]]
111 /following-sibling::tr
114 [./label[@for="name_second"]]
115 /following-sibling::td
116 [./input[@id="name_second"]]
118 /following-sibling::tr[@style="display: none"]
119 [./td[@colspan="2"]/input
128 public function testButtonRow()
130 $form = $this->factory
->createNamed('name', 'button');
131 $view = $form->createView();
132 $html = $this->renderRow($view);
134 $this->assertMatchesXpath($html,
139 /following-sibling::td
140 [./button[@type="button"][@name="name"]]
147 public function testRest()
149 $view = $this->factory
->createNamedBuilder('name', 'form')
150 ->add('field1', 'text')
151 ->add('field2', 'repeated')
152 ->add('field3', 'text')
153 ->add('field4', 'text')
157 // Render field2 row -> does not implicitly call renderWidget because
158 // it is a repeated field!
159 $this->renderRow($view['field2']);
161 // Render field3 widget
162 $this->renderWidget($view['field3']);
164 // Rest should only contain field1 and field4
165 $html = $this->renderRest($view);
167 $this->assertMatchesXpath($html,
171 [./label[@for="name_field1"]]
172 /following-sibling::td
173 [./input[@id="name_field1"]]
175 /following-sibling::tr
178 [./label[@for="name_field4"]]
179 /following-sibling::td
180 [./input[@id="name_field4"]]
185 /following-sibling::tr[@style="display: none"]
186 [./td[@colspan="2"]/input
194 public function testCollection()
196 $form = $this->factory
->createNamed('name', 'collection', array('a', 'b'), array(
200 $this->assertWidgetMatchesXpath($form->createView(), array(),
203 ./tr[./td/input[@type="text"][@value="a"]]
204 /following-sibling::tr[./td/input[@type="text"][@value="b"]]
205 /following-sibling::tr[@style="display: none"][./td[@colspan="2"]/input[@type="hidden"][@id="name__token"]]
207 [count(./tr[./td/input])=3]
212 public function testEmptyCollection()
214 $form = $this->factory
->createNamed('name', 'collection', array(), array(
218 $this->assertWidgetMatchesXpath($form->createView(), array(),
220 [./tr[@style="display: none"][./td[@colspan="2"]/input[@type="hidden"][@id="name__token"]]]
221 [count(./tr[./td/input])=1]
226 public function testForm()
228 $view = $this->factory
->createNamedBuilder('name', 'form')
230 ->setAction('http://example.com')
231 ->add('firstName', 'text')
232 ->add('lastName', 'text')
236 $html = $this->renderForm($view, array(
238 'attr' => array('class' => 'my&class'),
241 $this->assertMatchesXpath($html,
244 ./input[@type="hidden"][@name="_method"][@value="PUT"]
245 /following-sibling::table
250 [./label[@for="name_firstName"]]
251 /following-sibling::td
252 [./input[@id="name_firstName"]]
254 /following-sibling::tr
257 [./label[@for="name_lastName"]]
258 /following-sibling::td
259 [./input[@id="name_lastName"]]
261 /following-sibling::tr[@style="display: none"]
262 [./td[@colspan="2"]/input
272 [@action="http://example.com"]
278 public function testFormWidget()
280 $view = $this->factory
->createNamedBuilder('name', 'form')
281 ->add('firstName', 'text')
282 ->add('lastName', 'text')
286 $this->assertWidgetMatchesXpath($view, array(),
292 [./label[@for="name_firstName"]]
293 /following-sibling::td
294 [./input[@id="name_firstName"]]
296 /following-sibling::tr
299 [./label[@for="name_lastName"]]
300 /following-sibling::td
301 [./input[@id="name_lastName"]]
303 /following-sibling::tr[@style="display: none"]
304 [./td[@colspan="2"]/input
314 // https://github.com/symfony/symfony/issues/2308
315 public function testNestedFormError()
317 $form = $this->factory
->createNamedBuilder('name', 'form')
319 ->createNamedBuilder('child', 'form', null, array('error_bubbling' => false))
320 ->add('grandChild', 'form')
324 $form->get('child')->addError(new FormError('[trans]Error![/trans]'));
326 $this->assertWidgetMatchesXpath($form->createView(), array(),
329 ./tr/td/ul[./li[.="[trans]Error![/trans]"]]
330 /following-sibling::table[@id="name_child"]
332 [count(.//li[.="[trans]Error![/trans]"])=1]
337 public function testCsrf()
339 $this->csrfProvider
->expects($this->any())
340 ->method('generateCsrfToken')
341 ->will($this->returnValue('foo&bar'));
343 $form = $this->factory
->createNamedBuilder('name', 'form')
345 // No CSRF protection on nested forms
346 ->createNamedBuilder('child', 'form')
347 ->add($this->factory
->createNamedBuilder('grandchild', 'text'))
351 $this->assertWidgetMatchesXpath($form->createView(), array(),
354 ./tr[@style="display: none"]
355 [./td[@colspan="2"]/input
360 [count(.//input[@type="hidden"])=1]
365 public function testRepeated()
367 $form = $this->factory
->createNamed('name', 'repeated', 'foobar', array(
371 $this->assertWidgetMatchesXpath($form->createView(), array(),
377 [./label[@for="name_first"]]
378 /following-sibling::td
379 [./input[@type="text"][@id="name_first"]]
381 /following-sibling::tr
384 [./label[@for="name_second"]]
385 /following-sibling::td
386 [./input[@type="text"][@id="name_second"]]
388 /following-sibling::tr[@style="display: none"]
389 [./td[@colspan="2"]/input
399 public function testRepeatedWithCustomOptions()
401 $form = $this->factory
->createNamed('name', 'repeated', 'foobar', array(
402 'type' => 'password',
403 'first_options' => array('label' => 'Test', 'required' => false),
404 'second_options' => array('label' => 'Test2')
407 $this->assertWidgetMatchesXpath($form->createView(), array(),
413 [./label[@for="name_first"][.="[trans]Test[/trans]"]]
414 /following-sibling::td
415 [./input[@type="password"][@id="name_first"][@required="required"]]
417 /following-sibling::tr
420 [./label[@for="name_second"][.="[trans]Test2[/trans]"]]
421 /following-sibling::td
422 [./input[@type="password"][@id="name_second"][@required="required"]]
424 /following-sibling::tr[@style="display: none"]
425 [./td[@colspan="2"]/input
436 * The block "_name_child_label" should be overridden in the theme of the
437 * implemented driver.
439 public function testCollectionRowWithCustomBlock()
441 $collection = array('one', 'two', 'three');
442 $form = $this->factory
->createNamedBuilder('name', 'collection', $collection)
445 $this->assertWidgetMatchesXpath($form->createView(), array(),
448 ./tr[./td/label[.="Custom label: [trans]0[/trans]"]]
449 /following-sibling::tr[./td/label[.="Custom label: [trans]1[/trans]"]]
450 /following-sibling::tr[./td/label[.="Custom label: [trans]2[/trans]"]]
456 public function testFormEndWithRest()
458 $view = $this->factory
->createNamedBuilder('name', 'form')
459 ->add('field1', 'text')
460 ->add('field2', 'text')
464 $this->renderWidget($view['field1']);
466 // Rest should only contain field2
467 $html = $this->renderEnd($view);
469 // Insert the start tag, the end tag should be rendered by the helper
470 // Unfortunately this is not valid HTML, because the surrounding table
471 // tag is missing. If someone renders a form with table layout
472 // manually, she should call form_rest() explicitly within the <table>
474 $this->assertMatchesXpath('<form>' . $html,
480 [./label[@for="name_field2"]]
481 /following-sibling::td
482 [./input[@id="name_field2"]]
484 /following-sibling::tr[@style="display: none"]
485 [./td[@colspan="2"]/input
494 public function testFormEndWithoutRest()
496 $view = $this->factory
->createNamedBuilder('name', 'form')
497 ->add('field1', 'text')
498 ->add('field2', 'text')
502 $this->renderWidget($view['field1']);
504 // Rest should only contain field2, but isn't rendered
505 $html = $this->renderEnd($view, array('render_rest' => false));
507 $this->assertEquals('</form>', $html);