diff --git a/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_horizontal_layout.html.twig b/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_horizontal_layout.html.twig
index 767e2798f3..e997615d11 100644
--- a/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_horizontal_layout.html.twig
+++ b/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_horizontal_layout.html.twig
@@ -25,15 +25,13 @@ col-sm-2
{# Rows #}
{% block form_row -%}
-{% spaceless %}
-{% endspaceless %}
+{##}
{%- endblock form_row %}
{% block checkbox_row -%}
diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionBootstrap3HorizontalLayoutTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionBootstrap3HorizontalLayoutTest.php
new file mode 100644
index 0000000000..bf26dda05b
--- /dev/null
+++ b/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionBootstrap3HorizontalLayoutTest.php
@@ -0,0 +1,118 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bridge\Twig\Tests\Extension;
+
+use Symfony\Bridge\Twig\Extension\FormExtension;
+use Symfony\Bridge\Twig\Form\TwigRenderer;
+use Symfony\Bridge\Twig\Form\TwigRendererEngine;
+use Symfony\Bridge\Twig\Extension\TranslationExtension;
+use Symfony\Bridge\Twig\Tests\Extension\Fixtures\StubTranslator;
+use Symfony\Bridge\Twig\Tests\Extension\Fixtures\StubFilesystemLoader;
+use Symfony\Component\Form\FormView;
+use Symfony\Component\Form\Tests\AbstractBootstrap3HorizontalLayoutTest;
+
+class FormExtensionBootstrap3HorizontalLayoutTest extends AbstractBootstrap3HorizontalLayoutTest
+{
+ /**
+ * @var FormExtension
+ */
+ protected $extension;
+
+ protected $testableFeatures = array(
+ 'choice_attr',
+ );
+
+ protected function setUp()
+ {
+ parent::setUp();
+
+ $rendererEngine = new TwigRendererEngine(array(
+ 'bootstrap_3_horizontal_layout.html.twig',
+ 'custom_widgets.html.twig',
+ ));
+ $renderer = new TwigRenderer($rendererEngine, $this->getMock('Symfony\Component\Security\Csrf\CsrfTokenManagerInterface'));
+
+ $this->extension = new FormExtension($renderer);
+
+ $loader = new StubFilesystemLoader(array(
+ __DIR__.'/../../Resources/views/Form',
+ __DIR__.'/Fixtures/templates/form',
+ ));
+
+ $environment = new \Twig_Environment($loader, array('strict_variables' => true));
+ $environment->addExtension(new TranslationExtension(new StubTranslator()));
+ $environment->addExtension($this->extension);
+
+ $this->extension->initRuntime($environment);
+ }
+
+ protected function tearDown()
+ {
+ parent::tearDown();
+
+ $this->extension = null;
+ }
+
+ protected function renderForm(FormView $view, array $vars = array())
+ {
+ return (string) $this->extension->renderer->renderBlock($view, 'form', $vars);
+ }
+
+ protected function renderEnctype(FormView $view)
+ {
+ return (string) $this->extension->renderer->searchAndRenderBlock($view, 'enctype');
+ }
+
+ protected function renderLabel(FormView $view, $label = null, array $vars = array())
+ {
+ if ($label !== null) {
+ $vars += array('label' => $label);
+ }
+
+ return (string) $this->extension->renderer->searchAndRenderBlock($view, 'label', $vars);
+ }
+
+ protected function renderErrors(FormView $view)
+ {
+ return (string) $this->extension->renderer->searchAndRenderBlock($view, 'errors');
+ }
+
+ protected function renderWidget(FormView $view, array $vars = array())
+ {
+ return (string) $this->extension->renderer->searchAndRenderBlock($view, 'widget', $vars);
+ }
+
+ protected function renderRow(FormView $view, array $vars = array())
+ {
+ return (string) $this->extension->renderer->searchAndRenderBlock($view, 'row', $vars);
+ }
+
+ protected function renderRest(FormView $view, array $vars = array())
+ {
+ return (string) $this->extension->renderer->searchAndRenderBlock($view, 'rest', $vars);
+ }
+
+ protected function renderStart(FormView $view, array $vars = array())
+ {
+ return (string) $this->extension->renderer->renderBlock($view, 'form_start', $vars);
+ }
+
+ protected function renderEnd(FormView $view, array $vars = array())
+ {
+ return (string) $this->extension->renderer->renderBlock($view, 'form_end', $vars);
+ }
+
+ protected function setTheme(FormView $view, array $themes)
+ {
+ $this->extension->renderer->setTheme($view, $themes);
+ }
+}
diff --git a/src/Symfony/Component/Form/Tests/AbstractBootstrap3HorizontalLayoutTest.php b/src/Symfony/Component/Form/Tests/AbstractBootstrap3HorizontalLayoutTest.php
new file mode 100644
index 0000000000..1273fa505b
--- /dev/null
+++ b/src/Symfony/Component/Form/Tests/AbstractBootstrap3HorizontalLayoutTest.php
@@ -0,0 +1,157 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form\Tests;
+
+abstract class AbstractBootstrap3HorizontalLayoutTest extends AbstractBootstrap3LayoutTest
+{
+ public function testLabelOnForm()
+ {
+ $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\DateType');
+ $view = $form->createView();
+ $this->renderWidget($view, array('label' => 'foo'));
+ $html = $this->renderLabel($view);
+
+ $this->assertMatchesXpath($html,
+'/label
+ [@class="col-sm-2 control-label required"]
+ [.="[trans]Name[/trans]"]
+'
+ );
+ }
+
+ public function testLabelDoesNotRenderFieldAttributes()
+ {
+ $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TextType');
+ $html = $this->renderLabel($form->createView(), null, array(
+ 'attr' => array(
+ 'class' => 'my&class',
+ ),
+ ));
+
+ $this->assertMatchesXpath($html,
+'/label
+ [@for="name"]
+ [@class="col-sm-2 control-label required"]
+'
+ );
+ }
+
+ public function testLabelWithCustomAttributesPassedDirectly()
+ {
+ $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TextType');
+ $html = $this->renderLabel($form->createView(), null, array(
+ 'label_attr' => array(
+ 'class' => 'my&class',
+ ),
+ ));
+
+ $this->assertMatchesXpath($html,
+'/label
+ [@for="name"]
+ [@class="my&class col-sm-2 control-label required"]
+'
+ );
+ }
+
+ public function testLabelWithCustomTextAndCustomAttributesPassedDirectly()
+ {
+ $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TextType');
+ $html = $this->renderLabel($form->createView(), 'Custom label', array(
+ 'label_attr' => array(
+ 'class' => 'my&class',
+ ),
+ ));
+
+ $this->assertMatchesXpath($html,
+'/label
+ [@for="name"]
+ [@class="my&class col-sm-2 control-label required"]
+ [.="[trans]Custom label[/trans]"]
+'
+ );
+ }
+
+ public function testLabelWithCustomTextAsOptionAndCustomAttributesPassedDirectly()
+ {
+ $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TextType', null, array(
+ 'label' => 'Custom label',
+ ));
+ $html = $this->renderLabel($form->createView(), null, array(
+ 'label_attr' => array(
+ 'class' => 'my&class',
+ ),
+ ));
+
+ $this->assertMatchesXpath($html,
+'/label
+ [@for="name"]
+ [@class="my&class col-sm-2 control-label required"]
+ [.="[trans]Custom label[/trans]"]
+'
+ );
+ }
+
+ public function testStartTag()
+ {
+ $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\FormType', null, array(
+ 'method' => 'get',
+ 'action' => 'http://example.com/directory',
+ ));
+
+ $html = $this->renderStart($form->createView());
+
+ $this->assertSame('