ebeb4b8f2bfd191198fa3bca9c2211dd2cb4ed35
[yaffs-website] / web / core / modules / datetime_range / tests / src / Functional / DateRangeFieldTest.php
1 <?php
2
3 namespace Drupal\Tests\datetime_range\Functional;
4
5 use Drupal\Component\Render\FormattableMarkup;
6 use Drupal\Component\Utility\Unicode;
7 use Drupal\Core\Datetime\DrupalDateTime;
8 use Drupal\Core\Datetime\Entity\DateFormat;
9 use Drupal\Tests\datetime\Functional\DateTestBase;
10 use Drupal\datetime_range\Plugin\Field\FieldType\DateRangeItem;
11 use Drupal\entity_test\Entity\EntityTest;
12 use Drupal\field\Entity\FieldConfig;
13 use Drupal\field\Entity\FieldStorageConfig;
14 use Drupal\node\Entity\Node;
15
16 /**
17  * Tests Daterange field functionality.
18  *
19  * @group datetime
20  */
21 class DateRangeFieldTest extends DateTestBase {
22
23   /**
24    * Modules to enable.
25    *
26    * @var array
27    */
28   public static $modules = ['datetime_range'];
29
30   /**
31    * The default display settings to use for the formatters.
32    *
33    * @var array
34    */
35   protected $defaultSettings = ['timezone_override' => '', 'separator' => '-'];
36
37   /**
38    * {@inheritdoc}
39    */
40   protected function getTestFieldType() {
41     return 'daterange';
42   }
43
44   /**
45    * Tests date field functionality.
46    */
47   public function testDateRangeField() {
48     $field_name = $this->fieldStorage->getName();
49
50     // Loop through defined timezones to test that date-only fields work at the
51     // extremes.
52     foreach (static::$timezones as $timezone) {
53
54       $this->setSiteTimezone($timezone);
55
56       // Ensure field is set to a date-only field.
57       $this->fieldStorage->setSetting('datetime_type', DateRangeItem::DATETIME_TYPE_DATE);
58       $this->fieldStorage->save();
59
60       // Display creation form.
61       $this->drupalGet('entity_test/add');
62       $this->assertFieldByName("{$field_name}[0][value][date]", '', 'Start date element found.');
63       $this->assertFieldByName("{$field_name}[0][end_value][date]", '', 'End date element found.');
64       $this->assertFieldByXPath('//*[@id="edit-' . $field_name . '-wrapper"]//label[contains(@class, "js-form-required")]', TRUE, 'Required markup found');
65       $this->assertNoFieldByName("{$field_name}[0][value][time]", '', 'Start time element not found.');
66       $this->assertNoFieldByName("{$field_name}[0][end_value][time]", '', 'End time element not found.');
67       $this->assertFieldByXPath('//fieldset[@id="edit-' . $field_name . '-0"]/legend', $field_name, 'Fieldset and label found');
68       $this->assertFieldByXPath('//fieldset[@aria-describedby="edit-' . $field_name . '-0--description"]', NULL, 'ARIA described-by found');
69       $this->assertFieldByXPath('//div[@id="edit-' . $field_name . '-0--description"]', NULL, 'ARIA description found');
70
71       // Build up dates in the UTC timezone.
72       $value = '2012-12-31 00:00:00';
73       $start_date = new DrupalDateTime($value, 'UTC');
74       $end_value = '2013-06-06 00:00:00';
75       $end_date = new DrupalDateTime($end_value, 'UTC');
76
77       // Submit a valid date and ensure it is accepted.
78       $date_format = DateFormat::load('html_date')->getPattern();
79       $time_format = DateFormat::load('html_time')->getPattern();
80
81       $edit = [
82         "{$field_name}[0][value][date]" => $start_date->format($date_format),
83         "{$field_name}[0][end_value][date]" => $end_date->format($date_format),
84       ];
85       $this->drupalPostForm(NULL, $edit, t('Save'));
86       preg_match('|entity_test/manage/(\d+)|', $this->getUrl(), $match);
87       $id = $match[1];
88       $this->assertText(t('entity_test @id has been created.', ['@id' => $id]));
89       $this->assertRaw($start_date->format($date_format));
90       $this->assertNoRaw($start_date->format($time_format));
91       $this->assertRaw($end_date->format($date_format));
92       $this->assertNoRaw($end_date->format($time_format));
93
94       // Verify the date doesn't change when entity is edited through the form.
95       $entity = EntityTest::load($id);
96       $this->assertEqual('2012-12-31', $entity->{$field_name}->value);
97       $this->assertEqual('2013-06-06', $entity->{$field_name}->end_value);
98       $this->drupalGet('entity_test/manage/' . $id . '/edit');
99       $this->drupalPostForm(NULL, [], t('Save'));
100       $this->drupalGet('entity_test/manage/' . $id . '/edit');
101       $this->drupalPostForm(NULL, [], t('Save'));
102       $this->drupalGet('entity_test/manage/' . $id . '/edit');
103       $this->drupalPostForm(NULL, [], t('Save'));
104       $entity = EntityTest::load($id);
105       $this->assertEqual('2012-12-31', $entity->{$field_name}->value);
106       $this->assertEqual('2013-06-06', $entity->{$field_name}->end_value);
107
108       // Formats that display a time component for date-only fields will display
109       // the default time, so that is applied before calculating the expected
110       // value.
111       datetime_date_default_time($start_date);
112       datetime_date_default_time($end_date);
113
114       // Reset display options since these get changed below.
115       $this->displayOptions = [
116         'type' => 'daterange_default',
117         'label' => 'hidden',
118         'settings' => [
119           'format_type' => 'long',
120           'separator' => 'THESEPARATOR',
121         ] + $this->defaultSettings,
122       ];
123
124       // Verify that the default formatter works.
125       entity_get_display($this->field->getTargetEntityTypeId(), $this->field->getTargetBundle(), 'full')
126         ->setComponent($field_name, $this->displayOptions)
127         ->save();
128
129       $start_expected = $this->dateFormatter->format($start_date->getTimestamp(), 'long', '', DATETIME_STORAGE_TIMEZONE);
130       $start_expected_iso = $this->dateFormatter->format($start_date->getTimestamp(), 'custom', 'Y-m-d\TH:i:s\Z', DATETIME_STORAGE_TIMEZONE);
131       $start_expected_markup = '<time datetime="' . $start_expected_iso . '" class="datetime">' . $start_expected . '</time>';
132       $end_expected = $this->dateFormatter->format($end_date->getTimestamp(), 'long', '', DATETIME_STORAGE_TIMEZONE);
133       $end_expected_iso = $this->dateFormatter->format($end_date->getTimestamp(), 'custom', 'Y-m-d\TH:i:s\Z', DATETIME_STORAGE_TIMEZONE);
134       $end_expected_markup = '<time datetime="' . $end_expected_iso . '" class="datetime">' . $end_expected . '</time>';
135       $output = $this->renderTestEntity($id);
136       $this->assertContains($start_expected_markup, $output, new FormattableMarkup('Formatted date field using %value format displayed as %expected with %expected_iso attribute.', [
137         '%value' => 'long',
138         '%expected' => $start_expected,
139         '%expected_iso' => $start_expected_iso,
140       ]));
141       $this->assertContains($end_expected_markup, $output, new FormattableMarkup('Formatted date field using %value format displayed as %expected with %expected_iso attribute.', [
142         '%value' => 'long',
143         '%expected' => $end_expected,
144         '%expected_iso' => $end_expected_iso,
145       ]));
146       $this->assertContains(' THESEPARATOR ', $output, 'Found proper separator');
147
148       // Verify that hook_entity_prepare_view can add attributes.
149       // @see entity_test_entity_prepare_view()
150       $this->drupalGet('entity_test/' . $id);
151       $this->assertFieldByXPath('//div[@data-field-item-attr="foobar"]');
152
153       // Verify that the plain formatter works.
154       $this->displayOptions['type'] = 'daterange_plain';
155       $this->displayOptions['settings'] = $this->defaultSettings;
156       entity_get_display($this->field->getTargetEntityTypeId(), $this->field->getTargetBundle(), 'full')
157         ->setComponent($field_name, $this->displayOptions)
158         ->save();
159       $expected = $start_date->format(DATETIME_DATE_STORAGE_FORMAT) . ' - ' . $end_date->format(DATETIME_DATE_STORAGE_FORMAT);
160       $output = $this->renderTestEntity($id);
161       $this->assertContains($expected, $output, new FormattableMarkup('Formatted date field using plain format displayed as %expected.', ['%expected' => $expected]));
162
163       // Verify that the custom formatter works.
164       $this->displayOptions['type'] = 'daterange_custom';
165       $this->displayOptions['settings'] = ['date_format' => 'm/d/Y'] + $this->defaultSettings;
166       entity_get_display($this->field->getTargetEntityTypeId(), $this->field->getTargetBundle(), 'full')
167         ->setComponent($field_name, $this->displayOptions)
168         ->save();
169       $expected = $start_date->format($this->displayOptions['settings']['date_format']) . ' - ' . $end_date->format($this->displayOptions['settings']['date_format']);
170       $output = $this->renderTestEntity($id);
171       $this->assertContains($expected, $output, new FormattableMarkup('Formatted date field using daterange_custom format displayed as %expected.', ['%expected' => $expected]));
172
173       // Test formatters when start date and end date are the same
174       $this->drupalGet('entity_test/add');
175       $value = '2012-12-31 00:00:00';
176       $start_date = new DrupalDateTime($value, 'UTC');
177
178       $date_format = DateFormat::load('html_date')->getPattern();
179       $time_format = DateFormat::load('html_time')->getPattern();
180
181       $edit = [
182         "{$field_name}[0][value][date]" => $start_date->format($date_format),
183         "{$field_name}[0][end_value][date]" => $start_date->format($date_format),
184       ];
185
186       $this->drupalPostForm(NULL, $edit, t('Save'));
187       preg_match('|entity_test/manage/(\d+)|', $this->getUrl(), $match);
188       $id = $match[1];
189       $this->assertText(t('entity_test @id has been created.', ['@id' => $id]));
190
191       datetime_date_default_time($start_date);
192
193       $this->displayOptions = [
194         'type' => 'daterange_default',
195         'label' => 'hidden',
196         'settings' => [
197             'format_type' => 'long',
198             'separator' => 'THESEPARATOR',
199           ] + $this->defaultSettings,
200       ];
201
202       entity_get_display($this->field->getTargetEntityTypeId(), $this->field->getTargetBundle(), 'full')
203         ->setComponent($field_name, $this->displayOptions)
204         ->save();
205
206       $start_expected = $this->dateFormatter->format($start_date->getTimestamp(), 'long', '', DATETIME_STORAGE_TIMEZONE);
207       $start_expected_iso = $this->dateFormatter->format($start_date->getTimestamp(), 'custom', 'Y-m-d\TH:i:s\Z', DATETIME_STORAGE_TIMEZONE);
208       $start_expected_markup = '<time datetime="' . $start_expected_iso . '" class="datetime">' . $start_expected . '</time>';
209       $output = $this->renderTestEntity($id);
210       $this->assertContains($start_expected_markup, $output, new FormattableMarkup('Formatted date field using %value format displayed as %expected with %expected_iso attribute.', [
211         '%value' => 'long',
212         '%expected' => $start_expected,
213         '%expected_iso' => $start_expected_iso,
214       ]));
215       $this->assertNotContains(' THESEPARATOR ', $output, 'Separator not found on page');
216
217       // Verify that hook_entity_prepare_view can add attributes.
218       // @see entity_test_entity_prepare_view()
219       $this->drupalGet('entity_test/' . $id);
220       $this->assertFieldByXPath('//time[@data-field-item-attr="foobar"]');
221
222       $this->displayOptions['type'] = 'daterange_plain';
223       $this->displayOptions['settings'] = $this->defaultSettings;
224       entity_get_display($this->field->getTargetEntityTypeId(), $this->field->getTargetBundle(), 'full')
225         ->setComponent($field_name, $this->displayOptions)
226         ->save();
227       $expected = $start_date->format(DATETIME_DATE_STORAGE_FORMAT);
228       $output = $this->renderTestEntity($id);
229       $this->assertContains($expected, $output, new FormattableMarkup('Formatted date field using plain format displayed as %expected.', ['%expected' => $expected]));
230       $this->assertNotContains(' THESEPARATOR ', $output, 'Separator not found on page');
231
232       $this->displayOptions['type'] = 'daterange_custom';
233       $this->displayOptions['settings'] = ['date_format' => 'm/d/Y'] + $this->defaultSettings;
234       entity_get_display($this->field->getTargetEntityTypeId(), $this->field->getTargetBundle(), 'full')
235         ->setComponent($field_name, $this->displayOptions)
236         ->save();
237       $expected = $start_date->format($this->displayOptions['settings']['date_format']);
238       $output = $this->renderTestEntity($id);
239       $this->assertContains($expected, $output, new FormattableMarkup('Formatted date field using daterange_custom format displayed as %expected.', ['%expected' => $expected]));
240       $this->assertNotContains(' THESEPARATOR ', $output, 'Separator not found on page');
241     }
242   }
243
244   /**
245    * Tests date and time field.
246    */
247   public function testDatetimeRangeField() {
248     $field_name = $this->fieldStorage->getName();
249
250     // Ensure the field to a datetime field.
251     $this->fieldStorage->setSetting('datetime_type', DateRangeItem::DATETIME_TYPE_DATETIME);
252     $this->fieldStorage->save();
253
254     // Display creation form.
255     $this->drupalGet('entity_test/add');
256     $this->assertFieldByName("{$field_name}[0][value][date]", '', 'Start date element found.');
257     $this->assertFieldByName("{$field_name}[0][value][time]", '', 'Start time element found.');
258     $this->assertFieldByName("{$field_name}[0][end_value][date]", '', 'End date element found.');
259     $this->assertFieldByName("{$field_name}[0][end_value][time]", '', 'End time element found.');
260     $this->assertFieldByXPath('//fieldset[@id="edit-' . $field_name . '-0"]/legend', $field_name, 'Fieldset and label found');
261     $this->assertFieldByXPath('//fieldset[@aria-describedby="edit-' . $field_name . '-0--description"]', NULL, 'ARIA described-by found');
262     $this->assertFieldByXPath('//div[@id="edit-' . $field_name . '-0--description"]', NULL, 'ARIA description found');
263
264     // Build up dates in the UTC timezone.
265     $value = '2012-12-31 00:00:00';
266     $start_date = new DrupalDateTime($value, 'UTC');
267     $end_value = '2013-06-06 00:00:00';
268     $end_date = new DrupalDateTime($end_value, 'UTC');
269
270     // Update the timezone to the system default.
271     $start_date->setTimezone(timezone_open(drupal_get_user_timezone()));
272     $end_date->setTimezone(timezone_open(drupal_get_user_timezone()));
273
274     // Submit a valid date and ensure it is accepted.
275     $date_format = DateFormat::load('html_date')->getPattern();
276     $time_format = DateFormat::load('html_time')->getPattern();
277
278     $edit = [
279       "{$field_name}[0][value][date]" => $start_date->format($date_format),
280       "{$field_name}[0][value][time]" => $start_date->format($time_format),
281       "{$field_name}[0][end_value][date]" => $end_date->format($date_format),
282       "{$field_name}[0][end_value][time]" => $end_date->format($time_format),
283     ];
284     $this->drupalPostForm(NULL, $edit, t('Save'));
285     preg_match('|entity_test/manage/(\d+)|', $this->getUrl(), $match);
286     $id = $match[1];
287     $this->assertText(t('entity_test @id has been created.', ['@id' => $id]));
288     $this->assertRaw($start_date->format($date_format));
289     $this->assertRaw($start_date->format($time_format));
290     $this->assertRaw($end_date->format($date_format));
291     $this->assertRaw($end_date->format($time_format));
292
293     // Verify that the default formatter works.
294     $this->displayOptions['settings'] = [
295       'format_type' => 'long',
296       'separator' => 'THESEPARATOR',
297     ] + $this->defaultSettings;
298     entity_get_display($this->field->getTargetEntityTypeId(), $this->field->getTargetBundle(), 'full')
299       ->setComponent($field_name, $this->displayOptions)
300       ->save();
301
302     $start_expected = $this->dateFormatter->format($start_date->getTimestamp(), 'long');
303     $start_expected_iso = $this->dateFormatter->format($start_date->getTimestamp(), 'custom', 'Y-m-d\TH:i:s\Z', 'UTC');
304     $start_expected_markup = '<time datetime="' . $start_expected_iso . '" class="datetime">' . $start_expected . '</time>';
305     $end_expected = $this->dateFormatter->format($end_date->getTimestamp(), 'long');
306     $end_expected_iso = $this->dateFormatter->format($end_date->getTimestamp(), 'custom', 'Y-m-d\TH:i:s\Z', 'UTC');
307     $end_expected_markup = '<time datetime="' . $end_expected_iso . '" class="datetime">' . $end_expected . '</time>';
308     $output = $this->renderTestEntity($id);
309     $this->assertContains($start_expected_markup, $output, new FormattableMarkup('Formatted date field using %value format displayed as %expected with %expected_iso attribute.', ['%value' => 'long', '%expected' => $start_expected, '%expected_iso' => $start_expected_iso]));
310     $this->assertContains($end_expected_markup, $output, new FormattableMarkup('Formatted date field using %value format displayed as %expected with %expected_iso attribute.', ['%value' => 'long', '%expected' => $end_expected, '%expected_iso' => $end_expected_iso]));
311     $this->assertContains(' THESEPARATOR ', $output, 'Found proper separator');
312
313     // Verify that hook_entity_prepare_view can add attributes.
314     // @see entity_test_entity_prepare_view()
315     $this->drupalGet('entity_test/' . $id);
316     $this->assertFieldByXPath('//div[@data-field-item-attr="foobar"]');
317
318     // Verify that the plain formatter works.
319     $this->displayOptions['type'] = 'daterange_plain';
320     $this->displayOptions['settings'] = $this->defaultSettings;
321     entity_get_display($this->field->getTargetEntityTypeId(), $this->field->getTargetBundle(), 'full')
322       ->setComponent($field_name, $this->displayOptions)
323       ->save();
324     $expected = $start_date->format(DATETIME_DATETIME_STORAGE_FORMAT) . ' - ' . $end_date->format(DATETIME_DATETIME_STORAGE_FORMAT);
325     $output = $this->renderTestEntity($id);
326     $this->assertContains($expected, $output, new FormattableMarkup('Formatted date field using plain format displayed as %expected.', ['%expected' => $expected]));
327
328     // Verify that the 'datetime_custom' formatter works.
329     $this->displayOptions['type'] = 'daterange_custom';
330     $this->displayOptions['settings'] = ['date_format' => 'm/d/Y g:i:s A'] + $this->defaultSettings;
331     entity_get_display($this->field->getTargetEntityTypeId(), $this->field->getTargetBundle(), 'full')
332       ->setComponent($field_name, $this->displayOptions)
333       ->save();
334     $expected = $start_date->format($this->displayOptions['settings']['date_format']) . ' - ' . $end_date->format($this->displayOptions['settings']['date_format']);
335     $output = $this->renderTestEntity($id);
336     $this->assertContains($expected, $output, new FormattableMarkup('Formatted date field using daterange_custom format displayed as %expected.', ['%expected' => $expected]));
337
338     // Verify that the 'timezone_override' setting works.
339     $this->displayOptions['type'] = 'daterange_custom';
340     $this->displayOptions['settings'] = ['date_format' => 'm/d/Y g:i:s A', 'timezone_override' => 'America/New_York'] + $this->defaultSettings;
341     entity_get_display($this->field->getTargetEntityTypeId(), $this->field->getTargetBundle(), 'full')
342       ->setComponent($field_name, $this->displayOptions)
343       ->save();
344     $expected = $start_date->format($this->displayOptions['settings']['date_format'], ['timezone' => 'America/New_York']);
345     $expected .= ' - ' . $end_date->format($this->displayOptions['settings']['date_format'], ['timezone' => 'America/New_York']);
346     $output = $this->renderTestEntity($id);
347     $this->assertContains($expected, $output, new FormattableMarkup('Formatted date field using daterange_custom format displayed as %expected.', ['%expected' => $expected]));
348
349     // Test formatters when start date and end date are the same
350     $this->drupalGet('entity_test/add');
351     $value = '2012-12-31 00:00:00';
352     $start_date = new DrupalDateTime($value, 'UTC');
353     $start_date->setTimezone(timezone_open(drupal_get_user_timezone()));
354
355     $date_format = DateFormat::load('html_date')->getPattern();
356     $time_format = DateFormat::load('html_time')->getPattern();
357
358     $edit = [
359       "{$field_name}[0][value][date]" => $start_date->format($date_format),
360       "{$field_name}[0][value][time]" => $start_date->format($time_format),
361       "{$field_name}[0][end_value][date]" => $start_date->format($date_format),
362       "{$field_name}[0][end_value][time]" => $start_date->format($time_format),
363     ];
364
365     $this->drupalPostForm(NULL, $edit, t('Save'));
366     preg_match('|entity_test/manage/(\d+)|', $this->getUrl(), $match);
367     $id = $match[1];
368     $this->assertText(t('entity_test @id has been created.', ['@id' => $id]));
369
370     $this->displayOptions = [
371       'type' => 'daterange_default',
372       'label' => 'hidden',
373       'settings' => [
374         'format_type' => 'long',
375         'separator' => 'THESEPARATOR',
376       ] + $this->defaultSettings,
377     ];
378
379     entity_get_display($this->field->getTargetEntityTypeId(), $this->field->getTargetBundle(), 'full')
380       ->setComponent($field_name, $this->displayOptions)
381       ->save();
382
383     $start_expected = $this->dateFormatter->format($start_date->getTimestamp(), 'long');
384     $start_expected_iso = $this->dateFormatter->format($start_date->getTimestamp(), 'custom', 'Y-m-d\TH:i:s\Z', 'UTC');
385     $start_expected_markup = '<time datetime="' . $start_expected_iso . '" class="datetime">' . $start_expected . '</time>';
386     $output = $this->renderTestEntity($id);
387     $this->assertContains($start_expected_markup, $output, new FormattableMarkup('Formatted date field using %value format displayed as %expected with %expected_iso attribute.', ['%value' => 'long', '%expected' => $start_expected, '%expected_iso' => $start_expected_iso]));
388     $this->assertNotContains(' THESEPARATOR ', $output, 'Separator not found on page');
389
390     // Verify that hook_entity_prepare_view can add attributes.
391     // @see entity_test_entity_prepare_view()
392     $this->drupalGet('entity_test/' . $id);
393     $this->assertFieldByXPath('//time[@data-field-item-attr="foobar"]');
394
395     $this->displayOptions['type'] = 'daterange_plain';
396     $this->displayOptions['settings'] = $this->defaultSettings;
397     entity_get_display($this->field->getTargetEntityTypeId(), $this->field->getTargetBundle(), 'full')
398       ->setComponent($field_name, $this->displayOptions)
399       ->save();
400     $expected = $start_date->format(DATETIME_DATETIME_STORAGE_FORMAT);
401     $output = $this->renderTestEntity($id);
402     $this->assertContains($expected, $output, new FormattableMarkup('Formatted date field using plain format displayed as %expected.', ['%expected' => $expected]));
403     $this->assertNotContains(' THESEPARATOR ', $output, 'Separator not found on page');
404
405     $this->displayOptions['type'] = 'daterange_custom';
406     $this->displayOptions['settings'] = ['date_format' => 'm/d/Y g:i:s A'] + $this->defaultSettings;
407     entity_get_display($this->field->getTargetEntityTypeId(), $this->field->getTargetBundle(), 'full')
408       ->setComponent($field_name, $this->displayOptions)
409       ->save();
410     $expected = $start_date->format($this->displayOptions['settings']['date_format']);
411     $output = $this->renderTestEntity($id);
412     $this->assertContains($expected, $output, new FormattableMarkup('Formatted date field using daterange_custom format displayed as %expected.', ['%expected' => $expected]));
413     $this->assertNotContains(' THESEPARATOR ', $output, 'Separator not found on page');
414   }
415
416   /**
417    * Tests all-day field.
418    */
419   public function testAlldayRangeField() {
420     $field_name = $this->fieldStorage->getName();
421
422     // Ensure field is set to a all-day field.
423     $this->fieldStorage->setSetting('datetime_type', DateRangeItem::DATETIME_TYPE_ALLDAY);
424     $this->fieldStorage->save();
425
426     // Display creation form.
427     $this->drupalGet('entity_test/add');
428     $this->assertFieldByName("{$field_name}[0][value][date]", '', 'Start date element found.');
429     $this->assertFieldByName("{$field_name}[0][end_value][date]", '', 'End date element found.');
430     $this->assertFieldByXPath('//*[@id="edit-' . $field_name . '-wrapper"]//label[contains(@class, "js-form-required")]', TRUE, 'Required markup found');
431     $this->assertNoFieldByName("{$field_name}[0][value][time]", '', 'Start time element not found.');
432     $this->assertNoFieldByName("{$field_name}[0][end_value][time]", '', 'End time element not found.');
433     $this->assertFieldByXPath('//fieldset[@id="edit-' . $field_name . '-0"]/legend', $field_name, 'Fieldset and label found');
434     $this->assertFieldByXPath('//fieldset[@aria-describedby="edit-' . $field_name . '-0--description"]', NULL, 'ARIA described-by found');
435     $this->assertFieldByXPath('//div[@id="edit-' . $field_name . '-0--description"]', NULL, 'ARIA description found');
436
437     // Build up dates in the proper timezone.
438     $value = '2012-12-31 00:00:00';
439     $start_date = new DrupalDateTime($value, timezone_open(drupal_get_user_timezone()));
440     $end_value = '2013-06-06 23:59:59';
441     $end_date = new DrupalDateTime($end_value, timezone_open(drupal_get_user_timezone()));
442
443     // Submit a valid date and ensure it is accepted.
444     $date_format = DateFormat::load('html_date')->getPattern();
445     $time_format = DateFormat::load('html_time')->getPattern();
446
447     $edit = [
448       "{$field_name}[0][value][date]" => $start_date->format($date_format),
449       "{$field_name}[0][end_value][date]" => $end_date->format($date_format),
450     ];
451     $this->drupalPostForm(NULL, $edit, t('Save'));
452     preg_match('|entity_test/manage/(\d+)|', $this->getUrl(), $match);
453     $id = $match[1];
454     $this->assertText(t('entity_test @id has been created.', ['@id' => $id]));
455     $this->assertRaw($start_date->format($date_format));
456     $this->assertNoRaw($start_date->format($time_format));
457     $this->assertRaw($end_date->format($date_format));
458     $this->assertNoRaw($end_date->format($time_format));
459
460     // Verify that the default formatter works.
461     $this->displayOptions['settings'] = [
462       'format_type' => 'long',
463       'separator' => 'THESEPARATOR',
464     ] + $this->defaultSettings;
465     entity_get_display($this->field->getTargetEntityTypeId(), $this->field->getTargetBundle(), 'full')
466       ->setComponent($field_name, $this->displayOptions)
467       ->save();
468
469     $start_expected = $this->dateFormatter->format($start_date->getTimestamp(), 'long');
470     $start_expected_iso = $this->dateFormatter->format($start_date->getTimestamp(), 'custom', 'Y-m-d\TH:i:s\Z', 'UTC');
471     $start_expected_markup = '<time datetime="' . $start_expected_iso . '" class="datetime">' . $start_expected . '</time>';
472     $end_expected = $this->dateFormatter->format($end_date->getTimestamp(), 'long');
473     $end_expected_iso = $this->dateFormatter->format($end_date->getTimestamp(), 'custom', 'Y-m-d\TH:i:s\Z', 'UTC');
474     $end_expected_markup = '<time datetime="' . $end_expected_iso . '" class="datetime">' . $end_expected . '</time>';
475     $output = $this->renderTestEntity($id);
476     $this->assertContains($start_expected_markup, $output, new FormattableMarkup('Formatted date field using %value format displayed as %expected with %expected_iso attribute.', ['%value' => 'long', '%expected' => $start_expected, '%expected_iso' => $start_expected_iso]));
477     $this->assertContains($end_expected_markup, $output, new FormattableMarkup('Formatted date field using %value format displayed as %expected with %expected_iso attribute.', ['%value' => 'long', '%expected' => $end_expected, '%expected_iso' => $end_expected_iso]));
478     $this->assertContains(' THESEPARATOR ', $output, 'Found proper separator');
479
480     // Verify that hook_entity_prepare_view can add attributes.
481     // @see entity_test_entity_prepare_view()
482     $this->drupalGet('entity_test/' . $id);
483     $this->assertFieldByXPath('//div[@data-field-item-attr="foobar"]');
484
485     // Verify that the plain formatter works.
486     $this->displayOptions['type'] = 'daterange_plain';
487     $this->displayOptions['settings'] = $this->defaultSettings;
488     entity_get_display($this->field->getTargetEntityTypeId(), $this->field->getTargetBundle(), 'full')
489       ->setComponent($field_name, $this->displayOptions)
490       ->save();
491     $expected = $start_date->format(DATETIME_DATETIME_STORAGE_FORMAT) . ' - ' . $end_date->format(DATETIME_DATETIME_STORAGE_FORMAT);
492     $output = $this->renderTestEntity($id);
493     $this->assertContains($expected, $output, new FormattableMarkup('Formatted date field using plain format displayed as %expected.', ['%expected' => $expected]));
494
495     // Verify that the custom formatter works.
496     $this->displayOptions['type'] = 'daterange_custom';
497     $this->displayOptions['settings'] = ['date_format' => 'm/d/Y'] + $this->defaultSettings;
498     entity_get_display($this->field->getTargetEntityTypeId(), $this->field->getTargetBundle(), 'full')
499       ->setComponent($field_name, $this->displayOptions)
500       ->save();
501     $expected = $start_date->format($this->displayOptions['settings']['date_format']) . ' - ' . $end_date->format($this->displayOptions['settings']['date_format']);
502     $output = $this->renderTestEntity($id);
503     $this->assertContains($expected, $output, new FormattableMarkup('Formatted date field using daterange_custom format displayed as %expected.', ['%expected' => $expected]));
504
505     // Verify that the 'timezone_override' setting works.
506     $this->displayOptions['type'] = 'daterange_custom';
507     $this->displayOptions['settings'] = ['date_format' => 'm/d/Y g:i:s A', 'timezone_override' => 'America/New_York'] + $this->defaultSettings;
508     entity_get_display($this->field->getTargetEntityTypeId(), $this->field->getTargetBundle(), 'full')
509       ->setComponent($field_name, $this->displayOptions)
510       ->save();
511     $expected = $start_date->format($this->displayOptions['settings']['date_format'], ['timezone' => 'America/New_York']);
512     $expected .= ' - ' . $end_date->format($this->displayOptions['settings']['date_format'], ['timezone' => 'America/New_York']);
513     $output = $this->renderTestEntity($id);
514     $this->assertContains($expected, $output, new FormattableMarkup('Formatted date field using daterange_custom format displayed as %expected.', ['%expected' => $expected]));
515
516     // Test formatters when start date and end date are the same
517     $this->drupalGet('entity_test/add');
518
519     $value = '2012-12-31 00:00:00';
520     $start_date = new DrupalDateTime($value, timezone_open(drupal_get_user_timezone()));
521     $end_value = '2012-12-31 23:59:59';
522     $end_date = new DrupalDateTime($end_value, timezone_open(drupal_get_user_timezone()));
523
524     $date_format = DateFormat::load('html_date')->getPattern();
525     $time_format = DateFormat::load('html_time')->getPattern();
526
527     $edit = [
528       "{$field_name}[0][value][date]" => $start_date->format($date_format),
529       "{$field_name}[0][end_value][date]" => $start_date->format($date_format),
530     ];
531     $this->drupalPostForm(NULL, $edit, t('Save'));
532     preg_match('|entity_test/manage/(\d+)|', $this->getUrl(), $match);
533     $id = $match[1];
534     $this->assertText(t('entity_test @id has been created.', ['@id' => $id]));
535
536     $this->displayOptions = [
537       'type' => 'daterange_default',
538       'label' => 'hidden',
539       'settings' => [
540         'format_type' => 'long',
541         'separator' => 'THESEPARATOR',
542       ] + $this->defaultSettings,
543     ];
544
545     entity_get_display($this->field->getTargetEntityTypeId(), $this->field->getTargetBundle(), 'full')
546       ->setComponent($field_name, $this->displayOptions)
547       ->save();
548
549     $start_expected = $this->dateFormatter->format($start_date->getTimestamp(), 'long');
550     $start_expected_iso = $this->dateFormatter->format($start_date->getTimestamp(), 'custom', 'Y-m-d\TH:i:s\Z', 'UTC');
551     $start_expected_markup = '<time datetime="' . $start_expected_iso . '" class="datetime">' . $start_expected . '</time>';
552     $end_expected = $this->dateFormatter->format($end_date->getTimestamp(), 'long');
553     $end_expected_iso = $this->dateFormatter->format($end_date->getTimestamp(), 'custom', 'Y-m-d\TH:i:s\Z', 'UTC');
554     $end_expected_markup = '<time datetime="' . $end_expected_iso . '" class="datetime">' . $end_expected . '</time>';
555     $output = $this->renderTestEntity($id);
556     $this->assertContains($start_expected_markup, $output, new FormattableMarkup('Formatted date field using %value format displayed as %expected with %expected_iso attribute.', ['%value' => 'long', '%expected' => $start_expected, '%expected_iso' => $start_expected_iso]));
557     $this->assertContains($end_expected_markup, $output, new FormattableMarkup('Formatted date field using %value format displayed as %expected with %expected_iso attribute.', ['%value' => 'long', '%expected' => $end_expected, '%expected_iso' => $end_expected_iso]));
558     $this->assertContains(' THESEPARATOR ', $output, 'Found proper separator');
559
560     // Verify that hook_entity_prepare_view can add attributes.
561     // @see entity_test_entity_prepare_view()
562     $this->drupalGet('entity_test/' . $id);
563     $this->assertFieldByXPath('//div[@data-field-item-attr="foobar"]');
564
565     $this->displayOptions['type'] = 'daterange_plain';
566     entity_get_display($this->field->getTargetEntityTypeId(), $this->field->getTargetBundle(), 'full')
567       ->setComponent($field_name, $this->displayOptions)
568       ->save();
569     $expected = $start_date->format(DATETIME_DATETIME_STORAGE_FORMAT) . ' THESEPARATOR ' . $end_date->format(DATETIME_DATETIME_STORAGE_FORMAT);
570     $output = $this->renderTestEntity($id);
571     $this->assertContains($expected, $output, new FormattableMarkup('Formatted date field using plain format displayed as %expected.', ['%expected' => $expected]));
572     $this->assertContains(' THESEPARATOR ', $output, 'Found proper separator');
573
574     $this->displayOptions['type'] = 'daterange_custom';
575     $this->displayOptions['settings']['date_format'] = 'm/d/Y';
576     entity_get_display($this->field->getTargetEntityTypeId(), $this->field->getTargetBundle(), 'full')
577       ->setComponent($field_name, $this->displayOptions)
578       ->save();
579     $expected = $start_date->format($this->displayOptions['settings']['date_format']) . ' THESEPARATOR ' . $end_date->format($this->displayOptions['settings']['date_format']);
580     $output = $this->renderTestEntity($id);
581     $this->assertContains($expected, $output, new FormattableMarkup('Formatted date field using daterange_custom format displayed as %expected.', ['%expected' => $expected]));
582     $this->assertContains(' THESEPARATOR ', $output, 'Found proper separator');
583
584   }
585
586   /**
587    * Tests Date Range List Widget functionality.
588    */
589   public function testDatelistWidget() {
590     $field_name = $this->fieldStorage->getName();
591
592     // Ensure field is set to a date only field.
593     $this->fieldStorage->setSetting('datetime_type', DateRangeItem::DATETIME_TYPE_DATE);
594     $this->fieldStorage->save();
595
596     // Change the widget to a datelist widget.
597     entity_get_form_display($this->field->getTargetEntityTypeId(), $this->field->getTargetBundle(), 'default')
598       ->setComponent($field_name, [
599         'type' => 'daterange_datelist',
600         'settings' => [
601           'date_order' => 'YMD',
602         ],
603       ])
604       ->save();
605     \Drupal::service('entity_field.manager')->clearCachedFieldDefinitions();
606
607     // Display creation form.
608     $this->drupalGet('entity_test/add');
609     $this->assertFieldByXPath('//fieldset[@id="edit-' . $field_name . '-0"]/legend', $field_name, 'Fieldset and label found');
610     $this->assertFieldByXPath('//fieldset[@aria-describedby="edit-' . $field_name . '-0--description"]', NULL, 'ARIA described-by found');
611     $this->assertFieldByXPath('//div[@id="edit-' . $field_name . '-0--description"]', NULL, 'ARIA description found');
612
613     // Assert that Hour and Minute Elements do not appear on Date Only.
614     $this->assertNoFieldByXPath("//*[@id=\"edit-$field_name-0-value-hour\"]", NULL, 'Hour element not found on Date Only.');
615     $this->assertNoFieldByXPath("//*[@id=\"edit-$field_name-0-value-minute\"]", NULL, 'Minute element not found on Date Only.');
616     $this->assertNoFieldByXPath("//*[@id=\"edit-$field_name-0-end-value-hour\"]", NULL, 'Hour element not found on Date Only.');
617     $this->assertNoFieldByXPath("//*[@id=\"edit-$field_name-0-end-value-minute\"]", NULL, 'Minute element not found on Date Only.');
618
619     // Go to the form display page to assert that increment option does not
620     // appear on Date Only.
621     $fieldEditUrl = 'entity_test/structure/entity_test/form-display';
622     $this->drupalGet($fieldEditUrl);
623
624     // Click on the widget settings button to open the widget settings form.
625     $this->drupalPostForm(NULL, [], $field_name . "_settings_edit");
626     $xpathIncr = "//select[starts-with(@id, \"edit-fields-$field_name-settings-edit-form-settings-increment\")]";
627     $this->assertNoFieldByXPath($xpathIncr, NULL, 'Increment element not found for Date Only.');
628
629     // Change the field is set to an all day field.
630     $this->fieldStorage->setSetting('datetime_type', DateRangeItem::DATETIME_TYPE_ALLDAY);
631     $this->fieldStorage->save();
632
633     // Change the widget to a datelist widget.
634     entity_get_form_display($this->field->getTargetEntityTypeId(), $this->field->getTargetBundle(), 'default')
635       ->setComponent($field_name, [
636         'type' => 'daterange_datelist',
637         'settings' => [
638           'date_order' => 'YMD',
639         ],
640       ])
641       ->save();
642     \Drupal::service('entity_field.manager')->clearCachedFieldDefinitions();
643
644     // Display creation form.
645     $this->drupalGet('entity_test/add');
646
647     // Assert that Hour and Minute Elements do not appear on Date Only.
648     $this->assertNoFieldByXPath("//*[@id=\"edit-$field_name-0-value-hour\"]", NULL, 'Hour element not found on Date Only.');
649     $this->assertNoFieldByXPath("//*[@id=\"edit-$field_name-0-value-minute\"]", NULL, 'Minute element not found on Date Only.');
650     $this->assertNoFieldByXPath("//*[@id=\"edit-$field_name-0-end-value-hour\"]", NULL, 'Hour element not found on Date Only.');
651     $this->assertNoFieldByXPath("//*[@id=\"edit-$field_name-0-end-value-minute\"]", NULL, 'Minute element not found on Date Only.');
652
653     // Go to the form display page to assert that increment option does not
654     // appear on Date Only.
655     $fieldEditUrl = 'entity_test/structure/entity_test/form-display';
656     $this->drupalGet($fieldEditUrl);
657
658     // Click on the widget settings button to open the widget settings form.
659     $this->drupalPostForm(NULL, [], $field_name . "_settings_edit");
660     $xpathIncr = "//select[starts-with(@id, \"edit-fields-$field_name-settings-edit-form-settings-increment\")]";
661     $this->assertNoFieldByXPath($xpathIncr, NULL, 'Increment element not found for Date Only.');
662
663     // Change the field to a datetime field.
664     $this->fieldStorage->setSetting('datetime_type', DateRangeItem::DATETIME_TYPE_DATETIME);
665     $this->fieldStorage->save();
666
667     // Change the widget to a datelist widget.
668     entity_get_form_display($this->field->getTargetEntityTypeId(), $this->field->getTargetBundle(), 'default')
669       ->setComponent($field_name, [
670         'type' => 'daterange_datelist',
671         'settings' => [
672           'increment' => 1,
673           'date_order' => 'YMD',
674           'time_type' => '12',
675         ],
676       ])
677       ->save();
678     \Drupal::service('entity_field.manager')->clearCachedFieldDefinitions();
679
680     // Go to the form display page to assert that increment option does appear
681     // on Date Time.
682     $fieldEditUrl = 'entity_test/structure/entity_test/form-display';
683     $this->drupalGet($fieldEditUrl);
684
685     // Click on the widget settings button to open the widget settings form.
686     $this->drupalPostForm(NULL, [], $field_name . "_settings_edit");
687     $this->assertFieldByXPath($xpathIncr, NULL, 'Increment element found for Date and time.');
688
689     // Display creation form.
690     $this->drupalGet('entity_test/add');
691
692     foreach (['value', 'end-value'] as $column) {
693       foreach (['year', 'month', 'day', 'hour', 'minute', 'ampm'] as $element) {
694         $this->assertFieldByXPath("//*[@id=\"edit-$field_name-0-$column-$element\"]", NULL, $element . ' element found.');
695         $this->assertOptionSelected("edit-$field_name-0-$column-$element", '', 'No ' . $element . ' selected.');
696       }
697     }
698
699     // Submit a valid date and ensure it is accepted.
700     $start_date_value = ['year' => 2012, 'month' => 12, 'day' => 31, 'hour' => 5, 'minute' => 15];
701     $end_date_value = ['year' => 2013, 'month' => 1, 'day' => 15, 'hour' => 3, 'minute' => 30];
702
703     $edit = [];
704     // Add the ampm indicator since we are testing 12 hour time.
705     $start_date_value['ampm'] = 'am';
706     $end_date_value['ampm'] = 'pm';
707     foreach ($start_date_value as $part => $value) {
708       $edit["{$field_name}[0][value][$part]"] = $value;
709     }
710     foreach ($end_date_value as $part => $value) {
711       $edit["{$field_name}[0][end_value][$part]"] = $value;
712     }
713
714     $this->drupalPostForm(NULL, $edit, t('Save'));
715     preg_match('|entity_test/manage/(\d+)|', $this->getUrl(), $match);
716     $id = $match[1];
717     $this->assertText(t('entity_test @id has been created.', ['@id' => $id]));
718
719     $this->assertOptionSelected("edit-$field_name-0-value-year", '2012', 'Correct year selected.');
720     $this->assertOptionSelected("edit-$field_name-0-value-month", '12', 'Correct month selected.');
721     $this->assertOptionSelected("edit-$field_name-0-value-day", '31', 'Correct day selected.');
722     $this->assertOptionSelected("edit-$field_name-0-value-hour", '5', 'Correct hour selected.');
723     $this->assertOptionSelected("edit-$field_name-0-value-minute", '15', 'Correct minute selected.');
724     $this->assertOptionSelected("edit-$field_name-0-value-ampm", 'am', 'Correct ampm selected.');
725
726     $this->assertOptionSelected("edit-$field_name-0-end-value-year", '2013', 'Correct year selected.');
727     $this->assertOptionSelected("edit-$field_name-0-end-value-month", '1', 'Correct month selected.');
728     $this->assertOptionSelected("edit-$field_name-0-end-value-day", '15', 'Correct day selected.');
729     $this->assertOptionSelected("edit-$field_name-0-end-value-hour", '3', 'Correct hour selected.');
730     $this->assertOptionSelected("edit-$field_name-0-end-value-minute", '30', 'Correct minute selected.');
731     $this->assertOptionSelected("edit-$field_name-0-end-value-ampm", 'pm', 'Correct ampm selected.');
732
733     // Test the widget using increment other than 1 and 24 hour mode.
734     entity_get_form_display($this->field->getTargetEntityTypeId(), $this->field->getTargetBundle(), 'default')
735       ->setComponent($field_name, [
736         'type' => 'daterange_datelist',
737         'settings' => [
738           'increment' => 15,
739           'date_order' => 'YMD',
740           'time_type' => '24',
741         ],
742       ])
743       ->save();
744     \Drupal::service('entity_field.manager')->clearCachedFieldDefinitions();
745
746     // Display creation form.
747     $this->drupalGet('entity_test/add');
748
749     // Other elements are unaffected by the changed settings.
750     $this->assertFieldByXPath("//*[@id=\"edit-$field_name-0-value-hour\"]", NULL, 'Hour element found.');
751     $this->assertOptionSelected("edit-$field_name-0-value-hour", '', 'No hour selected.');
752     $this->assertNoFieldByXPath("//*[@id=\"edit-$field_name-0-value-ampm\"]", NULL, 'AMPM element not found.');
753     $this->assertFieldByXPath("//*[@id=\"edit-$field_name-0-end-value-hour\"]", NULL, 'Hour element found.');
754     $this->assertOptionSelected("edit-$field_name-0-end-value-hour", '', 'No hour selected.');
755     $this->assertNoFieldByXPath("//*[@id=\"edit-$field_name-0-end-value-ampm\"]", NULL, 'AMPM element not found.');
756
757     // Submit a valid date and ensure it is accepted.
758     $start_date_value = ['year' => 2012, 'month' => 12, 'day' => 31, 'hour' => 17, 'minute' => 15];
759     $end_date_value = ['year' => 2013, 'month' => 1, 'day' => 15, 'hour' => 3, 'minute' => 30];
760
761     $edit = [];
762     foreach ($start_date_value as $part => $value) {
763       $edit["{$field_name}[0][value][$part]"] = $value;
764     }
765     foreach ($end_date_value as $part => $value) {
766       $edit["{$field_name}[0][end_value][$part]"] = $value;
767     }
768
769     $this->drupalPostForm(NULL, $edit, t('Save'));
770     preg_match('|entity_test/manage/(\d+)|', $this->getUrl(), $match);
771     $id = $match[1];
772     $this->assertText(t('entity_test @id has been created.', ['@id' => $id]));
773
774     $this->assertOptionSelected("edit-$field_name-0-value-year", '2012', 'Correct year selected.');
775     $this->assertOptionSelected("edit-$field_name-0-value-month", '12', 'Correct month selected.');
776     $this->assertOptionSelected("edit-$field_name-0-value-day", '31', 'Correct day selected.');
777     $this->assertOptionSelected("edit-$field_name-0-value-hour", '17', 'Correct hour selected.');
778     $this->assertOptionSelected("edit-$field_name-0-value-minute", '15', 'Correct minute selected.');
779
780     $this->assertOptionSelected("edit-$field_name-0-end-value-year", '2013', 'Correct year selected.');
781     $this->assertOptionSelected("edit-$field_name-0-end-value-month", '1', 'Correct month selected.');
782     $this->assertOptionSelected("edit-$field_name-0-end-value-day", '15', 'Correct day selected.');
783     $this->assertOptionSelected("edit-$field_name-0-end-value-hour", '3', 'Correct hour selected.');
784     $this->assertOptionSelected("edit-$field_name-0-end-value-minute", '30', 'Correct minute selected.');
785
786     // Test the widget for partial completion of fields.
787     entity_get_form_display($this->field->getTargetEntityTypeId(), $this->field->getTargetBundle(), 'default')
788       ->setComponent($field_name, [
789         'type' => 'daterange_datelist',
790         'settings' => [
791           'increment' => 1,
792           'date_order' => 'YMD',
793           'time_type' => '24',
794         ],
795       ])
796       ->save();
797     \Drupal::service('entity_field.manager')->clearCachedFieldDefinitions();
798
799     // Test the widget for validation notifications.
800     foreach ($this->datelistDataProvider() as $data) {
801       list($start_date_value, $end_date_value, $expected) = $data;
802
803       // Display creation form.
804       $this->drupalGet('entity_test/add');
805
806       // Submit a partial date and ensure and error message is provided.
807       $edit = [];
808       foreach ($start_date_value as $part => $value) {
809         $edit["{$field_name}[0][value][$part]"] = $value;
810       }
811       foreach ($end_date_value as $part => $value) {
812         $edit["{$field_name}[0][end_value][$part]"] = $value;
813       }
814
815       $this->drupalPostForm(NULL, $edit, t('Save'));
816       $this->assertResponse(200);
817       foreach ($expected as $expected_text) {
818         $this->assertText(t($expected_text));
819       }
820     }
821
822     // Test the widget for complete input with zeros as part of selections.
823     $this->drupalGet('entity_test/add');
824
825     $start_date_value = ['year' => 2012, 'month' => 12, 'day' => 31, 'hour' => 0, 'minute' => 0];
826     $end_date_value = ['year' => 2013, 'month' => 1, 'day' => 15, 'hour' => 3, 'minute' => 30];
827     $edit = [];
828     foreach ($start_date_value as $part => $value) {
829       $edit["{$field_name}[0][value][$part]"] = $value;
830     }
831     foreach ($end_date_value as $part => $value) {
832       $edit["{$field_name}[0][end_value][$part]"] = $value;
833     }
834
835     $this->drupalPostForm(NULL, $edit, t('Save'));
836     $this->assertResponse(200);
837     preg_match('|entity_test/manage/(\d+)|', $this->getUrl(), $match);
838     $id = $match[1];
839     $this->assertText(t('entity_test @id has been created.', ['@id' => $id]));
840
841     // Test the widget to ensure zeros are not deselected on validation.
842     $this->drupalGet('entity_test/add');
843
844     $start_date_value = ['year' => 2012, 'month' => 12, 'day' => 31, 'hour' => 0, 'minute' => 0];
845     $end_date_value = ['year' => 2013, 'month' => 1, 'day' => 15, 'hour' => 3, 'minute' => 0];
846     $edit = [];
847     foreach ($start_date_value as $part => $value) {
848       $edit["{$field_name}[0][value][$part]"] = $value;
849     }
850     foreach ($end_date_value as $part => $value) {
851       $edit["{$field_name}[0][end_value][$part]"] = $value;
852     }
853
854     $this->drupalPostForm(NULL, $edit, t('Save'));
855     $this->assertResponse(200);
856     $this->assertOptionSelected("edit-$field_name-0-value-minute", '0', 'Correct minute selected.');
857     $this->assertOptionSelected("edit-$field_name-0-end-value-minute", '0', 'Correct minute selected.');
858   }
859
860   /**
861    * The data provider for testing the validation of the datelist widget.
862    *
863    * @return array
864    *   An array of datelist input permutations to test.
865    */
866   protected function datelistDataProvider() {
867     return [
868       // Year only selected, validation error on Month, Day, Hour, Minute.
869       [
870         ['year' => 2012, 'month' => '', 'day' => '', 'hour' => '', 'minute' => ''],
871         ['year' => 2013, 'month' => '1', 'day' => '15', 'hour' => '3', 'minute' => '30'], [
872           'A value must be selected for month.',
873           'A value must be selected for day.',
874           'A value must be selected for hour.',
875           'A value must be selected for minute.',
876         ],
877       ],
878       // Year and Month selected, validation error on Day, Hour, Minute.
879       [
880         ['year' => 2012, 'month' => '12', 'day' => '', 'hour' => '', 'minute' => ''],
881         ['year' => 2013, 'month' => '1', 'day' => '15', 'hour' => '3', 'minute' => '30'], [
882           'A value must be selected for day.',
883           'A value must be selected for hour.',
884           'A value must be selected for minute.',
885         ],
886       ],
887       // Year, Month and Day selected, validation error on Hour, Minute.
888       [
889         ['year' => 2012, 'month' => '12', 'day' => '31', 'hour' => '', 'minute' => ''],
890         ['year' => 2013, 'month' => '1', 'day' => '15', 'hour' => '3', 'minute' => '30'], [
891           'A value must be selected for hour.',
892           'A value must be selected for minute.',
893         ],
894       ],
895       // Year, Month, Day and Hour selected, validation error on Minute only.
896       [
897         ['year' => 2012, 'month' => '12', 'day' => '31', 'hour' => '0', 'minute' => ''],
898         ['year' => 2013, 'month' => '1', 'day' => '15', 'hour' => '3', 'minute' => '30'], [
899           'A value must be selected for minute.',
900         ],
901       ],
902       // Year selected, validation error on Month, Day, Hour, Minute.
903       [
904         ['year' => 2012, 'month' => '12', 'day' => '31', 'hour' => '0', 'minute' => '0'],
905         ['year' => 2013, 'month' => '', 'day' => '', 'hour' => '', 'minute' => ''], [
906           'A value must be selected for month.',
907           'A value must be selected for day.',
908           'A value must be selected for hour.',
909           'A value must be selected for minute.',
910         ],
911       ],
912       // Year and Month selected, validation error on Day, Hour, Minute.
913       [
914         ['year' => 2012, 'month' => '12', 'day' => '31', 'hour' => '0', 'minute' => '0'],
915         ['year' => 2013, 'month' => '1', 'day' => '', 'hour' => '', 'minute' => ''], [
916           'A value must be selected for day.',
917           'A value must be selected for hour.',
918           'A value must be selected for minute.',
919         ],
920       ],
921       // Year, Month and Day selected, validation error on Hour, Minute.
922       [
923         ['year' => 2012, 'month' => '12', 'day' => '31', 'hour' => '0', 'minute' => '0'],
924         ['year' => 2013, 'month' => '1', 'day' => '15', 'hour' => '', 'minute' => ''], [
925           'A value must be selected for hour.',
926           'A value must be selected for minute.',
927         ],
928       ],
929       // Year, Month, Day and Hour selected, validation error on Minute only.
930       [
931         ['year' => 2012, 'month' => '12', 'day' => '31', 'hour' => '0', 'minute' => '0'],
932         ['year' => 2013, 'month' => '1', 'day' => '15', 'hour' => '3', 'minute' => ''], [
933           'A value must be selected for minute.',
934         ],
935       ],
936     ];
937   }
938
939   /**
940    * Test default value functionality.
941    */
942   public function testDefaultValue() {
943     // Create a test content type.
944     $this->drupalCreateContentType(['type' => 'date_content']);
945
946     // Create a field storage with settings to validate.
947     $field_name = Unicode::strtolower($this->randomMachineName());
948     $field_storage = FieldStorageConfig::create([
949       'field_name' => $field_name,
950       'entity_type' => 'node',
951       'type' => 'daterange',
952       'settings' => ['datetime_type' => DateRangeItem::DATETIME_TYPE_DATE],
953     ]);
954     $field_storage->save();
955
956     $field = FieldConfig::create([
957       'field_storage' => $field_storage,
958       'bundle' => 'date_content',
959     ]);
960     $field->save();
961
962     // Set now as default_value.
963     $field_edit = [
964       'default_value_input[default_date_type]' => 'now',
965       'default_value_input[default_end_date_type]' => 'now',
966     ];
967     $this->drupalPostForm('admin/structure/types/manage/date_content/fields/node.date_content.' . $field_name, $field_edit, t('Save settings'));
968
969     // Check that default value is selected in default value form.
970     $this->drupalGet('admin/structure/types/manage/date_content/fields/node.date_content.' . $field_name);
971     $this->assertOptionSelected('edit-default-value-input-default-date-type', 'now', 'The default start value is selected in instance settings page');
972     $this->assertFieldByName('default_value_input[default_date]', '', 'The relative start default value is empty in instance settings page');
973     $this->assertOptionSelected('edit-default-value-input-default-end-date-type', 'now', 'The default end value is selected in instance settings page');
974     $this->assertFieldByName('default_value_input[default_end_date]', '', 'The relative end default value is empty in instance settings page');
975
976     // Check if default_date has been stored successfully.
977     $config_entity = $this->config('field.field.node.date_content.' . $field_name)->get();
978     $this->assertEqual($config_entity['default_value'][0], [
979       'default_date_type' => 'now',
980       'default_date' => 'now',
981       'default_end_date_type' => 'now',
982       'default_end_date' => 'now',
983     ], 'Default value has been stored successfully');
984
985     // Clear field cache in order to avoid stale cache values.
986     \Drupal::service('entity_field.manager')->clearCachedFieldDefinitions();
987
988     // Create a new node to check that datetime field default value is today.
989     $new_node = Node::create(['type' => 'date_content']);
990     $expected_date = new DrupalDateTime('now', DATETIME_STORAGE_TIMEZONE);
991     $this->assertEqual($new_node->get($field_name)->offsetGet(0)->value, $expected_date->format(DATETIME_DATE_STORAGE_FORMAT));
992     $this->assertEqual($new_node->get($field_name)->offsetGet(0)->end_value, $expected_date->format(DATETIME_DATE_STORAGE_FORMAT));
993
994     // Set an invalid relative default_value to test validation.
995     $field_edit = [
996       'default_value_input[default_date_type]' => 'relative',
997       'default_value_input[default_date]' => 'invalid date',
998       'default_value_input[default_end_date_type]' => 'relative',
999       'default_value_input[default_end_date]' => '+1 day',
1000     ];
1001     $this->drupalPostForm('admin/structure/types/manage/date_content/fields/node.date_content.' . $field_name, $field_edit, t('Save settings'));
1002     $this->assertText('The relative start date value entered is invalid.');
1003
1004     $field_edit = [
1005       'default_value_input[default_date_type]' => 'relative',
1006       'default_value_input[default_date]' => '+1 day',
1007       'default_value_input[default_end_date_type]' => 'relative',
1008       'default_value_input[default_end_date]' => 'invalid date',
1009     ];
1010     $this->drupalPostForm('admin/structure/types/manage/date_content/fields/node.date_content.' . $field_name, $field_edit, t('Save settings'));
1011     $this->assertText('The relative end date value entered is invalid.');
1012
1013     // Set a relative default_value.
1014     $field_edit = [
1015       'default_value_input[default_date_type]' => 'relative',
1016       'default_value_input[default_date]' => '+45 days',
1017       'default_value_input[default_end_date_type]' => 'relative',
1018       'default_value_input[default_end_date]' => '+90 days',
1019     ];
1020     $this->drupalPostForm('admin/structure/types/manage/date_content/fields/node.date_content.' . $field_name, $field_edit, t('Save settings'));
1021
1022     // Check that default value is selected in default value form.
1023     $this->drupalGet('admin/structure/types/manage/date_content/fields/node.date_content.' . $field_name);
1024     $this->assertOptionSelected('edit-default-value-input-default-date-type', 'relative', 'The default start value is selected in instance settings page');
1025     $this->assertFieldByName('default_value_input[default_date]', '+45 days', 'The relative default start value is displayed in instance settings page');
1026     $this->assertOptionSelected('edit-default-value-input-default-end-date-type', 'relative', 'The default end value is selected in instance settings page');
1027     $this->assertFieldByName('default_value_input[default_end_date]', '+90 days', 'The relative default end value is displayed in instance settings page');
1028
1029     // Check if default_date has been stored successfully.
1030     $config_entity = $this->config('field.field.node.date_content.' . $field_name)->get();
1031     $this->assertEqual($config_entity['default_value'][0], [
1032       'default_date_type' => 'relative',
1033       'default_date' => '+45 days',
1034       'default_end_date_type' => 'relative',
1035       'default_end_date' => '+90 days',
1036     ], 'Default value has been stored successfully');
1037
1038     // Clear field cache in order to avoid stale cache values.
1039     \Drupal::service('entity_field.manager')->clearCachedFieldDefinitions();
1040
1041     // Create a new node to check that datetime field default value is +90 days.
1042     $new_node = Node::create(['type' => 'date_content']);
1043     $expected_start_date = new DrupalDateTime('+45 days', DATETIME_STORAGE_TIMEZONE);
1044     $expected_end_date = new DrupalDateTime('+90 days', DATETIME_STORAGE_TIMEZONE);
1045     $this->assertEqual($new_node->get($field_name)->offsetGet(0)->value, $expected_start_date->format(DATETIME_DATE_STORAGE_FORMAT));
1046     $this->assertEqual($new_node->get($field_name)->offsetGet(0)->end_value, $expected_end_date->format(DATETIME_DATE_STORAGE_FORMAT));
1047
1048     // Remove default value.
1049     $field_edit = [
1050       'default_value_input[default_date_type]' => '',
1051       'default_value_input[default_end_date_type]' => '',
1052     ];
1053     $this->drupalPostForm('admin/structure/types/manage/date_content/fields/node.date_content.' . $field_name, $field_edit, t('Save settings'));
1054
1055     // Check that default value is selected in default value form.
1056     $this->drupalGet('admin/structure/types/manage/date_content/fields/node.date_content.' . $field_name);
1057     $this->assertOptionSelected('edit-default-value-input-default-date-type', '', 'The default start value is selected in instance settings page');
1058     $this->assertFieldByName('default_value_input[default_date]', '', 'The relative default start value is empty in instance settings page');
1059     $this->assertOptionSelected('edit-default-value-input-default-end-date-type', '', 'The default end value is selected in instance settings page');
1060     $this->assertFieldByName('default_value_input[default_end_date]', '', 'The relative default end value is empty in instance settings page');
1061
1062     // Check if default_date has been stored successfully.
1063     $config_entity = $this->config('field.field.node.date_content.' . $field_name)->get();
1064     $this->assertTrue(empty($config_entity['default_value']), 'Empty default value has been stored successfully');
1065
1066     // Clear field cache in order to avoid stale cache values.
1067     \Drupal::service('entity_field.manager')->clearCachedFieldDefinitions();
1068
1069     // Create a new node to check that datetime field default value is not set.
1070     $new_node = Node::create(['type' => 'date_content']);
1071     $this->assertNull($new_node->get($field_name)->value, 'Default value is not set');
1072
1073     // Set now as default_value for start date only.
1074     entity_get_form_display('node', 'date_content', 'default')
1075       ->setComponent($field_name, [
1076         'type' => 'datetime_default',
1077       ])
1078       ->save();
1079
1080     $expected_date = new DrupalDateTime('now', DATETIME_STORAGE_TIMEZONE);
1081
1082     $field_edit = [
1083       'default_value_input[default_date_type]' => 'now',
1084       'default_value_input[default_end_date_type]' => '',
1085     ];
1086     $this->drupalPostForm('admin/structure/types/manage/date_content/fields/node.date_content.' . $field_name, $field_edit, t('Save settings'));
1087
1088     // Make sure only the start value is populated on node add page.
1089     $this->drupalGet('node/add/date_content');
1090     $this->assertFieldByName("{$field_name}[0][value][date]", $expected_date->format(DATETIME_DATE_STORAGE_FORMAT), 'Start date element populated.');
1091     $this->assertFieldByName("{$field_name}[0][end_value][date]", '', 'End date element empty.');
1092
1093     // Set now as default_value for end date only.
1094     $field_edit = [
1095       'default_value_input[default_date_type]' => '',
1096       'default_value_input[default_end_date_type]' => 'now',
1097     ];
1098     $this->drupalPostForm('admin/structure/types/manage/date_content/fields/node.date_content.' . $field_name, $field_edit, t('Save settings'));
1099
1100     // Make sure only the start value is populated on node add page.
1101     $this->drupalGet('node/add/date_content');
1102     $this->assertFieldByName("{$field_name}[0][value][date]", '', 'Start date element empty.');
1103     $this->assertFieldByName("{$field_name}[0][end_value][date]", $expected_date->format(DATETIME_DATE_STORAGE_FORMAT), 'End date element populated.');
1104   }
1105
1106   /**
1107    * Test that invalid values are caught and marked as invalid.
1108    */
1109   public function testInvalidField() {
1110     // Change the field to a datetime field.
1111     $this->fieldStorage->setSetting('datetime_type', DateRangeItem::DATETIME_TYPE_DATETIME);
1112     $this->fieldStorage->save();
1113     $field_name = $this->fieldStorage->getName();
1114
1115     $this->drupalGet('entity_test/add');
1116     $this->assertFieldByName("{$field_name}[0][value][date]", '', 'Start date element found.');
1117     $this->assertFieldByName("{$field_name}[0][value][time]", '', 'Start time element found.');
1118     $this->assertFieldByName("{$field_name}[0][end_value][date]", '', 'End date element found.');
1119     $this->assertFieldByName("{$field_name}[0][end_value][time]", '', 'End time element found.');
1120
1121     // Submit invalid start dates and ensure they is not accepted.
1122     $date_value = '';
1123     $edit = [
1124       "{$field_name}[0][value][date]" => $date_value,
1125       "{$field_name}[0][value][time]" => '12:00:00',
1126       "{$field_name}[0][end_value][date]" => '2012-12-01',
1127       "{$field_name}[0][end_value][time]" => '12:00:00',
1128     ];
1129     $this->drupalPostForm(NULL, $edit, t('Save'));
1130     $this->assertText('date is invalid', 'Empty start date value has been caught.');
1131
1132     $date_value = 'aaaa-12-01';
1133     $edit = [
1134       "{$field_name}[0][value][date]" => $date_value,
1135       "{$field_name}[0][value][time]" => '00:00:00',
1136       "{$field_name}[0][end_value][date]" => '2012-12-01',
1137       "{$field_name}[0][end_value][time]" => '12:00:00',
1138     ];
1139     $this->drupalPostForm(NULL, $edit, t('Save'));
1140     $this->assertText('date is invalid', new FormattableMarkup('Invalid start year value %date has been caught.', ['%date' => $date_value]));
1141
1142     $date_value = '2012-75-01';
1143     $edit = [
1144       "{$field_name}[0][value][date]" => $date_value,
1145       "{$field_name}[0][value][time]" => '00:00:00',
1146       "{$field_name}[0][end_value][date]" => '2012-12-01',
1147       "{$field_name}[0][end_value][time]" => '12:00:00',
1148     ];
1149     $this->drupalPostForm(NULL, $edit, t('Save'));
1150     $this->assertText('date is invalid', new FormattableMarkup('Invalid start month value %date has been caught.', ['%date' => $date_value]));
1151
1152     $date_value = '2012-12-99';
1153     $edit = [
1154       "{$field_name}[0][value][date]" => $date_value,
1155       "{$field_name}[0][value][time]" => '00:00:00',
1156       "{$field_name}[0][end_value][date]" => '2012-12-01',
1157       "{$field_name}[0][end_value][time]" => '12:00:00',
1158     ];
1159     $this->drupalPostForm(NULL, $edit, t('Save'));
1160     $this->assertText('date is invalid', new FormattableMarkup('Invalid start day value %date has been caught.', ['%date' => $date_value]));
1161
1162     // Submit invalid start times and ensure they is not accepted.
1163     $time_value = '';
1164     $edit = [
1165       "{$field_name}[0][value][date]" => '2012-12-01',
1166       "{$field_name}[0][value][time]" => $time_value,
1167       "{$field_name}[0][end_value][date]" => '2012-12-01',
1168       "{$field_name}[0][end_value][time]" => '12:00:00',
1169     ];
1170     $this->drupalPostForm(NULL, $edit, t('Save'));
1171     $this->assertText('date is invalid', 'Empty start time value has been caught.');
1172
1173     $time_value = '49:00:00';
1174     $edit = [
1175       "{$field_name}[0][value][date]" => '2012-12-01',
1176       "{$field_name}[0][value][time]" => $time_value,
1177       "{$field_name}[0][end_value][date]" => '2012-12-01',
1178       "{$field_name}[0][end_value][time]" => '12:00:00',
1179     ];
1180     $this->drupalPostForm(NULL, $edit, t('Save'));
1181     $this->assertText('date is invalid', new FormattableMarkup('Invalid start hour value %time has been caught.', ['%time' => $time_value]));
1182
1183     $time_value = '12:99:00';
1184     $edit = [
1185       "{$field_name}[0][value][date]" => '2012-12-01',
1186       "{$field_name}[0][value][time]" => $time_value,
1187       "{$field_name}[0][end_value][date]" => '2012-12-01',
1188       "{$field_name}[0][end_value][time]" => '12:00:00',
1189     ];
1190     $this->drupalPostForm(NULL, $edit, t('Save'));
1191     $this->assertText('date is invalid', new FormattableMarkup('Invalid start minute value %time has been caught.', ['%time' => $time_value]));
1192
1193     $time_value = '12:15:99';
1194     $edit = [
1195       "{$field_name}[0][value][date]" => '2012-12-01',
1196       "{$field_name}[0][value][time]" => $time_value,
1197       "{$field_name}[0][end_value][date]" => '2012-12-01',
1198       "{$field_name}[0][end_value][time]" => '12:00:00',
1199     ];
1200     $this->drupalPostForm(NULL, $edit, t('Save'));
1201     $this->assertText('date is invalid', new FormattableMarkup('Invalid start second value %time has been caught.', ['%time' => $time_value]));
1202
1203     // Submit invalid end dates and ensure they is not accepted.
1204     $date_value = '';
1205     $edit = [
1206       "{$field_name}[0][value][date]" => '2012-12-01',
1207       "{$field_name}[0][value][time]" => '12:00:00',
1208       "{$field_name}[0][end_value][date]" => $date_value,
1209       "{$field_name}[0][end_value][time]" => '12:00:00',
1210     ];
1211     $this->drupalPostForm(NULL, $edit, t('Save'));
1212     $this->assertText('date is invalid', 'Empty end date value has been caught.');
1213
1214     $date_value = 'aaaa-12-01';
1215     $edit = [
1216       "{$field_name}[0][value][date]" => '2012-12-01',
1217       "{$field_name}[0][value][time]" => '12:00:00',
1218       "{$field_name}[0][end_value][date]" => $date_value,
1219       "{$field_name}[0][end_value][time]" => '00:00:00',
1220     ];
1221     $this->drupalPostForm(NULL, $edit, t('Save'));
1222     $this->assertText('date is invalid', new FormattableMarkup('Invalid end year value %date has been caught.', ['%date' => $date_value]));
1223
1224     $date_value = '2012-75-01';
1225     $edit = [
1226       "{$field_name}[0][value][date]" => '2012-12-01',
1227       "{$field_name}[0][value][time]" => '12:00:00',
1228       "{$field_name}[0][end_value][date]" => $date_value,
1229       "{$field_name}[0][end_value][time]" => '00:00:00',
1230     ];
1231     $this->drupalPostForm(NULL, $edit, t('Save'));
1232     $this->assertText('date is invalid', new FormattableMarkup('Invalid end month value %date has been caught.', ['%date' => $date_value]));
1233
1234     $date_value = '2012-12-99';
1235     $edit = [
1236       "{$field_name}[0][value][date]" => '2012-12-01',
1237       "{$field_name}[0][value][time]" => '12:00:00',
1238       "{$field_name}[0][end_value][date]" => $date_value,
1239       "{$field_name}[0][end_value][time]" => '00:00:00',
1240     ];
1241     $this->drupalPostForm(NULL, $edit, t('Save'));
1242     $this->assertText('date is invalid', new FormattableMarkup('Invalid end day value %date has been caught.', ['%date' => $date_value]));
1243
1244     // Submit invalid start times and ensure they is not accepted.
1245     $time_value = '';
1246     $edit = [
1247       "{$field_name}[0][value][date]" => '2012-12-01',
1248       "{$field_name}[0][value][time]" => '12:00:00',
1249       "{$field_name}[0][end_value][date]" => '2012-12-01',
1250       "{$field_name}[0][end_value][time]" => $time_value,
1251     ];
1252     $this->drupalPostForm(NULL, $edit, t('Save'));
1253     $this->assertText('date is invalid', 'Empty end time value has been caught.');
1254
1255     $time_value = '49:00:00';
1256     $edit = [
1257       "{$field_name}[0][value][date]" => '2012-12-01',
1258       "{$field_name}[0][value][time]" => '12:00:00',
1259       "{$field_name}[0][end_value][date]" => '2012-12-01',
1260       "{$field_name}[0][end_value][time]" => $time_value,
1261     ];
1262     $this->drupalPostForm(NULL, $edit, t('Save'));
1263     $this->assertText('date is invalid', new FormattableMarkup('Invalid end hour value %time has been caught.', ['%time' => $time_value]));
1264
1265     $time_value = '12:99:00';
1266     $edit = [
1267       "{$field_name}[0][value][date]" => '2012-12-01',
1268       "{$field_name}[0][value][time]" => '12:00:00',
1269       "{$field_name}[0][end_value][date]" => '2012-12-01',
1270       "{$field_name}[0][end_value][time]" => $time_value,
1271     ];
1272     $this->drupalPostForm(NULL, $edit, t('Save'));
1273     $this->assertText('date is invalid', new FormattableMarkup('Invalid end minute value %time has been caught.', ['%time' => $time_value]));
1274
1275     $time_value = '12:15:99';
1276     $edit = [
1277       "{$field_name}[0][value][date]" => '2012-12-01',
1278       "{$field_name}[0][value][time]" => '12:00:00',
1279       "{$field_name}[0][end_value][date]" => '2012-12-01',
1280       "{$field_name}[0][end_value][time]" => $time_value,
1281     ];
1282     $this->drupalPostForm(NULL, $edit, t('Save'));
1283     $this->assertText('date is invalid', new FormattableMarkup('Invalid end second value %time has been caught.', ['%time' => $time_value]));
1284
1285     $edit = [
1286       "{$field_name}[0][value][date]" => '2012-12-01',
1287       "{$field_name}[0][value][time]" => '12:00:00',
1288       "{$field_name}[0][end_value][date]" => '2010-12-01',
1289       "{$field_name}[0][end_value][time]" => '12:00:00',
1290     ];
1291     $this->drupalPostForm(NULL, $edit, t('Save'));
1292     $this->assertText(new FormattableMarkup('The @title end date cannot be before the start date', ['@title' => $field_name]), 'End date before start date has been caught.');
1293
1294     $edit = [
1295       "{$field_name}[0][value][date]" => '2012-12-01',
1296       "{$field_name}[0][value][time]" => '12:00:00',
1297       "{$field_name}[0][end_value][date]" => '2012-12-01',
1298       "{$field_name}[0][end_value][time]" => '11:00:00',
1299     ];
1300     $this->drupalPostForm(NULL, $edit, t('Save'));
1301     $this->assertText(new FormattableMarkup('The @title end date cannot be before the start date', ['@title' => $field_name]), 'End time before start time has been caught.');
1302   }
1303
1304   /**
1305    * Tests that 'Date' field storage setting form is disabled if field has data.
1306    */
1307   public function testDateStorageSettings() {
1308     // Create a test content type.
1309     $this->drupalCreateContentType(['type' => 'date_content']);
1310
1311     // Create a field storage with settings to validate.
1312     $field_name = Unicode::strtolower($this->randomMachineName());
1313     $field_storage = FieldStorageConfig::create([
1314       'field_name' => $field_name,
1315       'entity_type' => 'node',
1316       'type' => 'daterange',
1317       'settings' => [
1318         'datetime_type' => DateRangeItem::DATETIME_TYPE_DATE,
1319       ],
1320     ]);
1321     $field_storage->save();
1322     $field = FieldConfig::create([
1323       'field_storage' => $field_storage,
1324       'field_name' => $field_name,
1325       'bundle' => 'date_content',
1326     ]);
1327     $field->save();
1328
1329     entity_get_form_display('node', 'date_content', 'default')
1330       ->setComponent($field_name, [
1331         'type' => 'datetime_default',
1332       ])
1333       ->save();
1334     $edit = [
1335       'title[0][value]' => $this->randomString(),
1336       'body[0][value]' => $this->randomString(),
1337       $field_name . '[0][value][date]' => '2016-04-01',
1338       $field_name . '[0][end_value][date]' => '2016-04-02',
1339     ];
1340     $this->drupalPostForm('node/add/date_content', $edit, t('Save'));
1341     $this->drupalGet('admin/structure/types/manage/date_content/fields/node.date_content.' . $field_name . '/storage');
1342     $result = $this->xpath("//*[@id='edit-settings-datetime-type' and contains(@disabled, 'disabled')]");
1343     $this->assertEqual(count($result), 1, "Changing datetime setting is disabled.");
1344     $this->assertText('There is data for this field in the database. The field settings can no longer be changed.');
1345   }
1346
1347 }