64da6c1a00fb4272ec152c0841f1f6a4630996d3
[yaffs-website] / web / core / modules / field / src / Tests / Number / NumberFieldTest.php
1 <?php
2
3 namespace Drupal\field\Tests\Number;
4
5 use Drupal\Component\Utility\Unicode;
6 use Drupal\field\Entity\FieldConfig;
7 use Drupal\node\Entity\Node;
8 use Drupal\simpletest\WebTestBase;
9 use Drupal\field\Entity\FieldStorageConfig;
10
11 /**
12  * Tests the creation of numeric fields.
13  *
14  * @group field
15  */
16 class NumberFieldTest extends WebTestBase {
17
18   /**
19    * Modules to enable.
20    *
21    * @var array
22    */
23   public static $modules = ['node', 'entity_test', 'field_ui'];
24
25   protected function setUp() {
26     parent::setUp();
27     $this->drupalLogin($this->drupalCreateUser([
28       'view test entity',
29       'administer entity_test content',
30       'administer content types',
31       'administer node fields',
32       'administer node display',
33       'bypass node access',
34       'administer entity_test fields',
35     ]));
36   }
37
38   /**
39    * Test decimal field.
40    */
41   public function testNumberDecimalField() {
42     // Create a field with settings to validate.
43     $field_name = Unicode::strtolower($this->randomMachineName());
44     FieldStorageConfig::create([
45       'field_name' => $field_name,
46       'entity_type' => 'entity_test',
47       'type' => 'decimal',
48       'settings' => ['precision' => 8, 'scale' => 4],
49     ])->save();
50     FieldConfig::create([
51       'field_name' => $field_name,
52       'entity_type' => 'entity_test',
53       'bundle' => 'entity_test',
54     ])->save();
55
56     entity_get_form_display('entity_test', 'entity_test', 'default')
57       ->setComponent($field_name, [
58         'type' => 'number',
59         'settings' => [
60           'placeholder' => '0.00'
61         ],
62       ])
63       ->save();
64     entity_get_display('entity_test', 'entity_test', 'default')
65       ->setComponent($field_name, [
66         'type' => 'number_decimal',
67       ])
68       ->save();
69
70     // Display creation form.
71     $this->drupalGet('entity_test/add');
72     $this->assertFieldByName("{$field_name}[0][value]", '', 'Widget is displayed');
73     $this->assertRaw('placeholder="0.00"');
74
75     // Submit a signed decimal value within the allowed precision and scale.
76     $value = '-1234.5678';
77     $edit = [
78       "{$field_name}[0][value]" => $value,
79     ];
80     $this->drupalPostForm(NULL, $edit, t('Save'));
81     preg_match('|entity_test/manage/(\d+)|', $this->url, $match);
82     $id = $match[1];
83     $this->assertText(t('entity_test @id has been created.', ['@id' => $id]), 'Entity was created');
84     $this->assertRaw($value, 'Value is displayed.');
85
86     // Try to create entries with more than one decimal separator; assert fail.
87     $wrong_entries = [
88       '3.14.159',
89       '0..45469',
90       '..4589',
91       '6.459.52',
92       '6.3..25',
93     ];
94
95     foreach ($wrong_entries as $wrong_entry) {
96       $this->drupalGet('entity_test/add');
97       $edit = [
98         "{$field_name}[0][value]" => $wrong_entry,
99       ];
100       $this->drupalPostForm(NULL, $edit, t('Save'));
101       $this->assertRaw(t('%name must be a number.', ['%name' => $field_name]), 'Correctly failed to save decimal value with more than one decimal point.');
102     }
103
104     // Try to create entries with minus sign not in the first position.
105     $wrong_entries = [
106       '3-3',
107       '4-',
108       '1.3-',
109       '1.2-4',
110       '-10-10',
111     ];
112
113     foreach ($wrong_entries as $wrong_entry) {
114       $this->drupalGet('entity_test/add');
115       $edit = [
116         "{$field_name}[0][value]" => $wrong_entry,
117       ];
118       $this->drupalPostForm(NULL, $edit, t('Save'));
119       $this->assertRaw(t('%name must be a number.', ['%name' => $field_name]), 'Correctly failed to save decimal value with minus sign in the wrong position.');
120     }
121   }
122
123   /**
124    * Test integer field.
125    */
126   public function testNumberIntegerField() {
127     $minimum = rand(-4000, -2000);
128     $maximum = rand(2000, 4000);
129
130     // Create a field with settings to validate.
131     $field_name = Unicode::strtolower($this->randomMachineName());
132     $storage = FieldStorageConfig::create([
133       'field_name' => $field_name,
134       'entity_type' => 'entity_test',
135       'type' => 'integer',
136     ]);
137     $storage->save();
138
139     FieldConfig::create([
140       'field_name' => $field_name,
141       'entity_type' => 'entity_test',
142       'bundle' => 'entity_test',
143       'settings' => [
144         'min' => $minimum,
145         'max' => $maximum,
146         'prefix' => 'ThePrefix',
147       ],
148     ])->save();
149
150     entity_get_form_display('entity_test', 'entity_test', 'default')
151       ->setComponent($field_name, [
152         'type' => 'number',
153         'settings' => [
154           'placeholder' => '4'
155         ],
156       ])
157       ->save();
158     entity_get_display('entity_test', 'entity_test', 'default')
159       ->setComponent($field_name, [
160         'type' => 'number_integer',
161         'settings' => [
162           'prefix_suffix' => FALSE,
163         ],
164       ])
165       ->save();
166
167     // Check the storage schema.
168     $expected = [
169       'columns' => [
170         'value' => [
171           'type' => 'int',
172           'unsigned' => '',
173           'size' => 'normal'
174         ],
175       ],
176       'unique keys' => [],
177       'indexes' => [],
178       'foreign keys' => []
179     ];
180     $this->assertEqual($storage->getSchema(), $expected);
181
182     // Display creation form.
183     $this->drupalGet('entity_test/add');
184     $this->assertFieldByName("{$field_name}[0][value]", '', 'Widget is displayed');
185     $this->assertRaw('placeholder="4"');
186
187     // Submit a valid integer
188     $value = rand($minimum, $maximum);
189     $edit = [
190       "{$field_name}[0][value]" => $value,
191     ];
192     $this->drupalPostForm(NULL, $edit, t('Save'));
193     preg_match('|entity_test/manage/(\d+)|', $this->url, $match);
194     $id = $match[1];
195     $this->assertText(t('entity_test @id has been created.', ['@id' => $id]), 'Entity was created');
196
197     // Try to set a value below the minimum value
198     $this->drupalGet('entity_test/add');
199     $edit = [
200       "{$field_name}[0][value]" => $minimum - 1,
201     ];
202     $this->drupalPostForm(NULL, $edit, t('Save'));
203     $this->assertRaw(t('%name must be higher than or equal to %minimum.', ['%name' => $field_name, '%minimum' => $minimum]), 'Correctly failed to save integer value less than minimum allowed value.');
204
205     // Try to set a decimal value
206     $this->drupalGet('entity_test/add');
207     $edit = [
208       "{$field_name}[0][value]" => 1.5,
209     ];
210     $this->drupalPostForm(NULL, $edit, t('Save'));
211     $this->assertRaw(t('%name is not a valid number.', ['%name' => $field_name]), 'Correctly failed to save decimal value to integer field.');
212
213     // Try to set a value above the maximum value
214     $this->drupalGet('entity_test/add');
215     $edit = [
216       "{$field_name}[0][value]" => $maximum + 1,
217     ];
218     $this->drupalPostForm(NULL, $edit, t('Save'));
219     $this->assertRaw(t('%name must be lower than or equal to %maximum.', ['%name' => $field_name, '%maximum' => $maximum]), 'Correctly failed to save integer value greater than maximum allowed value.');
220
221     // Try to set a wrong integer value.
222     $this->drupalGet('entity_test/add');
223     $edit = [
224       "{$field_name}[0][value]" => '20-40',
225     ];
226     $this->drupalPostForm(NULL, $edit, t('Save'));
227     $this->assertRaw(t('%name must be a number.', ['%name' => $field_name]), 'Correctly failed to save wrong integer value.');
228
229     // Test with valid entries.
230     $valid_entries = [
231       '-1234',
232       '0',
233       '1234',
234     ];
235
236     foreach ($valid_entries as $valid_entry) {
237       $this->drupalGet('entity_test/add');
238       $edit = [
239         "{$field_name}[0][value]" => $valid_entry,
240       ];
241       $this->drupalPostForm(NULL, $edit, t('Save'));
242       preg_match('|entity_test/manage/(\d+)|', $this->url, $match);
243       $id = $match[1];
244       $this->assertText(t('entity_test @id has been created.', ['@id' => $id]), 'Entity was created');
245       $this->assertRaw($valid_entry, 'Value is displayed.');
246       $this->assertNoFieldByXpath('//div[@content="' . $valid_entry . '"]', NULL, 'The "content" attribute is not present since the Prefix is not being displayed');
247     }
248
249     // Test for the content attribute when a Prefix is displayed. Presumably this also tests for the attribute when a Suffix is displayed.
250     entity_get_display('entity_test', 'entity_test', 'default')
251       ->setComponent($field_name, [
252         'type' => 'number_integer',
253         'settings' => [
254           'prefix_suffix' => TRUE,
255         ],
256       ])
257       ->save();
258     $integer_value = '123';
259     $this->drupalGet('entity_test/add');
260     $edit = [
261       "{$field_name}[0][value]" => $integer_value,
262     ];
263     $this->drupalPostForm(NULL, $edit, t('Save'));
264     preg_match('|entity_test/manage/(\d+)|', $this->url, $match);
265     $id = $match[1];
266     $this->assertText(t('entity_test @id has been created.', ['@id' => $id]), 'Entity was created');
267     $this->drupalGet('entity_test/' . $id);
268     $this->assertFieldByXPath('//div[@content="' . $integer_value . '"]', 'ThePrefix' . $integer_value, 'The "content" attribute has been set to the value of the field, and the prefix is being displayed.');
269   }
270
271   /**
272   * Test float field.
273   */
274   public function testNumberFloatField() {
275     // Create a field with settings to validate.
276     $field_name = Unicode::strtolower($this->randomMachineName());
277     FieldStorageConfig::create([
278       'field_name' => $field_name,
279       'entity_type' => 'entity_test',
280       'type' => 'float',
281     ])->save();
282
283     FieldConfig::create([
284       'field_name' => $field_name,
285       'entity_type' => 'entity_test',
286       'bundle' => 'entity_test',
287     ])->save();
288
289     entity_get_form_display('entity_test', 'entity_test', 'default')
290       ->setComponent($field_name, [
291         'type' => 'number',
292         'settings' => [
293           'placeholder' => '0.00'
294         ],
295       ])
296       ->save();
297
298     entity_get_display('entity_test', 'entity_test', 'default')
299       ->setComponent($field_name, [
300         'type' => 'number_decimal',
301       ])
302       ->save();
303
304     // Display creation form.
305     $this->drupalGet('entity_test/add');
306     $this->assertFieldByName("{$field_name}[0][value]", '', 'Widget is displayed');
307     $this->assertRaw('placeholder="0.00"');
308
309     // Submit a signed decimal value within the allowed precision and scale.
310     $value = '-1234.5678';
311     $edit = [
312       "{$field_name}[0][value]" => $value,
313     ];
314     $this->drupalPostForm(NULL, $edit, t('Save'));
315     preg_match('|entity_test/manage/(\d+)|', $this->url, $match);
316     $id = $match[1];
317     $this->assertText(t('entity_test @id has been created.', ['@id' => $id]), 'Entity was created');
318
319     // Ensure that the 'number_decimal' formatter displays the number with the
320     // expected rounding.
321     $this->drupalGet('entity_test/' . $id);
322     $this->assertRaw(round($value, 2));
323
324     // Try to create entries with more than one decimal separator; assert fail.
325     $wrong_entries = [
326       '3.14.159',
327       '0..45469',
328       '..4589',
329       '6.459.52',
330       '6.3..25',
331     ];
332
333     foreach ($wrong_entries as $wrong_entry) {
334       $this->drupalGet('entity_test/add');
335       $edit = [
336         "{$field_name}[0][value]" => $wrong_entry,
337       ];
338       $this->drupalPostForm(NULL, $edit, t('Save'));
339       $this->assertRaw(t('%name must be a number.', ['%name' => $field_name]), 'Correctly failed to save float value with more than one decimal point.');
340     }
341
342     // Try to create entries with minus sign not in the first position.
343     $wrong_entries = [
344       '3-3',
345       '4-',
346       '1.3-',
347       '1.2-4',
348       '-10-10',
349     ];
350
351     foreach ($wrong_entries as $wrong_entry) {
352       $this->drupalGet('entity_test/add');
353       $edit = [
354         "{$field_name}[0][value]" => $wrong_entry,
355       ];
356       $this->drupalPostForm(NULL, $edit, t('Save'));
357       $this->assertRaw(t('%name must be a number.', ['%name' => $field_name]), 'Correctly failed to save float value with minus sign in the wrong position.');
358     }
359   }
360
361   /**
362    * Test default formatter behavior
363    */
364   public function testNumberFormatter() {
365     $type = Unicode::strtolower($this->randomMachineName());
366     $float_field = Unicode::strtolower($this->randomMachineName());
367     $integer_field = Unicode::strtolower($this->randomMachineName());
368     $thousand_separators = ['', '.', ',', ' ', chr(8201), "'"];
369     $decimal_separators = ['.', ','];
370     $prefix = $this->randomMachineName();
371     $suffix = $this->randomMachineName();
372     $random_float = rand(0, pow(10, 6));
373     $random_integer = rand(0, pow(10, 6));
374
375     // Create a content type containing float and integer fields.
376     $this->drupalCreateContentType(['type' => $type]);
377
378     FieldStorageConfig::create([
379       'field_name' => $float_field,
380       'entity_type' => 'node',
381       'type' => 'float',
382     ])->save();
383
384     FieldStorageConfig::create([
385       'field_name' => $integer_field,
386       'entity_type' => 'node',
387       'type' => 'integer',
388     ])->save();
389
390     FieldConfig::create([
391       'field_name' => $float_field,
392       'entity_type' => 'node',
393       'bundle' => $type,
394       'settings' => [
395         'prefix' => $prefix,
396         'suffix' => $suffix
397       ],
398     ])->save();
399
400     FieldConfig::create([
401       'field_name' => $integer_field,
402       'entity_type' => 'node',
403       'bundle' => $type,
404       'settings' => [
405         'prefix' => $prefix,
406         'suffix' => $suffix
407       ],
408     ])->save();
409
410     entity_get_form_display('node', $type, 'default')
411       ->setComponent($float_field, [
412         'type' => 'number',
413         'settings' => [
414           'placeholder' => '0.00'
415         ],
416       ])
417       ->setComponent($integer_field, [
418         'type' => 'number',
419         'settings' => [
420           'placeholder' => '0.00'
421         ],
422       ])
423       ->save();
424
425     entity_get_display('node', $type, 'default')
426       ->setComponent($float_field, [
427         'type' => 'number_decimal',
428       ])
429       ->setComponent($integer_field, [
430         'type' => 'number_unformatted',
431       ])
432       ->save();
433
434     // Create a node to test formatters.
435     $node = Node::create([
436       'type' => $type,
437       'title' => $this->randomMachineName(),
438       $float_field => ['value' => $random_float],
439       $integer_field => ['value' => $random_integer],
440     ]);
441     $node->save();
442
443     // Go to manage display page.
444     $this->drupalGet("admin/structure/types/manage/$type/display");
445
446     // Configure number_decimal formatter for the 'float' field type.
447     $thousand_separator = $thousand_separators[array_rand($thousand_separators)];
448     $decimal_separator = $decimal_separators[array_rand($decimal_separators)];
449     $scale = rand(0, 10);
450
451     $this->drupalPostAjaxForm(NULL, [], "${float_field}_settings_edit");
452     $edit = [
453       "fields[${float_field}][settings_edit_form][settings][prefix_suffix]" => TRUE,
454       "fields[${float_field}][settings_edit_form][settings][scale]" => $scale,
455       "fields[${float_field}][settings_edit_form][settings][decimal_separator]" => $decimal_separator,
456       "fields[${float_field}][settings_edit_form][settings][thousand_separator]" => $thousand_separator,
457     ];
458     $this->drupalPostAjaxForm(NULL, $edit, "${float_field}_plugin_settings_update");
459     $this->drupalPostForm(NULL, [], t('Save'));
460
461     // Check number_decimal and number_unformatted formatters behavior.
462     $this->drupalGet('node/' . $node->id());
463     $float_formatted = number_format($random_float, $scale, $decimal_separator, $thousand_separator);
464     $this->assertRaw("$prefix$float_formatted$suffix", 'Prefix and suffix added');
465     $this->assertRaw((string) $random_integer);
466
467     // Configure the number_decimal formatter.
468     entity_get_display('node', $type, 'default')
469       ->setComponent($integer_field, [
470         'type' => 'number_integer',
471       ])
472       ->save();
473     $this->drupalGet("admin/structure/types/manage/$type/display");
474
475     $thousand_separator = $thousand_separators[array_rand($thousand_separators)];
476
477     $this->drupalPostAjaxForm(NULL, [], "${integer_field}_settings_edit");
478     $edit = [
479       "fields[${integer_field}][settings_edit_form][settings][prefix_suffix]" => FALSE,
480       "fields[${integer_field}][settings_edit_form][settings][thousand_separator]" => $thousand_separator,
481     ];
482     $this->drupalPostAjaxForm(NULL, $edit, "${integer_field}_plugin_settings_update");
483     $this->drupalPostForm(NULL, [], t('Save'));
484
485     // Check number_integer formatter behavior.
486     $this->drupalGet('node/' . $node->id());
487
488     $integer_formatted = number_format($random_integer, 0, '', $thousand_separator);
489     $this->assertRaw($integer_formatted, 'Random integer formatted');
490   }
491
492   /**
493    * Tests setting the minimum value of a float field through the interface.
494    */
495   public function testCreateNumberFloatField() {
496     // Create a float field.
497     $field_name = Unicode::strtolower($this->randomMachineName());
498     FieldStorageConfig::create([
499       'field_name' => $field_name,
500       'entity_type' => 'entity_test',
501       'type' => 'float',
502     ])->save();
503
504     $field = FieldConfig::create([
505       'field_name' => $field_name,
506       'entity_type' => 'entity_test',
507       'bundle' => 'entity_test',
508     ]);
509     $field->save();
510
511     // Set the minimum value to a float value.
512     $this->assertSetMinimumValue($field, 0.0001);
513     // Set the minimum value to an integer value.
514     $this->assertSetMinimumValue($field, 1);
515   }
516
517   /**
518    * Tests setting the minimum value of a decimal field through the interface.
519    */
520   public function testCreateNumberDecimalField() {
521     // Create a decimal field.
522     $field_name = Unicode::strtolower($this->randomMachineName());
523     FieldStorageConfig::create([
524       'field_name' => $field_name,
525       'entity_type' => 'entity_test',
526       'type' => 'decimal',
527     ])->save();
528
529     $field = FieldConfig::create([
530       'field_name' => $field_name,
531       'entity_type' => 'entity_test',
532       'bundle' => 'entity_test',
533     ]);
534     $field->save();
535
536     // Set the minimum value to a decimal value.
537     $this->assertSetMinimumValue($field, 0.1);
538     // Set the minimum value to an integer value.
539     $this->assertSetMinimumValue($field, 1);
540   }
541
542   /**
543    * Helper function to set the minimum value of a field.
544    */
545   public function assertSetMinimumValue($field, $minimum_value) {
546     $field_configuration_url = 'entity_test/structure/entity_test/fields/entity_test.entity_test.' . $field->getName();
547
548     // Set the minimum value.
549     $edit = [
550       'settings[min]' => $minimum_value,
551     ];
552     $this->drupalPostForm($field_configuration_url, $edit, t('Save settings'));
553     // Check if an error message is shown.
554     $this->assertNoRaw(t('%name is not a valid number.', ['%name' => t('Minimum')]), 'Saved ' . gettype($minimum_value) . '  value as minimal value on a ' . $field->getType() . ' field');
555     // Check if a success message is shown.
556     $this->assertRaw(t('Saved %label configuration.', ['%label' => $field->getLabel()]));
557     // Check if the minimum value was actually set.
558     $this->drupalGet($field_configuration_url);
559     $this->assertFieldById('edit-settings-min', $minimum_value, 'Minimal ' . gettype($minimum_value) . '  value was set on a ' . $field->getType() . ' field.');
560   }
561
562 }