diff --git a/core/modules/datetime/datetime.views.inc b/core/modules/datetime/datetime.views.inc index b1742cf..d3b0d18 100644 --- a/core/modules/datetime/datetime.views.inc +++ b/core/modules/datetime/datetime.views.inc @@ -11,44 +11,42 @@ * Implements hook_field_views_data(). */ function datetime_field_views_data(FieldStorageConfigInterface $field_storage) { - if ($field_storage->getType() == 'datetime') { - // @todo This code only covers configurable fields, handle base table fields - // in https://www.drupal.org/node/2489476. - $data = views_field_default_views_data($field_storage); - foreach ($data as $table_name => $table_data) { - // Set the 'datetime' filter type. - $data[$table_name][$field_storage->getName() . '_value']['filter']['id'] = 'datetime'; + // @todo This code only covers configurable fields, handle base table fields + // in https://www.drupal.org/node/2489476. + $data = views_field_default_views_data($field_storage); + foreach ($data as $table_name => $table_data) { + // Set the 'datetime' filter type. + $data[$table_name][$field_storage->getName() . '_value']['filter']['id'] = 'datetime'; - // Set the 'datetime' argument type. - $data[$table_name][$field_storage->getName() . '_value']['argument']['id'] = 'datetime'; + // Set the 'datetime' argument type. + $data[$table_name][$field_storage->getName() . '_value']['argument']['id'] = 'datetime'; - // Create year, month, and day arguments. - $group = $data[$table_name][$field_storage->getName() . '_value']['group']; - $arguments = [ - // Argument type => help text. - 'year' => t('Date in the form of YYYY.'), - 'month' => t('Date in the form of MM (01 - 12).'), - 'day' => t('Date in the form of DD (01 - 31).'), - 'week' => t('Date in the form of WW (01 - 53).'), - 'year_month' => t('Date in the form of YYYYMM.'), - 'full_date' => t('Date in the form of CCYYMMDD.'), + // Create year, month, and day arguments. + $group = $data[$table_name][$field_storage->getName() . '_value']['group']; + $arguments = [ + // Argument type => help text. + 'year' => t('Date in the form of YYYY.'), + 'month' => t('Date in the form of MM (01 - 12).'), + 'day' => t('Date in the form of DD (01 - 31).'), + 'week' => t('Date in the form of WW (01 - 53).'), + 'year_month' => t('Date in the form of YYYYMM.'), + 'full_date' => t('Date in the form of CCYYMMDD.'), + ]; + foreach ($arguments as $argument_type => $help_text) { + $data[$table_name][$field_storage->getName() . '_value_' . $argument_type] = [ + 'title' => $field_storage->getLabel() . ' (' . $argument_type . ')', + 'help' => $help_text, + 'argument' => [ + 'field' => $field_storage->getName() . '_value', + 'id' => 'datetime_' . $argument_type, + ], + 'group' => $group, ]; - foreach ($arguments as $argument_type => $help_text) { - $data[$table_name][$field_storage->getName() . '_value_' . $argument_type] = [ - 'title' => $field_storage->getLabel() . ' (' . $argument_type . ')', - 'help' => $help_text, - 'argument' => [ - 'field' => $field_storage->getName() . '_value', - 'id' => 'datetime_' . $argument_type, - ], - 'group' => $group, - ]; - } - - // Set the 'datetime' sort handler. - $data[$table_name][$field_storage->getName() . '_value']['sort']['id'] = 'datetime'; } - return $data; + // Set the 'datetime' sort handler. + $data[$table_name][$field_storage->getName() . '_value']['sort']['id'] = 'datetime'; } + + return $data; } diff --git a/core/modules/datetime/src/Plugin/Field/FieldWidget/DateRangeDatelistWidget.php b/core/modules/datetime/src/Plugin/Field/FieldWidget/DateRangeDatelistWidget.php index 1693b61..a918213 100644 --- a/core/modules/datetime/src/Plugin/Field/FieldWidget/DateRangeDatelistWidget.php +++ b/core/modules/datetime/src/Plugin/Field/FieldWidget/DateRangeDatelistWidget.php @@ -4,6 +4,7 @@ use Drupal\Core\Field\FieldItemListInterface; use Drupal\Core\Form\FormStateInterface; +use Drupal\datetime\Plugin\Field\FieldType\DateRangeItem; /** * Plugin implementation of the 'daterange_datelist' widget. @@ -37,7 +38,7 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen $date_order = $this->getSetting('date_order'); - if ($this->getFieldSetting('daterange_type') == 'datetime') { + if ($this->getFieldSetting('daterange_type') == DateRangeItem::DATERANGE_TYPE_DATETIME) { $time_type = $this->getSetting('time_type'); $increment = $this->getSetting('increment'); } @@ -62,7 +63,6 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen break; } switch ($time_type) { - default: case '24': $date_part_order = array_merge($date_part_order, array('hour', 'minute')); break; diff --git a/core/modules/datetime/src/Tests/DateRangeFieldTest.php b/core/modules/datetime/src/Tests/DateRangeFieldTest.php index 8266183..08cbc8f 100644 --- a/core/modules/datetime/src/Tests/DateRangeFieldTest.php +++ b/core/modules/datetime/src/Tests/DateRangeFieldTest.php @@ -5,6 +5,7 @@ use Drupal\Component\Utility\SafeMarkup; use Drupal\Component\Utility\Unicode; use Drupal\Core\Datetime\DrupalDateTime; +use Drupal\Core\Datetime\Entity\DateFormat; use Drupal\Core\Entity\Entity\EntityViewDisplay; use Drupal\datetime\Plugin\Field\FieldType\DateRangeItem; use Drupal\entity_test\Entity\EntityTest; @@ -25,7 +26,7 @@ class DateRangeFieldTest extends WebTestBase { * * @var array */ - public static $modules = array('node', 'entity_test', 'datetime', 'field_ui'); + public static $modules = ['node', 'entity_test', 'datetime', 'field_ui']; /** * The default display settings to use for the formatters. @@ -65,14 +66,14 @@ protected function setUp() { ->set('timezone.default', 'Asia/Tokyo') ->save(); - $web_user = $this->drupalCreateUser(array( + $web_user = $this->drupalCreateUser([ 'access content', 'view test entity', 'administer entity_test content', 'administer entity_test form display', 'administer content types', 'administer node fields', - )); + ]); $this->drupalLogin($web_user); // Create a field with settings to validate. @@ -81,7 +82,7 @@ protected function setUp() { 'field_name' => $field_name, 'entity_type' => 'entity_test', 'type' => 'daterange', - 'settings' => array('daterange_type' => DateRangeItem::DATERANGE_TYPE_DATE), + 'settings' => ['daterange_type' => DateRangeItem::DATERANGE_TYPE_DATE], ]); $this->fieldStorage->save(); $this->field = FieldConfig::create([ @@ -92,9 +93,9 @@ protected function setUp() { $this->field->save(); entity_get_form_display($this->field->getTargetEntityTypeId(), $this->field->getTargetBundle(), 'default') - ->setComponent($field_name, array( + ->setComponent($field_name, [ 'type' => 'daterange_default', - )) + ]) ->save(); $this->defaultSettings = [ @@ -105,7 +106,7 @@ protected function setUp() { $this->displayOptions = [ 'type' => 'daterange_default', 'label' => 'hidden', - 'settings' => array('format_type' => 'medium') + $this->defaultSettings, + 'settings' => ['format_type' => 'medium'] + $this->defaultSettings, ]; entity_get_display($this->field->getTargetEntityTypeId(), $this->field->getTargetBundle(), 'full') ->setComponent($field_name, $this->displayOptions) @@ -116,28 +117,638 @@ protected function setUp() { * Tests date field functionality. */ function testDateRangeField() { - $this->fail('Write a real test.'); + $field_name = $this->fieldStorage->getName(); + + // Ensure field is set to a date-only field. + $this->fieldStorage->setSetting('daterange_type', DateRangeItem::DATERANGE_TYPE_DATE); + $this->fieldStorage->save(); + + // Display creation form. + $this->drupalGet('entity_test/add'); + $this->assertFieldByName("{$field_name}[0][value][date]", '', 'Start date element found.'); + $this->assertFieldByName("{$field_name}[0][value2][date]", '', 'End date element found.'); + $this->assertFieldByXPath('//*[@id="edit-' . $field_name . '-wrapper"]/h4[contains(@class, "js-form-required")]', TRUE, 'Required markup found'); + $this->assertNoFieldByName("{$field_name}[0][value][time]", '', 'Start time element not found.'); + $this->assertNoFieldByName("{$field_name}[0][value2][time]", '', 'End time element not found.'); + + // Build up dates in the UTC timezone. + $value = '2012-12-31 00:00:00'; + $start_date = new DrupalDateTime($value, 'UTC'); + $value2 = '2013-06-06 00:00:00'; + $end_date = new DrupalDateTime($value2, 'UTC'); + + // The expected values will use the default time. + datetime_date_default_time($start_date); + datetime_date_default_time($end_date); + + // Update the timezone to the system default. + $start_date->setTimezone(timezone_open(drupal_get_user_timezone())); + $end_date->setTimezone(timezone_open(drupal_get_user_timezone())); + + // Submit a valid date and ensure it is accepted. + $date_format = DateFormat::load('html_date')->getPattern(); + $time_format = DateFormat::load('html_time')->getPattern(); + + $edit = array( + "{$field_name}[0][value][date]" => $start_date->format($date_format), + "{$field_name}[0][value2][date]" => $end_date->format($date_format), + ); + $this->drupalPostForm(NULL, $edit, t('Save')); + preg_match('|entity_test/manage/(\d+)|', $this->url, $match); + $id = $match[1]; + $this->assertText(t('entity_test @id has been created.', array('@id' => $id))); + $this->assertRaw($start_date->format($date_format)); + $this->assertNoRaw($start_date->format($time_format)); + $this->assertRaw($end_date->format($date_format)); + $this->assertNoRaw($end_date->format($time_format)); + + // Verify that the default formatter works. + $this->displayOptions['settings'] = [ + 'format_type' => 'long', + 'separator' => 'THESEPARATOR', + ] + $this->defaultSettings; + entity_get_display($this->field->getTargetEntityTypeId(), $this->field->getTargetBundle(), 'full') + ->setComponent($field_name, $this->displayOptions) + ->save(); + + $start_expected = format_date($start_date->getTimestamp(), 'long'); + $start_expected_iso = format_date($start_date->getTimestamp(), 'custom', 'Y-m-d\TH:i:s\Z', 'UTC'); + $end_expected = format_date($end_date->getTimestamp(), 'long'); + $end_expected_iso = format_date($end_date->getTimestamp(), 'custom', 'Y-m-d\TH:i:s\Z', 'UTC'); + $this->renderTestEntity($id); + $this->assertFieldByXPath('//time[@datetime="' . $start_expected_iso . '"]', $start_expected, SafeMarkup::format('Formatted date field using %value format displayed as %expected with %expected_iso attribute.', ['%value' => 'long', '%expected' => $start_expected, '%expected_iso' => $start_expected_iso])); + $this->assertFieldByXPath('//time[@datetime="' . $end_expected_iso . '"]', $end_expected, SafeMarkup::format('Formatted date field using %value format displayed as %expected with %expected_iso attribute.', ['%value' => 'long', '%expected' => $end_expected, '%expected_iso' => $end_expected_iso])); + $this->assertText(' THESEPARATOR ', 'Found proper separator'); + + // Verify that the plain formatter works. + $this->displayOptions['type'] = 'daterange_plain'; + $this->displayOptions['settings'] = $this->defaultSettings; + entity_get_display($this->field->getTargetEntityTypeId(), $this->field->getTargetBundle(), 'full') + ->setComponent($field_name, $this->displayOptions) + ->save(); + $expected = $start_date->format(DATETIME_DATE_STORAGE_FORMAT) . ' - ' . $end_date->format(DATETIME_DATE_STORAGE_FORMAT); + $this->renderTestEntity($id); + $this->assertText($expected, SafeMarkup::format('Formatted date field using plain format displayed as %expected.', array('%expected' => $expected))); + + // Verify that the custom formatter works. + $this->displayOptions['type'] = 'daterange_custom'; + $this->displayOptions['settings'] = array('date_format' => 'm/d/Y') + $this->defaultSettings; + entity_get_display($this->field->getTargetEntityTypeId(), $this->field->getTargetBundle(), 'full') + ->setComponent($field_name, $this->displayOptions) + ->save(); + $expected = $start_date->format($this->displayOptions['settings']['date_format']) . ' - ' . $end_date->format($this->displayOptions['settings']['date_format']); + $this->renderTestEntity($id); + $this->assertText($expected, SafeMarkup::format('Formatted date field using daterange_custom format displayed as %expected.', array('%expected' => $expected))); } /** * Tests date and time field. */ function testDatetimeRangeField() { - $this->fail('Write a real test.'); + $field_name = $this->fieldStorage->getName(); + + // Ensure the field to a datetime field. + $this->fieldStorage->setSetting('daterange_type', DateRangeItem::DATERANGE_TYPE_DATETIME); + $this->fieldStorage->save(); + + // Display creation form. + $this->drupalGet('entity_test/add'); + $this->assertFieldByName("{$field_name}[0][value][date]", '', 'Start date element found.'); + $this->assertFieldByName("{$field_name}[0][value][time]", '', 'Start time element found.'); + $this->assertFieldByName("{$field_name}[0][value2][date]", '', 'End date element found.'); + $this->assertFieldByName("{$field_name}[0][value2][time]", '', 'End time element found.'); + + // Build up dates in the UTC timezone. + $value = '2012-12-31 00:00:00'; + $start_date = new DrupalDateTime($value, 'UTC'); + $value2 = '2013-06-06 00:00:00'; + $end_date = new DrupalDateTime($value2, 'UTC'); + + // Update the timezone to the system default. + $start_date->setTimezone(timezone_open(drupal_get_user_timezone())); + $end_date->setTimezone(timezone_open(drupal_get_user_timezone())); + + // Submit a valid date and ensure it is accepted. + $date_format = DateFormat::load('html_date')->getPattern(); + $time_format = DateFormat::load('html_time')->getPattern(); + + $edit = array( + "{$field_name}[0][value][date]" => $start_date->format($date_format), + "{$field_name}[0][value][time]" => $start_date->format($time_format), + "{$field_name}[0][value2][date]" => $end_date->format($date_format), + "{$field_name}[0][value2][time]" => $end_date->format($time_format), + ); + $this->drupalPostForm(NULL, $edit, t('Save')); + preg_match('|entity_test/manage/(\d+)|', $this->url, $match); + $id = $match[1]; + $this->assertText(t('entity_test @id has been created.', array('@id' => $id))); + $this->assertRaw($start_date->format($date_format)); + $this->assertRaw($start_date->format($time_format)); + $this->assertRaw($end_date->format($date_format)); + $this->assertRaw($end_date->format($time_format)); + + // Verify that the default formatter works. + $this->displayOptions['settings'] = [ + 'format_type' => 'long', + 'separator' => 'THESEPARATOR', + ] + $this->defaultSettings; + entity_get_display($this->field->getTargetEntityTypeId(), $this->field->getTargetBundle(), 'full') + ->setComponent($field_name, $this->displayOptions) + ->save(); + + $start_expected = format_date($start_date->getTimestamp(), 'long'); + $start_expected_iso = format_date($start_date->getTimestamp(), 'custom', 'Y-m-d\TH:i:s\Z', 'UTC'); + $end_expected = format_date($end_date->getTimestamp(), 'long'); + $end_expected_iso = format_date($end_date->getTimestamp(), 'custom', 'Y-m-d\TH:i:s\Z', 'UTC'); + $this->renderTestEntity($id); + $this->assertFieldByXPath('//time[@datetime="' . $start_expected_iso . '"]', $start_expected, SafeMarkup::format('Formatted date field using %value format displayed as %expected with %expected_iso attribute.', ['%value' => 'long', '%expected' => $start_expected, '%expected_iso' => $start_expected_iso])); + $this->assertFieldByXPath('//time[@datetime="' . $end_expected_iso . '"]', $end_expected, SafeMarkup::format('Formatted date field using %value format displayed as %expected with %expected_iso attribute.', ['%value' => 'long', '%expected' => $end_expected, '%expected_iso' => $end_expected_iso])); + $this->assertText(' THESEPARATOR ', 'Found proper separator'); + + // Verify that the plain formatter works. + $this->displayOptions['type'] = 'daterange_plain'; + $this->displayOptions['settings'] = $this->defaultSettings; + entity_get_display($this->field->getTargetEntityTypeId(), $this->field->getTargetBundle(), 'full') + ->setComponent($field_name, $this->displayOptions) + ->save(); + $expected = $start_date->format(DATETIME_DATETIME_STORAGE_FORMAT) . ' - ' . $end_date->format(DATETIME_DATETIME_STORAGE_FORMAT); + $this->renderTestEntity($id); + $this->assertText($expected, SafeMarkup::format('Formatted date field using plain format displayed as %expected.', array('%expected' => $expected))); + + // Verify that the 'datetime_custom' formatter works. + $this->displayOptions['type'] = 'daterange_custom'; + $this->displayOptions['settings'] = ['date_format' => 'm/d/Y g:i:s A'] + $this->defaultSettings; + entity_get_display($this->field->getTargetEntityTypeId(), $this->field->getTargetBundle(), 'full') + ->setComponent($field_name, $this->displayOptions) + ->save(); + $expected = $start_date->format($this->displayOptions['settings']['date_format']) . ' - ' . $end_date->format($this->displayOptions['settings']['date_format']); + $this->renderTestEntity($id); + $this->assertText($expected, SafeMarkup::format('Formatted date field using daterange_custom format displayed as %expected.', array('%expected' => $expected))); + + // Verify that the 'timezone_override' setting works. + $this->displayOptions['type'] = 'daterange_custom'; + $this->displayOptions['settings'] = ['date_format' => 'm/d/Y g:i:s A', 'timezone_override' => 'America/New_York'] + $this->defaultSettings; + entity_get_display($this->field->getTargetEntityTypeId(), $this->field->getTargetBundle(), 'full') + ->setComponent($field_name, $this->displayOptions) + ->save(); + $expected = $start_date->format($this->displayOptions['settings']['date_format'], ['timezone' => 'America/New_York']); + $expected .= ' - ' . $end_date->format($this->displayOptions['settings']['date_format'], ['timezone' => 'America/New_York']); + $this->renderTestEntity($id); + $this->assertText($expected, SafeMarkup::format('Formatted date field using daterange_custom format displayed as %expected.', array('%expected' => $expected))); } /** * Tests all-day field. */ function testAlldayRangeField() { - $this->fail('Write a real test.'); + $field_name = $this->fieldStorage->getName(); + + // Ensure field is set to a all-day field. + $this->fieldStorage->setSetting('daterange_type', DateRangeItem::DATERANGE_TYPE_ALLDAY); + $this->fieldStorage->save(); + + // Display creation form. + $this->drupalGet('entity_test/add'); + $this->assertFieldByName("{$field_name}[0][value][date]", '', 'Start date element found.'); + $this->assertFieldByName("{$field_name}[0][value2][date]", '', 'End date element found.'); + $this->assertFieldByXPath('//*[@id="edit-' . $field_name . '-wrapper"]/h4[contains(@class, "js-form-required")]', TRUE, 'Required markup found'); + $this->assertNoFieldByName("{$field_name}[0][value][time]", '', 'Start time element not found.'); + $this->assertNoFieldByName("{$field_name}[0][value2][time]", '', 'End time element not found.'); + + // Build up dates in the proper timezone. + $value = '2012-12-31 00:00:00'; + $start_date = new DrupalDateTime($value, timezone_open(drupal_get_user_timezone())); + $value2 = '2013-06-06 23:59:59'; + $end_date = new DrupalDateTime($value2, timezone_open(drupal_get_user_timezone())); + + // Submit a valid date and ensure it is accepted. + $date_format = DateFormat::load('html_date')->getPattern(); + $time_format = DateFormat::load('html_time')->getPattern(); + + $edit = array( + "{$field_name}[0][value][date]" => $start_date->format($date_format), + "{$field_name}[0][value2][date]" => $end_date->format($date_format), + ); + $this->drupalPostForm(NULL, $edit, t('Save')); + preg_match('|entity_test/manage/(\d+)|', $this->url, $match); + $id = $match[1]; + $this->assertText(t('entity_test @id has been created.', array('@id' => $id))); + $this->assertRaw($start_date->format($date_format)); + $this->assertNoRaw($start_date->format($time_format)); + $this->assertRaw($end_date->format($date_format)); + $this->assertNoRaw($end_date->format($time_format)); + + // Verify that the default formatter works. + $this->displayOptions['settings'] = [ + 'format_type' => 'long', + 'separator' => 'THESEPARATOR', + ] + $this->defaultSettings; + entity_get_display($this->field->getTargetEntityTypeId(), $this->field->getTargetBundle(), 'full') + ->setComponent($field_name, $this->displayOptions) + ->save(); + + $start_expected = format_date($start_date->getTimestamp(), 'long'); + $start_expected_iso = format_date($start_date->getTimestamp(), 'custom', 'Y-m-d\TH:i:s\Z', 'UTC'); + $end_expected = format_date($end_date->getTimestamp(), 'long'); + $end_expected_iso = format_date($end_date->getTimestamp(), 'custom', 'Y-m-d\TH:i:s\Z', 'UTC'); + $this->renderTestEntity($id); + $this->assertFieldByXPath('//time[@datetime="' . $start_expected_iso . '"]', $start_expected, SafeMarkup::format('Formatted date field using %value format displayed as %expected with %expected_iso attribute.', ['%value' => 'long', '%expected' => $start_expected, '%expected_iso' => $start_expected_iso])); + $this->assertFieldByXPath('//time[@datetime="' . $end_expected_iso . '"]', $end_expected, SafeMarkup::format('Formatted date field using %value format displayed as %expected with %expected_iso attribute.', ['%value' => 'long', '%expected' => $end_expected, '%expected_iso' => $end_expected_iso])); + $this->assertText(' THESEPARATOR ', 'Found proper separator'); + + // Verify that the plain formatter works. + $this->displayOptions['type'] = 'daterange_plain'; + $this->displayOptions['settings'] = $this->defaultSettings; + entity_get_display($this->field->getTargetEntityTypeId(), $this->field->getTargetBundle(), 'full') + ->setComponent($field_name, $this->displayOptions) + ->save(); + $expected = $start_date->format(DATETIME_DATETIME_STORAGE_FORMAT) . ' - ' . $end_date->format(DATETIME_DATETIME_STORAGE_FORMAT); + $this->renderTestEntity($id); + $this->assertText($expected, SafeMarkup::format('Formatted date field using plain format displayed as %expected.', array('%expected' => $expected))); + + // Verify that the custom formatter works. + $this->displayOptions['type'] = 'daterange_custom'; + $this->displayOptions['settings'] = array('date_format' => 'm/d/Y') + $this->defaultSettings; + entity_get_display($this->field->getTargetEntityTypeId(), $this->field->getTargetBundle(), 'full') + ->setComponent($field_name, $this->displayOptions) + ->save(); + $expected = $start_date->format($this->displayOptions['settings']['date_format']) . ' - ' . $end_date->format($this->displayOptions['settings']['date_format']); + $this->renderTestEntity($id); + $this->assertText($expected, SafeMarkup::format('Formatted date field using daterange_custom format displayed as %expected.', array('%expected' => $expected))); + + // Verify that the 'timezone_override' setting works. + $this->displayOptions['type'] = 'daterange_custom'; + $this->displayOptions['settings'] = ['date_format' => 'm/d/Y g:i:s A', 'timezone_override' => 'America/New_York'] + $this->defaultSettings; + entity_get_display($this->field->getTargetEntityTypeId(), $this->field->getTargetBundle(), 'full') + ->setComponent($field_name, $this->displayOptions) + ->save(); + $expected = $start_date->format($this->displayOptions['settings']['date_format'], ['timezone' => 'America/New_York']); + $expected .= ' - ' . $end_date->format($this->displayOptions['settings']['date_format'], ['timezone' => 'America/New_York']); + $this->renderTestEntity($id); + $this->assertText($expected, SafeMarkup::format('Formatted date field using daterange_custom format displayed as %expected.', array('%expected' => $expected))); } /** * Tests Date Range List Widget functionality. */ function testDatelistWidget() { - $this->fail('Write a real test.'); + $field_name = $this->fieldStorage->getName(); + + // Ensure field is set to a date only field. + $this->fieldStorage->setSetting('daterange_type', DateRangeItem::DATERANGE_TYPE_DATE); + $this->fieldStorage->save(); + + // Change the widget to a datelist widget. + entity_get_form_display($this->field->getTargetEntityTypeId(), $this->field->getTargetBundle(), 'default') + ->setComponent($field_name, [ + 'type' => 'daterange_datelist', + 'settings' => [ + 'date_order' => 'YMD', + ], + ]) + ->save(); + \Drupal::entityManager()->clearCachedFieldDefinitions(); + + // Display creation form. + $this->drupalGet('entity_test/add'); + + // Assert that Hour and Minute Elements do not appear on Date Only + $this->assertNoFieldByXPath("//*[@id=\"edit-$field_name-0-value-hour\"]", NULL, 'Hour element not found on Date Only.'); + $this->assertNoFieldByXPath("//*[@id=\"edit-$field_name-0-value-minute\"]", NULL, 'Minute element not found on Date Only.'); + $this->assertNoFieldByXPath("//*[@id=\"edit-$field_name-0-value2-hour\"]", NULL, 'Hour element not found on Date Only.'); + $this->assertNoFieldByXPath("//*[@id=\"edit-$field_name-0-value2-minute\"]", NULL, 'Minute element not found on Date Only.'); + + // Go to the form display page to assert that increment option does not appear on Date Only + $fieldEditUrl = 'entity_test/structure/entity_test/form-display'; + $this->drupalGet($fieldEditUrl); + + // Click on the widget settings button to open the widget settings form. + $this->drupalPostAjaxForm(NULL, [], $field_name . "_settings_edit"); + $xpathIncr = "//select[starts-with(@id, \"edit-fields-$field_name-settings-edit-form-settings-increment\")]"; + $this->assertNoFieldByXPath($xpathIncr, NULL, 'Increment element not found for Date Only.'); + + // Change the field is set to an all day field. + $this->fieldStorage->setSetting('daterange_type', DateRangeItem::DATERANGE_TYPE_ALLDAY); + $this->fieldStorage->save(); + + // Change the widget to a datelist widget. + entity_get_form_display($this->field->getTargetEntityTypeId(), $this->field->getTargetBundle(), 'default') + ->setComponent($field_name, [ + 'type' => 'daterange_datelist', + 'settings' => [ + 'date_order' => 'YMD', + ], + ]) + ->save(); + \Drupal::entityManager()->clearCachedFieldDefinitions(); + + // Display creation form. + $this->drupalGet('entity_test/add'); + + // Assert that Hour and Minute Elements do not appear on Date Only + $this->assertNoFieldByXPath("//*[@id=\"edit-$field_name-0-value-hour\"]", NULL, 'Hour element not found on Date Only.'); + $this->assertNoFieldByXPath("//*[@id=\"edit-$field_name-0-value-minute\"]", NULL, 'Minute element not found on Date Only.'); + $this->assertNoFieldByXPath("//*[@id=\"edit-$field_name-0-value2-hour\"]", NULL, 'Hour element not found on Date Only.'); + $this->assertNoFieldByXPath("//*[@id=\"edit-$field_name-0-value2-minute\"]", NULL, 'Minute element not found on Date Only.'); + + // Go to the form display page to assert that increment option does not appear on Date Only + $fieldEditUrl = 'entity_test/structure/entity_test/form-display'; + $this->drupalGet($fieldEditUrl); + + // Click on the widget settings button to open the widget settings form. + $this->drupalPostAjaxForm(NULL, [], $field_name . "_settings_edit"); + $xpathIncr = "//select[starts-with(@id, \"edit-fields-$field_name-settings-edit-form-settings-increment\")]"; + $this->assertNoFieldByXPath($xpathIncr, NULL, 'Increment element not found for Date Only.'); + + // Change the field to a datetime field. + $this->fieldStorage->setSetting('daterange_type', DateRangeItem::DATERANGE_TYPE_DATETIME); + $this->fieldStorage->save(); + + // Change the widget to a datelist widget. + entity_get_form_display($this->field->getTargetEntityTypeId(), $this->field->getTargetBundle(), 'default') + ->setComponent($field_name, [ + 'type' => 'daterange_datelist', + 'settings' => [ + 'increment' => 1, + 'date_order' => 'YMD', + 'time_type' => '12', + ], + ]) + ->save(); + \Drupal::entityManager()->clearCachedFieldDefinitions(); + + // Go to the form display page to assert that increment option does appear on Date Time + $fieldEditUrl = 'entity_test/structure/entity_test/form-display'; + $this->drupalGet($fieldEditUrl); + + // Click on the widget settings button to open the widget settings form. + $this->drupalPostAjaxForm(NULL, [], $field_name . "_settings_edit"); + $this->assertFieldByXPath($xpathIncr, NULL, 'Increment element found for Date and time.'); + + // Display creation form. + $this->drupalGet('entity_test/add'); + + $this->assertFieldByXPath("//*[@id=\"edit-$field_name-0-value-year\"]", NULL, 'Year element found.'); + $this->assertOptionSelected("edit-$field_name-0-value-year", '', 'No year selected.'); + $this->assertOptionByText("edit-$field_name-0-value-year", t('Year')); + $this->assertFieldByXPath("//*[@id=\"edit-$field_name-0-value-month\"]", NULL, 'Month element found.'); + $this->assertOptionSelected("edit-$field_name-0-value-month", '', 'No month selected.'); + $this->assertOptionByText("edit-$field_name-0-value-month", t('Month')); + $this->assertFieldByXPath("//*[@id=\"edit-$field_name-0-value-day\"]", NULL, 'Day element found.'); + $this->assertOptionSelected("edit-$field_name-0-value-day", '', 'No day selected.'); + $this->assertOptionByText("edit-$field_name-0-value-day", t('Day')); + $this->assertFieldByXPath("//*[@id=\"edit-$field_name-0-value-hour\"]", NULL, 'Hour element found.'); + $this->assertOptionSelected("edit-$field_name-0-value-hour", '', 'No hour selected.'); + $this->assertOptionByText("edit-$field_name-0-value-hour", t('Hour')); + $this->assertFieldByXPath("//*[@id=\"edit-$field_name-0-value-minute\"]", NULL, 'Minute element found.'); + $this->assertOptionSelected("edit-$field_name-0-value-minute", '', 'No minute selected.'); + $this->assertOptionByText("edit-$field_name-0-value-minute", t('Minute')); + $this->assertNoFieldByXPath("//*[@id=\"edit-$field_name-0-value-second\"]", NULL, 'Second element not found.'); + $this->assertFieldByXPath("//*[@id=\"edit-$field_name-0-value-ampm\"]", NULL, 'AMPM element found.'); + $this->assertOptionSelected("edit-$field_name-0-value-ampm", '', 'No ampm selected.'); + $this->assertOptionByText("edit-$field_name-0-value-ampm", t('AM/PM')); + + $this->assertFieldByXPath("//*[@id=\"edit-$field_name-0-value2-year\"]", NULL, 'Year element found.'); + $this->assertOptionSelected("edit-$field_name-0-value2-year", '', 'No year selected.'); + $this->assertOptionByText("edit-$field_name-0-value2-year", t('Year')); + $this->assertFieldByXPath("//*[@id=\"edit-$field_name-0-value2-month\"]", NULL, 'Month element found.'); + $this->assertOptionSelected("edit-$field_name-0-value2-month", '', 'No month selected.'); + $this->assertOptionByText("edit-$field_name-0-value2-month", t('Month')); + $this->assertFieldByXPath("//*[@id=\"edit-$field_name-0-value2-day\"]", NULL, 'Day element found.'); + $this->assertOptionSelected("edit-$field_name-0-value2-day", '', 'No day selected.'); + $this->assertOptionByText("edit-$field_name-0-value2-day", t('Day')); + $this->assertFieldByXPath("//*[@id=\"edit-$field_name-0-value2-hour\"]", NULL, 'Hour element found.'); + $this->assertOptionSelected("edit-$field_name-0-value2-hour", '', 'No hour selected.'); + $this->assertOptionByText("edit-$field_name-0-value2-hour", t('Hour')); + $this->assertFieldByXPath("//*[@id=\"edit-$field_name-0-value2-minute\"]", NULL, 'Minute element found.'); + $this->assertOptionSelected("edit-$field_name-0-value2-minute", '', 'No minute selected.'); + $this->assertOptionByText("edit-$field_name-0-value2-minute", t('Minute')); + $this->assertNoFieldByXPath("//*[@id=\"edit-$field_name-0-value2-second\"]", NULL, 'Second element not found.'); + $this->assertFieldByXPath("//*[@id=\"edit-$field_name-0-value2-ampm\"]", NULL, 'AMPM element found.'); + $this->assertOptionSelected("edit-$field_name-0-value2-ampm", '', 'No ampm selected.'); + $this->assertOptionByText("edit-$field_name-0-value2-ampm", t('AM/PM')); + + // Submit a valid date and ensure it is accepted. + $start_date_value = ['year' => 2012, 'month' => 12, 'day' => 31, 'hour' => 5, 'minute' => 15]; + $end_date_value = ['year' => 2013, 'month' => 1, 'day' => 15, 'hour' => 3, 'minute' => 30]; + + $edit = []; + // Add the ampm indicator since we are testing 12 hour time. + $start_date_value['ampm'] = 'am'; + $end_date_value['ampm'] = 'pm'; + foreach ($start_date_value as $part => $value) { + $edit["{$field_name}[0][value][$part]"] = $value; + } + foreach ($end_date_value as $part => $value) { + $edit["{$field_name}[0][value2][$part]"] = $value; + } + + $this->drupalPostForm(NULL, $edit, t('Save')); + preg_match('|entity_test/manage/(\d+)|', $this->url, $match); + $id = $match[1]; + $this->assertText(t('entity_test @id has been created.', ['@id' => $id])); + + $this->assertOptionSelected("edit-$field_name-0-value-year", '2012', 'Correct year selected.'); + $this->assertOptionSelected("edit-$field_name-0-value-month", '12', 'Correct month selected.'); + $this->assertOptionSelected("edit-$field_name-0-value-day", '31', 'Correct day selected.'); + $this->assertOptionSelected("edit-$field_name-0-value-hour", '5', 'Correct hour selected.'); + $this->assertOptionSelected("edit-$field_name-0-value-minute", '15', 'Correct minute selected.'); + $this->assertOptionSelected("edit-$field_name-0-value-ampm", 'am', 'Correct ampm selected.'); + + $this->assertOptionSelected("edit-$field_name-0-value2-year", '2013', 'Correct year selected.'); + $this->assertOptionSelected("edit-$field_name-0-value2-month", '1', 'Correct month selected.'); + $this->assertOptionSelected("edit-$field_name-0-value2-day", '15', 'Correct day selected.'); + $this->assertOptionSelected("edit-$field_name-0-value2-hour", '3', 'Correct hour selected.'); + $this->assertOptionSelected("edit-$field_name-0-value2-minute", '30', 'Correct minute selected.'); + $this->assertOptionSelected("edit-$field_name-0-value2-ampm", 'pm', 'Correct ampm selected.'); + + // Test the widget using increment other than 1 and 24 hour mode. + entity_get_form_display($this->field->getTargetEntityTypeId(), $this->field->getTargetBundle(), 'default') + ->setComponent($field_name, [ + 'type' => 'daterange_datelist', + 'settings' => [ + 'increment' => 15, + 'date_order' => 'YMD', + 'time_type' => '24', + ], + ]) + ->save(); + \Drupal::entityManager()->clearCachedFieldDefinitions(); + + // Display creation form. + $this->drupalGet('entity_test/add'); + + // Other elements are unaffected by the changed settings. + $this->assertFieldByXPath("//*[@id=\"edit-$field_name-0-value-hour\"]", NULL, 'Hour element found.'); + $this->assertOptionSelected("edit-$field_name-0-value-hour", '', 'No hour selected.'); + $this->assertNoFieldByXPath("//*[@id=\"edit-$field_name-0-value-ampm\"]", NULL, 'AMPM element not found.'); + $this->assertFieldByXPath("//*[@id=\"edit-$field_name-0-value2-hour\"]", NULL, 'Hour element found.'); + $this->assertOptionSelected("edit-$field_name-0-value2-hour", '', 'No hour selected.'); + $this->assertNoFieldByXPath("//*[@id=\"edit-$field_name-0-value2-ampm\"]", NULL, 'AMPM element not found.'); + + // Submit a valid date and ensure it is accepted. + $start_date_value = ['year' => 2012, 'month' => 12, 'day' => 31, 'hour' => 17, 'minute' => 15]; + $end_date_value = ['year' => 2013, 'month' => 1, 'day' => 15, 'hour' => 3, 'minute' => 30]; + + $edit = []; + foreach ($start_date_value as $part => $value) { + $edit["{$field_name}[0][value][$part]"] = $value; + } + foreach ($end_date_value as $part => $value) { + $edit["{$field_name}[0][value2][$part]"] = $value; + } + + $this->drupalPostForm(NULL, $edit, t('Save')); + preg_match('|entity_test/manage/(\d+)|', $this->url, $match); + $id = $match[1]; + $this->assertText(t('entity_test @id has been created.', ['@id' => $id])); + + $this->assertOptionSelected("edit-$field_name-0-value-year", '2012', 'Correct year selected.'); + $this->assertOptionSelected("edit-$field_name-0-value-month", '12', 'Correct month selected.'); + $this->assertOptionSelected("edit-$field_name-0-value-day", '31', 'Correct day selected.'); + $this->assertOptionSelected("edit-$field_name-0-value-hour", '17', 'Correct hour selected.'); + $this->assertOptionSelected("edit-$field_name-0-value-minute", '15', 'Correct minute selected.'); + + $this->assertOptionSelected("edit-$field_name-0-value2-year", '2013', 'Correct year selected.'); + $this->assertOptionSelected("edit-$field_name-0-value2-month", '1', 'Correct month selected.'); + $this->assertOptionSelected("edit-$field_name-0-value2-day", '15', 'Correct day selected.'); + $this->assertOptionSelected("edit-$field_name-0-value2-hour", '3', 'Correct hour selected.'); + $this->assertOptionSelected("edit-$field_name-0-value2-minute", '30', 'Correct minute selected.'); + + // Test the widget for partial completion of fields. + entity_get_form_display($this->field->getTargetEntityTypeId(), $this->field->getTargetBundle(), 'default') + ->setComponent($field_name, [ + 'type' => 'daterange_datelist', + 'settings' => [ + 'increment' => 1, + 'date_order' => 'YMD', + 'time_type' => '24', + ], + ]) + ->save(); + \Drupal::entityManager()->clearCachedFieldDefinitions(); + + // Test the widget for validation notifications. + foreach ($this->datelistDataProvider() as $data) { + list($start_date_value, $end_date_value, $expected) = $data; + + // Display creation form. + $this->drupalGet('entity_test/add'); + + // Submit a partial date and ensure and error message is provided. + $edit = []; + foreach ($start_date_value as $part => $value) { + $edit["{$field_name}[0][value][$part]"] = $value; + } + foreach ($end_date_value as $part => $value) { + $edit["{$field_name}[0][value2][$part]"] = $value; + } + + $this->drupalPostForm(NULL, $edit, t('Save')); + $this->assertResponse(200); + foreach ($expected as $expected_text) { + $this->assertText(t($expected_text)); + } + } + + // Test the widget for complete input with zeros as part of selections. + $this->drupalGet('entity_test/add'); + + $start_date_value = ['year' => 2012, 'month' => 12, 'day' => 31, 'hour' => 0, 'minute' => 0]; + $end_date_value = ['year' => 2013, 'month' => 1, 'day' => 15, 'hour' => 3, 'minute' => 30]; + $edit = []; + foreach ($start_date_value as $part => $value) { + $edit["{$field_name}[0][value][$part]"] = $value; + } + foreach ($end_date_value as $part => $value) { + $edit["{$field_name}[0][value2][$part]"] = $value; + } + + $this->drupalPostForm(NULL, $edit, t('Save')); + $this->assertResponse(200); + preg_match('|entity_test/manage/(\d+)|', $this->url, $match); + $id = $match[1]; + $this->assertText(t('entity_test @id has been created.', ['@id' => $id])); + + // Test the widget to ensure zeros are not deselected on validation. + $this->drupalGet('entity_test/add'); + + $start_date_value = ['year' => 2012, 'month' => 12, 'day' => 31, 'hour' => 0, 'minute' => 0]; + $end_date_value = ['year' => 2013, 'month' => 1, 'day' => 15, 'hour' => 3, 'minute' => 0]; + $edit = []; + foreach ($start_date_value as $part => $value) { + $edit["{$field_name}[0][value][$part]"] = $value; + } + foreach ($end_date_value as $part => $value) { + $edit["{$field_name}[0][value2][$part]"] = $value; + } + + $this->drupalPostForm(NULL, $edit, t('Save')); + $this->assertResponse(200); + $this->assertOptionSelected("edit-$field_name-0-value-minute", '0', 'Correct minute selected.'); + $this->assertOptionSelected("edit-$field_name-0-value2-minute", '0', 'Correct minute selected.'); + } + + /** + * The data provider for testing the validation of the datelist widget. + * + * @return array + * An array of datelist input permutations to test. + */ + protected function datelistDataProvider() { + return [ + // Year only selected, validation error on Month, Day, Hour, Minute. + [['year' => 2012, 'month' => '', 'day' => '', 'hour' => '', 'minute' => ''], + ['year' => 2013, 'month' => '1', 'day' => '15', 'hour' => '3', 'minute' => '30'], [ + 'A value must be selected for month.', + 'A value must be selected for day.', + 'A value must be selected for hour.', + 'A value must be selected for minute.', + ]], + // Year and Month selected, validation error on Day, Hour, Minute. + [['year' => 2012, 'month' => '12', 'day' => '', 'hour' => '', 'minute' => ''], + ['year' => 2013, 'month' => '1', 'day' => '15', 'hour' => '3', 'minute' => '30'], [ + 'A value must be selected for day.', + 'A value must be selected for hour.', + 'A value must be selected for minute.', + ]], + // Year, Month and Day selected, validation error on Hour, Minute. + [['year' => 2012, 'month' => '12', 'day' => '31', 'hour' => '', 'minute' => ''], + ['year' => 2013, 'month' => '1', 'day' => '15', 'hour' => '3', 'minute' => '30'], [ + 'A value must be selected for hour.', + 'A value must be selected for minute.', + ]], + // Year, Month, Day and Hour selected, validation error on Minute only. + [['year' => 2012, 'month' => '12', 'day' => '31', 'hour' => '0', 'minute' => ''], + ['year' => 2013, 'month' => '1', 'day' => '15', 'hour' => '3', 'minute' => '30'], [ + 'A value must be selected for minute.', + ]], + [['year' => 2012, 'month' => '12', 'day' => '31', 'hour' => '0', 'minute' => '0'], + ['year' => 2013, 'month' => '', 'day' => '', 'hour' => '', 'minute' => ''], [ + 'A value must be selected for month.', + 'A value must be selected for day.', + 'A value must be selected for hour.', + 'A value must be selected for minute.', + ]], + // Year and Month selected, validation error on Day, Hour, Minute. + [['year' => 2012, 'month' => '12', 'day' => '31', 'hour' => '0', 'minute' => '0'], + ['year' => 2013, 'month' => '1', 'day' => '', 'hour' => '', 'minute' => ''], [ + 'A value must be selected for day.', + 'A value must be selected for hour.', + 'A value must be selected for minute.', + ]], + // Year, Month and Day selected, validation error on Hour, Minute. + [['year' => 2012, 'month' => '12', 'day' => '31', 'hour' => '0', 'minute' => '0'], + ['year' => 2013, 'month' => '1', 'day' => '15', 'hour' => '', 'minute' => ''], [ + 'A value must be selected for hour.', + 'A value must be selected for minute.', + ]], + // Year, Month, Day and Hour selected, validation error on Minute only. + [['year' => 2012, 'month' => '12', 'day' => '31', 'hour' => '0', 'minute' => '0'], + ['year' => 2013, 'month' => '1', 'day' => '15', 'hour' => '3', 'minute' => ''], [ + 'A value must be selected for minute.', + ]], + ]; } /**