OptionsFieldUITest.php

Same filename and directory in other branches
  1. 8.9.x core/modules/options/tests/src/Functional/OptionsFieldUITest.php
  2. 10 core/modules/options/tests/src/FunctionalJavascript/OptionsFieldUITest.php
  3. 10 core/modules/options/tests/src/Functional/OptionsFieldUITest.php
  4. 11.x core/modules/options/tests/src/FunctionalJavascript/OptionsFieldUITest.php
  5. 11.x core/modules/options/tests/src/Functional/OptionsFieldUITest.php

Namespace

Drupal\Tests\options\Functional

File

core/modules/options/tests/src/Functional/OptionsFieldUITest.php

View source
<?php

namespace Drupal\Tests\options\Functional;

use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\Tests\field\Functional\FieldTestBase;

/**
 * Tests the Options field UI functionality.
 *
 * @group options
 */
class OptionsFieldUITest extends FieldTestBase {
  
  /**
   * Modules to enable.
   *
   * @var array
   */
  protected static $modules = [
    'node',
    'options',
    'field_test',
    'taxonomy',
    'field_ui',
  ];
  
  /**
   * {@inheritdoc}
   */
  protected $defaultTheme = 'stark';
  
  /**
   * The name of the created content type.
   *
   * @var string
   */
  protected $typeName;
  
  /**
   * Machine name of the created content type.
   *
   * @var string
   */
  protected $type;
  
  /**
   * Name of the option field.
   *
   * @var string
   */
  protected $fieldName;
  
  /**
   * Admin path to manage field storage settings.
   *
   * @var string
   */
  protected $adminPath;
  
  /**
   * {@inheritdoc}
   */
  protected function setUp() : void {
    parent::setUp();
    // Create test user.
    $admin_user = $this->drupalCreateUser([
      'access content',
      'administer taxonomy',
      'access administration pages',
      'administer site configuration',
      'administer content types',
      'administer nodes',
      'bypass node access',
      'administer node fields',
      'administer node display',
    ]);
    $this->drupalLogin($admin_user);
    // Create content type, with underscores.
    $this->typeName = 'test_' . strtolower($this->randomMachineName());
    $type = $this->drupalCreateContentType([
      'name' => $this->typeName,
      'type' => $this->typeName,
    ]);
    $this->type = $type->id();
  }
  
  /**
   * Options (integer) : test 'allowed values' input.
   */
  public function testOptionsAllowedValuesInteger() {
    $this->fieldName = 'field_options_integer';
    $this->createOptionsField('list_integer');
    // Flat list of textual values.
    $string = "Zero\nOne";
    $array = [
      '0' => 'Zero',
      '1' => 'One',
    ];
    $this->assertAllowedValuesInput($string, $array, 'Unkeyed lists are accepted.');
    // Explicit integer keys.
    $string = "0|Zero\n2|Two";
    $array = [
      '0' => 'Zero',
      '2' => 'Two',
    ];
    $this->assertAllowedValuesInput($string, $array, 'Integer keys are accepted.');
    // Check that values can be added and removed.
    $string = "0|Zero\n1|One";
    $array = [
      '0' => 'Zero',
      '1' => 'One',
    ];
    $this->assertAllowedValuesInput($string, $array, 'Values can be added and removed.');
    // Non-integer keys.
    $this->assertAllowedValuesInput("1.1|One", 'keys must be integers', 'Non integer keys are rejected.');
    $this->assertAllowedValuesInput("abc|abc", 'keys must be integers', 'Non integer keys are rejected.');
    // Mixed list of keyed and unkeyed values.
    $this->assertAllowedValuesInput("Zero\n1|One", 'invalid input', 'Mixed lists are rejected.');
    // Create a node with actual data for the field.
    $settings = [
      'type' => $this->type,
      $this->fieldName => [
        [
          'value' => 1,
        ],
      ],
    ];
    $node = $this->drupalCreateNode($settings);
    // Check that a flat list of values is rejected once the field has data.
    $this->assertAllowedValuesInput("Zero\nOne", 'invalid input', 'Unkeyed lists are rejected once the field has data.');
    // Check that values can be added but values in use cannot be removed.
    $string = "0|Zero\n1|One\n2|Two";
    $array = [
      '0' => 'Zero',
      '1' => 'One',
      '2' => 'Two',
    ];
    $this->assertAllowedValuesInput($string, $array, 'Values can be added.');
    $string = "0|Zero\n1|One";
    $array = [
      '0' => 'Zero',
      '1' => 'One',
    ];
    $this->assertAllowedValuesInput($string, $array, 'Values not in use can be removed.');
    $this->assertAllowedValuesInput("0|Zero", 'some values are being removed while currently in use', 'Values in use cannot be removed.');
    // Delete the node, remove the value.
    $node->delete();
    $string = "0|Zero";
    $array = [
      '0' => 'Zero',
    ];
    $this->assertAllowedValuesInput($string, $array, 'Values not in use can be removed.');
    // Check that the same key can only be used once.
    $string = "0|Zero\n0|One";
    $array = [
      '0' => 'One',
    ];
    $this->assertAllowedValuesInput($string, $array, 'Same value cannot be used multiple times.');
  }
  
