Commit a7b2f68e authored by Evgenii Nikitin's avatar Evgenii Nikitin Committed by Timo Kirkkala
Browse files

Issue #3276492: Provide test coverage for the module.

parent a62afb00
Loading
Loading
Loading
Loading
+95 −0
Original line number Diff line number Diff line
# Schema for the configuration files of the Link description module.

field.formatter.settings.link_description:
  type: mapping
  label: 'Link description format settings'
  mapping:
    trim_length:
      type: integer
      label: 'Trim link text length'
    url_only:
      type: boolean
      label: 'URL only'
    url_plain:
      type: boolean
      label: 'Show URL as plain text'
    rel:
      type: string
      label: 'Add rel="nofollow" to links'
    target:
      type: string
      label: 'Open link in new window'

field.formatter.settings.link_separate_description:
  type: field.formatter.settings.link_description
  label: 'Link description format settings'

field.widget.settings.link_description:
  type: mapping
  label: 'Link description widget settings'
  mapping:
    placeholder_url:
      type: string
      label: 'Placeholder for URL'
    placeholder_title:
      type: label
      label: 'Placeholder for link text'

field.storage_settings.link_description:
  type: mapping
  label: 'Link description settings'

field.field_settings.link_description:
  type: mapping
  label: 'Link description settings'
  mapping:
    title:
      type: integer
      label: 'Allow link text'
    link_type:
      type: integer
      label: 'Allowed link type'

field.value.link_description:
  type: mapping
  label: 'Default value'
  mapping:
    attributes:
      type: mapping
      label: 'Link attributes'
      mapping:
        title:
          type: label
          label: 'Link text'
    uri:
      type: string
      label: 'URL'
    title:
      type: label
      label: 'Link text'
    description:
      type: string
      label: 'Link description'
    options:
      type: mapping
      label: 'Link options'
      mapping:
        query:
          type: sequence
          label: 'URL query key value pairs'
          sequence:
            type: string
        fragment:
          type: string
          label: 'URL fragment'
        absolute:
          type: boolean
          label: 'Whether to force the output to be an absolute link (beginning with http: or https:)'
        https:
          type: boolean
          label: 'Whether to force this URL to point to a secure location (beginning with https:)'
        attributes:
          type: sequence
          label: 'Link attributes'
          sequence:
            type: string
+286 −0
Original line number Diff line number Diff line
<?php

namespace Drupal\Tests\link_description\Functional;

use Drupal\Component\Utility\Html;
use Drupal\Component\Utility\Unicode;
use Drupal\entity_test\Entity\EntityTest;

/**
 * Tests link description field formatters.
 *
 * @group link_description
 */
class LinkDescriptionFieldFormattersTest extends LinkDescriptionFieldTestBase {

