HandlerFieldFieldTest.php

Same filename and directory in other branches
  1. 9 core/modules/field/tests/src/Kernel/Views/HandlerFieldFieldTest.php
  2. 8.9.x core/modules/field/tests/src/Kernel/Views/HandlerFieldFieldTest.php
  3. 11.x core/modules/field/tests/src/Kernel/Views/HandlerFieldFieldTest.php

Namespace

Drupal\Tests\field\Kernel\Views

File

core/modules/field/tests/src/Kernel/Views/HandlerFieldFieldTest.php

View source
<?php

declare (strict_types=1);
namespace Drupal\Tests\field\Kernel\Views;

use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\KernelTests\KernelTestBase;
use Drupal\node\Entity\NodeType;
use Drupal\Tests\node\Traits\NodeCreationTrait;
use Drupal\views\Tests\ViewTestData;
use Drupal\views\ViewExecutable;
use Drupal\views\Views;

/**
 * Tests the field rendering in views.
 *
 * @group field
 *
 * @todo Extend test coverage in #3046722.
 *
 * @see https://www.drupal.org/project/drupal/issues/3046722
 */
class HandlerFieldFieldTest extends KernelTestBase {
  use NodeCreationTrait;
  
  /**
   * {@inheritdoc}
   */
  protected static $modules = [
    'field',
    'field_test',
    'field_test_views',
    'filter',
    'node',
    'system',
    'text',
    'user',
    'views',
  ];
  
  /**
   * Views used by this test.
   *
   * @var array
   */
  public static $testViews = [
    'test_view_fieldapi',
  ];
  
  /**
   * Test field storage.
   *
   * @var \Drupal\field\FieldStorageConfigInterface[]
   */
  protected $fieldStorages = [];
  
  /**
   * Test nodes.
   *
   * @var \Drupal\node\NodeInterface[]
   */
  protected $nodes = [];
  
  /**
   * Tests fields rendering in views.
   */
  public function testFieldRender() : void {
    $this->installConfig([
      'filter',
    ]);
    $this->installEntitySchema('user');
    $this->installEntitySchema('node');
    NodeType::create([
      'type' => 'page',
      'name' => 'Page',
    ])->save();
    ViewTestData::createTestViews(static::class, [
      'field_test_views',
    ]);
    // Setup basic fields.
    $this->createFields();
    // Create some nodes.
    $this->nodes = [];
    for ($i = 0; $i < 3; $i++) {
      $values = [
        'type' => 'page',
      ];
      foreach ([
        0,
        1,
        2,
        5,
      ] as $key) {
        $field_storage = $this->fieldStorages[$key];
        $values[$field_storage->getName()][0]['value'] = $this->randomMachineName(8);
      }
      // Add a hidden value for the no-view field.
      $values[$this->fieldStorages[6]
        ->getName()][0]['value'] = 'ssh secret squirrel';
      for ($j = 0; $j < 5; $j++) {
        $values[$this->fieldStorages[3]
          ->getName()][$j]['value'] = $this->randomMachineName(8);
      }
      // Set this field to be empty.
      $values[$this->fieldStorages[4]
        ->getName()] = [
        [
          'value' => NULL,
        ],
      ];
      $this->nodes[$i] = $this->createNode($values);
    }
    // Perform actual tests.
    $this->doTestSimpleFieldRender();
    $this->doTestInaccessibleFieldRender();
    $this->doTestFormatterSimpleFieldRender();
    $this->doTestMultipleFieldRender();
  }
  
  /**
   * Tests simple field rendering.
   */
  public function doTestSimpleFieldRender() {
    $view = Views::getView('test_view_fieldapi');
    $this->prepareView($view);
    $view->preview();
    // Tests that the rendered fields match the actual value of the fields.
    for ($i = 0; $i < 3; $i++) {
      for ($key = 0; $key < 2; $key++) {
        $field_name = $this->fieldStorages[$key]
          ->getName();
        $rendered_field = $view->style_plugin
          ->getField($i, $field_name);
        $expected_field = $this->nodes[$i]->{$field_name}->value;
        $this->assertEquals($expected_field, $rendered_field);
      }
    }
  }
  
  /**
   * Tests inaccessible field rendering.
   */
  public function doTestInaccessibleFieldRender() {
    $view = Views::getView('test_view_fieldapi');
    $this->prepareView($view);
    $view->preview();
    // Check that the field handler for the hidden field is correctly removed
    // from the display.
    // @see https://www.drupal.org/node/2382931
    $this->assertArrayNotHasKey('field_no_view_access', $view->field);
    // Check that the access-denied field is not visible.
    for ($i = 0; $i < 3; $i++) {
      $field_name = $this->fieldStorages[6]
        ->getName();
      $rendered_field = $view->style_plugin
        ->getField($i, $field_name);
      // Check that a hidden field is not rendered.
      $this->assertNull($rendered_field);
    }
  }
  
