class FormValidatorTest

Same name in other branches
  1. 9 core/tests/Drupal/Tests/Core/Form/FormValidatorTest.php \Drupal\Tests\Core\Form\FormValidatorTest
  2. 8.9.x core/tests/Drupal/Tests/Core/Form/FormValidatorTest.php \Drupal\Tests\Core\Form\FormValidatorTest
  3. 10 core/tests/Drupal/Tests/Core/Form/FormValidatorTest.php \Drupal\Tests\Core\Form\FormValidatorTest

@coversDefaultClass \Drupal\Core\Form\FormValidator @group Form

Hierarchy

Expanded class hierarchy of FormValidatorTest

File

core/tests/Drupal/Tests/Core/Form/FormValidatorTest.php, line 18

Namespace

Drupal\Tests\Core\Form
View source
class FormValidatorTest extends UnitTestCase {
    
    /**
     * A logger instance.
     *
     * @var \Psr\Log\LoggerInterface|\PHPUnit\Framework\MockObject\MockObject
     */
    protected $logger;
    
    /**
     * The CSRF token generator to validate the form token.
     *
     * @var \Drupal\Core\Access\CsrfTokenGenerator|\PHPUnit\Framework\MockObject\MockObject
     */
    protected $csrfToken;
    
    /**
     * The form error handler.
     *
     * @var \Drupal\Core\Form\FormErrorHandlerInterface|\PHPUnit\Framework\MockObject\MockObject
     */
    protected $formErrorHandler;
    
    /**
     * {@inheritdoc}
     */
    protected function setUp() : void {
        parent::setUp();
        $this->logger = $this->createMock('Psr\\Log\\LoggerInterface');
        $this->csrfToken = $this->getMockBuilder('Drupal\\Core\\Access\\CsrfTokenGenerator')
            ->disableOriginalConstructor()
            ->getMock();
        $this->formErrorHandler = $this->createMock('Drupal\\Core\\Form\\FormErrorHandlerInterface');
    }
    
    /**
     * Tests the 'validation_complete' $form_state flag.
     *
     * @covers ::validateForm
     * @covers ::finalizeValidation
     */
    public function testValidationComplete() : void {
        $form_validator = new FormValidator(new RequestStack(), $this->getStringTranslationStub(), $this->csrfToken, $this->logger, $this->formErrorHandler);
        $form = [];
        $form_state = new FormState();
        $this->assertFalse($form_state->isValidationComplete());
        $form_validator->validateForm('test_form_id', $form, $form_state);
        $this->assertTrue($form_state->isValidationComplete());
    }
    
    /**
     * Tests the 'must_validate' $form_state flag.
     *
     * @covers ::validateForm
     */
    public function testPreventDuplicateValidation() : void {
        $form_validator = $this->getMockBuilder('Drupal\\Core\\Form\\FormValidator')
            ->setConstructorArgs([
            new RequestStack(),
            $this->getStringTranslationStub(),
            $this->csrfToken,
            $this->logger,
            $this->formErrorHandler,
        ])
            ->onlyMethods([
            'doValidateForm',
        ])
            ->getMock();
        $form_validator->expects($this->never())
            ->method('doValidateForm');
        $form = [];
        $form_state = (new FormState())->setValidationComplete();
        $form_validator->validateForm('test_form_id', $form, $form_state);
        $this->assertArrayNotHasKey('#errors', $form);
    }
    
    /**
     * Tests the 'must_validate' $form_state flag.
     *
     * @covers ::validateForm
     */
    public function testMustValidate() : void {
        $form_validator = $this->getMockBuilder('Drupal\\Core\\Form\\FormValidator')
            ->setConstructorArgs([
            new RequestStack(),
            $this->getStringTranslationStub(),
            $this->csrfToken,
            $this->logger,
            $this->formErrorHandler,
        ])
            ->onlyMethods([
            'doValidateForm',
        ])
            ->getMock();
        $form_validator->expects($this->once())
            ->method('doValidateForm');
        $this->formErrorHandler
            ->expects($this->once())
            ->method('handleFormErrors');
        $form = [];
        $form_state = (new FormState())->setValidationComplete()
            ->setValidationEnforced();
        $form_validator->validateForm('test_form_id', $form, $form_state);
    }
    
