Security update for Core, with self-updated composer
[yaffs-website] / web / core / modules / options / tests / src / Functional / OptionsFieldUITest.php
1 <?php
2
3 namespace Drupal\Tests\options\Functional;
4
5 use Drupal\field\Entity\FieldConfig;
6 use Drupal\field\Entity\FieldStorageConfig;
7 use Drupal\field\Tests\FieldTestBase;
8
9 /**
10  * Tests the Options field UI functionality.
11  *
12  * @group options
13  */
14 class OptionsFieldUITest extends FieldTestBase {
15
16   /**
17    * Modules to enable.
18    *
19    * @var array
20    */
21   public static $modules = ['node', 'options', 'field_test', 'taxonomy', 'field_ui'];
22
23   /**
24    * The name of the created content type.
25    *
26    * @var string
27    */
28   protected $typeName;
29
30   /**
31    * Machine name of the created content type.
32    *
33    * @var string
34    */
35   protected $type;
36
37   /**
38    * Name of the option field.
39    *
40    * @var string
41    */
42   protected $fieldName;
43
44   /**
45    * Admin path to manage field storage settings.
46    *
47    * @var string
48    */
49   protected $adminPath;
50
51   protected function setUp() {
52     parent::setUp();
53
54     // Create test user.
55     $admin_user = $this->drupalCreateUser(['access content', 'administer taxonomy', 'access administration pages', 'administer site configuration', 'administer content types', 'administer nodes', 'bypass node access', 'administer node fields', 'administer node display']);
56     $this->drupalLogin($admin_user);
57
58     // Create content type, with underscores.
59     $this->typeName = 'test_' . strtolower($this->randomMachineName());
60     $type = $this->drupalCreateContentType(['name' => $this->typeName, 'type' => $this->typeName]);
61     $this->type = $type->id();
62   }
63
64   /**
65    * Options (integer) : test 'allowed values' input.
66    */
67   public function testOptionsAllowedValuesInteger() {
68     $this->fieldName = 'field_options_integer';
69     $this->createOptionsField('list_integer');
70
71     // Flat list of textual values.
72     $string = "Zero\nOne";
73     $array = ['0' => 'Zero', '1' => 'One'];
74     $this->assertAllowedValuesInput($string, $array, 'Unkeyed lists are accepted.');
75     // Explicit integer keys.
76     $string = "0|Zero\n2|Two";
77     $array = ['0' => 'Zero', '2' => 'Two'];
78     $this->assertAllowedValuesInput($string, $array, 'Integer keys are accepted.');
79     // Check that values can be added and removed.
80     $string = "0|Zero\n1|One";
81     $array = ['0' => 'Zero', '1' => 'One'];
82     $this->assertAllowedValuesInput($string, $array, 'Values can be added and removed.');
83     // Non-integer keys.
84     $this->assertAllowedValuesInput("1.1|One", 'keys must be integers', 'Non integer keys are rejected.');
85     $this->assertAllowedValuesInput("abc|abc", 'keys must be integers', 'Non integer keys are rejected.');
86     // Mixed list of keyed and unkeyed values.
87     $this->assertAllowedValuesInput("Zero\n1|One", 'invalid input', 'Mixed lists are rejected.');
88
89     // Create a node with actual data for the field.
90     $settings = [
91       'type' => $this->type,
92       $this->fieldName => [['value' => 1]],
93     ];
94     $node = $this->drupalCreateNode($settings);
95
96     // Check that a flat list of values is rejected once the field has data.
97     $this->assertAllowedValuesInput("Zero\nOne", 'invalid input', 'Unkeyed lists are rejected once the field has data.');
98
99     // Check that values can be added but values in use cannot be removed.
100     $string = "0|Zero\n1|One\n2|Two";
101     $array = ['0' => 'Zero', '1' => 'One', '2' => 'Two'];
102     $this->assertAllowedValuesInput($string, $array, 'Values can be added.');
103     $string = "0|Zero\n1|One";
104     $array = ['0' => 'Zero', '1' => 'One'];
105     $this->assertAllowedValuesInput($string, $array, 'Values not in use can be removed.');
106     $this->assertAllowedValuesInput("0|Zero", 'some values are being removed while currently in use', 'Values in use cannot be removed.');
107
108     // Delete the node, remove the value.
109     $node->delete();
110     $string = "0|Zero";
111     $array = ['0' => 'Zero'];
112     $this->assertAllowedValuesInput($string, $array, 'Values not in use can be removed.');
113
114     // Check that the same key can only be used once.
115     $string = "0|Zero\n0|One";
116     $array = ['0' => 'One'];
117     $this->assertAllowedValuesInput($string, $array, 'Same value cannot be used multiple times.');
118   }
119
120   /**
121    * Options (float) : test 'allowed values' input.
122    */
123   public function testOptionsAllowedValuesFloat() {
124     $this->fieldName = 'field_options_float';
125     $this->createOptionsField('list_float');
126
127     // Flat list of textual values.
128     $string = "Zero\nOne";
129     $array = ['0' => 'Zero', '1' => 'One'];
130     $this->assertAllowedValuesInput($string, $array, 'Unkeyed lists are accepted.');
131     // Explicit numeric keys.
132     $string = "0|Zero\n.5|Point five";
133     $array = ['0' => 'Zero', '0.5' => 'Point five'];
134     $this->assertAllowedValuesInput($string, $array, 'Integer keys are accepted.');
135     // Check that values can be added and removed.
136     $string = "0|Zero\n.5|Point five\n1.0|One";
137     $array = ['0' => 'Zero', '0.5' => 'Point five', '1' => 'One'];
138     $this->assertAllowedValuesInput($string, $array, 'Values can be added and removed.');
139     // Non-numeric keys.
140     $this->assertAllowedValuesInput("abc|abc\n", 'each key must be a valid integer or decimal', 'Non numeric keys are rejected.');
141     // Mixed list of keyed and unkeyed values.
142     $this->assertAllowedValuesInput("Zero\n1|One\n", 'invalid input', 'Mixed lists are rejected.');
143
144     // Create a node with actual data for the field.
145     $settings = [
146       'type' => $this->type,
147       $this->fieldName => [['value' => .5]],
148     ];
149     $node = $this->drupalCreateNode($settings);
150
151     // Check that a flat list of values is rejected once the field has data.
152     $this->assertAllowedValuesInput("Zero\nOne", 'invalid input', 'Unkeyed lists are rejected once the field has data.');
153
154     // Check that values can be added but values in use cannot be removed.
155     $string = "0|Zero\n.5|Point five\n2|Two";
156     $array = ['0' => 'Zero', '0.5' => 'Point five', '2' => 'Two'];
157     $this->assertAllowedValuesInput($string, $array, 'Values can be added.');
158     $string = "0|Zero\n.5|Point five";
159     $array = ['0' => 'Zero', '0.5' => 'Point five'];
160     $this->assertAllowedValuesInput($string, $array, 'Values not in use can be removed.');
161     $this->assertAllowedValuesInput("0|Zero", 'some values are being removed while currently in use', 'Values in use cannot be removed.');
162
163     // Delete the node, remove the value.
164     $node->delete();
165     $string = "0|Zero";
166     $array = ['0' => 'Zero'];
167     $this->assertAllowedValuesInput($string, $array, 'Values not in use can be removed.');
168
169     // Check that the same key can only be used once.
170     $string = "0.5|Point five\n0.5|Half";
171     $array = ['0.5' => 'Half'];
172     $this->assertAllowedValuesInput($string, $array, 'Same value cannot be used multiple times.');
173
174     // Check that different forms of the same float value cannot be used.
175     $string = "0|Zero\n.5|Point five\n0.5|Half";
176     $array = ['0' => 'Zero', '0.5' => 'Half'];
177     $this->assertAllowedValuesInput($string, $array, 'Different forms of the same value cannot be used.');
178   }
179
180   /**
181    * Options (text) : test 'allowed values' input.
182    */
183   public function testOptionsAllowedValuesText() {
184     $this->fieldName = 'field_options_text';
185     $this->createOptionsField('list_string');
186
187     // Flat list of textual values.
188     $string = "Zero\nOne";
189     $array = ['Zero' => 'Zero', 'One' => 'One'];
190     $this->assertAllowedValuesInput($string, $array, 'Unkeyed lists are accepted.');
191     // Explicit keys.
192     $string = "zero|Zero\none|One";
193     $array = ['zero' => 'Zero', 'one' => 'One'];
194     $this->assertAllowedValuesInput($string, $array, 'Explicit keys are accepted.');
195     // Check that values can be added and removed.
196     $string = "zero|Zero\ntwo|Two";
197     $array = ['zero' => 'Zero', 'two' => 'Two'];
198     $this->assertAllowedValuesInput($string, $array, 'Values can be added and removed.');
199     // Mixed list of keyed and unkeyed values.
200     $string = "zero|Zero\nOne\n";
201     $array = ['zero' => 'Zero', 'One' => 'One'];
202     $this->assertAllowedValuesInput($string, $array, 'Mixed lists are accepted.');
203     // Overly long keys.
204     $this->assertAllowedValuesInput("zero|Zero\n" . $this->randomMachineName(256) . "|One", 'each key must be a string at most 255 characters long', 'Overly long keys are rejected.');
205
206     // Create a node with actual data for the field.
207     $settings = [
208       'type' => $this->type,
209       $this->fieldName => [['value' => 'One']],
210     ];
211     $node = $this->drupalCreateNode($settings);
212
213     // Check that flat lists of values are still accepted once the field has
214     // data.
215     $string = "Zero\nOne";
216     $array = ['Zero' => 'Zero', 'One' => 'One'];
217     $this->assertAllowedValuesInput($string, $array, 'Unkeyed lists are still accepted once the field has data.');
218
219     // Check that values can be added but values in use cannot be removed.
220     $string = "Zero\nOne\nTwo";
221     $array = ['Zero' => 'Zero', 'One' => 'One', 'Two' => 'Two'];
222     $this->assertAllowedValuesInput($string, $array, 'Values can be added.');
223     $string = "Zero\nOne";
224     $array = ['Zero' => 'Zero', 'One' => 'One'];
225     $this->assertAllowedValuesInput($string, $array, 'Values not in use can be removed.');
226     $this->assertAllowedValuesInput("Zero", 'some values are being removed while currently in use', 'Values in use cannot be removed.');
227
228     // Delete the node, remove the value.
229     $node->delete();
230     $string = "Zero";
231     $array = ['Zero' => 'Zero'];
232     $this->assertAllowedValuesInput($string, $array, 'Values not in use can be removed.');
233
234     // Check that string values with dots can be used.
235     $string = "Zero\nexample.com|Example";
236     $array = ['Zero' => 'Zero', 'example.com' => 'Example'];
237     $this->assertAllowedValuesInput($string, $array, 'String value with dot is supported.');
238
239     // Check that the same key can only be used once.
240     $string = "zero|Zero\nzero|One";
241     $array = ['zero' => 'One'];
242     $this->assertAllowedValuesInput($string, $array, 'Same value cannot be used multiple times.');
243   }
244
245   /**
246    * Options (text) : test 'trimmed values' input.
247    */
248   public function testOptionsTrimmedValuesText() {
249     $this->fieldName = 'field_options_trimmed_text';
250     $this->createOptionsField('list_string');
251
252     // Explicit keys.
253     $string = "zero |Zero\none | One";
254     $array = ['zero' => 'Zero', 'one' => 'One'];
255     $this->assertAllowedValuesInput($string, $array, 'Explicit keys are accepted and trimmed.');
256   }
257
258   /**
259    * Helper function to create list field of a given type.
260    *
261    * @param string $type
262    *   One of 'list_integer', 'list_float' or 'list_string'.
263    */
264   protected function createOptionsField($type) {
265     // Create a field.
266     FieldStorageConfig::create([
267       'field_name' => $this->fieldName,
268       'entity_type' => 'node',
269       'type' => $type,
270     ])->save();
271     FieldConfig::create([
272       'field_name' => $this->fieldName,
273       'entity_type' => 'node',
274       'bundle' => $this->type,
275     ])->save();
276
277     entity_get_form_display('node', $this->type, 'default')->setComponent($this->fieldName)->save();
278
279     $this->adminPath = 'admin/structure/types/manage/' . $this->type . '/fields/node.' . $this->type . '.' . $this->fieldName . '/storage';
280   }
281
282   /**
283    * Tests a string input for the 'allowed values' form element.
284    *
285    * @param $input_string
286    *   The input string, in the pipe-linefeed format expected by the form
287    *   element.
288    * @param $result
289    *   Either an expected resulting array in
290    *   $field->getSetting('allowed_values'), or an expected error message.
291    * @param $message
292    *   Message to display.
293    */
294   public function assertAllowedValuesInput($input_string, $result, $message) {
295     $edit = ['settings[allowed_values]' => $input_string];
296     $this->drupalPostForm($this->adminPath, $edit, t('Save field settings'));
297     $this->assertNoRaw('&amp;lt;', 'The page does not have double escaped HTML tags.');
298
299     if (is_string($result)) {
300       $this->assertText($result, $message);
301     }
302     else {
303       $field_storage = FieldStorageConfig::loadByName('node', $this->fieldName);
304       $this->assertIdentical($field_storage->getSetting('allowed_values'), $result, $message);
305     }
306   }
307
308   /**
309    * Tests normal and key formatter display on node display.
310    */
311   public function testNodeDisplay() {
312     $this->fieldName = strtolower($this->randomMachineName());
313     $this->createOptionsField('list_integer');
314     $node = $this->drupalCreateNode(['type' => $this->type]);
315
316     $on = $this->randomMachineName();
317     $off = $this->randomMachineName();
318     $edit = [
319       'settings[allowed_values]' =>
320         "1|$on
321         0|$off",
322     ];
323
324     $this->drupalPostForm($this->adminPath, $edit, t('Save field settings'));
325     $this->assertText(format_string('Updated field @field_name field settings.', ['@field_name' => $this->fieldName]), "The 'On' and 'Off' form fields work for boolean fields.");
326
327     // Select a default value.
328     $edit = [
329       $this->fieldName => '1',
330     ];
331     $this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Save'));
332
333     // Check the node page and see if the values are correct.
334     $file_formatters = ['list_default', 'list_key'];
335     foreach ($file_formatters as $formatter) {
336       $edit = [
337         "fields[$this->fieldName][type]" => $formatter,
338         "fields[$this->fieldName][region]" => 'content',
339       ];
340       $this->drupalPostForm('admin/structure/types/manage/' . $this->typeName . '/display', $edit, t('Save'));
341       $this->drupalGet('node/' . $node->id());
342
343       if ($formatter == 'list_default') {
344         $output = $on;
345       }
346       else {
347         $output = '1';
348       }
349
350       $elements = $this->xpath('//div[text()="' . $output . '"]');
351       $this->assertEqual(count($elements), 1, 'Correct options found.');
352     }
353   }
354
355 }