  /**
   * Tests that fields with formatters runs as expected.
   */
  public function doTestFormatterSimpleFieldRender() {
    $view = Views::getView('test_view_fieldapi');
    $this->prepareView($view);
    $view->displayHandlers
      ->get('default')->options['fields'][$this->fieldStorages[5]
      ->getName()]['type'] = 'text_trimmed';
    $view->displayHandlers
      ->get('default')->options['fields'][$this->fieldStorages[5]
      ->getName()]['settings'] = [
      'trim_length' => 3,
    ];
    $view->preview();
    // Make sure that the formatter works as expected.
    // @todo Add a specific formatter in #3046722.
    // @see https://www.drupal.org/project/drupal/issues/3046722
    for ($i = 0; $i < 2; $i++) {
      $field_name = $this->fieldStorages[5]
        ->getName();
      $rendered_field = (string) $view->style_plugin
        ->getField($i, $field_name);
      $this->assertEquals(3, strlen(html_entity_decode($rendered_field)));
    }
  }
  
  /**
   * Tests multi-value field rendering.
   */
  public function doTestMultipleFieldRender() {
    $view = Views::getView('test_view_fieldapi');
    $field_name = $this->fieldStorages[3]
      ->getName();
    // Test delta limit.
    $this->prepareView($view);
    $view->displayHandlers
      ->get('default')->options['fields'][$field_name]['group_rows'] = TRUE;
    $view->displayHandlers
      ->get('default')->options['fields'][$field_name]['delta_limit'] = 3;
    $view->preview();
    for ($i = 0; $i < 3; $i++) {
      $rendered_field = $view->style_plugin
        ->getField($i, $field_name);
      $items = [];
      $pure_items = $this->nodes[$i]->{$field_name}
        ->getValue();
      $pure_items = array_splice($pure_items, 0, 3);
      foreach ($pure_items as $j => $item) {
        $items[] = $pure_items[$j]['value'];
      }
      // Check that the amount of items is limited.
      $this->assertEquals(implode(', ', $items), $rendered_field);
    }
    // Test that an empty field is rendered without error.
    $view->style_plugin
      ->getField(4, $this->fieldStorages[4]
      ->getName());
    $view->destroy();
    // Test delta limit + offset
    $this->prepareView($view);
    $view->displayHandlers
      ->get('default')->options['fields'][$field_name]['group_rows'] = TRUE;
    $view->displayHandlers
      ->get('default')->options['fields'][$field_name]['delta_limit'] = 3;
    $view->displayHandlers
      ->get('default')->options['fields'][$field_name]['delta_offset'] = 1;
    $view->preview();
    for ($i = 0; $i < 3; $i++) {
      $rendered_field = $view->style_plugin
        ->getField($i, $field_name);
      $items = [];
      $pure_items = $this->nodes[$i]->{$field_name}
        ->getValue();
      $pure_items = array_splice($pure_items, 1, 3);
      foreach ($pure_items as $j => $item) {
        $items[] = $pure_items[$j]['value'];
      }
      // Check that the amount of items is limited and the offset is correct.
      $this->assertEquals(implode(', ', $items), $rendered_field);
    }
    $view->destroy();
    // Test delta limit + reverse.
    $this->prepareView($view);
    $view->displayHandlers
      ->get('default')->options['fields'][$field_name]['delta_offset'] = 0;
    $view->displayHandlers
      ->get('default')->options['fields'][$field_name]['group_rows'] = TRUE;
    $view->displayHandlers
      ->get('default')->options['fields'][$field_name]['delta_limit'] = 3;
    $view->displayHandlers
      ->get('default')->options['fields'][$field_name]['delta_reversed'] = TRUE;
    $view->preview();
    for ($i = 0; $i < 3; $i++) {
      $rendered_field = $view->style_plugin
        ->getField($i, $field_name);
      $items = [];
      $pure_items = $this->nodes[$i]->{$field_name}
        ->getValue();
      array_splice($pure_items, 0, -3);
      $pure_items = array_reverse($pure_items);
      foreach ($pure_items as $j => $item) {
        $items[] = $pure_items[$j]['value'];
      }
      // Check that the amount of items is limited and they are reversed.
      $this->assertEquals(implode(', ', $items), $rendered_field);
    }
    $view->destroy();
    // Test delta first last.
    $this->prepareView($view);
    $view->displayHandlers
      ->get('default')->options['fields'][$field_name]['group_rows'] = TRUE;
    $view->displayHandlers
      ->get('default')->options['fields'][$field_name]['delta_limit'] = 0;
    $view->displayHandlers
      ->get('default')->options['fields'][$field_name]['delta_first_last'] = TRUE;
    $view->displayHandlers
      ->get('default')->options['fields'][$field_name]['delta_reversed'] = FALSE;
    $view->preview();
    for ($i = 0; $i < 3; $i++) {
      $rendered_field = $view->style_plugin
        ->getField($i, $field_name);
      $items = [];
      $pure_items = $this->nodes[$i]->{$field_name}
        ->getValue();
      $items[] = $pure_items[0]['value'];
      $items[] = $pure_items[4]['value'];
      // Check that items are limited to first and last.
      $this->assertEquals(implode(', ', $items), $rendered_field);
    }
    $view->destroy();
    // Test delta limit + custom separator.
    $this->prepareView($view);
    $view->displayHandlers
      ->get('default')->options['fields'][$field_name]['delta_first_last'] = FALSE;
    $view->displayHandlers
      ->get('default')->options['fields'][$field_name]['delta_limit'] = 3;
    $view->displayHandlers
      ->get('default')->options['fields'][$field_name]['group_rows'] = TRUE;
    $view->displayHandlers
      ->get('default')->options['fields'][$field_name]['separator'] = ':';
    $view->preview();
    for ($i = 0; $i < 3; $i++) {
      $rendered_field = $view->style_plugin
        ->getField($i, $field_name);
      $items = [];
      $pure_items = $this->nodes[$i]->{$field_name}
        ->getValue();
      $pure_items = array_splice($pure_items, 0, 3);
      foreach ($pure_items as $j => $item) {
        $items[] = $pure_items[$j]['value'];
      }
      // Check that the amount of items is limited and the custom separator is
      // correct.
      $this->assertEquals(implode(':', $items), $rendered_field);
    }
    $view->destroy();
    // Test separator with HTML, ensure it is escaped.
    $this->prepareView($view);
    $view->displayHandlers
      ->get('default')->options['fields'][$field_name]['group_rows'] = TRUE;
    $view->displayHandlers
      ->get('default')->options['fields'][$field_name]['delta_limit'] = 3;
    $view->displayHandlers
      ->get('default')->options['fields'][$field_name]['separator'] = '<h2>test</h2>';
    $view->preview();
    for ($i = 0; $i < 3; $i++) {
      $rendered_field = $view->style_plugin
        ->getField($i, $field_name);
      $items = [];
      $pure_items = $this->nodes[$i]->{$field_name}
        ->getValue();
      $pure_items = array_splice($pure_items, 0, 3);
      foreach ($pure_items as $j => $item) {
        $items[] = $pure_items[$j]['value'];
      }
      // Check that the custom separator is correctly escaped.
      $this->assertSame(implode('<h2>test</h2>', $items), (string) $rendered_field);
    }
  }
  