    /**
     * @covers ::validateForm
     */
    public function testValidateInvalidFormToken() : void {
        $request_stack = new RequestStack();
        $request = new Request([], [], [], [], [], [
            'REQUEST_URI' => '/test/example?foo=bar',
        ]);
        $request_stack->push($request);
        $this->csrfToken
            ->expects($this->once())
            ->method('validate')
            ->willReturn(FALSE);
        $form_validator = $this->getMockBuilder('Drupal\\Core\\Form\\FormValidator')
            ->setConstructorArgs([
            $request_stack,
            $this->getStringTranslationStub(),
            $this->csrfToken,
            $this->logger,
            $this->formErrorHandler,
        ])
            ->onlyMethods([
            'doValidateForm',
        ])
            ->getMock();
        $form_validator->expects($this->never())
            ->method('doValidateForm');
        $form['#token'] = 'test_form_id';
        $form_state = $this->getMockBuilder('Drupal\\Core\\Form\\FormState')
            ->onlyMethods([
            'setErrorByName',
        ])
            ->getMock();
        $form_state->expects($this->once())
            ->method('setErrorByName')
            ->with('form_token', 'The form has become outdated. Press the back button, copy any unsaved work in the form, and then reload the page.');
        $form_state->setValue('form_token', 'some_random_token');
        $form_validator->validateForm('test_form_id', $form, $form_state);
        $this->assertTrue($form_state->isValidationComplete());
    }
    
    /**
     * @covers ::validateForm
     */
    public function testValidateValidFormToken() : void {
        $request_stack = new RequestStack();
        $this->csrfToken
            ->expects($this->once())
            ->method('validate')
            ->willReturn(TRUE);
        $form_validator = $this->getMockBuilder('Drupal\\Core\\Form\\FormValidator')
            ->setConstructorArgs([
            $request_stack,
            $this->getStringTranslationStub(),
            $this->csrfToken,
            $this->logger,
            $this->formErrorHandler,
        ])
            ->onlyMethods([
            'doValidateForm',
        ])
            ->getMock();
        $form_validator->expects($this->once())
            ->method('doValidateForm');
        $form['#token'] = 'test_form_id';
        $form_state = $this->getMockBuilder('Drupal\\Core\\Form\\FormState')
            ->onlyMethods([
            'setErrorByName',
        ])
            ->getMock();
        $form_state->expects($this->never())
            ->method('setErrorByName');
        $form_state->setValue('form_token', 'some_random_token');
        $form_validator->validateForm('test_form_id', $form, $form_state);
        $this->assertTrue($form_state->isValidationComplete());
    }
    
