Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 61 additions & 0 deletions Tests/Unit/FactoryTestTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<?php

namespace Fp\JsFormValidatorBundle\Tests\Unit;

use Fp\JsFormValidatorBundle\Factory\JsFormValidatorFactory;
use Fp\JsFormValidatorBundle\Form\Extension\FormExtension;
use Symfony\Component\Form\Extension\Validator\ValidatorExtension;
use Symfony\Component\Form\Forms;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Validator\Validation;
use Symfony\Component\Validator\Validator\ValidatorInterface;
use Symfony\Contracts\Translation\TranslatorInterface;

trait FactoryTestTrait
{
private function createFactory(
?ValidatorInterface $validator = null,
?UrlGeneratorInterface $router = null,
array $config = array('js_validation' => true)
): JsFormValidatorFactory {
if (!$router) {
$router = $this->createStub(UrlGeneratorInterface::class);
$router
->method('generate')
->willReturn('/generated-route')
;
}

return new JsFormValidatorFactory(
$validator ?: Validation::createValidator(),
new IdentityTranslator(),
$router,
$config,
'validators'
);
}

private function createFormFactory(JsFormValidatorFactory $factory, ?ValidatorInterface $validator = null)
{
$validator = $validator ?: Validation::createValidator();

return Forms::createFormFactoryBuilder()
->addExtension(new ValidatorExtension($validator))
->addTypeExtension(new FormExtension($factory))
->getFormFactory()
;
}
}

class IdentityTranslator implements TranslatorInterface
{
public function trans(string $id, array $parameters = array(), ?string $domain = null, ?string $locale = null): string
{
return strtr($id, $parameters);
}

public function getLocale(): string
{
return 'en';
}
}
65 changes: 9 additions & 56 deletions Tests/Unit/JsFormValidatorFactoryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,50 +2,33 @@

namespace Fp\JsFormValidatorBundle\Tests\Unit;

use Fp\JsFormValidatorBundle\Factory\JsFormValidatorFactory;
use Fp\JsFormValidatorBundle\Form\Extension\FormExtension;
use Fp\JsFormValidatorBundle\Form\Constraint\UniqueEntity as JsUniqueEntity;
use PHPUnit\Framework\TestCase;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity as SymfonyUniqueEntity;
use Symfony\Component\Form\Extension\Core\Type\FormType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Validator\ValidatorExtension;
use Symfony\Component\Form\Forms;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Validator\Constraints\NotBlank;
use Symfony\Component\Validator\Validation;
use Symfony\Contracts\Translation\TranslatorInterface;