  /**
   * Sets up the testing view with random field data.
   *
   * @param \Drupal\views\ViewExecutable $view
   *   The view to add field data to.
   */
  protected function prepareView(ViewExecutable $view) {
    $view->storage
      ->invalidateCaches();
    $view->initDisplay();
    foreach ($this->fieldStorages as $field_storage) {
      $field_name = $field_storage->getName();
      $view->display_handler->options['fields'][$field_name]['id'] = $field_name;
      $view->display_handler->options['fields'][$field_name]['table'] = 'node__' . $field_name;
      $view->display_handler->options['fields'][$field_name]['field'] = $field_name;
    }
  }
  
  /**
   * Creates the testing fields.
   */
  protected function createFields() {
    $fields_data = [
      [
        'field_name' => 'field_name_0',
        'type' => 'string',
      ],
      [
        'field_name' => 'field_name_1',
        'type' => 'string',
      ],
      [
        'field_name' => 'field_name_2',
        'type' => 'string',
      ],
      // Field with cardinality > 1.
[
        'field_name' => 'field_name_3',
        'type' => 'string',
        'cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED,
      ],
      // Field that will have no value.
[
        'field_name' => 'field_name_4',
        'type' => 'string',
        'cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED,
      ],
      // Text field.
[
        'field_name' => 'field_name_5',
        'type' => 'text',
      ],
      // Text field with access control.
      // @see field_test_entity_field_access()
[
        'field_name' => 'field_no_view_access',
        'type' => 'text',
      ],
    ];
    foreach ($fields_data as $field_data) {
      $field_data += [
        'entity_type' => 'node',
      ];
      $field_storage = FieldStorageConfig::create($field_data);
      $field_storage->save();
      $this->fieldStorages[] = $field_storage;
      FieldConfig::create([
        'field_storage' => $field_storage,
        'bundle' => 'page',
      ])->save();
    }
  }

}

Classes

Title Deprecated Summary
HandlerFieldFieldTest Tests the field rendering in views.

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