3 namespace Drupal\Tests\options\Functional;
5 use Drupal\entity_test\Entity\EntityTest;
6 use Drupal\field\Entity\FieldConfig;
7 use Drupal\field\Tests\FieldTestBase;
8 use Drupal\field\Entity\FieldStorageConfig;
11 * Tests the Options widgets.
15 class OptionsWidgetsTest extends FieldTestBase {
22 public static $modules = ['node', 'options', 'entity_test', 'options_test', 'taxonomy', 'field_ui'];
25 * A field storage with cardinality 1 to use in this test class.
27 * @var \Drupal\field\Entity\FieldStorageConfig
32 * A field storage with cardinality 2 to use in this test class.
34 * @var \Drupal\field\Entity\FieldStorageConfig
39 protected function setUp() {
42 // Field storage with cardinality 1.
43 $this->card1 = FieldStorageConfig::create([
44 'field_name' => 'card_1',
45 'entity_type' => 'entity_test',
46 'type' => 'list_integer',
50 // Make sure that 0 works as an option.
53 // Make sure that option text is properly sanitized.
54 2 => 'Some <script>dangerous</script> & unescaped <strong>markup</strong>',
55 // Make sure that HTML entities in option text are not double-encoded.
56 3 => 'Some HTML encoded markup with < & >',
62 // Field storage with cardinality 2.
63 $this->card2 = FieldStorageConfig::create([
64 'field_name' => 'card_2',
65 'entity_type' => 'entity_test',
66 'type' => 'list_integer',
70 // Make sure that 0 works as an option.
73 // Make sure that option text is properly sanitized.
74 2 => 'Some <script>dangerous</script> & unescaped <strong>markup</strong>',
81 $this->drupalLogin($this->drupalCreateUser(['view test entity', 'administer entity_test content']));
85 * Tests the 'options_buttons' widget (single select).
87 public function testRadioButtons() {
88 // Create an instance of the 'single value' field.
89 $field = FieldConfig::create([
90 'field_storage' => $this->card1,
91 'bundle' => 'entity_test',
94 entity_get_form_display('entity_test', 'entity_test', 'default')
95 ->setComponent($this->card1->getName(), [
96 'type' => 'options_buttons',
101 $entity = EntityTest::create([
103 'name' => $this->randomMachineName(),
106 $entity_init = clone $entity;
108 // With no field data, no buttons are checked.
109 $this->drupalGet('entity_test/manage/' . $entity->id() . '/edit');
110 $this->assertNoFieldChecked('edit-card-1-0');
111 $this->assertNoFieldChecked('edit-card-1-1');
112 $this->assertNoFieldChecked('edit-card-1-2');
113 $this->assertRaw('Some dangerous & unescaped <strong>markup</strong>', 'Option text was properly filtered.');
114 $this->assertRaw('Some HTML encoded markup with < & >');
116 // Select first option.
117 $edit = ['card_1' => 0];
118 $this->drupalPostForm(NULL, $edit, t('Save'));
119 $this->assertFieldValues($entity_init, 'card_1', [0]);
121 // Check that the selected button is checked.
122 $this->drupalGet('entity_test/manage/' . $entity->id() . '/edit');
123 $this->assertFieldChecked('edit-card-1-0');
124 $this->assertNoFieldChecked('edit-card-1-1');
125 $this->assertNoFieldChecked('edit-card-1-2');
128 $edit = ['card_1' => '_none'];
129 $this->drupalPostForm(NULL, $edit, t('Save'));
130 $this->assertFieldValues($entity_init, 'card_1', []);
132 // Check that required radios with one option is auto-selected.
133 $this->card1->setSetting('allowed_values', [99 => 'Only allowed value']);
134 $this->card1->save();
135 $field->setRequired(TRUE);
137 $this->drupalGet('entity_test/manage/' . $entity->id() . '/edit');
138 $this->assertFieldChecked('edit-card-1-99');
142 * Tests the 'options_buttons' widget (multiple select).
144 public function testCheckBoxes() {
145 // Create an instance of the 'multiple values' field.
146 $field = FieldConfig::create([
147 'field_storage' => $this->card2,
148 'bundle' => 'entity_test',
151 entity_get_form_display('entity_test', 'entity_test', 'default')
152 ->setComponent($this->card2->getName(), [
153 'type' => 'options_buttons',
158 $entity = EntityTest::create([
160 'name' => $this->randomMachineName(),
163 $entity_init = clone $entity;
165 // Display form: with no field data, nothing is checked.
166 $this->drupalGet('entity_test/manage/' . $entity->id() . '/edit');
167 $this->assertNoFieldChecked('edit-card-2-0');
168 $this->assertNoFieldChecked('edit-card-2-1');
169 $this->assertNoFieldChecked('edit-card-2-2');
170 $this->assertRaw('Some dangerous & unescaped <strong>markup</strong>', 'Option text was properly filtered.');
172 // Submit form: select first and third options.
175 'card_2[1]' => FALSE,
178 $this->drupalPostForm(NULL, $edit, t('Save'));
179 $this->assertFieldValues($entity_init, 'card_2', [0, 2]);
181 // Display form: check that the right options are selected.
182 $this->drupalGet('entity_test/manage/' . $entity->id() . '/edit');
183 $this->assertFieldChecked('edit-card-2-0');
184 $this->assertNoFieldChecked('edit-card-2-1');
185 $this->assertFieldChecked('edit-card-2-2');
187 // Submit form: select only first option.
190 'card_2[1]' => FALSE,
191 'card_2[2]' => FALSE,
193 $this->drupalPostForm(NULL, $edit, t('Save'));
194 $this->assertFieldValues($entity_init, 'card_2', [0]);
196 // Display form: check that the right options are selected.
197 $this->drupalGet('entity_test/manage/' . $entity->id() . '/edit');
198 $this->assertFieldChecked('edit-card-2-0');
199 $this->assertNoFieldChecked('edit-card-2-1');
200 $this->assertNoFieldChecked('edit-card-2-2');
202 // Submit form: select the three options while the field accepts only 2.
208 $this->drupalPostForm(NULL, $edit, t('Save'));
209 $this->assertText('this field cannot hold more than 2 values', 'Validation error was displayed.');
211 // Submit form: uncheck all options.
213 'card_2[0]' => FALSE,
214 'card_2[1]' => FALSE,
215 'card_2[2]' => FALSE,
217 $this->drupalPostForm(NULL, $edit, t('Save'));
218 // Check that the value was saved.
219 $this->assertFieldValues($entity_init, 'card_2', []);
221 // Required checkbox with one option is auto-selected.
222 $this->card2->setSetting('allowed_values', [99 => 'Only allowed value']);
223 $this->card2->save();
224 $field->setRequired(TRUE);
226 $this->drupalGet('entity_test/manage/' . $entity->id() . '/edit');
227 $this->assertFieldChecked('edit-card-2-99');
231 * Tests the 'options_select' widget (single select).
233 public function testSelectListSingle() {
234 // Create an instance of the 'single value' field.
235 $field = FieldConfig::create([
236 'field_storage' => $this->card1,
237 'bundle' => 'entity_test',
241 entity_get_form_display('entity_test', 'entity_test', 'default')
242 ->setComponent($this->card1->getName(), [
243 'type' => 'options_select',
248 $entity = EntityTest::create([
250 'name' => $this->randomMachineName(),
253 $entity_init = clone $entity;
256 $this->drupalGet('entity_test/manage/' . $entity->id() . '/edit');
257 // A required field without any value has a "none" option.
258 $this->assertTrue($this->xpath('//select[@id=:id]//option[@value="_none" and text()=:label]', [':id' => 'edit-card-1', ':label' => t('- Select a value -')]), 'A required select list has a "Select a value" choice.');
260 // With no field data, nothing is selected.
261 $this->assertNoOptionSelected('edit-card-1', '_none');
262 $this->assertNoOptionSelected('edit-card-1', 0);
263 $this->assertNoOptionSelected('edit-card-1', 1);
264 $this->assertNoOptionSelected('edit-card-1', 2);
265 $this->assertRaw('Some dangerous & unescaped markup', 'Option text was properly filtered.');
267 // Submit form: select invalid 'none' option.
268 $edit = ['card_1' => '_none'];
269 $this->drupalPostForm(NULL, $edit, t('Save'));
270 $this->assertRaw(t('@title field is required.', ['@title' => $field->getName()]), 'Cannot save a required field when selecting "none" from the select list.');
272 // Submit form: select first option.
273 $edit = ['card_1' => 0];
274 $this->drupalPostForm(NULL, $edit, t('Save'));
275 $this->assertFieldValues($entity_init, 'card_1', [0]);
277 // Display form: check that the right options are selected.
278 $this->drupalGet('entity_test/manage/' . $entity->id() . '/edit');
279 // A required field with a value has no 'none' option.
280 $this->assertFalse($this->xpath('//select[@id=:id]//option[@value="_none"]', [':id' => 'edit-card-1']), 'A required select list with an actual value has no "none" choice.');
281 $this->assertOptionSelected('edit-card-1', 0);
282 $this->assertNoOptionSelected('edit-card-1', 1);
283 $this->assertNoOptionSelected('edit-card-1', 2);
285 // Make the field non required.
286 $field->setRequired(FALSE);
290 $this->drupalGet('entity_test/manage/' . $entity->id() . '/edit');
291 // A non-required field has a 'none' option.
292 $this->assertTrue($this->xpath('//select[@id=:id]//option[@value="_none" and text()=:label]', [':id' => 'edit-card-1', ':label' => t('- None -')]), 'A non-required select list has a "None" choice.');
293 // Submit form: Unselect the option.
294 $edit = ['card_1' => '_none'];
295 $this->drupalPostForm('entity_test/manage/' . $entity->id() . '/edit', $edit, t('Save'));
296 $this->assertFieldValues($entity_init, 'card_1', []);
300 $this->card1->setSetting('allowed_values', []);
301 $this->card1->setSetting('allowed_values_function', 'options_test_allowed_values_callback');
302 $this->card1->save();
304 // Display form: with no field data, nothing is selected
305 $this->drupalGet('entity_test/manage/' . $entity->id() . '/edit');
306 $this->assertNoOptionSelected('edit-card-1', 0);
307 $this->assertNoOptionSelected('edit-card-1', 1);
308 $this->assertNoOptionSelected('edit-card-1', 2);
309 $this->assertRaw('Some dangerous & unescaped markup', 'Option text was properly filtered.');
310 $this->assertRaw('More <script>dangerous</script> markup', 'Option group text was properly filtered.');
311 $this->assertRaw('Group 1', 'Option groups are displayed.');
313 // Submit form: select first option.
314 $edit = ['card_1' => 0];
315 $this->drupalPostForm(NULL, $edit, t('Save'));
316 $this->assertFieldValues($entity_init, 'card_1', [0]);
318 // Display form: check that the right options are selected.
319 $this->drupalGet('entity_test/manage/' . $entity->id() . '/edit');
320 $this->assertOptionSelected('edit-card-1', 0);
321 $this->assertNoOptionSelected('edit-card-1', 1);
322 $this->assertNoOptionSelected('edit-card-1', 2);
324 // Submit form: Unselect the option.
325 $edit = ['card_1' => '_none'];
326 $this->drupalPostForm('entity_test/manage/' . $entity->id() . '/edit', $edit, t('Save'));
327 $this->assertFieldValues($entity_init, 'card_1', []);
331 * Tests the 'options_select' widget (multiple select).
333 public function testSelectListMultiple() {
334 // Create an instance of the 'multiple values' field.
335 $field = FieldConfig::create([
336 'field_storage' => $this->card2,
337 'bundle' => 'entity_test',
340 entity_get_form_display('entity_test', 'entity_test', 'default')
341 ->setComponent($this->card2->getName(), [
342 'type' => 'options_select',
347 $entity = EntityTest::create([
349 'name' => $this->randomMachineName(),
352 $entity_init = clone $entity;
354 // Display form: with no field data, nothing is selected.
355 $this->drupalGet('entity_test/manage/' . $entity->id() . '/edit');
356 $this->assertOptionSelected("edit-card-2", '_none');
357 $this->assertNoOptionSelected('edit-card-2', 0);
358 $this->assertNoOptionSelected('edit-card-2', 1);
359 $this->assertNoOptionSelected('edit-card-2', 2);
360 $this->assertRaw('Some dangerous & unescaped markup', 'Option text was properly filtered.');
362 // Submit form: select first and third options.
363 $edit = ['card_2[]' => [0 => 0, 2 => 2]];
364 $this->drupalPostForm(NULL, $edit, t('Save'));
365 $this->assertFieldValues($entity_init, 'card_2', [0, 2]);
367 // Display form: check that the right options are selected.
368 $this->drupalGet('entity_test/manage/' . $entity->id() . '/edit');
369 $this->assertOptionSelected('edit-card-2', 0);
370 $this->assertNoOptionSelected('edit-card-2', 1);
371 $this->assertOptionSelected('edit-card-2', 2);
373 // Submit form: select only first option.
374 $edit = ['card_2[]' => [0 => 0]];
375 $this->drupalPostForm(NULL, $edit, t('Save'));
376 $this->assertFieldValues($entity_init, 'card_2', [0]);
378 // Display form: check that the right options are selected.
379 $this->drupalGet('entity_test/manage/' . $entity->id() . '/edit');
380 $this->assertOptionSelected('edit-card-2', 0);
381 $this->assertNoOptionSelected('edit-card-2', 1);
382 $this->assertNoOptionSelected('edit-card-2', 2);
384 // Submit form: select the three options while the field accepts only 2.
385 $edit = ['card_2[]' => [0 => 0, 1 => 1, 2 => 2]];
386 $this->drupalPostForm(NULL, $edit, t('Save'));
387 $this->assertText('this field cannot hold more than 2 values', 'Validation error was displayed.');
389 // Submit form: uncheck all options.
390 $edit = ['card_2[]' => []];
391 $this->drupalPostForm(NULL, $edit, t('Save'));
392 $this->assertFieldValues($entity_init, 'card_2', []);
394 // Test the 'None' option.
396 // Check that the 'none' option has no effect if actual options are selected
398 $edit = ['card_2[]' => ['_none' => '_none', 0 => 0]];
399 $this->drupalPostForm('entity_test/manage/' . $entity->id() . '/edit', $edit, t('Save'));
400 $this->assertFieldValues($entity_init, 'card_2', [0]);
402 // Check that selecting the 'none' option empties the field.
403 $edit = ['card_2[]' => ['_none' => '_none']];
404 $this->drupalPostForm('entity_test/manage/' . $entity->id() . '/edit', $edit, t('Save'));
405 $this->assertFieldValues($entity_init, 'card_2', []);
407 // A required select list does not have an empty key.
408 $field->setRequired(TRUE);
410 $this->drupalGet('entity_test/manage/' . $entity->id() . '/edit');
411 $this->assertFalse($this->xpath('//select[@id=:id]//option[@value=""]', [':id' => 'edit-card-2']), 'A required select list does not have an empty key.');
413 // We do not have to test that a required select list with one option is
414 // auto-selected because the browser does it for us.
418 // Use a callback function defining optgroups.
419 $this->card2->setSetting('allowed_values', []);
420 $this->card2->setSetting('allowed_values_function', 'options_test_allowed_values_callback');
421 $this->card2->save();
422 $field->setRequired(FALSE);
425 // Display form: with no field data, nothing is selected.
426 $this->drupalGet('entity_test/manage/' . $entity->id() . '/edit');
427 $this->assertNoOptionSelected('edit-card-2', 0);
428 $this->assertNoOptionSelected('edit-card-2', 1);
429 $this->assertNoOptionSelected('edit-card-2', 2);
430 $this->assertRaw('Some dangerous & unescaped markup', 'Option text was properly filtered.');
431 $this->assertRaw('More <script>dangerous</script> markup', 'Option group text was properly filtered.');
432 $this->assertRaw('Group 1', 'Option groups are displayed.');
434 // Submit form: select first option.
435 $edit = ['card_2[]' => [0 => 0]];
436 $this->drupalPostForm(NULL, $edit, t('Save'));
437 $this->assertFieldValues($entity_init, 'card_2', [0]);
439 // Display form: check that the right options are selected.
440 $this->drupalGet('entity_test/manage/' . $entity->id() . '/edit');
441 $this->assertOptionSelected('edit-card-2', 0);
442 $this->assertNoOptionSelected('edit-card-2', 1);
443 $this->assertNoOptionSelected('edit-card-2', 2);
445 // Submit form: Unselect the option.
446 $edit = ['card_2[]' => ['_none' => '_none']];
447 $this->drupalPostForm('entity_test/manage/' . $entity->id() . '/edit', $edit, t('Save'));
448 $this->assertFieldValues($entity_init, 'card_2', []);
452 * Tests the 'options_select' and 'options_button' widget for empty value.
454 public function testEmptyValue() {
455 // Create an instance of the 'single value' field.
456 $field = FieldConfig::create([
457 'field_storage' => $this->card1,
458 'bundle' => 'entity_test',
462 // Change it to the check boxes/radio buttons widget.
463 entity_get_form_display('entity_test', 'entity_test', 'default')
464 ->setComponent($this->card1->getName(), [
465 'type' => 'options_buttons',
470 $entity = EntityTest::create([
472 'name' => $this->randomMachineName(),
476 // Display form: check that _none options are present and has label.
477 $this->drupalGet('entity_test/manage/' . $entity->id() . '/edit');
478 $this->assertTrue($this->xpath('//div[@id=:id]//input[@value=:value]', [':id' => 'edit-card-1', ':value' => '_none']), 'A test radio button has a "None" choice.');
479 $this->assertTrue($this->xpath('//div[@id=:id]//label[@for=:for and text()=:label]', [':id' => 'edit-card-1', ':for' => 'edit-card-1-none', ':label' => 'N/A']), 'A test radio button has a "N/A" choice.');
481 // Change it to the select widget.
482 entity_get_form_display('entity_test', 'entity_test', 'default')
483 ->setComponent($this->card1->getName(), [
484 'type' => 'options_select',
488 // Display form: check that _none options are present and has label.
489 $this->drupalGet('entity_test/manage/' . $entity->id() . '/edit');
490 // A required field without any value has a "none" option.
491 $this->assertTrue($this->xpath('//select[@id=:id]//option[@value="_none" and text()=:label]', [':id' => 'edit-card-1', ':label' => t('- None -')]), 'A test select has a "None" choice.');