class JsFormValidatorFactoryTest extends TestCase
{
use FactoryTestTrait;
public function testCreatesModelFromModernSymfonyForm()
{
$validator = Validation::createValidator();
$router = $this->createMock(UrlGeneratorInterface::class);
$router
->method('generate')
->with('fp_js_form_validator.check_unique_entity')
->willReturn('/fp_js_form_validator/check_unique_entity')
;

$factory = new JsFormValidatorFactory(
$validator,
new IdentityTranslator(),
$router,
array(
'js_validation' => true,
'routing' => array(
'check_unique_entity' => 'fp_js_form_validator.check_unique_entity',
),
$factory = $this->createFactory(null, $router, array(
'js_validation' => true,
'routing' => array(
'check_unique_entity' => 'fp_js_form_validator.check_unique_entity',
),
'validators'
);

$formFactory = Forms::createFormFactoryBuilder()
->addExtension(new ValidatorExtension($validator))
->addTypeExtension(new FormExtension($factory))
->getFormFactory()
;
));
$formFactory = $this->createFormFactory($factory);

$form = $formFactory
->createBuilder(FormType::class, null, array('validation_groups' => array('Default')))
Expand All @@ -68,21 +51,8 @@ public function testCreatesModelFromModernSymfonyForm()

public function testUniqueEntityConstraintIncludesBoundEntityId()
{
$validator = Validation::createValidator();
$router = $this->createMock(UrlGeneratorInterface::class);
$factory = new JsFormValidatorFactory(
$validator,
new IdentityTranslator(),
$router,
array('js_validation' => true),
'validators'
);

$formFactory = Forms::createFormFactoryBuilder()
->addExtension(new ValidatorExtension($validator))
->addTypeExtension(new FormExtension($factory))
->getFormFactory()
;
$factory = $this->createFactory();
$formFactory = $this->createFormFactory($factory);

$form = $formFactory
->createBuilder(
Expand All @@ -106,23 +76,6 @@ public function testUniqueEntityConstraintIncludesBoundEntityId()
}
}

class IdentityTranslator implements TranslatorInterface
{
public function trans(
string $id,
array $parameters = array(),
?string $domain = null,
?string $locale = null
): string {
return strtr($id, $parameters);
}

public function getLocale(): string
{
return 'en';
}
}

class UniqueEntityUser
{
public $email;
Expand Down
102 changes: 102 additions & 0 deletions Tests/Unit/SubscriberToQueueTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
<?php

namespace Fp\JsFormValidatorBundle\Tests\Unit;

use Fp\JsFormValidatorBundle\Factory\JsFormValidatorFactory;
use Fp\JsFormValidatorBundle\Form\Subscriber\SubscriberToQueue;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Form\Extension\Core\Type\FormType;
use Symfony\Component\Form\FormEvent;
use Symfony\Contracts\Translation\TranslatorInterface;

class SubscriberToQueueTest extends TestCase
{
use FactoryTestTrait;
public function testAddToQueueWhenGlobalDisabledButLocalExplicitlyEnabled()
{
$factory = $this->createFactory(null, null, array('js_validation' => false));
$formFactory = $this->createFormFactory($factory);
$subscriber = new SubscriberToQueue($factory);

$form = $formFactory
->createNamedBuilder('test_form', FormType::class, null, array('js_validation' => true))
->getForm()
;

$subscriber->onFormSetData(new FormEvent($form, null));

$this->assertTrue($factory->inQueue($form));
}

public function testDoesNotAddToQueueWhenGlobalDisabledAndLocalNotSet()
{
$factory = $this->createFactory(null, null, array('js_validation' => false));
$formFactory = $this->createFormFactory($factory);
$subscriber = new SubscriberToQueue($factory);

// Form with no explicit js_validation option (defaults to null)
$form = $formFactory
->createNamedBuilder('test_form', FormType::class)
->getForm()
;

$subscriber->onFormSetData(new FormEvent($form, null));

$this->assertFalse($factory->inQueue($form));
}

public function testAddToQueueWhenGlobalEnabledAndLocalNotSet()
{
$factory = $this->createFactory(null, null, array('js_validation' => true));
$formFactory = $this->createFormFactory($factory);
$subscriber = new SubscriberToQueue($factory);

// Form with no explicit js_validation option (defaults to null, inherits global)
$form = $formFactory
->createNamedBuilder('test_form', FormType::class)
->getForm()
;

$subscriber->onFormSetData(new FormEvent($form, null));

$this->assertTrue($factory->inQueue($form));
}

public function testDoesNotAddToQueueWhenLocalExplicitlyDisabled()
{
$factory = $this->createFactory(null, null, array('js_validation' => true));
$formFactory = $this->createFormFactory($factory);
$subscriber = new SubscriberToQueue($factory);

$form = $formFactory
->createNamedBuilder('test_form', FormType::class, null, array('js_validation' => false))
->getForm()
;

$subscriber->onFormSetData(new FormEvent($form, null));

$this->assertFalse($factory->inQueue($form));
}

public function testChildFieldOptInAddsEntireFormToQueue()
{
// Global disabled, but a child field explicitly opts in
$factory = $this->createFactory(null, null, array('js_validation' => false));
$formFactory = $this->createFormFactory($factory);
$subscriber = new SubscriberToQueue($factory);

$form = $formFactory
->createNamedBuilder('test_form', FormType::class)
->add('email', FormType::class, array('js_validation' => true))
->getForm()
;

// Trigger subscriber on the child field that opts in
$child = $form->get('email');
$subscriber->onFormSetData(new FormEvent($child, null));

// The entire parent form should be in the queue
$this->assertTrue($factory->inQueue($form));
$this->assertArrayHasKey('test_form', $factory->getQueue());
}
}
3 changes: 2 additions & 1 deletion src/Factory/JsFormValidatorFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,8 @@ public function createJsModel(FormInterface $form)
$this->currentElement = $form;

$conf = $form->getConfig();
// If field is disabled or has no any validations
// If field is explicitly disabled, skip it
// null means "inherit" which is treated as enabled (same as true)
if (false === $conf->getOption('js_validation')) {
return null;
}
Expand Down
2 changes: 1 addition & 1 deletion src/Form/Extension/FormExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public function buildForm(FormBuilderInterface $builder, array $options): void
*/
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults(array('js_validation' => true));
$resolver->setDefaults(array('js_validation' => null));
}

/**
Expand Down
9 changes: 6 additions & 3 deletions src/Form/Subscriber/SubscriberToQueue.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,14 @@ public function onFormSetData(FormEvent $event): void
$globalSwitch = $this->factory->getConfig('js_validation');
$localSwitch = $form->getConfig()->getOption('js_validation');

// Add only parent forms which are not disabled
if ($globalSwitch && $localSwitch) {
// If local option is null (not explicitly set), inherit from global
$enabled = null === $localSwitch ? $globalSwitch : $localSwitch;

// Add only parent forms which are enabled
if ($enabled) {
$parent = $this->getParent($form);
if (!$this->factory->inQueue($parent)) {
$this->factory->addToQueue($this->getParent($form));
$this->factory->addToQueue($parent);
}
}
}
Expand Down
19 changes: 16 additions & 3 deletions src/Resources/doc/2_1.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
### 2.1 Disabling validation
### 2.1 Enabling and Disabling validation

You can disable JavaScript validation in three ways.
JavaScript validation is **enabled by default** for all forms.
The `js_validation` option can be set at three levels:

#### Globally

Disable validation for all forms:

```yaml
# config/packages/fp_js_form_validator.yaml
fp_js_form_validator:
Expand All @@ -12,6 +15,8 @@ fp_js_form_validator:

#### For a form type

Override the global setting for a specific form type:

```php
<?php

Expand All @@ -25,12 +30,20 @@ class UserType extends AbstractType
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'js_validation' => false,
'js_validation' => false, // Disable for this form only
]);
}
}
```

When the form option is not explicitly set (i.e., it is `null`), the form
inherits the global configuration value. This allows you to:

- Disable validation globally (`js_validation: false`) and then enable it
**only for selected forms** by setting `'js_validation' => true` on those forms.
- Enable validation globally (the default) and disable it for specific forms
by setting `'js_validation' => false` on those forms.

#### For a field

See [disable validation for a specified field](3_1.md).
Loading