  /**
   * Options (float) : test 'allowed values' input.
   */
  public function testOptionsAllowedValuesFloat() {
    $this->fieldName = 'field_options_float';
    $this->createOptionsField('list_float');
    // Flat list of textual values.
    $string = "Zero\nOne";
    $array = [
      '0' => 'Zero',
      '1' => 'One',
    ];
    $this->assertAllowedValuesInput($string, $array, 'Unkeyed lists are accepted.');
    // Explicit numeric keys.
    $string = "0|Zero\n.5|Point five";
    $array = [
      '0' => 'Zero',
      '0.5' => 'Point five',
    ];
    $this->assertAllowedValuesInput($string, $array, 'Integer keys are accepted.');
    // Check that values can be added and removed.
    $string = "0|Zero\n.5|Point five\n1.0|One";
    $array = [
      '0' => 'Zero',
      '0.5' => 'Point five',
      '1' => 'One',
    ];
    $this->assertAllowedValuesInput($string, $array, 'Values can be added and removed.');
    // Non-numeric keys.
    $this->assertAllowedValuesInput("abc|abc\n", 'each key must be a valid integer or decimal', 'Non numeric keys are rejected.');
    // Mixed list of keyed and unkeyed values.
    $this->assertAllowedValuesInput("Zero\n1|One\n", 'invalid input', 'Mixed lists are rejected.');
    // Create a node with actual data for the field.
    $settings = [
      'type' => $this->type,
      $this->fieldName => [
        [
          'value' => 0.5,
        ],
      ],
    ];
    $node = $this->drupalCreateNode($settings);
    // Check that a flat list of values is rejected once the field has data.
    $this->assertAllowedValuesInput("Zero\nOne", 'invalid input', 'Unkeyed lists are rejected once the field has data.');
    // Check that values can be added but values in use cannot be removed.
    $string = "0|Zero\n.5|Point five\n2|Two";
    $array = [
      '0' => 'Zero',
      '0.5' => 'Point five',
      '2' => 'Two',
    ];
    $this->assertAllowedValuesInput($string, $array, 'Values can be added.');
    $string = "0|Zero\n.5|Point five";
    $array = [
      '0' => 'Zero',
      '0.5' => 'Point five',
    ];
    $this->assertAllowedValuesInput($string, $array, 'Values not in use can be removed.');
    $this->assertAllowedValuesInput("0|Zero", 'some values are being removed while currently in use', 'Values in use cannot be removed.');
    // Delete the node, remove the value.
    $node->delete();
    $string = "0|Zero";
    $array = [
      '0' => 'Zero',
    ];
    $this->assertAllowedValuesInput($string, $array, 'Values not in use can be removed.');
    // Check that the same key can only be used once.
    $string = "0.5|Point five\n0.5|Half";
    $array = [
      '0.5' => 'Half',
    ];
    $this->assertAllowedValuesInput($string, $array, 'Same value cannot be used multiple times.');
    // Check that different forms of the same float value cannot be used.
    $string = "0|Zero\n.5|Point five\n0.5|Half";
    $array = [
      '0' => 'Zero',
      '0.5' => 'Half',
    ];
    $this->assertAllowedValuesInput($string, $array, 'Different forms of the same value cannot be used.');
  }
  