    /**
     * @covers ::handleErrorsWithLimitedValidation
     *
     * @dataProvider providerTestHandleErrorsWithLimitedValidation
     */
    public function testHandleErrorsWithLimitedValidation($sections, $triggering_element, $values, $expected) : void {
        $form_validator = new FormValidator(new RequestStack(), $this->getStringTranslationStub(), $this->csrfToken, $this->logger, $this->formErrorHandler);
        $triggering_element['#limit_validation_errors'] = $sections;
        $form = [];
        $form_state = (new FormState())->setValues($values)
            ->setTriggeringElement($triggering_element);
        $form_validator->validateForm('test_form_id', $form, $form_state);
        $this->assertSame($expected, $form_state->getValues());
    }
    public static function providerTestHandleErrorsWithLimitedValidation() {
        return [
            // Test with a non-existent section.
[
                [
                    [
                        'test1',
                    ],
                    [
                        'test3',
                    ],
                ],
                [],
                [
                    'test1' => 'foo',
                    'test2' => 'bar',
                ],
                [
                    'test1' => 'foo',
                ],
            ],
            // Test with buttons in a non-validated section.
[
                [
                    [
                        'test1',
                    ],
                ],
                [
                    '#is_button' => TRUE,
                    '#value' => 'baz',
                    '#name' => 'op',
                    '#parents' => [
                        'submit',
                    ],
                ],
                [
                    'test1' => 'foo',
                    'test2' => 'bar',
                    'op' => 'baz',
                    'submit' => 'baz',
                ],
                [
                    'test1' => 'foo',
                    'submit' => 'baz',
                    'op' => 'baz',
                ],
            ],
            // Test with a matching button #value and $form_state value.
[
                [
                    [
                        'submit',
                    ],
                ],
                [
                    '#is_button' => TRUE,
                    '#value' => 'baz',
                    '#name' => 'op',
                    '#parents' => [
                        'submit',
                    ],
                ],
                [
                    'test1' => 'foo',
                    'test2' => 'bar',
                    'op' => 'baz',
                    'submit' => 'baz',
                ],
                [
                    'submit' => 'baz',
                    'op' => 'baz',
                ],
            ],
            // Test with a mismatched button #value and $form_state value.
[
                [
                    [
                        'submit',
                    ],
                ],
                [
                    '#is_button' => TRUE,
                    '#value' => 'bar',
                    '#name' => 'op',
                    '#parents' => [
                        'submit',
                    ],
                ],
                [
                    'test1' => 'foo',
                    'test2' => 'bar',
                    'op' => 'baz',
                    'submit' => 'baz',
                ],
                [
                    'submit' => 'baz',
                ],
            ],
        ];
    }
    
    /**
     * @covers ::executeValidateHandlers
     */
    public function testExecuteValidateHandlers() : void {
        $form_validator = new FormValidator(new RequestStack(), $this->getStringTranslationStub(), $this->csrfToken, $this->logger, $this->formErrorHandler);
        $mock = $this->getMockBuilder(FormValidatorTestMockInterface::class)
            ->onlyMethods([
            'validate_handler',
            'hash_validate',
            'element_validate',
        ])
            ->getMock();
        $mock->expects($this->once())
            ->method('validate_handler')
            ->with($this->isType('array'), $this->isInstanceOf('Drupal\\Core\\Form\\FormStateInterface'));
        $mock->expects($this->once())
            ->method('hash_validate')
            ->with($this->isType('array'), $this->isInstanceOf('Drupal\\Core\\Form\\FormStateInterface'));
        $form = [];
        $form_state = new FormState();
        $form_validator->executeValidateHandlers($form, $form_state);
        $form['#validate'][] = [
            $mock,
            'hash_validate',
        ];
        $form_validator->executeValidateHandlers($form, $form_state);
        // $form_state validate handlers will supersede $form handlers.
        $validate_handlers[] = [
            $mock,
            'validate_handler',
        ];
        $form_state->setValidateHandlers($validate_handlers);
        $form_validator->executeValidateHandlers($form, $form_state);
    }
    
    /**
     * @covers ::doValidateForm
     *
     * @dataProvider providerTestRequiredErrorMessage
     */
    public function testRequiredErrorMessage($element, $expected_message) : void {
        $form_validator = $this->getMockBuilder('Drupal\\Core\\Form\\FormValidator')
            ->setConstructorArgs([
            new RequestStack(),
            $this->getStringTranslationStub(),
            $this->csrfToken,
            $this->logger,
            $this->formErrorHandler,
        ])
            ->onlyMethods([
            'executeValidateHandlers',
        ])
            ->getMock();
        $form_validator->expects($this->once())
            ->method('executeValidateHandlers');
        $form = [];
        $form['test'] = $element + [
            '#type' => 'textfield',
            '#value' => '',
            '#needs_validation' => TRUE,
            '#required' => TRUE,
            '#parents' => [
                'test',
            ],
        ];
        $form_state = $this->getMockBuilder('Drupal\\Core\\Form\\FormState')
            ->onlyMethods([
            'setError',
        ])
            ->getMock();
        $form_state->expects($this->once())
            ->method('setError')
            ->with($this->isType('array'), $expected_message);
        $form_validator->validateForm('test_form_id', $form, $form_state);
    }
    public static function providerTestRequiredErrorMessage() {
        return [
            [
                // Use the default message with a title.
[
                    '#title' => 'Test',
                ],
                'Test field is required.',
            ],
            // Use a custom message.
[
                [
                    '#required_error' => 'FAIL',
                ],
                'FAIL',
            ],
            // No title or custom message.
[
                [],
                '',
            ],
        ];
    }
    
