Security update for Core, with self-updated composer
[yaffs-website] / web / core / modules / options / tests / src / Functional / OptionsWidgetsTest.php
1 <?php
2
3 namespace Drupal\Tests\options\Functional;
4
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;
9
10 /**
11  * Tests the Options widgets.
12  *
13  * @group options
14  */
15 class OptionsWidgetsTest extends FieldTestBase {
16
17   /**
18    * Modules to enable.
19    *
20    * @var array
21    */
22   public static $modules = ['node', 'options', 'entity_test', 'options_test', 'taxonomy', 'field_ui'];
23
24   /**
25    * A field storage with cardinality 1 to use in this test class.
26    *
27    * @var \Drupal\field\Entity\FieldStorageConfig
28    */
29   protected $card1;
30
31   /**
32    * A field storage with cardinality 2 to use in this test class.
33    *
34    * @var \Drupal\field\Entity\FieldStorageConfig
35    */
36   protected $card2;
37
38
39   protected function setUp() {
40     parent::setUp();
41
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',
47       'cardinality' => 1,
48       'settings' => [
49         'allowed_values' => [
50           // Make sure that 0 works as an option.
51           0 => 'Zero',
52           1 => 'One',
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 &lt; &amp; &gt;',
57         ],
58       ],
59     ]);
60     $this->card1->save();
61
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',
67       'cardinality' => 2,
68       'settings' => [
69         'allowed_values' => [
70           // Make sure that 0 works as an option.
71           0 => 'Zero',
72           1 => 'One',
73           // Make sure that option text is properly sanitized.
74           2 => 'Some <script>dangerous</script> & unescaped <strong>markup</strong>',
75         ],
76       ],
77     ]);
78     $this->card2->save();
79
80     // Create a web user.
81     $this->drupalLogin($this->drupalCreateUser(['view test entity', 'administer entity_test content']));
82   }
83
84   /**
85    * Tests the 'options_buttons' widget (single select).
86    */
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',
92     ]);
93     $field->save();
94     entity_get_form_display('entity_test', 'entity_test', 'default')
95       ->setComponent($this->card1->getName(), [
96         'type' => 'options_buttons',
97       ])
98       ->save();
99
100     // Create an entity.
101     $entity = EntityTest::create([
102       'user_id' => 1,
103       'name' => $this->randomMachineName(),
104     ]);
105     $entity->save();
106     $entity_init = clone $entity;
107
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 &amp; unescaped <strong>markup</strong>', 'Option text was properly filtered.');
114     $this->assertRaw('Some HTML encoded markup with &lt; &amp; &gt;');
115
116     // Select first option.
117     $edit = ['card_1' => 0];
118     $this->drupalPostForm(NULL, $edit, t('Save'));
119     $this->assertFieldValues($entity_init, 'card_1', [0]);
120
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');
126
127     // Unselect option.
128     $edit = ['card_1' => '_none'];
129     $this->drupalPostForm(NULL, $edit, t('Save'));
130     $this->assertFieldValues($entity_init, 'card_1', []);
131
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);
136     $field->save();
137     $this->drupalGet('entity_test/manage/' . $entity->id() . '/edit');
138     $this->assertFieldChecked('edit-card-1-99');
139   }
140
141   /**
142    * Tests the 'options_buttons' widget (multiple select).
143    */
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',
149     ]);
150     $field->save();
151     entity_get_form_display('entity_test', 'entity_test', 'default')
152       ->setComponent($this->card2->getName(), [
153         'type' => 'options_buttons',
154       ])
155       ->save();
156
157     // Create an entity.
158     $entity = EntityTest::create([
159       'user_id' => 1,
160       'name' => $this->randomMachineName(),
161     ]);
162     $entity->save();
163     $entity_init = clone $entity;
164
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 &amp; unescaped <strong>markup</strong>', 'Option text was properly filtered.');
171
172     // Submit form: select first and third options.
173     $edit = [
174       'card_2[0]' => TRUE,
175       'card_2[1]' => FALSE,
176       'card_2[2]' => TRUE,
177     ];
178     $this->drupalPostForm(NULL, $edit, t('Save'));
179     $this->assertFieldValues($entity_init, 'card_2', [0, 2]);
180
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');
186
187     // Submit form: select only first option.
188     $edit = [
189       'card_2[0]' => TRUE,
190       'card_2[1]' => FALSE,
191       'card_2[2]' => FALSE,
192     ];
193     $this->drupalPostForm(NULL, $edit, t('Save'));
194     $this->assertFieldValues($entity_init, 'card_2', [0]);
195
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');
201
202     // Submit form: select the three options while the field accepts only 2.
203     $edit = [
204       'card_2[0]' => TRUE,
205       'card_2[1]' => TRUE,
206       'card_2[2]' => TRUE,
207     ];
208     $this->drupalPostForm(NULL, $edit, t('Save'));
209     $this->assertText('this field cannot hold more than 2 values', 'Validation error was displayed.');
210
211     // Submit form: uncheck all options.
212     $edit = [
213       'card_2[0]' => FALSE,
214       'card_2[1]' => FALSE,
215       'card_2[2]' => FALSE,
216     ];
217     $this->drupalPostForm(NULL, $edit, t('Save'));
218     // Check that the value was saved.
219     $this->assertFieldValues($entity_init, 'card_2', []);
220
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);
225     $field->save();
226     $this->drupalGet('entity_test/manage/' . $entity->id() . '/edit');
227     $this->assertFieldChecked('edit-card-2-99');
228   }
229
230   /**
231    * Tests the 'options_select' widget (single select).
232    */
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',
238       'required' => TRUE,
239     ]);
240     $field->save();
241     entity_get_form_display('entity_test', 'entity_test', 'default')
242       ->setComponent($this->card1->getName(), [
243         'type' => 'options_select',
244       ])
245       ->save();
246
247     // Create an entity.
248     $entity = EntityTest::create([
249       'user_id' => 1,
250       'name' => $this->randomMachineName(),
251     ]);
252     $entity->save();
253     $entity_init = clone $entity;
254
255     // Display form.
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.');
259
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 &amp; unescaped markup', 'Option text was properly filtered.');
266
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.');
271
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]);
276
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);
284
285     // Make the field non required.
286     $field->setRequired(FALSE);
287     $field->save();
288
289     // Display form.
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', []);
297
298     // Test optgroups.
299
300     $this->card1->setSetting('allowed_values', []);
301     $this->card1->setSetting('allowed_values_function', 'options_test_allowed_values_callback');
302     $this->card1->save();
303
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 &amp; unescaped markup', 'Option text was properly filtered.');
310     $this->assertRaw('More &lt;script&gt;dangerous&lt;/script&gt; markup', 'Option group text was properly filtered.');
311     $this->assertRaw('Group 1', 'Option groups are displayed.');
312
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]);
317
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);
323
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', []);
328   }
329
330   /**
331    * Tests the 'options_select' widget (multiple select).
332    */
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',
338     ]);
339     $field->save();
340     entity_get_form_display('entity_test', 'entity_test', 'default')
341       ->setComponent($this->card2->getName(), [
342         'type' => 'options_select',
343       ])
344       ->save();
345
346     // Create an entity.
347     $entity = EntityTest::create([
348       'user_id' => 1,
349       'name' => $this->randomMachineName(),
350     ]);
351     $entity->save();
352     $entity_init = clone $entity;
353
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 &amp; unescaped markup', 'Option text was properly filtered.');
361
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]);
366
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);
372
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]);
377
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);
383
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.');
388
389     // Submit form: uncheck all options.
390     $edit = ['card_2[]' => []];
391     $this->drupalPostForm(NULL, $edit, t('Save'));
392     $this->assertFieldValues($entity_init, 'card_2', []);
393
394     // Test the 'None' option.
395
396     // Check that the 'none' option has no effect if actual options are selected
397     // as well.
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]);
401
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', []);
406
407     // A required select list does not have an empty key.
408     $field->setRequired(TRUE);
409     $field->save();
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.');
412
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.
415
416     // Test optgroups.
417
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);
423     $field->save();
424
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 &amp; unescaped markup', 'Option text was properly filtered.');
431     $this->assertRaw('More &lt;script&gt;dangerous&lt;/script&gt; markup', 'Option group text was properly filtered.');
432     $this->assertRaw('Group 1', 'Option groups are displayed.');
433
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]);
438
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);
444
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', []);
449   }
450
451   /**
452    * Tests the 'options_select' and 'options_button' widget for empty value.
453    */
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',
459     ]);
460     $field->save();
461
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',
466       ])
467       ->save();
468
469     // Create an entity.
470     $entity = EntityTest::create([
471       'user_id' => 1,
472       'name' => $this->randomMachineName(),
473     ]);
474     $entity->save();
475
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.');
480
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',
485       ])
486       ->save();
487
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.');
492   }
493
494 }