  /**
   * Options (text) : test 'allowed values' input.
   */
  public function testOptionsAllowedValuesText() {
    $this->fieldName = 'field_options_text';
    $this->createOptionsField('list_string');
    // Flat list of textual values.
    $string = "Zero\nOne";
    $array = [
      'Zero' => 'Zero',
      'One' => 'One',
    ];
    $this->assertAllowedValuesInput($string, $array, 'Unkeyed lists are accepted.');
    // Explicit keys.
    $string = "zero|Zero\none|One";
    $array = [
      'zero' => 'Zero',
      'one' => 'One',
    ];
    $this->assertAllowedValuesInput($string, $array, 'Explicit keys are accepted.');
    // Check that values can be added and removed.
    $string = "zero|Zero\ntwo|Two";
    $array = [
      'zero' => 'Zero',
      'two' => 'Two',
    ];
    $this->assertAllowedValuesInput($string, $array, 'Values can be added and removed.');
    // Mixed list of keyed and unkeyed values.
    $string = "zero|Zero\nOne\n";
    $array = [
      'zero' => 'Zero',
      'One' => 'One',
    ];
    $this->assertAllowedValuesInput($string, $array, 'Mixed lists are accepted.');
    // Overly long keys.
    $this->assertAllowedValuesInput("zero|Zero\n" . $this->randomMachineName(256) . "|One", 'each key must be a string at most 255 characters long', 'Overly long keys are rejected.');
    // Create a node with actual data for the field.
    $settings = [
      'type' => $this->type,
      $this->fieldName => [
        [
          'value' => 'One',
        ],
      ],
    ];
    $node = $this->drupalCreateNode($settings);
    // Check that flat lists of values are still accepted once the field has
    // data.
    $string = "Zero\nOne";
    $array = [
      'Zero' => 'Zero',
      'One' => 'One',
    ];
    $this->assertAllowedValuesInput($string, $array, 'Unkeyed lists are still accepted once the field has data.');
    // Check that values can be added but values in use cannot be removed.
    $string = "Zero\nOne\nTwo";
    $array = [
      'Zero' => 'Zero',
      'One' => 'One',
      'Two' => 'Two',
    ];
    $this->assertAllowedValuesInput($string, $array, 'Values can be added.');
    $string = "Zero\nOne";
    $array = [
      'Zero' => 'Zero',
      'One' => 'One',
    ];
    $this->assertAllowedValuesInput($string, $array, 'Values not in use can be removed.');
    $this->assertAllowedValuesInput("Zero", 'some values are being removed while currently in use', 'Values in use cannot be removed.');
    // Delete the node, remove the value.
    $node->delete();
    $string = "Zero";
    $array = [
      'Zero' => 'Zero',
    ];
    $this->assertAllowedValuesInput($string, $array, 'Values not in use can be removed.');
    // Check that string values with dots can be used.
    $string = "Zero\nexample.com|Example";
    $array = [
      'Zero' => 'Zero',
      'example.com' => 'Example',
    ];
    $this->assertAllowedValuesInput($string, $array, 'String value with dot is supported.');
    // Check that the same key can only be used once.
    $string = "zero|Zero\nzero|One";
    $array = [
      'zero' => 'One',
    ];
    $this->assertAllowedValuesInput($string, $array, 'Same value cannot be used multiple times.');
  }
  