    /**
     * @covers ::doValidateForm
     */
    public function testElementValidate() : void {
        $form_validator = $this->getMockBuilder('Drupal\\Core\\Form\\FormValidator')
            ->setConstructorArgs([
            new RequestStack(),
            $this->getStringTranslationStub(),
            $this->csrfToken,
            $this->logger,
            $this->formErrorHandler,
        ])
            ->onlyMethods([
            'executeValidateHandlers',
        ])
            ->getMock();
        $form_validator->expects($this->once())
            ->method('executeValidateHandlers');
        $mock = $this->getMockBuilder(FormValidatorTestMockInterface::class)
            ->onlyMethods([
            'validate_handler',
            'hash_validate',
            'element_validate',
        ])
            ->getMock();
        $mock->expects($this->once())
            ->method('element_validate')
            ->with($this->isType('array'), $this->isInstanceOf('Drupal\\Core\\Form\\FormStateInterface'), NULL);
        $form = [];
        $form['test'] = [
            '#type' => 'textfield',
            '#title' => 'Test',
            '#parents' => [
                'test',
            ],
            '#element_validate' => [
                [
                    $mock,
                    'element_validate',
                ],
            ],
        ];
        $form_state = new FormState();
        $form_validator->validateForm('test_form_id', $form, $form_state);
    }
    
    /**
     * @covers ::performRequiredValidation
     *
     * @dataProvider providerTestPerformRequiredValidation
     */
    public function testPerformRequiredValidation($element, $expected_message, $call_watchdog) : void {
        $form_validator = new FormValidator(new RequestStack(), $this->getStringTranslationStub(), $this->csrfToken, $this->logger, $this->formErrorHandler);
        if ($call_watchdog) {
            $this->logger
                ->expects($this->once())
                ->method('error')
                ->with($this->isType('string'), $this->isType('array'));
        }
        $form = [];
        $form['test'] = $element + [
            '#title' => 'Test',
            '#needs_validation' => TRUE,
            '#required' => FALSE,
            '#parents' => [
                'test',
            ],
        ];
        $form_state = $this->getMockBuilder('Drupal\\Core\\Form\\FormState')
            ->onlyMethods([
            'setError',
        ])
            ->getMock();
        $form_state->expects($this->once())
            ->method('setError')
            ->with($this->isType('array'), $expected_message);
        $form_validator->validateForm('test_form_id', $form, $form_state);
    }
    public static function providerTestPerformRequiredValidation() {
        return [
            [
                [
                    '#type' => 'select',
                    '#options' => [
                        'foo' => 'Foo',
                        'bar' => 'Bar',
                    ],
                    '#required' => TRUE,
                    '#value' => 'baz',
                    '#empty_value' => 'baz',
                    '#multiple' => FALSE,
                ],
                'Test field is required.',
                FALSE,
            ],
            [
                [
                    '#type' => 'select',
                    '#options' => [
                        'foo' => 'Foo',
                        'bar' => 'Bar',
                    ],
                    '#value' => 'baz',
                    '#multiple' => FALSE,
                ],
                'The submitted value <em class="placeholder">baz</em> in the <em class="placeholder">Test</em> element is not allowed.',
                TRUE,
            ],
            [
                [
                    '#type' => 'checkboxes',
                    '#options' => [
                        'foo' => 'Foo',
                        'bar' => 'Bar',
                    ],
                    '#value' => [
                        'baz',
                    ],
                    '#multiple' => TRUE,
                ],
                'The submitted value <em class="placeholder">0</em> in the <em class="placeholder">Test</em> element is not allowed.',
                TRUE,
            ],
            [
                [
                    '#type' => 'select',
                    '#options' => [
                        'foo' => 'Foo',
                        'bar' => 'Bar',
                    ],
                    '#value' => [
                        'baz',
                    ],
                    '#multiple' => TRUE,
                ],
                'The submitted value <em class="placeholder">baz</em> in the <em class="placeholder">Test</em> element is not allowed.',
                TRUE,
            ],
            [
                [
                    '#type' => 'textfield',
                    '#maxlength' => 7,
                    '#value' => Random::machineName(8),
                ],
                'Test cannot be longer than <em class="placeholder">7</em> characters but is currently <em class="placeholder">8</em> characters long.',
                FALSE,
            ],
            [
                [
                    '#type' => 'select',
                    '#options' => [
                        'foo' => 'Foo',
                        'bar' => 'Bar',
                    ],
                    '#value' => [
                        [],
                    ],
                    '#multiple' => TRUE,
                ],
                'The submitted value type <em class="placeholder">array</em> in the <em class="placeholder">Test</em> element is not allowed.',
                TRUE,
            ],
        ];
    }

}