  /**
   * Tests the 'link_description' formatter.
   *
   * Rework of the test from Drupal\Tests\link\Functional\LinkFieldTest.
   */
  public function testLinkDescriptionFormatter() {
    $display_options = [
      'type' => 'link_description',
      'label' => 'hidden',
    ];
    $this->setViewDisplayOptions($display_options);

    // Create an entity with three link field values:
    // The first field item uses a URL only.
    // The second field item uses URL, link text.
    // The third field item uses a fragment-only URL with text and description.
    $this->drupalGet('entity_test/add');

    $url1 = 'http://www.example.com/content/articles/archive?author=John&year=2012#com';
    $url2 = 'http://www.example.org/content/articles/archive?author=John&year=2012#org';
    $url3 = '#net';
    $title1 = $url1;
    // Intentionally contains an ampersand that needs sanitization on output.
    $title2 = 'A very long & strange example title that could break the nice layout of the site';
    $title3 = 'Fragment only';
    $edit = [
      "{$this->fieldName}[0][uri]" => $url1,
      "{$this->fieldName}[1][uri]" => $url2,
      "{$this->fieldName}[1][title]" => $title2,
      "{$this->fieldName}[2][uri]" => $url3,
      "{$this->fieldName}[2][title]" => $title3,
      "{$this->fieldName}[2][description]" => "Link 1 & &amp;\nLine 2",
    ];
    // Assert label is shown.
    $this->assertSession()->pageTextContains('Read more about this entity');
    $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.');

    $options = [
      'trim_length' => [NULL, 6],
      'rel' => [NULL, 'nofollow'],
      'target' => [NULL, '_blank'],
    ];
    foreach ($options as $setting => $values) {
      foreach ($values as $new_value) {
        // Update the field formatter settings.
        $display_options['settings'] = [$setting => $new_value];
        $this->setViewDisplayOptions($display_options);

        $output = $this->renderTestEntity($id);
        switch ($setting) {
          case 'trim_length':
            $url = $url1;
            $title = isset($new_value) ? Unicode::truncate($title1, $new_value, FALSE, TRUE) : $title1;
            $expected = '<div class="link-item">';
            $expected .= '<a href="' . Html::escape($url) . '">' . Html::escape($title) . '</a>';
            $expected .= '</div>';
            $this->assertStringContainsString($expected, $output);

            $url = $url2;
            $title = isset($new_value) ? Unicode::truncate($title2, $new_value, FALSE, TRUE) : $title2;
            $expected = '<div class="link-item">';
            $expected .= '<a href="' . Html::escape($url) . '">' . Html::escape($title) . '</a>';
            $expected .= '</div>';
            $this->assertStringContainsString($expected, $output);

            $url = $url3;
            $title = isset($new_value) ? Unicode::truncate($title3, $new_value, FALSE, TRUE) : $title3;
            $expected = '<div class="link-item">';
            $expected .= '<a href="' . Html::escape($url) . '">' . Html::escape($title) . '</a>';
            $expected .= "<p class=\"link-description\">Link 1 &amp; &amp;amp;<br />\nLine 2</p>";
            $expected .= '</div>';
            $this->assertStringContainsString($expected, $output);
            break;

          case 'rel':
            $rel = isset($new_value) ? ' rel="' . $new_value . '"' : '';
            $expected = '<div class="link-item">';
            $expected .= '<a href="' . Html::escape($url1) . '"' . $rel . '>' . Html::escape($title1) . '</a>';
            $expected .= '</div>';
            $this->assertStringContainsString($expected, $output);
            $expected = '<div class="link-item">';
            $expected .= '<a href="' . Html::escape($url2) . '"' . $rel . '>' . Html::escape($title2) . '</a>';
            $expected .= '</div>';
            $this->assertStringContainsString($expected, $output);
            $expected = '<div class="link-item">';
            $expected .= '<a href="' . Html::escape($url3) . '"' . $rel . '>' . Html::escape($title3) . '</a>';
            $expected .= "<p class=\"link-description\">Link 1 &amp; &amp;amp;<br />\nLine 2</p>";
            $expected .= '</div>';
            $this->assertStringContainsString($expected, $output);
            break;

          case 'target':
            $target = isset($new_value) ? ' target="' . $new_value . '"' : '';
            $expected = '<div class="link-item">';
            $expected .= '<a href="' . Html::escape($url1) . '"' . $target . '>' . Html::escape($title1) . '</a>';
            $expected .= '</div>';
            $this->assertStringContainsString($expected, $output);
            $expected = '<div class="link-item">';
            $expected .= '<a href="' . Html::escape($url2) . '"' . $target . '>' . Html::escape($title2) . '</a>';
            $expected .= '</div>';
            $this->assertStringContainsString($expected, $output);
            $expected = '<div class="link-item">';
            $expected .= '<a href="' . Html::escape($url3) . '"' . $target . '>' . Html::escape($title3) . '</a>';
            $expected .= "<p class=\"link-description\">Link 1 &amp; &amp;amp;<br />\nLine 2</p>";
            $expected .= '</div>';
            $this->assertStringContainsString($expected, $output);
            break;
        }
      }
    }
  }