  /**
   * Options (text) : test 'trimmed values' input.
   */
  public function testOptionsTrimmedValuesText() {
    $this->fieldName = 'field_options_trimmed_text';
    $this->createOptionsField('list_string');
    // Explicit keys.
    $string = "zero |Zero\none | One";
    $array = [
      'zero' => 'Zero',
      'one' => 'One',
    ];
    $this->assertAllowedValuesInput($string, $array, 'Explicit keys are accepted and trimmed.');
  }
  
  /**
   * Helper function to create list field of a given type.
   *
   * @param string $type
   *   One of 'list_integer', 'list_float' or 'list_string'.
   */
  protected function createOptionsField($type) {
    // Create a field.
    FieldStorageConfig::create([
      'field_name' => $this->fieldName,
      'entity_type' => 'node',
      'type' => $type,
    ])
      ->save();
    FieldConfig::create([
      'field_name' => $this->fieldName,
      'entity_type' => 'node',
      'bundle' => $this->type,
    ])
      ->save();
    \Drupal::service('entity_display.repository')->getFormDisplay('node', $this->type)
      ->setComponent($this->fieldName)
      ->save();
    $this->adminPath = 'admin/structure/types/manage/' . $this->type . '/fields/node.' . $this->type . '.' . $this->fieldName . '/storage';
  }
  
  /**
   * Tests a string input for the 'allowed values' form element.
   *
   * @param string $input_string
   *   The input string, in the pipe-linefeed format expected by the form
   *   element.
   * @param array|string $result
   *   Either an expected resulting array in
   *   $field->getSetting('allowed_values'), or an expected error message.
   * @param string $message
   *   Message to display.
   *
   * @internal
   */
  public function assertAllowedValuesInput(string $input_string, $result, string $message) : void {
    $edit = [
      'settings[allowed_values]' => $input_string,
    ];
    $this->drupalGet($this->adminPath);
    $this->submitForm($edit, 'Save field settings');
    // Verify that the page does not have double escaped HTML tags.
    $this->assertSession()
      ->responseNotContains('&amp;lt;');
    if (is_string($result)) {
      $this->assertSession()
        ->pageTextContains($result);
    }
    else {
      $field_storage = FieldStorageConfig::loadByName('node', $this->fieldName);
      $this->assertSame($field_storage->getSetting('allowed_values'), $result, $message);
    }
  }
  
  /**
   * Tests normal and key formatter display on node display.
   */
  public function testNodeDisplay() {
    $this->fieldName = strtolower($this->randomMachineName());
    $this->createOptionsField('list_integer');
    $node = $this->drupalCreateNode([
      'type' => $this->type,
    ]);
    $on = $this->randomMachineName();
    $off = $this->randomMachineName();
    $edit = [
      'settings[allowed_values]' => "1|{$on}" . PHP_EOL . "0|{$off}",
    ];
    $this->drupalGet($this->adminPath);
    $this->submitForm($edit, 'Save field settings');
    $this->assertSession()
      ->pageTextContains('Updated field ' . $this->fieldName . ' field settings.');
    // Select a default value.
    $edit = [
      $this->fieldName => '1',
    ];
    $this->drupalGet('node/' . $node->id() . '/edit');
    $this->submitForm($edit, 'Save');
    // Check the node page and see if the values are correct.
    $file_formatters = [
      'list_default',
      'list_key',
    ];
    foreach ($file_formatters as $formatter) {
      $edit = [
        "fields[{$this->fieldName}][type]" => $formatter,
        "fields[{$this->fieldName}][region]" => 'content',
      ];
      $this->drupalGet('admin/structure/types/manage/' . $this->typeName . '/display');
      $this->submitForm($edit, 'Save');
      $this->drupalGet('node/' . $node->id());
      if ($formatter == 'list_default') {
        $output = $on;
      }
      else {
        $output = '1';
      }
      // Verify that correct options are found.
      $this->assertSession()
        ->elementsCount('xpath', '//div[text()="' . $output . '"]', 1);
    }
  }

}

Classes

Title Deprecated Summary
OptionsFieldUITest Tests the Options field UI functionality.

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