Members

Title Sort descending Modifiers Object type Summary Overriden Title
ExpectDeprecationTrait::expectDeprecation public function Adds an expected deprecation.
ExpectDeprecationTrait::setUpErrorHandler public function Sets up the test error handler.
ExpectDeprecationTrait::tearDownErrorHandler public function Tears down the test error handler.
FormValidatorTest::$csrfToken protected property The CSRF token generator to validate the form token.
FormValidatorTest::$formErrorHandler protected property The form error handler.
FormValidatorTest::$logger protected property A logger instance.
FormValidatorTest::providerTestHandleErrorsWithLimitedValidation public static function
FormValidatorTest::providerTestPerformRequiredValidation public static function
FormValidatorTest::providerTestRequiredErrorMessage public static function
FormValidatorTest::setUp protected function Overrides UnitTestCase::setUp
FormValidatorTest::testElementValidate public function @covers ::doValidateForm
FormValidatorTest::testExecuteValidateHandlers public function @covers ::executeValidateHandlers
FormValidatorTest::testHandleErrorsWithLimitedValidation public function @covers ::handleErrorsWithLimitedValidation
FormValidatorTest::testMustValidate public function Tests the &#039;must_validate&#039; $form_state flag.
FormValidatorTest::testPerformRequiredValidation public function @covers ::performRequiredValidation
FormValidatorTest::testPreventDuplicateValidation public function Tests the &#039;must_validate&#039; $form_state flag.
FormValidatorTest::testRequiredErrorMessage public function @covers ::doValidateForm
FormValidatorTest::testValidateInvalidFormToken public function @covers ::validateForm
FormValidatorTest::testValidateValidFormToken public function @covers ::validateForm
FormValidatorTest::testValidationComplete public function Tests the &#039;validation_complete&#039; $form_state flag.
RandomGeneratorTrait::getRandomGenerator protected function Gets the random generator for the utility methods.
RandomGeneratorTrait::randomMachineName protected function Generates a unique random string containing letters and numbers.
RandomGeneratorTrait::randomObject public function Generates a random PHP object.
RandomGeneratorTrait::randomString public function Generates a pseudo-random string of ASCII characters of codes 32 to 126.
UnitTestCase::$root protected property The app root.
UnitTestCase::getClassResolverStub protected function Returns a stub class resolver.
UnitTestCase::getConfigFactoryStub public function Returns a stub config factory that behaves according to the passed array.
UnitTestCase::getContainerWithCacheTagsInvalidator protected function Sets up a container with a cache tags invalidator.
UnitTestCase::getStringTranslationStub public function Returns a stub translation manager that just returns the passed string.
UnitTestCase::setDebugDumpHandler public static function Registers the dumper CLI handler when the DebugDump extension is enabled.

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.