  /**
   * Tests the 'link_separate_description' formatter.
   *
   * Rework of the test from Drupal\Tests\link\Functional\LinkFieldTest.
   */
  public function testLinkSeparateFormatter() {
    $display_options = [
      'type' => 'link_separate_description',
      'label' => 'hidden',
    ];
    $this->setViewDisplayOptions($display_options);

    // Create an entity with three link field values:
    // The first field item uses a URL only.
    // The second field item uses a URL and link text.
    // The third field item uses a fragment-only URL with text and description.
    $this->drupalGet('entity_test/add');
    $url1 = 'http://www.example.com/content/articles/archive?author=John&year=2012#com';
    $url2 = 'http://www.example.org/content/articles/archive?author=John&year=2012#org';
    $url3 = '#net';
    // Intentionally contains an ampersand that needs sanitization on output.
    $title2 = 'A very long & strange example title that could break the nice layout of the site';
    $title3 = 'Fragment only';
    $edit = [
      "{$this->fieldName}[0][uri]" => $url1,
      "{$this->fieldName}[1][uri]" => $url2,
      "{$this->fieldName}[1][title]" => $title2,
      "{$this->fieldName}[2][uri]" => $url3,
      "{$this->fieldName}[2][title]" => $title3,
      "{$this->fieldName}[2][description]" => "Link 1 & &amp;\nLine 2",
    ];
    $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.');

    // Verify that the link is output according to the formatter settings.
    $options = [
      'trim_length' => [NULL, 6],
      'rel' => [NULL, 'nofollow'],
      'target' => [NULL, '_blank'],
    ];
    foreach ($options as $setting => $values) {
      foreach ($values as $new_value) {
        // Update the field formatter settings.
        $display_options['settings'] = [$setting => $new_value];
        $this->setViewDisplayOptions($display_options);

        $output = $this->renderTestEntity($id);
        switch ($setting) {
          case 'trim_length':
            $url = $url1;
            $url_title = isset($new_value) ? Unicode::truncate($url, $new_value, FALSE, TRUE) : $url;
            $expected = '<div class="link-item">';
            $expected .= '<div class="link-url"><a href="' . Html::escape($url) . '">' . Html::escape($url_title) . '</a></div>';
            $expected .= '</div>';
            $this->assertStringContainsString($expected, $output);

            $url = $url2;
            $url_title = isset($new_value) ? Unicode::truncate($url, $new_value, FALSE, TRUE) : $url;
            $title = isset($new_value) ? Unicode::truncate($title2, $new_value, FALSE, TRUE) : $title2;
            $expected = '<div class="link-item">';
            $expected .= '<div class="link-title">' . Html::escape($title) . '</div>';
            $expected .= '<div class="link-url"><a href="' . Html::escape($url) . '">' . Html::escape($url_title) . '</a></div>';
            $expected .= '</div>';
            $this->assertStringContainsString($expected, $output);

            $url = $url3;
            $url_title = isset($new_value) ? Unicode::truncate($url, $new_value, FALSE, TRUE) : $url;
            $title = isset($new_value) ? Unicode::truncate($title3, $new_value, FALSE, TRUE) : $title3;
            $expected = '<div class="link-item">';
            $expected .= '<div class="link-title">' . Html::escape($title) . '</div>';
            $expected .= '<div class="link-url"><a href="' . Html::escape($url) . '">' . Html::escape($url_title) . '</a></div>';
            $expected .= "<p class=\"link-description\">Link 1 &amp; &amp;amp;<br />\nLine 2</p>";
            $expected .= '</div>';
            $this->assertStringContainsString($expected, $output);
            break;

          case 'rel':
            $rel = isset($new_value) ? ' rel="' . $new_value . '"' : '';
            $expected = '<div class="link-item">';
            $expected .= '<div class="link-url"><a href="' . Html::escape($url1) . '"' . $rel . '>' . Html::escape($url1) . '</a></div>';
            $expected .= '</div>';
            $this->assertStringContainsString($expected, $output);

            $expected = '<div class="link-item">';
            $expected .= '<div class="link-title">' . Html::escape($title2) . '</div>';
            $expected .= '<div class="link-url"><a href="' . Html::escape($url2) . '"' . $rel . '>' . Html::escape($url2) . '</a></div>';
            $expected .= '</div>';
            $this->assertStringContainsString($expected, $output);

            $expected = '<div class="link-item">';
            $expected .= '<div class="link-title">' . Html::escape($title3) . '</div>';
            $expected .= '<div class="link-url"><a href="' . Html::escape($url3) . '"' . $rel . '>' . Html::escape($url3) . '</a></div>';
            $expected .= "<p class=\"link-description\">Link 1 &amp; &amp;amp;<br />\nLine 2</p>";
            $expected .= '</div>';
            $this->assertStringContainsString($expected, $output);
            break;

          case 'target':
            $target = isset($new_value) ? ' target="' . $new_value . '"' : '';
            $expected = '<div class="link-item">';
            $expected .= '<div class="link-url"><a href="' . Html::escape($url1) . '"' . $target . '>' . Html::escape($url1) . '</a></div>';
            $expected .= '</div>';
            $this->assertStringContainsString($expected, $output);

            $expected = '<div class="link-item">';
            $expected .= '<div class="link-title">' . Html::escape($title2) . '</div>';
            $expected .= '<div class="link-url"><a href="' . Html::escape($url2) . '"' . $target . '>' . Html::escape($url2) . '</a></div>';
            $expected .= '</div>';
            $this->assertStringContainsString($expected, $output);

            $expected = '<div class="link-item">';
            $expected .= '<div class="link-title">' . Html::escape($title3) . '</div>';
            $expected .= '<div class="link-url"><a href="' . Html::escape($url3) . '"' . $target . '>' . Html::escape($url3) . '</a></div>';
            $expected .= "<p class=\"link-description\">Link 1 &amp; &amp;amp;<br />\nLine 2</p>";
            $expected .= '</div>';
            $this->assertStringContainsString($expected, $output);
            break;
        }
      }
    }
  }

