3 namespace Drupal\Tests\system\Functional\Form;
5 use Drupal\Core\Render\Element;
6 use Drupal\Tests\BrowserTestBase;
9 * Tests form processing and alteration via form validation handlers.
13 class ValidationTest extends BrowserTestBase {
20 public static $modules = ['form_test'];
23 * Tests #element_validate and #validate.
25 public function testValidate() {
26 $this->drupalGet('form-test/validate');
27 // Verify that #element_validate handlers can alter the form and submitted
30 'name' => 'element_validate',
32 $this->drupalPostForm(NULL, $edit, 'Save');
33 $this->assertFieldByName('name', '#value changed by #element_validate', 'Form element #value was altered.');
34 $this->assertText('Name value: value changed by setValueForElement() in #element_validate', 'Form element value in $form_state was altered.');
36 // Verify that #validate handlers can alter the form and submitted
41 $this->drupalPostForm(NULL, $edit, 'Save');
42 $this->assertFieldByName('name', '#value changed by #validate', 'Form element #value was altered.');
43 $this->assertText('Name value: value changed by setValueForElement() in #validate', 'Form element value in $form_state was altered.');
45 // Verify that #element_validate handlers can make form elements
46 // inaccessible, but values persist.
48 'name' => 'element_validate_access',
50 $this->drupalPostForm(NULL, $edit, 'Save');
51 $this->assertNoFieldByName('name', 'Form element was hidden.');
52 $this->assertText('Name value: element_validate_access', 'Value for inaccessible form element exists.');
54 // Verify that value for inaccessible form element persists.
55 $this->drupalPostForm(NULL, [], 'Save');
56 $this->assertNoFieldByName('name', 'Form element was hidden.');
57 $this->assertText('Name value: element_validate_access', 'Value for inaccessible form element exists.');
59 // Verify that #validate handlers don't run if the CSRF token is invalid.
60 $this->drupalLogin($this->drupalCreateUser());
61 $this->drupalGet('form-test/validate');
62 // $this->assertSession()->fieldExists() does not recognize hidden fields,
63 // which breaks $this->drupalPostForm() if we try to change the value of a
64 // hidden field such as form_token.
65 $this->assertSession()
66 ->elementExists('css', 'input[name="form_token"]')
67 ->setValue('invalid_token');
68 $this->drupalPostForm(NULL, ['name' => 'validate'], 'Save');
69 $this->assertNoFieldByName('name', '#value changed by #validate', 'Form element #value was not altered.');
70 $this->assertNoText('Name value: value changed by setValueForElement() in #validate', 'Form element value in $form_state was not altered.');
71 $this->assertText('The form has become outdated. Copy any unsaved work in the form below');
75 * Tests that a form with a disabled CSRF token can be validated.
77 public function testDisabledToken() {
78 $this->drupalPostForm('form-test/validate-no-token', [], 'Save');
79 $this->assertText('The form_test_validate_no_token form has been submitted successfully.');
83 * Tests partial form validation through #limit_validation_errors.
85 public function testValidateLimitErrors() {
88 'test_numeric_index[0]' => 'invalid',
89 'test_substring[foo]' => 'invalid',
91 $path = 'form-test/limit-validation-errors';
93 // Render the form, and verify that the buttons with limited server-side
94 // validation have the proper 'formnovalidate' attribute (to prevent
95 // client-side validation by the browser).
96 $this->drupalGet($path);
97 $expected = 'formnovalidate';
98 foreach (['partial', 'partial-numeric-index', 'substring'] as $type) {
99 $element = $this->xpath('//input[@id=:id and @formnovalidate=:expected]', [
100 ':id' => 'edit-' . $type,
101 ':expected' => $expected,
103 $this->assertTrue(!empty($element), format_string('The @type button has the proper formnovalidate attribute.', ['@type' => $type]));
105 // The button with full server-side validation should not have the
106 // 'formnovalidate' attribute.
107 $element = $this->xpath('//input[@id=:id and not(@formnovalidate)]', [
108 ':id' => 'edit-full',
110 $this->assertTrue(!empty($element), 'The button with full server-side validation does not have the formnovalidate attribute.');
112 // Submit the form by pressing the 'Partial validate' button (uses
113 // #limit_validation_errors) and ensure that the title field is not
114 // validated, but the #element_validate handler for the 'test' field
116 $this->drupalPostForm($path, $edit, t('Partial validate'));
117 $this->assertNoText(t('@name field is required.', ['@name' => 'Title']));
118 $this->assertText('Test element is invalid');
120 // Edge case of #limit_validation_errors containing numeric indexes: same
121 // thing with the 'Partial validate (numeric index)' button and the
122 // 'test_numeric_index' field.
123 $this->drupalPostForm($path, $edit, t('Partial validate (numeric index)'));
124 $this->assertNoText(t('@name field is required.', ['@name' => 'Title']));
125 $this->assertText('Test (numeric index) element is invalid');
127 // Ensure something like 'foobar' isn't considered "inside" 'foo'.
128 $this->drupalPostForm($path, $edit, t('Partial validate (substring)'));
129 $this->assertNoText(t('@name field is required.', ['@name' => 'Title']));
130 $this->assertText('Test (substring) foo element is invalid');
132 // Ensure not validated values are not available to submit handlers.
133 $this->drupalPostForm($path, ['title' => '', 'test' => 'valid'], t('Partial validate'));
134 $this->assertText('Only validated values appear in the form values.');
136 // Now test full form validation and ensure that the #element_validate
137 // handler is still triggered.
138 $this->drupalPostForm($path, $edit, t('Full validate'));
139 $this->assertText(t('@name field is required.', ['@name' => 'Title']));
140 $this->assertText('Test element is invalid');
144 * Tests #pattern validation.
146 public function testPatternValidation() {
147 $textfield_error = t('%name field is not in the right format.', ['%name' => 'One digit followed by lowercase letters']);
148 $tel_error = t('%name field is not in the right format.', ['%name' => 'Everything except numbers']);
149 $password_error = t('%name field is not in the right format.', ['%name' => 'Password']);
151 // Invalid textfield, valid tel.
153 'textfield' => 'invalid',
156 $this->drupalPostForm('form-test/pattern', $edit, 'Submit');
157 $this->assertRaw($textfield_error);
158 $this->assertNoRaw($tel_error);
159 $this->assertNoRaw($password_error);
161 // Valid textfield, invalid tel, valid password.
163 'textfield' => '7seven',
165 'password' => '0100110',
167 $this->drupalPostForm('form-test/pattern', $edit, 'Submit');
168 $this->assertNoRaw($textfield_error);
169 $this->assertRaw($tel_error);
170 $this->assertNoRaw($password_error);
172 // Non required fields are not validated if empty.
177 $this->drupalPostForm('form-test/pattern', $edit, 'Submit');
178 $this->assertNoRaw($textfield_error);
179 $this->assertNoRaw($tel_error);
180 $this->assertNoRaw($password_error);
184 'password' => $this->randomMachineName(),
186 $this->drupalPostForm('form-test/pattern', $edit, 'Submit');
187 $this->assertNoRaw($textfield_error);
188 $this->assertNoRaw($tel_error);
189 $this->assertRaw($password_error);
191 // The pattern attribute overrides #pattern and is not validated on the
196 'url' => 'http://www.example.com/',
198 $this->drupalPostForm('form-test/pattern', $edit, 'Submit');
199 $this->assertNoRaw(t('%name field is not in the right format.', ['%name' => 'Client side validation']));
203 * Tests #required with custom validation errors.
205 * @see \Drupal\form_test\Form\FormTestValidateRequiredForm
207 public function testCustomRequiredError() {
208 $form = \Drupal::formBuilder()->getForm('\Drupal\form_test\Form\FormTestValidateRequiredForm');
210 // Verify that a custom #required error can be set.
212 $this->drupalPostForm('form-test/validate-required', $edit, 'Submit');
214 foreach (Element::children($form) as $key) {
215 if (isset($form[$key]['#required_error'])) {
216 $this->assertNoText(t('@name field is required.', ['@name' => $form[$key]['#title']]));
217 $this->assertText($form[$key]['#required_error']);
219 elseif (isset($form[$key]['#form_test_required_error'])) {
220 $this->assertNoText(t('@name field is required.', ['@name' => $form[$key]['#title']]));
221 $this->assertText($form[$key]['#form_test_required_error']);
224 $this->assertNoText(t('An illegal choice has been detected. Please contact the site administrator.'));
226 // Verify that no custom validation error appears with valid values.
228 'textfield' => $this->randomString(),
229 'checkboxes[foo]' => TRUE,
232 $this->drupalPostForm('form-test/validate-required', $edit, 'Submit');
234 foreach (Element::children($form) as $key) {
235 if (isset($form[$key]['#required_error'])) {
236 $this->assertNoText(t('@name field is required.', ['@name' => $form[$key]['#title']]));
237 $this->assertNoText($form[$key]['#required_error']);
239 elseif (isset($form[$key]['#form_test_required_error'])) {
240 $this->assertNoText(t('@name field is required.', ['@name' => $form[$key]['#title']]));
241 $this->assertNoText($form[$key]['#form_test_required_error']);
244 $this->assertNoText(t('An illegal choice has been detected. Please contact the site administrator.'));