function DateTimeFieldTest::testDateField

Same name in other branches
  1. 9 core/modules/datetime/tests/src/Functional/DateTimeFieldTest.php \Drupal\Tests\datetime\Functional\DateTimeFieldTest::testDateField()
  2. 8.9.x core/modules/datetime/tests/src/Functional/DateTimeFieldTest.php \Drupal\Tests\datetime\Functional\DateTimeFieldTest::testDateField()
  3. 11.x core/modules/datetime/tests/src/Functional/DateTimeFieldTest.php \Drupal\Tests\datetime\Functional\DateTimeFieldTest::testDateField()

Tests date field functionality.

File

core/modules/datetime/tests/src/Functional/DateTimeFieldTest.php, line 46

Class

DateTimeFieldTest
Tests Datetime field functionality.

Namespace

Drupal\Tests\datetime\Functional

Code

public function testDateField() : void {
    $field_name = $this->fieldStorage
        ->getName();
    $display_repository = \Drupal::service('entity_display.repository');
    // Loop through defined timezones to test that date-only fields work at the
    // extremes.
    foreach (static::$timezones as $timezone) {
        $this->setSiteTimezone($timezone);
        $this->assertEquals($timezone, $this->config('system.date')
            ->get('timezone.default'), 'Time zone set to ' . $timezone);
        // Display creation form.
        $this->drupalGet('entity_test/add');
        $this->assertSession()
            ->fieldValueEquals("{$field_name}[0][value][date]", '');
        $this->assertSession()
            ->elementExists('xpath', '//*[@id="edit-' . $field_name . '-wrapper"]//label[contains(@class,"js-form-required")]');
        $this->assertSession()
            ->fieldNotExists("{$field_name}[0][value][time]");
        // ARIA described-by.
        $this->assertSession()
            ->elementExists('xpath', '//input[@aria-describedby="edit-' . $field_name . '-0-value--description"]');
        $this->assertSession()
            ->elementExists('xpath', '//div[@id="edit-' . $field_name . '-0-value--description"]');
        // Build up a date in the UTC timezone. Note that using this will also
        // mimic the user in a different timezone simply entering '2012-12-31' via
        // the UI.
        $value = '2012-12-31 00:00:00';
        $date = new DrupalDateTime($value, DateTimeItemInterface::STORAGE_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 = [
            "{$field_name}[0][value][date]" => $date->format($date_format),
        ];
        $this->submitForm($edit, 'Save');
        preg_match('|entity_test/manage/(\\d+)|', $this->getUrl(), $match);
        $id = $match[1];
        $this->assertSession()
            ->pageTextContains('entity_test ' . $id . ' has been created.');
        $this->assertSession()
            ->responseContains($date->format($date_format));
        $this->assertSession()
            ->responseNotContains($date->format($time_format));
        // Verify the date doesn't change if using a timezone that is UTC+12 when
        // the entity is edited through the form.
        $entity = EntityTest::load($id);
        $this->assertEquals('2012-12-31', $entity->{$field_name}->value);
        $this->drupalGet('entity_test/manage/' . $id . '/edit');
        $this->submitForm([], 'Save');
        $this->drupalGet('entity_test/manage/' . $id . '/edit');
        $this->submitForm([], 'Save');
        $this->drupalGet('entity_test/manage/' . $id . '/edit');
        $this->submitForm([], 'Save');
        $entity = EntityTest::load($id);
        $this->assertEquals('2012-12-31', $entity->{$field_name}->value);
        // Reset display options since these get changed below.
        $this->displayOptions = [
            'type' => 'datetime_default',
            'label' => 'hidden',
            'settings' => [
                'format_type' => 'medium',
            ] + $this->defaultSettings,
        ];
        // Verify that the date is output according to the formatter settings.
        $options = [
            'format_type' => [
                'short',
                'medium',
                'long',
            ],
        ];
        // Formats that display a time component for date-only fields will display
        // the default time, so that is applied before calculating the expected
        // value.
        $this->massageTestDate($date);
        foreach ($options as $setting => $values) {
            foreach ($values as $new_value) {
                // Update the entity display settings.
                $this->displayOptions['settings'] = [
                    $setting => $new_value,
                ] + $this->defaultSettings;
                $this->container
                    ->get('entity_display.repository')
                    ->getViewDisplay($this->field
                    ->getTargetEntityTypeId(), $this->field
                    ->getTargetBundle(), 'full')
                    ->setComponent($field_name, $this->displayOptions)
                    ->save();
                $this->renderTestEntity($id);
                switch ($setting) {
                    case 'format_type':
                        // Verify that a date is displayed. Since this is a date-only
                        // field, it is expected to display the time as 00:00:00.
                        
                        /** @var \Drupal\Core\Datetime\DateFormatterInterface $date_formatter */
                        $date_formatter = $this->container
                            ->get('date.formatter');
                        $expected = $date_formatter->format($date->getTimestamp(), $new_value, '', DateTimeItemInterface::STORAGE_TIMEZONE);
                        $expected_iso = $date_formatter->format($date->getTimestamp(), 'custom', 'Y-m-d\\TH:i:s\\Z', DateTimeItemInterface::STORAGE_TIMEZONE);
                        $output = $this->renderTestEntity($id);
                        $expected_markup = '<time datetime="' . $expected_iso . '">' . $expected . '</time>';
                        $this->assertStringContainsString($expected_markup, $output, "Formatted date field using {$new_value} format displayed as {$expected} with {$expected_iso} attribute in {$timezone}.");
                        break;
                }
            }
        }
        // Verify that the plain formatter works.
        $this->displayOptions['type'] = 'datetime_plain';
        $this->displayOptions['settings'] = $this->defaultSettings;
        $display_repository->getViewDisplay($this->field
            ->getTargetEntityTypeId(), $this->field
            ->getTargetBundle(), 'full')
            ->setComponent($field_name, $this->displayOptions)
            ->save();
        $expected = $date->format(DateTimeItemInterface::DATE_STORAGE_FORMAT);
        $output = $this->renderTestEntity($id);
        $this->assertStringContainsString($expected, $output, "Formatted date field using plain format displayed as {$expected} in {$timezone}.");
        // Verify that the 'datetime_custom' formatter works.
        $this->displayOptions['type'] = 'datetime_custom';
        $this->displayOptions['settings'] = [
            'date_format' => 'm/d/Y',
        ] + $this->defaultSettings;
        $display_repository->getViewDisplay($this->field
            ->getTargetEntityTypeId(), $this->field
            ->getTargetBundle(), 'full')
            ->setComponent($field_name, $this->displayOptions)
            ->save();
        $expected = $date->format($this->displayOptions['settings']['date_format']);
        $output = $this->renderTestEntity($id);
        $this->assertStringContainsString($expected, $output, "Formatted date field using datetime_custom format displayed as {$expected} in {$timezone}.");
        // Test that allowed markup in custom format is preserved and XSS is
        // removed.
        $this->displayOptions['settings']['date_format'] = '\\<\\s\\t\\r\\o\\n\\g\\>m/d/Y\\<\\/\\s\\t\\r\\o\\n\\g\\>\\<\\s\\c\\r\\i\\p\\t\\>\\a\\l\\e\\r\\t\\(\\S\\t\\r\\i\\n\\g\\.\\f\\r\\o\\m\\C\\h\\a\\r\\C\\o\\d\\e\\(\\8\\8\\,\\8\\3\\,\\8\\3\\)\\)\\<\\/\\s\\c\\r\\i\\p\\t\\>';
        $display_repository->getViewDisplay($this->field
            ->getTargetEntityTypeId(), $this->field
            ->getTargetBundle(), 'full')
            ->setComponent($field_name, $this->displayOptions)
            ->save();
        $expected = '<strong>' . $date->format('m/d/Y') . '</strong>alert(String.fromCharCode(88,83,83))';
        $output = $this->renderTestEntity($id);
        $this->assertStringContainsString($expected, $output, "Formatted date field using daterange_custom format displayed as {$expected} in {$timezone}.");
        // Verify that the 'datetime_time_ago' formatter works for intervals in the
        // past.  First update the test entity so that the date difference always
        // has the same interval.  Since the database always stores UTC, and the
        // interval will use this, force the test date to use UTC and not the local
        // or user timezone.
        $timestamp = \Drupal::time()->getRequestTime() - 87654321;
        $entity = EntityTest::load($id);
        $field_name = $this->fieldStorage
            ->getName();
        $date = DrupalDateTime::createFromTimestamp($timestamp, 'UTC');
        $entity->{$field_name}->value = $date->format($date_format);
        $entity->save();
        $this->displayOptions['type'] = 'datetime_time_ago';
        $this->displayOptions['settings'] = [
            'future_format' => '@interval in the future',
            'past_format' => '@interval in the past',
            'granularity' => 3,
        ];
        $display_repository->getViewDisplay($this->field
            ->getTargetEntityTypeId(), $this->field
            ->getTargetBundle(), 'full')
            ->setComponent($field_name, $this->displayOptions)
            ->save();
        $expected = new FormattableMarkup($this->displayOptions['settings']['past_format'], [
            '@interval' => $this->dateFormatter
                ->formatTimeDiffSince($timestamp, [
                'granularity' => $this->displayOptions['settings']['granularity'],
            ]),
        ]);
        $output = $this->renderTestEntity($id);
        $this->assertStringContainsString((string) $expected, $output, "Formatted date field using datetime_time_ago format displayed as {$expected} in {$timezone}.");
        // Verify that the 'datetime_time_ago' formatter works for intervals in the
        // future.  First update the test entity so that the date difference always
        // has the same interval.  Since the database always stores UTC, and the
        // interval will use this, force the test date to use UTC and not the local
        // or user timezone.
        $timestamp = \Drupal::time()->getRequestTime() + 87654321;
        $entity = EntityTest::load($id);
        $field_name = $this->fieldStorage
            ->getName();
        $date = DrupalDateTime::createFromTimestamp($timestamp, 'UTC');
        $entity->{$field_name}->value = $date->format($date_format);
        $entity->save();
        $display_repository->getViewDisplay($this->field
            ->getTargetEntityTypeId(), $this->field
            ->getTargetBundle(), 'full')
            ->setComponent($field_name, $this->displayOptions)
            ->save();
        $expected = new FormattableMarkup($this->displayOptions['settings']['future_format'], [
            '@interval' => $this->dateFormatter
                ->formatTimeDiffUntil($timestamp, [
                'granularity' => $this->displayOptions['settings']['granularity'],
            ]),
        ]);
        $output = $this->renderTestEntity($id);
        $this->assertStringContainsString((string) $expected, $output, "Formatted date field using datetime_time_ago format displayed as {$expected} in {$timezone}.");
    }
}

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.