  /**
   * Renders a test_entity and returns the output.
   *
   * @param int $id
   *   The test_entity ID to render.
   *
   * @return string
   *   The rendered HTML output.
   */
  protected function renderTestEntity($id) {
    \Drupal::entityTypeManager()->getStorage('entity_test')->resetCache([$id]);
    $entity = EntityTest::load($id);
    $display = \Drupal::service('entity_display.repository')
      ->getViewDisplay($entity->getEntityTypeId(), $entity->bundle(), 'full');
    $content = $display->build($entity);
    $output = \Drupal::service('renderer')->renderRoot($content);
    return (string) $output;
  }

  /**
   * Sets display options for the test field in the test entity.
   *
   * @param array $display_options
   *   Display options.
   */
  protected function setViewDisplayOptions(array $display_options) {
    \Drupal::service('entity_display.repository')
      ->getViewDisplay('entity_test', 'entity_test', 'full')
      ->setComponent($this->fieldName, $display_options)
      ->save();
  }
}
+76 −0
Original line number Diff line number Diff line
<?php

namespace Drupal\Tests\link_description\Functional;

use Drupal\field\Entity\FieldConfig;
use Drupal\link\LinkItemInterface;
use Drupal\Tests\BrowserTestBase;
use Drupal\field\Entity\FieldStorageConfig;

/**
 * Tests link description field widgets and formatters.
 *
 * @group link_description
 */
abstract class LinkDescriptionFieldTestBase extends BrowserTestBase {

  /**
   * {@inheritdoc}
   */
  protected static $modules = [
    'entity_test',
    'link_description',
  ];

  /**
   * {@inheritdoc}
   */
  protected $defaultTheme = 'stark';

  /**
   * Link description test field name.
   *
   * @var string
   */
  protected $fieldName;

  /**
   * {@inheritdoc}
   */
  protected function setUp(): void {
    parent::setUp();

    $this->fieldName = mb_strtolower($this->randomMachineName());

    // Create a field with settings to validate.
    $field_storage = FieldStorageConfig::create([
      'field_name' => $this->fieldName,
      'entity_type' => 'entity_test',
      'type' => 'link_description',
      'cardinality' => 3,
    ]);
    $field_storage->save();

    FieldConfig::create([
      'field_storage' => $field_storage,
      'label' => 'Read more about this entity',
      'bundle' => 'entity_test',
      'settings' => [
        'title' => DRUPAL_OPTIONAL,
        'link_type' => LinkItemInterface::LINK_GENERIC,
      ],
    ])->save();

    \Drupal::service('entity_display.repository')->getFormDisplay('entity_test', 'entity_test')
      ->setComponent($this->fieldName, [
        'type' => 'link_description',
      ])
      ->save();

    $this->drupalLogin($this->drupalCreateUser([
      'view test entity',
      'administer entity_test content',
      'link to any page',
    ]));
  }
}
+37 −0
Original line number Diff line number Diff line
<?php

namespace Drupal\Tests\link_description\Functional;

use Drupal\entity_test\Entity\EntityTest;

/**
 * Tests link description field widget.
 *
 * @group link_description
 */
class LinkDescriptionFieldWidgetTest extends LinkDescriptionFieldTestBase {

  /**
   * Tests 'description' field exists on the 'link_description' widget.
   */
  public function testLinkDescriptionWidget() {
    $entity_form_builder = \Drupal::service('entity.form_builder');
    $form = $entity_form_builder->getForm(EntityTest::create());
    $this->assertEquals('textarea', $form[$this->fieldName]['widget'][0]['description']['#type']);
    $this->assertEquals('Long description', $form[$this->fieldName]['widget'][0]['description']['#title']);
    $this->assertEmpty($form[$this->fieldName]['widget'][0]['description']['#default_value']);

    // Assert default value of the link description field.
    $entity_test = EntityTest::create([
      'name' => $this->randomMachineName(),
      $this->fieldName => [
        'uri' => 'https://www.example.com',
        'title' => 'Example site',
        'description' => 'Link description &'
      ],
    ]);
    $entity_test->save();
    $form = $entity_form_builder->getForm($entity_test);
    $this->assertEquals('Link description &', $form[$this->fieldName]['widget'][0]['description']['#default_value']);
  }
}