first commit

This commit is contained in:
2024-07-15 12:33:27 +02:00
commit ce50ae282b
22084 changed files with 2623791 additions and 0 deletions

View File

@@ -0,0 +1,13 @@
name: 'Options config install test'
type: module
description: 'Support module for the Options module tests.'
package: Testing
# version: VERSION
dependencies:
- drupal:node
- drupal:options
# Information added by Drupal.org packaging script on 2024-07-04
version: '10.3.1'
project: 'drupal'
datestamp: 1720094222

View File

@@ -0,0 +1,10 @@
name: 'Options test'
type: module
description: 'Support module for the Options module tests.'
package: Testing
# version: VERSION
# Information added by Drupal.org packaging script on 2024-07-04
version: '10.3.1'
project: 'drupal'
datestamp: 1720094222

View File

@@ -0,0 +1,81 @@
<?php
/**
* @file
* Helper module for the List module tests.
*/
use Drupal\Core\Entity\FieldableEntityInterface;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\Core\Form\FormStateInterface;
/**
* Implements callback_allowed_values_function().
*
* @see options_allowed_values()
*/
function options_test_allowed_values_callback(FieldStorageDefinitionInterface $definition, ?FieldableEntityInterface $entity = NULL) {
$values = [
'Group 1' => [
0 => 'Zero',
],
1 => 'One',
'Group 2' => [
2 => 'Some <script>dangerous</script> & unescaped <strong>markup</strong>',
],
'More <script>dangerous</script> markup' => [
3 => 'Three',
],
];
return $values;
}
/**
* Implements callback_allowed_values_function().
*
* @todo This function violates the recommendation in options_allowed_values()
* to return a list of all possible values in any context when $items is
* NULL. Since this is not yet used for testing Views integration, that is
* alright for now. Fix this in https://www.drupal.org/node/2012130.
*
* @see options_allowed_values()
*/
function options_test_dynamic_values_callback(FieldStorageDefinitionInterface $definition, ?FieldableEntityInterface $entity = NULL, &$cacheable = NULL) {
$values = [];
if (isset($entity)) {
$cacheable = FALSE;
$values = [
$entity->label(),
$entity->toUrl()->toString(),
$entity->uuid(),
$entity->bundle(),
];
}
// We need the values of the entity as keys.
return array_combine($values, $values);
}
/**
* Implements hook_form_FORM_ID_alter().
*/
function options_test_form_entity_test_entity_test_form_alter(&$form, FormStateInterface $form_state, $form_id) {
if (\Drupal::state()->get('options_test.form_alter_enable', FALSE)) {
$form['card_1']['widget']['#required_error'] = t('This is custom message for required field.');
}
}
/**
* Implements hook_options_list_alter().
*/
function options_test_options_list_alter(array &$options, array $context) {
if ($context['fieldDefinition']->getName() === 'card_4' && $context['widget']->getPluginId() === 'options_select') {
// Rename _none option.
$options['_none'] = '- Select something -';
}
if ($context['fieldDefinition']->getName() === 'card_4' && $context['entity']->bundle() === 'entity_test') {
// Remove 0 option.
unset($options[0]);
}
}

View File

@@ -0,0 +1,13 @@
name: 'Options test views'
type: module
description: 'Provides default views for views options tests.'
package: Testing
# version: VERSION
dependencies:
- drupal:options
- drupal:views
# Information added by Drupal.org packaging script on 2024-07-04
version: '10.3.1'
project: 'drupal'
datestamp: 1720094222

View File

@@ -0,0 +1,198 @@
langcode: en
status: true
dependencies:
config:
- node.type.article
module:
- node
- user
id: test_options_list_argument_numeric
label: 'test options list argument (numeric)'
module: views
description: ''
tag: ''
base_table: node_field_data
base_field: nid
display:
default:
display_plugin: default
id: default
display_title: Default
position: 1
display_options:
access:
type: perm
options:
perm: 'access content'
cache:
type: tag
options: { }
query:
type: views_query
options:
disable_sql_rewrite: false
distinct: false
replica: false
query_comment: ''
query_tags: { }
exposed_form:
type: basic
options:
submit_button: Apply
reset_button: false
reset_button_label: Reset
exposed_sorts_label: 'Sort by'
expose_sort_order: true
sort_asc_label: Asc
sort_desc_label: Desc
pager:
type: some
options:
items_per_page: 5
offset: 0
style:
type: default
row:
type: fields
fields:
title:
id: title
table: node_field_data
field: title
label: ''
alter:
alter_text: false
make_link: false
absolute: false
trim: false
word_boundary: false
ellipsis: false
strip_tags: false
html: false
hide_empty: false
empty_zero: false
relationship: none
group_type: group
admin_label: ''
exclude: false
element_type: ''
element_class: ''
element_label_type: ''
element_label_class: ''
element_label_colon: true
element_wrapper_type: ''
element_wrapper_class: ''
element_default_classes: true
empty: ''
hide_alter_empty: true
plugin_id: field
filters:
status:
value: '1'
table: node_field_data
field: status
id: status
expose:
operator: ''
group: 1
plugin_id: boolean
type:
id: type
table: node_field_data
field: type
relationship: none
group_type: group
admin_label: ''
operator: in
value:
article: article
group: 1
exposed: false
expose:
operator_id: '0'
label: ''
description: ''
use_operator: false
operator: ''
identifier: ''
required: false
remember: false
multiple: false
remember_roles:
authenticated: authenticated
reduce: false
is_grouped: false
group_info:
label: ''
description: ''
identifier: ''
optional: true
widget: select
multiple: false
remember: false
default_group: All
default_group_multiple: { }
group_items: { }
plugin_id: bundle
sorts:
nid:
id: nid
table: nid
field: nid
order: DESC
relationship: none
group_type: group
admin_label: ''
exposed: false
expose:
label: ''
plugin_id: standard
title: 'test options list argument'
header: { }
footer: { }
empty: { }
relationships: { }
arguments:
field_test_list_integer_value:
id: field_test_list_integer_value
table: field_data_field_test_list_integer
field: field_test_list_integer_value
relationship: none
group_type: group
admin_label: ''
default_action: empty
exception:
value: all
title_enable: false
title: All
title_enable: false
title: ''
default_argument_type: fixed
default_argument_options:
argument: ''
summary_options:
base_path: ''
items_per_page: 25
count: false
override: false
summary:
sort_order: asc
number_of_records: 0
format: default_summary
human: true
specify_validation: false
validate:
type: none
fail: 'not found'
validate_options: { }
break_phrase: false
not: false
plugin_id: number_list_field
display_extenders: { }
block_1:
display_plugin: block
id: block_1
display_title: Block
position: 1
display_options:
display_extenders: { }

View File

@@ -0,0 +1,197 @@
langcode: en
status: true
dependencies:
config:
- node.type.article
module:
- node
- user
id: test_options_list_argument_string
label: 'test options list argument (string)'
module: views
description: ''
tag: ''
base_table: node_field_data
base_field: nid
display:
default:
display_plugin: default
id: default
display_title: Default
position: 1
display_options:
access:
type: perm
options:
perm: 'access content'
cache:
type: tag
options: { }
query:
type: views_query
options:
disable_sql_rewrite: false
distinct: false
replica: false
query_comment: ''
query_tags: { }
exposed_form:
type: basic
options:
submit_button: Apply
reset_button: false
reset_button_label: Reset
exposed_sorts_label: 'Sort by'
expose_sort_order: true
sort_asc_label: Asc
sort_desc_label: Desc
pager:
type: some
options:
items_per_page: 5
offset: 0
style:
type: default
row:
type: fields
fields:
title:
id: title
table: node_field_data
field: title
label: ''
alter:
alter_text: false
make_link: false
absolute: false
trim: false
word_boundary: false
ellipsis: false
strip_tags: false
html: false
hide_empty: false
empty_zero: false
relationship: none
group_type: group
admin_label: ''
exclude: false
element_type: ''
element_class: ''
element_label_type: ''
element_label_class: ''
element_label_colon: true
element_wrapper_type: ''
element_wrapper_class: ''
element_default_classes: true
empty: ''
hide_alter_empty: true
plugin_id: field
filters:
status:
value: '1'
table: node_field_data
field: status
id: status
expose:
operator: ''
group: 1
plugin_id: boolean
type:
id: type
table: node_field_data
field: type
relationship: none
group_type: group
admin_label: ''
operator: in
value:
article: article
group: 1
exposed: false
expose:
operator_id: '0'
label: ''
description: ''
use_operator: false
operator: ''
identifier: ''
required: false
remember: false
multiple: false
remember_roles:
authenticated: authenticated
reduce: false
is_grouped: false
group_info:
label: ''
description: ''
identifier: ''
optional: true
widget: select
multiple: false
remember: false
default_group: All
default_group_multiple: { }
group_items: { }
plugin_id: bundle
sorts:
nid:
id: nid
table: nid
field: nid
order: DESC
relationship: none
group_type: group
admin_label: ''
exposed: false
expose:
label: ''
plugin_id: standard
title: 'test options list argument'
header: { }
footer: { }
empty: { }
relationships: { }
arguments:
field_test_list_string_value:
id: field_test_list_string_value
table: field_data_field_test_list_string
field: field_test_list_string_value
relationship: none
group_type: group
admin_label: ''
default_action: empty
exception:
value: all
title_enable: false
title: All
title_enable: false
title: ''
default_argument_type: fixed
default_argument_options:
argument: ''
summary_options:
base_path: ''
items_per_page: 25
count: false
override: false
summary:
sort_order: asc
number_of_records: 0
format: default_summary
human: true
specify_validation: false
validate:
type: none
fail: 'not found'
validate_options: { }
break_phrase: false
plugin_id: string_list_field
display_extenders: { }
block_1:
display_plugin: block
id: block_1
display_title: Block
position: 1
display_options:
display_extenders: { }

View File

@@ -0,0 +1,203 @@
langcode: en
status: true
dependencies:
config:
- node.type.article
module:
- node
- user
id: test_options_list_filter
label: test_options_list_filter
module: views
description: ''
tag: ''
base_table: node_field_data
base_field: nid
display:
default:
display_plugin: default
id: default
display_title: Default
position: 1
display_options:
access:
type: perm
options:
perm: 'access content'
cache:
type: tag
options: { }
query:
type: views_query
options:
disable_sql_rewrite: false
distinct: false
replica: false
query_comment: ''
query_tags: { }
exposed_form:
type: basic
options:
submit_button: Apply
reset_button: false
reset_button_label: Reset
exposed_sorts_label: 'Sort by'
expose_sort_order: true
sort_asc_label: Asc
sort_desc_label: Desc
pager:
type: some
options:
items_per_page: 5
offset: 0
style:
type: default
row:
type: fields
fields:
title:
id: title
table: node_field_data
field: title
label: ''
alter:
alter_text: false
make_link: false
absolute: false
trim: false
word_boundary: false
ellipsis: false
strip_tags: false
html: false
hide_empty: false
empty_zero: false
relationship: none
group_type: group
admin_label: ''
exclude: false
element_type: ''
element_class: ''
element_label_type: ''
element_label_class: ''
element_label_colon: true
element_wrapper_type: ''
element_wrapper_class: ''
element_default_classes: true
empty: ''
hide_alter_empty: true
plugin_id: field
filters:
status:
value: '1'
table: node_field_data
field: status
id: status
expose:
operator: ''
group: 1
plugin_id: boolean
field_test_list_string_value:
id: field_test_list_string_value
table: field_data_field_test_list_string
field: field_test_list_string_value
relationship: none
group_type: group
admin_label: ''
operator: or
value:
man: man
woman: woman
group: 1
exposed: false
expose:
operator_id: ''
label: ''
description: ''
use_operator: false
operator: ''
identifier: ''
required: false
remember: false
multiple: false
remember_roles:
authenticated: authenticated
reduce: false
is_grouped: false
group_info:
label: ''
description: ''
identifier: ''
optional: true
widget: select
multiple: false
remember: false
default_group: All
default_group_multiple: { }
group_items: { }
reduce_duplicates: false
plugin_id: list_field
type:
id: type
table: node_field_data
field: type
relationship: none
group_type: group
admin_label: ''
operator: in
value:
article: article
group: 1
exposed: false
expose:
operator_id: ''
label: ''
description: ''
use_operator: false
operator: ''
identifier: ''
required: false
remember: false
multiple: false
remember_roles:
authenticated: authenticated
reduce: false
is_grouped: false
group_info:
label: ''
description: ''
identifier: ''
optional: true
widget: select
multiple: false
remember: false
default_group: All
default_group_multiple: { }
group_items: { }
plugin_id: bundle
sorts:
nid:
id: nid
table: nid
field: nid
order: DESC
relationship: none
group_type: group
admin_label: ''
exposed: false
expose:
label: ''
plugin_id: standard
title: test_options_list_filter
header: { }
footer: { }
empty: { }
relationships: { }
arguments: { }
display_extenders: { }
block_1:
display_plugin: block
id: block_1
display_title: Block
position: 1
display_options:
display_extenders: { }

View File

@@ -0,0 +1,14 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\options\Functional;
use Drupal\Tests\system\Functional\Module\GenericModuleTestBase;
/**
* Generic module test for options.
*
* @group options
*/
class GenericTest extends GenericModuleTestBase {}

View File

@@ -0,0 +1,97 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\options\Functional;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\entity_test\Entity\EntityTestRev;
use Drupal\Tests\field\Functional\FieldTestBase;
/**
* Base class for testing allowed values of options fields.
*/
abstract class OptionsDynamicValuesTestBase extends FieldTestBase {
/**
* Modules to enable.
*
* @var array
*/
protected static $modules = ['options', 'entity_test', 'options_test'];
/**
* The created entity.
*
* @var \Drupal\Core\Entity\EntityInterface
*/
protected $entity;
/**
* The field storage.
*
* @var \Drupal\Core\Field\FieldStorageDefinitionInterface
*/
protected $fieldStorage;
/**
* @var int
*/
protected int $field;
/**
* Test data.
*
* @var array
*/
protected array $test;
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$field_name = 'test_options';
$this->fieldStorage = FieldStorageConfig::create([
'field_name' => $field_name,
'entity_type' => 'entity_test_rev',
'type' => 'list_string',
'cardinality' => 1,
'settings' => [
'allowed_values_function' => 'options_test_dynamic_values_callback',
],
]);
$this->fieldStorage->save();
$this->field = FieldConfig::create([
'field_name' => $field_name,
'entity_type' => 'entity_test_rev',
'bundle' => 'entity_test_rev',
'required' => TRUE,
])->save();
\Drupal::service('entity_display.repository')
->getFormDisplay('entity_test_rev', 'entity_test_rev')
->setComponent($field_name, [
'type' => 'options_select',
])
->save();
// Create an entity and prepare test data that will be used by
// options_test_dynamic_values_callback().
$values = [
'user_id' => mt_rand(1, 10),
'name' => $this->randomMachineName(),
];
$this->entity = EntityTestRev::create($values);
$this->entity->save();
$this->test = [
'label' => $this->entity->label(),
'uuid' => $this->entity->uuid(),
'bundle' => $this->entity->bundle(),
'uri' => $this->entity->toUrl()->toString(),
];
}
}

View File

@@ -0,0 +1,490 @@
<?php
declare(strict_types=1);
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
* @group #slow
*/
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_' . $this->randomMachineName();
$type = $this->drupalCreateContentType(['name' => $this->typeName, 'type' => $this->typeName]);
$this->type = $type->id();
}
/**
* Options (integer) : test 'allowed values' input.
*/
public function testOptionsAllowedValuesInteger(): void {
$this->fieldName = 'field_options_integer';
$this->createOptionsField('list_integer');
$page = $this->getSession()->getPage();
$assert_session = $this->assertSession();
// Explicit integer keys.
$input = [
'field_storage[subform][settings][allowed_values][table][0][item][key]' => 0,
'field_storage[subform][settings][allowed_values][table][0][item][label]' => 'Zero',
'field_storage[subform][settings][allowed_values][table][1][item][key]' => 2,
'field_storage[subform][settings][allowed_values][table][1][item][label]' => 'Two',
];
$array = [0 => 'Zero', 2 => 'Two'];
$this->assertAllowedValuesInput($input, $array, 'Integer keys are accepted.');
// Non-integer keys.
$input = [
'field_storage[subform][settings][allowed_values][table][0][item][key]' => 1.1,
'field_storage[subform][settings][allowed_values][table][0][item][label]' => 'One',
];
$this->assertAllowedValuesInput($input, 'keys must be integers', 'Non integer keys are rejected.');
$input = [
'field_storage[subform][settings][allowed_values][table][0][item][key]' => 'abc',
'field_storage[subform][settings][allowed_values][table][0][item][label]' => 'abc',
];
$this->assertAllowedValuesInput($input, 'keys must be integers', 'Non integer keys are rejected.');
$input = [
'field_storage[subform][settings][allowed_values][table][0][item][key]' => 0,
'field_storage[subform][settings][allowed_values][table][0][item][label]' => 'Zero',
'field_storage[subform][settings][allowed_values][table][1][item][key]' => 1,
'field_storage[subform][settings][allowed_values][table][1][item][label]' => 'One',
];
$array = [0 => 'Zero', 1 => 'One'];
$this->assertAllowedValuesInput($input, $array, '');
// Create a node with actual data for the field.
$settings = [
'type' => $this->type,
$this->fieldName => [['value' => 1]],
];
$node = $this->drupalCreateNode($settings);
// Check that the values in use cannot be removed.
$this->drupalGet($this->adminPath);
$assert_session->elementExists('css', '#remove_row_button__1');
$delete_button_1 = $page->findById('remove_row_button__1');
$this->assertTrue($delete_button_1->hasAttribute('disabled'), 'Button is disabled');
// Delete the node, remove the value.
$node->delete();
$this->drupalGet($this->adminPath);
$delete_button_1->click();
$assert_session->pageTextNotContains('Processing...');
$page->findById('edit-submit')->click();
$field_storage = FieldStorageConfig::loadByName('node', $this->fieldName);
$this->assertSame($field_storage->getSetting('allowed_values'), [0 => 'Zero']);
// Check that the same key can only be used once.
$input = [
'field_storage[subform][settings][allowed_values][table][0][item][key]' => 0,
'field_storage[subform][settings][allowed_values][table][0][item][label]' => 'Zero',
'field_storage[subform][settings][allowed_values][table][1][item][key]' => 0,
'field_storage[subform][settings][allowed_values][table][1][item][label]' => 'One',
];
$array = ['0' => 'One'];
$this->assertAllowedValuesInput($input, $array, 'Same value cannot be used multiple times.');
}
/**
* Options (float) : test 'allowed values' input.
*/
public function testOptionsAllowedValuesFloat(): void {
$this->fieldName = 'field_options_float';
$this->createOptionsField('list_float');
$page = $this->getSession()->getPage();
$assert_session = $this->assertSession();
// Explicit numeric keys.
$input = [
'field_storage[subform][settings][allowed_values][table][0][item][key]' => 0,
'field_storage[subform][settings][allowed_values][table][0][item][label]' => 'Zero',
'field_storage[subform][settings][allowed_values][table][1][item][key]' => .5,
'field_storage[subform][settings][allowed_values][table][1][item][label]' => 'Point five',
];
$array = ['0' => 'Zero', '0.5' => 'Point five'];
$this->assertAllowedValuesInput($input, $array, 'Integer keys are accepted.');
// Check that values can be added.
$input = [
'field_storage[subform][settings][allowed_values][table][0][item][key]' => 0,
'field_storage[subform][settings][allowed_values][table][0][item][label]' => 'Zero',
'field_storage[subform][settings][allowed_values][table][1][item][key]' => .5,
'field_storage[subform][settings][allowed_values][table][1][item][label]' => 'Point five',
'field_storage[subform][settings][allowed_values][table][2][item][key]' => 1,
'field_storage[subform][settings][allowed_values][table][2][item][label]' => 'One',
];
$array = ['0' => 'Zero', '0.5' => 'Point five', '1' => 'One'];
$this->assertAllowedValuesInput($input, $array, 'Values can be added.');
// Non-numeric keys.
$input = [
'field_storage[subform][settings][allowed_values][table][0][item][key]' => 'abc',
'field_storage[subform][settings][allowed_values][table][0][item][label]' => 'abc',
];
$this->assertAllowedValuesInput($input, 'each key must be a valid integer or decimal', 'Non numeric keys are rejected.');
$input = [
'field_storage[subform][settings][allowed_values][table][0][item][key]' => 0,
'field_storage[subform][settings][allowed_values][table][0][item][label]' => 'Zero',
'field_storage[subform][settings][allowed_values][table][1][item][key]' => .5,
'field_storage[subform][settings][allowed_values][table][1][item][label]' => 'Point five',
'field_storage[subform][settings][allowed_values][table][2][item][key]' => 2,
'field_storage[subform][settings][allowed_values][table][2][item][label]' => 'Two',
];
$array = ['0' => 'Zero', '0.5' => 'Point five', '2' => 'Two'];
$this->assertAllowedValuesInput($input, $array, '');
// Create a node with actual data for the field.
$settings = [
'type' => $this->type,
$this->fieldName => [['value' => .5]],
];
$node = $this->drupalCreateNode($settings);
// Check that the values in use cannot be removed.
$this->drupalGet($this->adminPath);
$assert_session->elementExists('css', '#remove_row_button__1');
$delete_button_1 = $page->findById('remove_row_button__1');
$this->assertTrue($delete_button_1->hasAttribute('disabled'), 'Button is disabled');
// Delete the node, remove the value.
$node->delete();
$this->drupalGet($this->adminPath);
$delete_button_1->click();
$assert_session->pageTextNotContains('Processing...');
$page->findById('edit-submit')->click();
$field_storage = FieldStorageConfig::loadByName('node', $this->fieldName);
$this->assertSame($field_storage->getSetting('allowed_values'), [0 => 'Zero', 2 => 'Two']);
$input = [
'field_storage[subform][settings][allowed_values][table][0][item][key]' => .5,
'field_storage[subform][settings][allowed_values][table][0][item][label]' => 'Point five',
'field_storage[subform][settings][allowed_values][table][1][item][key]' => .5,
'field_storage[subform][settings][allowed_values][table][1][item][label]' => 'Half',
];
$array = ['0.5' => 'Half'];
$this->assertAllowedValuesInput($input, $array, 'Same value cannot be used multiple times.');
// Check that different forms of the same float value cannot be used.
$input = [
'field_storage[subform][settings][allowed_values][table][0][item][key]' => .5,
'field_storage[subform][settings][allowed_values][table][0][item][label]' => 'Point five',
'field_storage[subform][settings][allowed_values][table][1][item][key]' => 0.5,
'field_storage[subform][settings][allowed_values][table][1][item][label]' => 'Half',
];
$array = ['0.5' => 'Half'];
$this->assertAllowedValuesInput($input, $array, 'Different forms of the same value cannot be used.');
}
/**
* Options (text) : test 'allowed values' input.
*/
public function testOptionsAllowedValuesText(): void {
$this->fieldName = 'field_options_text';
$this->createOptionsField('list_string');
$page = $this->getSession()->getPage();
$assert_session = $this->assertSession();
// Explicit keys.
$input = [
'field_storage[subform][settings][allowed_values][table][0][item][key]' => '_zero',
'field_storage[subform][settings][allowed_values][table][0][item][label]' => 'Zero',
'field_storage[subform][settings][allowed_values][table][1][item][key]' => '_one',
'field_storage[subform][settings][allowed_values][table][1][item][label]' => 'One',
];
$array = ['_zero' => 'Zero', '_one' => 'One'];
$this->assertAllowedValuesInput($input, $array, 'Explicit keys are accepted.');
// Overly long keys.
$input = [
'field_storage[subform][settings][allowed_values][table][0][item][key]' => 'zero',
'field_storage[subform][settings][allowed_values][table][0][item][label]' => 'Zero',
'field_storage[subform][settings][allowed_values][table][1][item][key]' => $this->randomMachineName(256),
'field_storage[subform][settings][allowed_values][table][1][item][label]' => 'One',
];
$this->assertAllowedValuesInput($input, 'each key must be a string at most 255 characters long', 'Overly long keys are rejected.');
$input = [
'field_storage[subform][settings][allowed_values][table][0][item][key]' => 'zero',
'field_storage[subform][settings][allowed_values][table][0][item][label]' => 'Zero',
'field_storage[subform][settings][allowed_values][table][1][item][key]' => 'one',
'field_storage[subform][settings][allowed_values][table][1][item][label]' => 'One',
];
$array = ['zero' => 'Zero', 'one' => 'One'];
$this->assertAllowedValuesInput($input, $array, '');
// Create a node with actual data for the field.
$settings = [
'type' => $this->type,
$this->fieldName => [['value' => 'one']],
];
$node = $this->drupalCreateNode($settings);
// Check that the values in use cannot be removed.
$this->drupalGet($this->adminPath);
$assert_session->elementExists('css', '#remove_row_button__1');
$delete_button_1 = $page->findById('remove_row_button__1');
$value_field_1 = $page->findField('field_storage[subform][settings][allowed_values][table][1][item][key]');
$this->assertTrue($delete_button_1->hasAttribute('disabled'), 'Button is disabled');
$this->assertTrue($value_field_1->hasAttribute('disabled'), 'Button is disabled');
// Delete the node, remove the value.
$node->delete();
$this->drupalGet($this->adminPath);
$delete_button_1->click();
$assert_session->pageTextNotContains('Processing...');
$page->findById('edit-submit')->click();
$field_storage = FieldStorageConfig::loadByName('node', $this->fieldName);
$this->assertSame($field_storage->getSetting('allowed_values'), ['zero' => 'Zero']);
// Check that string values with special characters can be used.
$input = [
'field_storage[subform][settings][allowed_values][table][0][item][key]' => 'zero',
'field_storage[subform][settings][allowed_values][table][0][item][label]' => 'Zero',
'field_storage[subform][settings][allowed_values][table][1][item][key]' => '.example #example',
'field_storage[subform][settings][allowed_values][table][1][item][label]' => 'Example',
];
$array = ['zero' => 'Zero', '.example #example' => 'Example'];
$this->assertAllowedValuesInput($input, $array, '');
// Check that the same key can only be used once.
$input = [
'field_storage[subform][settings][allowed_values][table][0][item][key]' => 'zero',
'field_storage[subform][settings][allowed_values][table][0][item][label]' => 'Zero',
'field_storage[subform][settings][allowed_values][table][1][item][key]' => 'zero',
'field_storage[subform][settings][allowed_values][table][1][item][label]' => 'One',
];
$array = ['zero' => 'One'];
$this->assertAllowedValuesInput($input, $array, 'Same value cannot be used multiple times.');
}
/**
* 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;
}
/**
* Tests an input array for the 'allowed values' form element.
*
* @param array $input
* The input array.
* @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(array $input, $result, string $message): void {
$this->drupalGet($this->adminPath);
$page = $this->getSession()->getPage();
$add_button = $page->findButton('Add another item');
$add_button->click();
$add_button->click();
$this->submitForm($input, 'Save');
// 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(): void {
$this->fieldName = $this->randomMachineName();
$this->createOptionsField('list_integer');
$node = $this->drupalCreateNode(['type' => $this->type]);
$on = $this->randomMachineName();
$off = $this->randomMachineName();
$edit = [
'field_storage[subform][settings][allowed_values][table][0][item][key]' => 1,
'field_storage[subform][settings][allowed_values][table][0][item][label]' => $on,
'field_storage[subform][settings][allowed_values][table][1][item][key]' => 0,
'field_storage[subform][settings][allowed_values][table][1][item][label]' => $off,
];
$this->drupalGet($this->adminPath);
$page = $this->getSession()->getPage();
$page->findButton('Add another item')->click();
$this->submitForm($edit, 'Save');
// 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);
}
}
/**
* Confirms the allowed value list is a required field.
*/
public function testRequiredPropertyForAllowedValuesList(): void {
$field_types = [
'list_float',
'list_string',
'list_integer',
];
foreach ($field_types as $field_type) {
$this->fieldName = "field_options_$field_type";
$this->createOptionsField($field_type);
$page = $this->getSession()->getPage();
$this->drupalGet($this->adminPath);
// Assert that the delete button for a single row is disabled.
$this->assertCount(1, $page->findAll('css', '#allowed-values-order tr.draggable'));
$delete_button_0 = $page->findById('remove_row_button__0');
$this->assertTrue($delete_button_0->hasAttribute('disabled'), 'Button is disabled');
$page->findButton('Add another item')->click();
// Assert that the delete button for the first row is enabled if there are
// more that one rows.
$this->assertCount(2, $page->findAll('css', '#allowed-values-order tr.draggable'));
$this->assertFalse($delete_button_0->hasAttribute('disabled'), 'Button is enabled');
// Delete a row.
$delete_button_0->click();
// Assert that the button is disabled again.
$this->assertTrue($delete_button_0->hasAttribute('disabled'), 'Button is disabled');
// Try to proceed without entering any value.
$page->findButton('Save')->click();
if ($field_type == 'list_string') {
// Asserting only name field as there is no value field for list_string.
$this->assertSession()->pageTextContains('Name field is required.');
}
else {
// Confirmation message that name and value are required fields for
// list_float and list_integer.
$this->assertSession()->pageTextContains('Name field is required.');
$this->assertSession()->pageTextContains('Value field is required.');
}
}
}
}

View File

@@ -0,0 +1,104 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\options\Functional;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\Tests\field\Functional\FieldTestBase;
/**
* Tests option fields can be updated and created through config synchronization.
*
* @group options
*/
class OptionsFloatFieldImportTest extends FieldTestBase {
/**
* Modules to enable.
*
* @var array
*/
protected static $modules = [
'node',
'options',
'field_ui',
'config',
'options_config_install_test',
];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
// Create test user.
$admin_user = $this->drupalCreateUser([
'synchronize configuration',
'access content',
'access administration pages',
'administer site configuration',
'administer content types',
'administer nodes',
'bypass node access',
'administer node fields',
'administer node display',
]);
$this->drupalLogin($admin_user);
}
/**
* Tests that importing list_float fields works.
*/
public function testImport(): void {
$field_name = 'field_options_float';
$type = 'options_install_test';
// Test the results on installing options_config_install_test. All the
// necessary configuration for this test is created by installing that
// module.
$field_storage = FieldStorageConfig::loadByName('node', $field_name);
$this->assertSame($array = ['0' => 'Zero', '0.5' => 'Point five'], $field_storage->getSetting('allowed_values'));
$admin_path = 'admin/structure/types/manage/' . $type . '/fields/node.' . $type . '.' . $field_name;
// Export active config to sync.
$this->copyConfig($this->container->get('config.storage'), $this->container->get('config.storage.sync'));
// Set the active to not use dots in the allowed values key names.
$edit = [
'field_storage[subform][settings][allowed_values][table][0][item][key]' => 0,
'field_storage[subform][settings][allowed_values][table][0][item][label]' => 'Zero',
'field_storage[subform][settings][allowed_values][table][1][item][key]' => 1,
'field_storage[subform][settings][allowed_values][table][1][item][label]' => 'One',
];
$this->drupalGet($admin_path);
$this->submitForm($edit, 'Save');
$field_storage = FieldStorageConfig::loadByName('node', $field_name);
$this->assertSame($array = ['0' => 'Zero', '1' => 'One'], $field_storage->getSetting('allowed_values'));
// Import configuration with dots in the allowed values key names. This
// tests \Drupal\Core\Config\Entity\ConfigEntityStorage::importUpdate().
$this->drupalGet('admin/config/development/configuration');
$this->submitForm([], 'Import all');
$field_storage = FieldStorageConfig::loadByName('node', $field_name);
$this->assertSame($array = ['0' => 'Zero', '0.5' => 'Point five'], $field_storage->getSetting('allowed_values'));
// Delete field to test creation. This tests
// \Drupal\Core\Config\Entity\ConfigEntityStorage::importCreate().
FieldConfig::loadByName('node', $type, $field_name)->delete();
$this->drupalGet('admin/config/development/configuration');
$this->submitForm([], 'Import all');
$field_storage = FieldStorageConfig::loadByName('node', $field_name);
$this->assertSame($array = ['0' => 'Zero', '0.5' => 'Point five'], $field_storage->getSetting('allowed_values'));
}
}

View File

@@ -0,0 +1,45 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\options\Functional;
/**
* Tests an options select with a dynamic allowed values function.
*
* @group options
*/
class OptionsSelectDynamicValuesTest extends OptionsDynamicValuesTestBase {
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* Tests the 'options_select' widget (single select).
*/
public function testSelectListDynamic(): void {
// Create an entity.
$this->entity->save();
// Create a web user.
$web_user = $this->drupalCreateUser([
'view test entity',
'administer entity_test content',
]);
$this->drupalLogin($web_user);
// Display form.
$this->drupalGet('entity_test_rev/manage/' . $this->entity->id() . '/edit');
$options = $this->assertSession()->selectExists('edit-test-options')->findAll('css', 'option');
$this->assertCount(count($this->test) + 1, $options);
foreach ($options as $option) {
$value = $option->getValue();
if ($value != '_none') {
$this->assertContains($value, $this->test);
}
}
}
}

View File

@@ -0,0 +1,708 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\options\Functional;
use Drupal\entity_test\Entity\EntityTest;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\Tests\field\Functional\FieldTestBase;
/**
* Tests the Options widgets.
*
* @group options
* @group #slow
*/
class OptionsWidgetsTest extends FieldTestBase {
/**
* Modules to enable.
*
* @var array
*/
protected static $modules = [
'node',
'options',
'entity_test',
'options_test',
'taxonomy',
'field_ui',
];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* A field storage with cardinality 1 to use in this test class.
*
* @var \Drupal\field\Entity\FieldStorageConfig
*/
protected $card1;
/**
* A field storage with cardinality 2 to use in this test class.
*
* @var \Drupal\field\Entity\FieldStorageConfig
*/
protected $card2;
/**
* A field storage with float values to use in this test class.
*
* @var \Drupal\field\Entity\FieldStorageConfig
*/
protected $float;
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
// Field storage with cardinality 1.
$this->card1 = FieldStorageConfig::create([
'field_name' => 'card_1',
'entity_type' => 'entity_test',
'type' => 'list_integer',
'cardinality' => 1,
'settings' => [
'allowed_values' => [
// Make sure that 0 works as an option.
0 => 'Zero',
1 => 'One',
// Make sure that option text is properly sanitized.
2 => 'Some <script>dangerous</script> & unescaped <strong>markup</strong>',
// Make sure that HTML entities in option text are not double-encoded.
3 => 'Some HTML encoded markup with &lt; &amp; &gt;',
],
],
]);
$this->card1->save();
// Field storage with cardinality 2.
$this->card2 = FieldStorageConfig::create([
'field_name' => 'card_2',
'entity_type' => 'entity_test',
'type' => 'list_integer',
'cardinality' => 2,
'settings' => [
'allowed_values' => [
// Make sure that 0 works as an option.
0 => 'Zero',
1 => 'One',
// Make sure that option text is properly sanitized.
2 => 'Some <script>dangerous</script> & unescaped <strong>markup</strong>',
],
],
]);
$this->card2->save();
// Field storage with list of float values.
$this->float = FieldStorageConfig::create([
'field_name' => 'float',
'entity_type' => 'entity_test',
'type' => 'list_float',
'cardinality' => 1,
'settings' => [
'allowed_values' => [
'0.0' => '0.0',
'1.5' => '1.5',
'2.0' => '2.0',
],
],
]);
$this->float->save();
// Create a web user.
$this->drupalLogin($this->drupalCreateUser([
'view test entity',
'administer entity_test content',
]));
}
/**
* Tests the 'options_buttons' widget (single select).
*/
public function testRadioButtons(): void {
// Create an instance of the 'single value' field.
$field = FieldConfig::create([
'field_storage' => $this->card1,
'bundle' => 'entity_test',
]);
$field->save();
\Drupal::service('entity_display.repository')
->getFormDisplay('entity_test', 'entity_test')
->setComponent($this->card1->getName(), [
'type' => 'options_buttons',
])
->save();
// Create an entity.
$entity = EntityTest::create([
'user_id' => 1,
'name' => $this->randomMachineName(),
]);
$entity->save();
$entity_init = clone $entity;
// With no field data, no buttons are checked.
$this->drupalGet('entity_test/manage/' . $entity->id() . '/edit');
$this->assertSession()->checkboxNotChecked('edit-card-1-0');
$this->assertSession()->checkboxNotChecked('edit-card-1-1');
$this->assertSession()->checkboxNotChecked('edit-card-1-2');
$this->assertSession()->responseContains('Some dangerous &amp; unescaped <strong>markup</strong>');
$this->assertSession()->responseContains('Some HTML encoded markup with &lt; &amp; &gt;');
// Select first option.
$edit = ['card_1' => 0];
$this->submitForm($edit, 'Save');
$this->assertFieldValues($entity_init, 'card_1', [0]);
// Check that the selected button is checked.
$this->drupalGet('entity_test/manage/' . $entity->id() . '/edit');
$this->assertSession()->checkboxChecked('edit-card-1-0');
$this->assertSession()->checkboxNotChecked('edit-card-1-1');
$this->assertSession()->checkboxNotChecked('edit-card-1-2');
// Unselect option.
$edit = ['card_1' => '_none'];
$this->submitForm($edit, 'Save');
$this->assertFieldValues($entity_init, 'card_1', []);
// Check that required radios with one option is auto-selected.
$this->card1->setSetting('allowed_values', [99 => 'Only allowed value']);
$this->card1->save();
$field->setRequired(TRUE);
$field->save();
$this->drupalGet('entity_test/manage/' . $entity->id() . '/edit');
$this->assertSession()->checkboxChecked('edit-card-1-99');
}
/**
* Tests the 'options_buttons' widget (multiple select).
*/
public function testCheckBoxes(): void {
// Create an instance of the 'multiple values' field.
$field = FieldConfig::create([
'field_storage' => $this->card2,
'bundle' => 'entity_test',
]);
$field->save();
\Drupal::service('entity_display.repository')
->getFormDisplay('entity_test', 'entity_test')
->setComponent($this->card2->getName(), [
'type' => 'options_buttons',
])
->save();
// Create an entity.
$entity = EntityTest::create([
'user_id' => 1,
'name' => $this->randomMachineName(),
]);
$entity->save();
$entity_init = clone $entity;
// Display form: with no field data, nothing is checked.
$this->drupalGet('entity_test/manage/' . $entity->id() . '/edit');
$this->assertSession()->checkboxNotChecked('edit-card-2-0');
$this->assertSession()->checkboxNotChecked('edit-card-2-1');
$this->assertSession()->checkboxNotChecked('edit-card-2-2');
$this->assertSession()->responseContains('Some dangerous &amp; unescaped <strong>markup</strong>');
// Submit form: select first and third options.
$edit = [
'card_2[0]' => TRUE,
'card_2[1]' => FALSE,
'card_2[2]' => TRUE,
];
$this->submitForm($edit, 'Save');
$this->assertFieldValues($entity_init, 'card_2', [0, 2]);
// Display form: check that the right options are selected.
$this->drupalGet('entity_test/manage/' . $entity->id() . '/edit');
$this->assertSession()->checkboxChecked('edit-card-2-0');
$this->assertSession()->checkboxNotChecked('edit-card-2-1');
$this->assertSession()->checkboxChecked('edit-card-2-2');
// Submit form: select only first option.
$edit = [
'card_2[0]' => TRUE,
'card_2[1]' => FALSE,
'card_2[2]' => FALSE,
];
$this->submitForm($edit, 'Save');
$this->assertFieldValues($entity_init, 'card_2', [0]);
// Display form: check that the right options are selected.
$this->drupalGet('entity_test/manage/' . $entity->id() . '/edit');
$this->assertSession()->checkboxChecked('edit-card-2-0');
$this->assertSession()->checkboxNotChecked('edit-card-2-1');
$this->assertSession()->checkboxNotChecked('edit-card-2-2');
// Submit form: select the three options while the field accepts only 2.
$edit = [
'card_2[0]' => TRUE,
'card_2[1]' => TRUE,
'card_2[2]' => TRUE,
];
$this->submitForm($edit, 'Save');
$this->assertSession()->pageTextContains('this field cannot hold more than 2 values');
// Submit form: uncheck all options.
$edit = [
'card_2[0]' => FALSE,
'card_2[1]' => FALSE,
'card_2[2]' => FALSE,
];
$this->submitForm($edit, 'Save');
// Check that the value was saved.
$this->assertFieldValues($entity_init, 'card_2', []);
// Required checkbox with one option is auto-selected.
$this->card2->setSetting('allowed_values', [99 => 'Only allowed value']);
$this->card2->save();
$field->setRequired(TRUE);
$field->save();
$this->drupalGet('entity_test/manage/' . $entity->id() . '/edit');
$this->assertSession()->checkboxChecked('edit-card-2-99');
}
/**
* Tests the 'options_select' widget (single select).
*/
public function testSelectListSingle(): void {
// Create an instance of the 'single value' field.
$field = FieldConfig::create([
'field_storage' => $this->card1,
'bundle' => 'entity_test',
'required' => TRUE,
]);
$field->save();
\Drupal::service('entity_display.repository')
->getFormDisplay('entity_test', 'entity_test')
->setComponent($this->card1->getName(), [
'type' => 'options_select',
])
->save();
// Create an entity.
$entity = EntityTest::create([
'user_id' => 1,
'name' => $this->randomMachineName(),
]);
$entity->save();
$entity_init = clone $entity;
// Display form.
$this->drupalGet('entity_test/manage/' . $entity->id() . '/edit');
// A required field without any value has a "none" option.
$option = $this->assertSession()->optionExists('edit-card-1', '_none');
$this->assertSame('- Select a value -', $option->getText());
// With no field data, nothing is selected.
$this->assertTrue($this->assertSession()->optionExists('card_1', '_none')->isSelected());
$this->assertFalse($this->assertSession()->optionExists('card_1', 0)->isSelected());
$this->assertFalse($this->assertSession()->optionExists('card_1', 1)->isSelected());
$this->assertFalse($this->assertSession()->optionExists('card_1', 2)->isSelected());
$this->assertSession()->responseContains('Some dangerous &amp; unescaped markup');
// Submit form: select invalid 'none' option.
$edit = ['card_1' => '_none'];
$this->submitForm($edit, 'Save');
$this->assertSession()->pageTextContains("{$field->getName()} field is required.");
// Submit form: select first option.
$edit = ['card_1' => 0];
$this->submitForm($edit, 'Save');
$this->assertFieldValues($entity_init, 'card_1', [0]);
// Display form: check that the right options are selected.
$this->drupalGet('entity_test/manage/' . $entity->id() . '/edit');
// A required field with a value has no 'none' option.
$this->assertSession()->optionNotExists('edit-card-1', '_none');
$this->assertTrue($this->assertSession()->optionExists('card_1', 0)->isSelected());
$this->assertFalse($this->assertSession()->optionExists('card_1', 1)->isSelected());
$this->assertFalse($this->assertSession()->optionExists('card_1', 2)->isSelected());
// Make the field non required.
$field->setRequired(FALSE);
$field->save();
// Display form.
$this->drupalGet('entity_test/manage/' . $entity->id() . '/edit');
// A non-required field has a 'none' option.
$option = $this->assertSession()->optionExists('edit-card-1', '_none');
$this->assertSame('- None -', $option->getText());
// Submit form: Unselect the option.
$edit = ['card_1' => '_none'];
$this->drupalGet('entity_test/manage/' . $entity->id() . '/edit');
$this->submitForm($edit, 'Save');
$this->assertFieldValues($entity_init, 'card_1', []);
// Test optgroups.
$this->card1->setSetting('allowed_values', []);
$this->card1->setSetting('allowed_values_function', 'options_test_allowed_values_callback');
$this->card1->save();
// Display form: with no field data, nothing is selected
$this->drupalGet('entity_test/manage/' . $entity->id() . '/edit');
$this->assertFalse($this->assertSession()->optionExists('card_1', 0)->isSelected());
$this->assertFalse($this->assertSession()->optionExists('card_1', 1)->isSelected());
$this->assertFalse($this->assertSession()->optionExists('card_1', 2)->isSelected());
$this->assertSession()->responseContains('Some dangerous &amp; unescaped markup');
$this->assertSession()->responseContains('More &lt;script&gt;dangerous&lt;/script&gt; markup');
$this->assertSession()->responseContains('Group 1');
// Submit form: select first option.
$edit = ['card_1' => 0];
$this->submitForm($edit, 'Save');
$this->assertFieldValues($entity_init, 'card_1', [0]);
// Display form: check that the right options are selected.
$this->drupalGet('entity_test/manage/' . $entity->id() . '/edit');
$this->assertTrue($this->assertSession()->optionExists('card_1', 0)->isSelected());
$this->assertFalse($this->assertSession()->optionExists('card_1', 1)->isSelected());
$this->assertFalse($this->assertSession()->optionExists('card_1', 2)->isSelected());
// Submit form: Unselect the option.
$edit = ['card_1' => '_none'];
$this->drupalGet('entity_test/manage/' . $entity->id() . '/edit');
$this->submitForm($edit, 'Save');
$this->assertFieldValues($entity_init, 'card_1', []);
}
/**
* Tests the '#required_error' attribute for the select list.
*/
public function testSelectListRequiredErrorAttribute(): void {
// Enable form alter hook.
\Drupal::state()->set('options_test.form_alter_enable', TRUE);
// Create an instance of the 'single value' field.
$field = FieldConfig::create([
'field_storage' => $this->card1,
'bundle' => 'entity_test',
'required' => TRUE,
]);
$field->save();
\Drupal::service('entity_display.repository')
->getFormDisplay('entity_test', 'entity_test')
->setComponent($this->card1->getName(), [
'type' => 'options_select',
])
->save();
// Create an entity.
$entity = EntityTest::create([
'user_id' => 1,
'name' => $this->randomMachineName(),
]);
$entity->save();
$this->drupalGet('entity_test/manage/' . $entity->id() . '/edit');
// A required field without any value has a "none" option.
$option = $this->assertSession()->optionExists('edit-card-1', '_none');
$this->assertSame('- Select a value -', $option->getText());
// Submit form: select invalid 'none' option.
$edit = ['card_1' => '_none'];
$this->submitForm($edit, 'Save');
$this->assertSession()->responseContains(t('This is custom message for required field.'));
}
/**
* Tests the 'options_select' widget (multiple select).
*/
public function testSelectListMultiple(): void {
// Create an instance of the 'multiple values' field.
$field = FieldConfig::create([
'field_storage' => $this->card2,
'bundle' => 'entity_test',
]);
$field->save();
\Drupal::service('entity_display.repository')
->getFormDisplay('entity_test', 'entity_test')
->setComponent($this->card2->getName(), [
'type' => 'options_select',
])
->save();
// Create an entity.
$entity = EntityTest::create([
'user_id' => 1,
'name' => $this->randomMachineName(),
]);
$entity->save();
$entity_init = clone $entity;
// Display form: with no field data, nothing is selected.
$this->drupalGet('entity_test/manage/' . $entity->id() . '/edit');
$this->assertTrue($this->assertSession()->optionExists('card_2', '_none')->isSelected());
$this->assertFalse($this->assertSession()->optionExists('card_2', 0)->isSelected());
$this->assertFalse($this->assertSession()->optionExists('card_2', 1)->isSelected());
$this->assertFalse($this->assertSession()->optionExists('card_2', 2)->isSelected());
$this->assertSession()->responseContains('Some dangerous &amp; unescaped markup');
// Submit form: select first and third options.
$edit = ['card_2[]' => [0 => 0, 2 => 2]];
$this->submitForm($edit, 'Save');
$this->assertFieldValues($entity_init, 'card_2', [0, 2]);
// Display form: check that the right options are selected.
$this->drupalGet('entity_test/manage/' . $entity->id() . '/edit');
$this->assertTrue($this->assertSession()->optionExists('card_2', 0)->isSelected());
$this->assertFalse($this->assertSession()->optionExists('card_2', 1)->isSelected());
$this->assertTrue($this->assertSession()->optionExists('card_2', 2)->isSelected());
// Submit form: select only first option.
$edit = ['card_2[]' => [0 => 0]];
$this->submitForm($edit, 'Save');
$this->assertFieldValues($entity_init, 'card_2', [0]);
// Display form: check that the right options are selected.
$this->drupalGet('entity_test/manage/' . $entity->id() . '/edit');
$this->assertTrue($this->assertSession()->optionExists('card_2', 0)->isSelected());
$this->assertFalse($this->assertSession()->optionExists('card_2', 1)->isSelected());
$this->assertFalse($this->assertSession()->optionExists('card_2', 2)->isSelected());
// Submit form: select the three options while the field accepts only 2.
$edit = ['card_2[]' => [0 => 0, 1 => 1, 2 => 2]];
$this->submitForm($edit, 'Save');
$this->assertSession()->pageTextContains('this field cannot hold more than 2 values');
// Submit form: uncheck all options.
$edit = ['card_2[]' => []];
$this->submitForm($edit, 'Save');
$this->assertFieldValues($entity_init, 'card_2', []);
// Test the 'None' option.
// Check that the 'none' option has no effect if actual options are selected
// as well.
$edit = ['card_2[]' => ['_none' => '_none', 0 => 0]];
$this->drupalGet('entity_test/manage/' . $entity->id() . '/edit');
$this->submitForm($edit, 'Save');
$this->assertFieldValues($entity_init, 'card_2', [0]);
// Check that selecting the 'none' option empties the field.
$edit = ['card_2[]' => ['_none' => '_none']];
$this->drupalGet('entity_test/manage/' . $entity->id() . '/edit');
$this->submitForm($edit, 'Save');
$this->assertFieldValues($entity_init, 'card_2', []);
// A required select list does not have an empty key.
$field->setRequired(TRUE);
$field->save();
$this->drupalGet('entity_test/manage/' . $entity->id() . '/edit');
$this->assertEmpty($this->assertSession()->selectExists('edit-card-2')->find('xpath', 'option[@value=""]'));
// We do not have to test that a required select list with one option is
// auto-selected because the browser does it for us.
// Test optgroups.
// Use a callback function defining optgroups.
$this->card2->setSetting('allowed_values', []);
$this->card2->setSetting('allowed_values_function', 'options_test_allowed_values_callback');
$this->card2->save();
$field->setRequired(FALSE);
$field->save();
// Display form: with no field data, nothing is selected.
$this->drupalGet('entity_test/manage/' . $entity->id() . '/edit');
$this->assertFalse($this->assertSession()->optionExists('card_2', 0)->isSelected());
$this->assertFalse($this->assertSession()->optionExists('card_2', 1)->isSelected());
$this->assertFalse($this->assertSession()->optionExists('card_2', 2)->isSelected());
$this->assertSession()->responseContains('Some dangerous &amp; unescaped markup');
$this->assertSession()->responseContains('More &lt;script&gt;dangerous&lt;/script&gt; markup');
$this->assertSession()->responseContains('Group 1');
// Submit form: select first option.
$edit = ['card_2[]' => [0 => 0]];
$this->submitForm($edit, 'Save');
$this->assertFieldValues($entity_init, 'card_2', [0]);
// Display form: check that the right options are selected.
$this->drupalGet('entity_test/manage/' . $entity->id() . '/edit');
$this->assertTrue($this->assertSession()->optionExists('card_2', 0)->isSelected());
$this->assertFalse($this->assertSession()->optionExists('card_2', 1)->isSelected());
$this->assertFalse($this->assertSession()->optionExists('card_2', 2)->isSelected());
// Submit form: Unselect the option.
$edit = ['card_2[]' => ['_none' => '_none']];
$this->drupalGet('entity_test/manage/' . $entity->id() . '/edit');
$this->submitForm($edit, 'Save');
$this->assertFieldValues($entity_init, 'card_2', []);
}
/**
* Tests the 'options_select' widget (float values).
*/
public function testSelectListFloat(): void {
// Create an instance of the 'float value' field.
$field = FieldConfig::create([
'field_storage' => $this->float,
'bundle' => 'entity_test',
'required' => TRUE,
]);
$field->save();
$this->container
->get('entity_type.manager')
->getStorage('entity_form_display')
->load('entity_test.entity_test.default')
->setComponent($this->float->getName(), ['type' => 'options_select'])
->save();
// Create an entity.
$entity = EntityTest::create([
'user_id' => 1,
'name' => $this->randomMachineName(),
]);
$entity->save();
// Display form.
$this->drupalGet('entity_test/manage/' . $entity->id() . '/edit');
// With no field data, nothing is selected.
$this->assertFalse($this->assertSession()->optionExists('float', 0)->isSelected());
$this->assertFalse($this->assertSession()->optionExists('float', 1.5)->isSelected());
$this->assertFalse($this->assertSession()->optionExists('float', 2)->isSelected());
// Submit form.
$edit = ['float' => 1.5];
$this->submitForm($edit, 'Save');
$this->assertFieldValues($entity, 'float', [1.5]);
// Display form: check that the right options are selected.
$this->drupalGet('entity_test/manage/' . $entity->id() . '/edit');
$this->assertFalse($this->assertSession()->optionExists('float', 0)->isSelected());
$this->assertTrue($this->assertSession()->optionExists('float', 1.5)->isSelected());
$this->assertFalse($this->assertSession()->optionExists('float', 2)->isSelected());
}
/**
* Tests the 'options_select' and 'options_button' widget for empty value.
*/
public function testEmptyValue(): void {
// Create an instance of the 'single value' field.
$field = FieldConfig::create([
'field_storage' => $this->card1,
'bundle' => 'entity_test',
]);
$field->save();
/** @var \Drupal\Core\Entity\EntityDisplayRepositoryInterface $display_repository */
$display_repository = \Drupal::service('entity_display.repository');
// Change it to the check boxes/radio buttons widget.
$display_repository->getFormDisplay('entity_test', 'entity_test')
->setComponent($this->card1->getName(), [
'type' => 'options_buttons',
])
->save();
// Create an entity.
$entity = EntityTest::create([
'user_id' => 1,
'name' => $this->randomMachineName(),
]);
$entity->save();
// Display form: check that _none options are present and has label.
$this->drupalGet('entity_test/manage/' . $entity->id() . '/edit');
// Verify that a test radio button has a "None" choice.
$this->assertSession()->elementExists('xpath', '//div[@id="edit-card-1"]//input[@value="_none"]');
// Verify that a test radio button has a "N/A" choice..
$this->assertSession()->elementExists('xpath', '//div[@id="edit-card-1"]//label[@for="edit-card-1-none"]');
$this->assertSession()->elementTextEquals('xpath', '//div[@id="edit-card-1"]//label[@for="edit-card-1-none"]', "N/A");
// Change it to the select widget.
$display_repository->getFormDisplay('entity_test', 'entity_test')
->setComponent($this->card1->getName(), [
'type' => 'options_select',
])
->save();
// Display form: check that _none options are present and has label.
$this->drupalGet('entity_test/manage/' . $entity->id() . '/edit');
// A required field without any value has a "none" option.
$option = $this->assertSession()->optionExists('edit-card-1', '_none');
$this->assertSame('- None -', $option->getText());
}
/**
* Tests hook_options_list_alter().
*
* @see options_test_options_list_alter()
*/
public function testOptionsListAlter(): void {
$field1 = FieldConfig::create([
'field_storage' => $this->card1,
'bundle' => 'entity_test',
]);
$field1->save();
// Create a new field that will be altered.
$card4 = FieldStorageConfig::create([
'field_name' => 'card_4',
'entity_type' => 'entity_test',
'type' => 'list_integer',
'cardinality' => 1,
'settings' => [
'allowed_values' => [
0 => 'Zero',
1 => 'One',
],
],
]);
$card4->save();
$field2 = FieldConfig::create([
'field_storage' => $card4,
'bundle' => 'entity_test',
]);
$field2->save();
/** @var \Drupal\Core\Entity\EntityDisplayRepositoryInterface $display_repository */
$display_repository = \Drupal::service('entity_display.repository');
// Change it to the check boxes/radio buttons widget.
$display_repository->getFormDisplay('entity_test', 'entity_test')
->setComponent($this->card1->getName(), [
'type' => 'options_select',
])
->setComponent($card4->getName(), [
'type' => 'options_select',
])
->save();
// Create an entity.
$entity = EntityTest::create([
'user_id' => 1,
'name' => $this->randomMachineName(),
]);
$entity->save();
// Display form: check that _none options are present.
$this->drupalGet('entity_test/manage/' . $entity->id() . '/edit');
$xpath = '//select[@id=:id]//option[@value="_none" and text()=:label]';
$xpath_args = [':id' => 'edit-card-1', ':label' => '- None -'];
$this->assertSession()->elementExists('xpath', $this->assertSession()->buildXPathQuery($xpath, $xpath_args));
$xpath_args = [':id' => 'edit-card-4', ':label' => '- Select something -'];
$this->assertSession()->elementExists('xpath', $this->assertSession()->buildXPathQuery($xpath, $xpath_args));
// Display form: check that options are displayed correctly.
$this->assertSession()->optionExists('card_1', 0);
$this->assertSession()->optionExists('card_1', 1);
$this->assertSession()->optionNotExists('card_4', 0);
$this->assertSession()->optionExists('card_4', 1);
}
}

View File

@@ -0,0 +1,450 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\options\FunctionalJavascript;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
use Drupal\Tests\field_ui\Traits\FieldUiJSTestTrait;
/**
* Tests the Options field UI functionality.
*
* @group options
* @group #slow
*/
class OptionsFieldUITest extends WebDriverTestBase {
use FieldUiJSTestTrait;
/**
* {@inheritdoc}
*/
protected static $modules = [
'node',
'options',
'field_ui',
];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* 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;
/**
* Node form path for created content type.
*
* @var string
*/
protected $nodeFormPath;
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
// Create test user.
$admin_user = $this->drupalCreateUser([
'bypass node access',
'administer node fields',
'administer node display',
]);
$this->drupalLogin($admin_user);
$type = $this->drupalCreateContentType(['type' => 'plan']);
$this->type = $type->id();
$this->nodeFormPath = 'node/add/' . $this->type;
}
/**
* Tests option types allowed values.
*
* @dataProvider providerTestOptionsAllowedValues
*/
public function testOptionsAllowedValues($option_type, $options, $is_string_option, string $add_row_method): void {
$assert = $this->assertSession();
$this->fieldName = 'field_options_text';
$this->createOptionsField($option_type);
$page = $this->getSession()->getPage();
$this->drupalGet($this->adminPath);
$i = 0;
$expected_rows = 1;
$this->assertAllowValuesRowCount(1);
foreach ($options as $option_key => $option_label) {
$enter_element_name = $label_element_name = "field_storage[subform][settings][allowed_values][table][$i][item][label]";
$page->fillField($label_element_name, $option_label);
$this->assertSession()->assertWaitOnAjaxRequest();
$key_element_name = "field_storage[subform][settings][allowed_values][table][$i][item][key]";
// Add keys if not string option list.
if (!$is_string_option) {
$this->pressEnterOnElement("[name=\"$label_element_name\"]");
// Assert that pressing enter on label field does not create the new
// row if the key field is visible.
$this->assertAllowValuesRowCount($expected_rows);
$enter_element_name = $key_element_name;
$this->assertHasFocusByAttribute('name', $key_element_name);
$page->fillField($key_element_name, $option_key);
$this->assertSession()->assertWaitOnAjaxRequest();
}
else {
$this->assertFalse($assert->fieldExists($key_element_name)->isVisible());
}
switch ($add_row_method) {
case 'Press button':
$page->pressButton('Add another item');
break;
case 'Enter button':
$button = $assert->buttonExists('Add another item');
$this->pressEnterOnElement('[data-drupal-selector="' . $button->getAttribute('data-drupal-selector') . '"]');
break;
case 'Enter element':
// If testing using the "enter" key while focused on element there a
// few different scenarios to test.
switch ($i) {
case 0:
// For string options the machine name input can be exposed which
// will mean the label input will no longer create the next row.
if ($is_string_option) {
$this->exposeOptionMachineName($expected_rows);
$this->pressEnterOnElement("[name=\"$enter_element_name\"]");
$this->assertHasFocusByAttribute('name', $key_element_name);
// Ensure that pressing enter while focused on the label input
// did not create a new row if the machine name field is
// visible.
$this->assertAllowValuesRowCount($expected_rows);
$enter_element_name = $key_element_name;
}
break;
}
$this->pressEnterOnElement("[name=\"$enter_element_name\"]");
break;
default:
throw new \UnexpectedValueException("Unknown method $add_row_method");
}
$i++;
$expected_rows++;
$this->assertSession()->waitForElementVisible('css', "[name='field_storage[subform][settings][allowed_values][table][$i][item][label]']");
$this->assertHasFocusByAttribute('name', "field_storage[subform][settings][allowed_values][table][$i][item][label]");
$this->assertAllowValuesRowCount($expected_rows);
if ($is_string_option) {
// Expose the key input for string options for the previous row to test
// shifting focus from the label to key inputs on the previous row by
// pressing enter.
$this->exposeOptionMachineName($expected_rows - 1);
}
// Test that pressing enter on the label input on previous row will shift
// focus to key input of that row.
$this->pressEnterOnElement("[name=\"$label_element_name\"]");
$this->assertHasFocusByAttribute('name', $key_element_name);
$this->assertAllowValuesRowCount($expected_rows);
}
$page->pressButton('Save');
// Test the order of the option list on node form.
$this->drupalGet($this->nodeFormPath);
$this->assertNodeFormOrder(['- None -', 'First', 'Second', 'Third']);
// Test the order of the option list on admin path.
$this->drupalGet($this->adminPath);
$this->assertOrder(['First', 'Second', 'Third', ''], $is_string_option);
$drag_handle = $page->find('css', '[data-drupal-selector="edit-field-storage-subform-settings-allowed-values-table-0"] .tabledrag-handle');
$target = $page->find('css', '[data-drupal-selector="edit-field-storage-subform-settings-allowed-values-table-2"]');
// Change the order the items appear.
$drag_handle->dragTo($target);
$this->assertOrder(['Second', 'Third', 'First', ''], $is_string_option);
$page->pressButton('Save');
$this->drupalGet($this->nodeFormPath);
$this->assertNodeFormOrder(['- None -', 'Second', 'Third', 'First']);
$this->drupalGet($this->adminPath);
// Confirm the change in order was saved.
$this->assertOrder(['Second', 'Third', 'First', ''], $is_string_option);
// Delete an item.
$page->pressButton('remove_row_button__1');
$this->assertSession()->assertWaitOnAjaxRequest();
$this->assertOrder(['Second', 'First', ''], $is_string_option);
$page->pressButton('Save');
$this->drupalGet($this->nodeFormPath);
$this->assertNodeFormOrder(['- None -', 'Second', 'First']);
$this->drupalGet($this->adminPath);
// Confirm the item removal was saved.
$this->assertOrder(['Second', 'First', ''], $is_string_option);
}
/**
* Tests that the allowed options are available to the default value widget.
*/
public function testDefaultValueOptions(): void {
$page = $this->getSession()->getPage();
$assert_session = $this->assertSession();
$bundle_path = 'admin/structure/types/manage/' . $this->type;
// Create a field of type list:string.
$this->fieldUIAddNewFieldJS($bundle_path, 'test_string_list', 'Test string list', 'list_string', FALSE);
$page->findField('field_storage[subform][settings][allowed_values][table][0][item][label]')->setValue('first');
$assert_session->assertWaitOnAjaxRequest();
$page->findField('set_default_value')->setValue(TRUE);
// Assert that the option added in the subform is available to the default
// value field.
$this->assertSession()->optionExists('default_value_input[field_test_string_list]', 'first');
$page->pressButton('Add another item');
$this->assertNotNull($assert_session->waitForElement('css', "[name='field_storage[subform][settings][allowed_values][table][1][item][label]']"));
$page->findField('field_storage[subform][settings][allowed_values][table][1][item][label]')->setValue('second');
$assert_session->assertWaitOnAjaxRequest();
$assert_session->optionExists('default_value_input[field_test_string_list]', 'second');
$page->selectFieldOption('default_value_input[field_test_string_list]', 'second');
$page->pressButton('Save settings');
$assert_session->pageTextContains('Saved Test string list configuration.');
// Create a field of type list:integer.
$this->fieldUIAddNewFieldJS($bundle_path, 'test_int_list', 'Test int list', 'list_integer', FALSE);
$page->findField('field_storage[subform][settings][allowed_values][table][0][item][label]')->setValue('first');
$assert_session->assertWaitOnAjaxRequest();
// Assert that no validation is performed.
$assert_session->statusMessageNotContains('Value field is required.');
$page->findField('field_storage[subform][settings][allowed_values][table][0][item][key]')->setValue(1);
$assert_session->assertWaitOnAjaxRequest();
$page->findField('set_default_value')->setValue(TRUE);
// Assert that the option added in the subform is available to the default
// value field.
$this->assertSession()->optionExists('default_value_input[field_test_int_list]', 'first');
$page->selectFieldOption('default_value_input[field_test_int_list]', 'first');
$page->pressButton('Save settings');
$assert_session->pageTextContains('Saved Test int list configuration.');
}
/**
* Asserts the order of provided option list on admin path.
*
* @param array $expected
* Expected order.
* @param bool $is_string_option
* Whether the request is for string option list.
*/
protected function assertOrder($expected, $is_string_option) {
$page = $this->getSession()->getPage();
if ($is_string_option) {
$inputs = $page->findAll('css', '.draggable .form-text.machine-name-source');
}
else {
$inputs = $page->findAll('css', '.draggable .form-text');
}
foreach ($expected as $step => $expected_input_value) {
$value = $inputs[$step]->getValue();
$this->assertSame($expected_input_value, $value, "Item $step should be $expected_input_value, but got $value");
}
}
/**
* Asserts the order of provided option list on node form.
*
* @param array $expected
* Expected order.
*/
protected function assertNodeFormOrder($expected) {
$elements = $this->assertSession()->selectExists('field_options_text')->findAll('css', 'option');
$elements = array_map(function ($element) {
return $element->getText();
}, $elements);
$this->assertSame($expected, $elements);
}
/**
* 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;
}
/**
* Presses "Enter" on the specified element.
*
* @param string $selector
* Current element having focus.
*/
private function pressEnterOnElement(string $selector): void {
$javascript = <<<JS
const element = document.querySelector('$selector');
const event = new KeyboardEvent('keypress', { key: 'Enter', keyCode: 13, bubbles: true });
element.dispatchEvent(event);
JS;
$this->getSession()->executeScript($javascript);
}
/**
* Data provider for testOptionsAllowedValues().
*
* @return array
* Array of arrays with the following elements:
* - Option type.
* - Array of option type values.
* - Whether option type is string type or not.
* - The method which should be used to add another row to the table. The
* possible values are 'Press button', 'Enter button' or 'Enter element'.
*/
public static function providerTestOptionsAllowedValues() {
$type_cases = [
'List integer' => [
'list_integer',
[1 => 'First', 2 => 'Second', 3 => 'Third'],
FALSE,
],
'List float' => [
'list_float',
['0.1' => 'First', '0.2' => 'Second', '0.3' => 'Third'],
FALSE,
],
'List string' => [
'list_string',
['first' => 'First', 'second' => 'Second', 'third' => 'Third'],
TRUE,
],
];
// Test adding options for each option field type using several possible
// methods that could be used for navigating the options list:
// - Press button: add a new item by pressing the 'Add another item'
// button using mouse.
// - Enter button: add a new item by pressing the 'Add another item'
// button using enter key on the keyboard.
// - Enter element: add a new item by pressing enter on the last text
// field inside the table.
$test_cases = [];
foreach ($type_cases as $key => $type_case) {
foreach (['Press button', 'Enter button', 'Enter element'] as $add_more_method) {
$test_cases["$key: $add_more_method"] = array_merge($type_case, [$add_more_method]);
}
}
return $test_cases;
}
/**
* Tests `list_string` machine name with special characters.
*/
public function testMachineNameSpecialCharacters(): void {
$this->fieldName = 'field_options_text';
$this->createOptionsField('list_string');
$this->drupalGet($this->adminPath);
$label_element_name = "field_storage[subform][settings][allowed_values][table][0][item][label]";
$this->getSession()->getPage()->fillField($label_element_name, 'Hello world');
$this->assertSession()->assertWaitOnAjaxRequest();
$this->exposeOptionMachineName(1);
$key_element_name = "field_storage[subform][settings][allowed_values][table][0][item][key]";
// Ensure that the machine name was generated correctly.
$this->assertSession()->fieldValueEquals($key_element_name, 'hello_world');
// Ensure that the machine name can be overridden with a value that includes
// special characters.
$this->getSession()->getPage()->fillField($key_element_name, '.hello #world');
$this->assertSession()->assertWaitOnAjaxRequest();
$this->getSession()->getPage()->pressButton('Save settings');
$this->assertSession()->statusMessageContains("Saved {$this->fieldName} configuration.");
// Ensure that the machine name was saved correctly.
$allowed_values = FieldStorageConfig::loadByName('node', $this->fieldName)
->getSetting('allowed_values');
$this->assertSame(['.hello #world'], array_keys($allowed_values));
}
/**
* Assert the count of the allowed values rows.
*
* @param int $expected_count
* The expected row count.
*/
private function assertAllowValuesRowCount(int $expected_count): void {
$this->assertCount(
$expected_count,
$this->getSession()->getPage()->findAll('css', '#allowed-values-order tr.draggable')
);
}
/**
* Asserts an element specified by an attribute value has focus.
*
* @param string $name
* The attribute name.
* @param string $value
* The attribute value.
*
* @todo Replace with assertHasFocus() in https://drupal.org/i/3041768.
*/
private function assertHasFocusByAttribute(string $name, string $value): void {
$active_element = $this->getSession()->evaluateScript('document.activeElement');
$this->assertSame($value, $active_element->attribute($name));
}
/**
* Exposes the machine name input for a row.
*
* @param int $row
* The row number.
*/
private function exposeOptionMachineName(int $row): void {
$index = $row - 1;
$rows = $this->getSession()->getPage()->findAll('css', '#allowed-values-order tr.draggable');
$this->assertSession()->buttonExists('Edit', $rows[$index])->click();
$this->assertSession()->waitForElementVisible('css', "[name='field_storage[subform][settings][allowed_values][table][$index][item][key]']");
}
}

View File

@@ -0,0 +1,99 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\options\Kernel;
use Drupal\Core\Entity\EntityInterface;
use Drupal\entity_test\Entity\EntityTestRev;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
/**
* Tests the options allowed values api.
*
* @group options
*/
class OptionsDynamicValuesApiTest extends OptionsFieldUnitTestBase {
/**
* {@inheritdoc}
*/
protected static $modules = [
'entity_test',
'options_test',
'node',
];
/**
* The created entity.
*/
protected EntityInterface $entity;
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$this->installEntitySchema('entity_test_rev');
$this->installEntitySchema('user');
$this->installEntitySchema('node');
$this->fieldStorage = FieldStorageConfig::create([
'field_name' => 'test_options',
'entity_type' => 'entity_test_rev',
'type' => 'list_string',
'cardinality' => 1,
'settings' => [
'allowed_values_function' => 'options_test_dynamic_values_callback',
],
]);
$this->fieldStorage->save();
FieldConfig::create([
'field_name' => 'test_options',
'entity_type' => 'entity_test_rev',
'bundle' => 'entity_test_rev',
'required' => TRUE,
])->save();
\Drupal::service('entity_display.repository')
->getFormDisplay('entity_test_rev', 'entity_test_rev')
->setComponent('test_options', [
'type' => 'options_select',
])
->save();
// Create an entity and prepare test data that will be used by
// options_test_dynamic_values_callback().
$values = [
'user_id' => 2,
'name' => $this->randomMachineName(),
];
$this->entity = EntityTestRev::create($values);
$this->entity->save();
}
/**
* Tests options_allowed_values().
*
* @see options_test_dynamic_values_callback()
*/
public function testOptionsAllowedValues(): void {
// Test allowed values without passed $items.
$values = options_allowed_values($this->fieldStorage);
$this->assertEquals([], $values);
$values = options_allowed_values($this->fieldStorage, $this->entity);
$expected_values = [
$this->entity->label(),
$this->entity->toUrl()->toString(),
$this->entity->uuid(),
$this->entity->bundle(),
];
$expected_values = array_combine($expected_values, $expected_values);
$this->assertEquals($expected_values, $values);
}
}

View File

@@ -0,0 +1,107 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\options\Kernel;
use Drupal\Core\Entity\EntityInterface;
use Drupal\entity_test\Entity\EntityTestRev;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
/**
* Tests the Options field allowed values function.
*
* @group options
*/
class OptionsDynamicValuesValidationTest extends OptionsFieldUnitTestBase {
/**
* {@inheritdoc}
*/
protected static $modules = [
'entity_test',
'options_test',
'node',
];
/**
* The created entity.
*/
protected EntityInterface $entity;
/**
* Test data.
*/
protected array $test;
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$this->installEntitySchema('entity_test_rev');
$this->installEntitySchema('user');
$this->installEntitySchema('node');
FieldStorageConfig::create([
'field_name' => 'test_options',
'entity_type' => 'entity_test_rev',
'type' => 'list_string',
'cardinality' => 1,
'settings' => [
'allowed_values_function' => 'options_test_dynamic_values_callback',
],
])->save();
FieldConfig::create([
'field_name' => 'test_options',
'entity_type' => 'entity_test_rev',
'bundle' => 'entity_test_rev',
'required' => TRUE,
])->save();
\Drupal::service('entity_display.repository')
->getFormDisplay('entity_test_rev', 'entity_test_rev')
->setComponent('test_options', [
'type' => 'options_select',
])
->save();
// Create an entity and prepare test data that will be used by
// options_test_dynamic_values_callback().
$values = [
'user_id' => 2,
'name' => $this->randomMachineName(),
];
$this->entity = EntityTestRev::create($values);
$this->entity->save();
$this->test = [
'label' => $this->entity->label(),
'uuid' => $this->entity->uuid(),
'bundle' => $this->entity->bundle(),
'uri' => $this->entity->toUrl()->toString(),
];
}
/**
* Tests that allowed values function gets the entity.
*/
public function testDynamicAllowedValues(): void {
// Verify that validation passes against every value we had.
foreach ($this->test as $key => $value) {
$this->entity->test_options->value = $value;
$violations = $this->entity->test_options->validate();
$this->assertCount(0, $violations, "$key is a valid value");
}
// Now verify that validation does not pass against anything else.
foreach ($this->test as $key => $value) {
$this->entity->test_options->value = is_numeric($value) ? (100 - $value) : ('X' . $value);
$violations = $this->entity->test_options->validate();
$this->assertCount(1, $violations, "$key is not a valid value");
}
}
}

View File

@@ -0,0 +1,113 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\options\Kernel;
use Drupal\Core\Entity\Exception\FieldStorageDefinitionUpdateForbiddenException;
use Drupal\entity_test\Entity\EntityTest;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
/**
* Tests for the 'Options' field types.
*
* @group options
*/
class OptionsFieldTest extends OptionsFieldUnitTestBase {
/**
* Modules to enable.
*
* @var array
*/
protected static $modules = ['options'];
/**
* Tests that allowed values can be updated.
*/
public function testUpdateAllowedValues(): void {
// All three options appear.
$entity = EntityTest::create();
$form = \Drupal::service('entity.form_builder')->getForm($entity);
$this->assertArrayHasKey(1, $form[$this->fieldName]['widget'], 'Option 1 exists');
$this->assertArrayHasKey(2, $form[$this->fieldName]['widget'], 'Option 2 exists');
$this->assertArrayHasKey(3, $form[$this->fieldName]['widget'], 'Option 3 exists');
// Use one of the values in an actual entity, and check that this value
// cannot be removed from the list.
$entity = EntityTest::create();
$entity->{$this->fieldName}->value = 1;
$entity->save();
$this->fieldStorage->setSetting('allowed_values', [2 => 'Two']);
try {
$this->fieldStorage->save();
$this->fail('Cannot update a list field storage to not include keys with existing data.');
}
catch (FieldStorageDefinitionUpdateForbiddenException $e) {
// Expected exception; just continue testing.
}
// Empty the value, so that we can actually remove the option.
unset($entity->{$this->fieldName});
$entity->save();
// Removed options do not appear.
$this->fieldStorage->setSetting('allowed_values', [2 => 'Two']);
$this->fieldStorage->save();
$entity = EntityTest::create();
$form = \Drupal::service('entity.form_builder')->getForm($entity);
$this->assertArrayNotHasKey(1, $form[$this->fieldName]['widget'], 'Option 1 does not exist');
$this->assertArrayHasKey(2, $form[$this->fieldName]['widget'], 'Option 2 exists');
$this->assertArrayNotHasKey(3, $form[$this->fieldName]['widget'], 'Option 3 does not exist');
// Completely new options appear.
$this->fieldStorage->setSetting('allowed_values', [10 => 'Update', 20 => 'Twenty']);
$this->fieldStorage->save();
// The entity holds an outdated field object with the old allowed values
// setting, so we need to reinitialize the entity object.
$entity = EntityTest::create();
$form = \Drupal::service('entity.form_builder')->getForm($entity);
$this->assertArrayNotHasKey(1, $form[$this->fieldName]['widget'], 'Option 1 does not exist');
$this->assertArrayNotHasKey(2, $form[$this->fieldName]['widget'], 'Option 2 does not exist');
$this->assertArrayNotHasKey(3, $form[$this->fieldName]['widget'], 'Option 3 does not exist');
$this->assertArrayHasKey(10, $form[$this->fieldName]['widget'], 'Option 10 exists');
$this->assertArrayHasKey(20, $form[$this->fieldName]['widget'], 'Option 20 exists');
// Options are reset when a new field with the same name is created.
$this->fieldStorage->delete();
FieldStorageConfig::create($this->fieldStorageDefinition)->save();
FieldConfig::create([
'field_name' => $this->fieldName,
'entity_type' => 'entity_test',
'bundle' => 'entity_test',
'required' => TRUE,
])->save();
\Drupal::service('entity_display.repository')
->getFormDisplay('entity_test', 'entity_test')
->setComponent($this->fieldName, [
'type' => 'options_buttons',
])
->save();
$entity = EntityTest::create();
$form = \Drupal::service('entity.form_builder')->getForm($entity);
$this->assertArrayHasKey(1, $form[$this->fieldName]['widget'], 'Option 1 exists');
$this->assertArrayHasKey(2, $form[$this->fieldName]['widget'], 'Option 2 exists');
$this->assertArrayHasKey(3, $form[$this->fieldName]['widget'], 'Option 3 exists');
// Test the generateSampleValue() method.
$entity = EntityTest::create();
$entity->{$this->fieldName}->generateSampleItems();
$this->entityValidateAndSave($entity);
}
/**
* Tests that ::generateSampleItems does not fail with empty allowed values.
*/
public function testGenerateSampleItemsWithNoAllowedValues(): void {
$this->fieldStorage->setSetting('allowed_values', [])->save();
$entity = EntityTest::create();
$value = $entity->{$this->fieldName}->generateSampleItems();
$this->assertNull($value);
}
}

View File

@@ -0,0 +1,83 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\options\Kernel;
use Drupal\field\Entity\FieldConfig;
use Drupal\Tests\field\Kernel\FieldKernelTestBase;
use Drupal\field\Entity\FieldStorageConfig;
/**
* Base class for Options module integration tests.
*/
abstract class OptionsFieldUnitTestBase extends FieldKernelTestBase {
/**
* Modules to enable.
*
* @var array
*/
protected static $modules = ['options'];
/**
* The field name used in the test.
*
* @var string
*/
protected $fieldName = 'test_options';
/**
* The field storage definition used to created the field storage.
*
* @var array
*/
protected $fieldStorageDefinition;
/**
* The list field storage used in the test.
*
* @var \Drupal\field\Entity\FieldStorageConfig
*/
protected $fieldStorage;
/**
* The list field used in the test.
*
* @var \Drupal\field\Entity\FieldConfig
*/
protected $field;
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$this->fieldStorageDefinition = [
'field_name' => $this->fieldName,
'entity_type' => 'entity_test',
'type' => 'list_integer',
'cardinality' => 1,
'settings' => [
'allowed_values' => [1 => 'One', 2 => 'Two', 3 => 'Three'],
],
];
$this->fieldStorage = FieldStorageConfig::create($this->fieldStorageDefinition);
$this->fieldStorage->save();
$this->field = FieldConfig::create([
'field_storage' => $this->fieldStorage,
'bundle' => 'entity_test',
]);
$this->field->save();
\Drupal::service('entity_display.repository')
->getFormDisplay('entity_test', 'entity_test')
->setComponent($this->fieldName, [
'type' => 'options_buttons',
])
->save();
}
}

View File

@@ -0,0 +1,43 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\options\Kernel;
use Drupal\entity_test\Entity\EntityTest;
/**
* Tests the Options field type formatters.
*
* @group options
* @see \Drupal\options\Plugin\Field\FieldFormatter\OptionsDefaultFormatter
* @see \Drupal\options\Plugin\Field\FieldFormatter\OptionsKeyFormatter
*/
class OptionsFormattersTest extends OptionsFieldUnitTestBase {
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
}
/**
* Tests the formatters.
*/
public function testFormatter(): void {
$entity = EntityTest::create();
$entity->{$this->fieldName}->value = 1;
$items = $entity->get($this->fieldName);
$build = $items->view();
$this->assertEquals('list_default', $build['#formatter'], 'Ensure to fall back to the default formatter.');
$this->assertEquals('One', $build[0]['#markup']);
$build = $items->view(['type' => 'list_key']);
$this->assertEquals('list_key', $build['#formatter'], 'The chosen formatter is used.');
$this->assertEquals(1, (string) $build[0]['#markup']);
}
}

View File

@@ -0,0 +1,109 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\options\Kernel\Views;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\field\Entity\FieldConfig;
use Drupal\Tests\views\Kernel\ViewsKernelTestBase;
use Drupal\views\Views;
/**
* Tests file views data.
*
* @group file
*/
class FileViewsDataTest extends ViewsKernelTestBase {
/**
* Modules to install.
*
* @var array
*/
protected static $modules = [
'file',
'views',
'entity_test',
'user',
'field',
];
/**
* {@inheritdoc}
*/
protected function setUp($import_test_views = TRUE): void {
parent::setUp($import_test_views);
$this->installEntitySchema('entity_test');
$this->installEntitySchema('entity_test_mul');
}
/**
* Tests views data generated for file field relationship.
*
* @see file_field_views_data()
* @see file_field_views_data_views_data_alter()
*/
public function testRelationshipViewsData(): void {
// Create file field to entity_test.
FieldStorageConfig::create([
'entity_type' => 'entity_test',
'field_name' => 'field_base_file',
'type' => 'file',
])->save();
FieldConfig::create([
'entity_type' => 'entity_test',
'field_name' => 'field_base_file',
'bundle' => 'entity_test',
])->save();
// Check the generated views data.
$views_data = Views::viewsData()->get('entity_test__field_base_file');
$relationship = $views_data['field_base_file_target_id']['relationship'];
$this->assertEquals('standard', $relationship['id']);
$this->assertEquals('file_managed', $relationship['base']);
$this->assertEquals('fid', $relationship['base field']);
$this->assertEquals('file', $relationship['entity type']);
// Check the backwards reference.
$views_data = Views::viewsData()->get('file_managed');
$relationship = $views_data['reverse_field_base_file_entity_test']['relationship'];
$this->assertEquals('entity_reverse', $relationship['id']);
$this->assertEquals('entity_test', $relationship['base']);
$this->assertEquals('id', $relationship['base field']);
$this->assertEquals('entity_test__field_base_file', $relationship['field table']);
$this->assertEquals('field_base_file_target_id', $relationship['field field']);
$this->assertEquals('field_base_file', $relationship['field_name']);
$this->assertEquals('entity_test', $relationship['entity_type']);
$this->assertEquals(['field' => 'deleted', 'value' => 0, 'numeric' => TRUE], $relationship['join_extra'][0]);
// Create file field to entity_test_mul.
FieldStorageConfig::create([
'entity_type' => 'entity_test_mul',
'field_name' => 'field_data_file',
'type' => 'file',
])->save();
FieldConfig::create([
'entity_type' => 'entity_test_mul',
'field_name' => 'field_data_file',
'bundle' => 'entity_test_mul',
])->save();
// Check the generated views data.
$views_data = Views::viewsData()->get('entity_test_mul__field_data_file');
$relationship = $views_data['field_data_file_target_id']['relationship'];
$this->assertEquals('standard', $relationship['id']);
$this->assertEquals('file_managed', $relationship['base']);
$this->assertEquals('fid', $relationship['base field']);
$this->assertEquals('file', $relationship['entity type']);
// Check the backwards reference.
$views_data = Views::viewsData()->get('file_managed');
$relationship = $views_data['reverse_field_data_file_entity_test_mul']['relationship'];
$this->assertEquals('entity_reverse', $relationship['id']);
$this->assertEquals('entity_test_mul_property_data', $relationship['base']);
$this->assertEquals('id', $relationship['base field']);
$this->assertEquals('entity_test_mul__field_data_file', $relationship['field table']);
$this->assertEquals('field_data_file_target_id', $relationship['field field']);
$this->assertEquals('field_data_file', $relationship['field_name']);
$this->assertEquals('entity_test_mul', $relationship['entity_type']);
$this->assertEquals(['field' => 'deleted', 'value' => 0, 'numeric' => TRUE], $relationship['join_extra'][0]);
}
}

View File

@@ -0,0 +1,51 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\options\Kernel\Views;
use Drupal\views\Views;
/**
* Tests options list argument for views.
*
* @see \Drupal\options\Plugin\views\argument\NumberListField.
* @group views
*/
class OptionsListArgumentTest extends OptionsTestBase {
/**
* Views used by this test.
*
* @var array
*/
public static $testViews = ['test_options_list_argument_numeric', 'test_options_list_argument_string'];
/**
* Tests the options field argument.
*/
public function testViewsTestOptionsListArgument(): void {
$view = Views::getView('test_options_list_argument_numeric');
$this->executeView($view, [1]);
$resultset = [
['nid' => $this->nodes[0]->nid->value],
['nid' => $this->nodes[1]->nid->value],
];
$column_map = ['nid' => 'nid'];
$this->assertIdenticalResultset($view, $resultset, $column_map);
$view = Views::getView('test_options_list_argument_string');
$this->executeView($view, ['man', 'woman']);
$resultset = [
['nid' => $this->nodes[0]->nid->value],
['nid' => $this->nodes[1]->nid->value],
];
$column_map = ['nid' => 'nid'];
$this->assertIdenticalResultset($view, $resultset, $column_map);
}
}

View File

@@ -0,0 +1,114 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\options\Kernel\Views;
use Drupal\views\Views;
/**
* Tests options list filter for views.
*
* @see \Drupal\field\Plugin\views\filter\ListField.
* @group views
*/
class OptionsListFilterTest extends OptionsTestBase {
/**
* Views used by this test.
*
* @var array
*/
public static $testViews = ['test_options_list_filter'];
/**
* Tests options list field filter.
*/
public function testViewsTestOptionsListFilter(): void {
$view = Views::getView('test_options_list_filter');
$this->executeView($view);
$resultset = [
['nid' => $this->nodes[0]->nid->value],
['nid' => $this->nodes[1]->nid->value],
];
$column_map = ['nid' => 'nid'];
$this->assertIdenticalResultset($view, $resultset, $column_map);
}
/**
* Tests options list field filter when grouped.
*/
public function testViewsTestOptionsListGroupedFilter(): void {
$view = Views::getView('test_options_list_filter');
$filters = [
'field_test_list_string_value' => [
'id' => 'field_test_list_string_value',
'table' => 'field_data_field_test_list_string',
'field' => 'field_test_list_string_value',
'relationship' => 'none',
'group_type' => 'group',
'admin_label' => '',
'operator' => 'or',
'value' => [
'man' => 'man',
'woman' => 'woman',
],
'group' => '1',
'exposed' => TRUE,
'expose' => [
'operator_id' => 'field_test_list_string_value_op',
'label' => 'list-text',
'description' => '',
'identifier' => 'field_test_list_string_value',
],
'is_grouped' => TRUE,
'group_info' => [
'label' => 'list-text (field_list_text)',
'description' => '',
'identifier' => 'field_test_list_string_value',
'optional' => TRUE,
'widget' => 'radios',
'multiple' => TRUE,
'remember' => FALSE,
'default_group' => '1',
'group_items' => [
1 => [
'title' => 'First',
'operator' => 'or',
'value' => [
$this->fieldValues[0] => $this->fieldValues[0],
],
],
2 => [
'title' => 'Second',
'operator' => 'or',
'value' => [
$this->fieldValues[1] => $this->fieldValues[1],
],
],
],
],
'reduce_duplicates' => '',
'plugin_id' => 'list_field',
],
];
$view->setDisplay();
$view->displayHandlers->get('default')->overrideOption('filters', $filters);
$view->storage->save();
$this->executeView($view);
$resultset = [
['nid' => $this->nodes[0]->nid->value],
['nid' => $this->nodes[1]->nid->value],
];
$column_map = ['nid' => 'nid'];
$this->assertIdenticalResultset($view, $resultset, $column_map);
}
}

View File

@@ -0,0 +1,129 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\options\Kernel\Views;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\node\Entity\Node;
use Drupal\node\Entity\NodeType;
use Drupal\Tests\views\Kernel\ViewsKernelTestBase;
use Drupal\views\Tests\ViewTestData;
/**
* Base class for options views tests.
*/
abstract class OptionsTestBase extends ViewsKernelTestBase {
/**
* Modules to enable.
*
* @var array
*/
protected static $modules = [
'options',
'options_test_views',
'node',
'user',
'field',
];
/**
* Stores the nodes used for the different tests.
*
* @var array
*/
protected $nodes = [];
/**
* Stores the field values used for the different tests.
*
* @var array
*/
protected $fieldValues = [];
/**
* The used field names.
*
* @var string[]
*/
protected $fieldNames;
/**
* {@inheritdoc}
*/
protected function setUp($import_test_views = TRUE): void {
parent::setUp();
$this->mockStandardInstall();
ViewTestData::createTestViews(static::class, ['options_test_views']);
$settings = [];
$settings['type'] = 'article';
$settings['title'] = $this->randomString();
$settings['field_test_list_string'][]['value'] = $this->fieldValues[0];
$settings['field_test_list_integer'][]['value'] = 0;
$node = Node::create($settings);
$node->save();
$this->nodes[] = $node;
$node = $node->createDuplicate();
$node->save();
$this->nodes[] = $node;
}
/**
* Provides a workaround for the inability to use the standard profile.
*
* @see https://www.drupal.org/node/1708692
*/
protected function mockStandardInstall() {
$this->installEntitySchema('user');
$this->installEntitySchema('node');
NodeType::create(['type' => 'article', 'name' => 'Article'])->save();
$this->fieldValues = [
$this->randomMachineName(),
$this->randomMachineName(),
];
$this->fieldNames = ['field_test_list_string', 'field_test_list_integer'];
// Create two field entities.
FieldStorageConfig::create([
'field_name' => $this->fieldNames[0],
'entity_type' => 'node',
'type' => 'list_string',
'cardinality' => 1,
'settings' => [
'allowed_values' => [
$this->fieldValues[0] => $this->fieldValues[0],
$this->fieldValues[1] => $this->fieldValues[1],
],
],
])->save();
FieldStorageConfig::create([
'field_name' => $this->fieldNames[1],
'entity_type' => 'node',
'type' => 'list_integer',
'cardinality' => 1,
'settings' => [
'allowed_values' => [
$this->fieldValues[0],
$this->fieldValues[1],
],
],
])->save();
foreach ($this->fieldNames as $field_name) {
FieldConfig::create([
'field_name' => $field_name,
'entity_type' => 'node',
'label' => 'Test options list field',
'bundle' => 'article',
])->save();
}
}
}

View File

@@ -0,0 +1,78 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\options\Kernel\Views;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
/**
* Test to ensure views data is properly created for the Options module.
*
* @group views
*/
class ViewsDataTest extends OptionsTestBase {
/**
* {@inheritdoc}
*/
protected static $modules = [
'options',
'options_test',
'entity_test',
'views',
];
/**
* The field storage.
*
* @var \Drupal\Core\Field\FieldStorageDefinitionInterface
*/
protected $fieldStorage;
/**
* @var int
*/
protected int $field;
/**
* {@inheritdoc}
*/
protected function setUp($import_test_views = TRUE): void {
parent::setUp();
$this->installEntitySchema('entity_test');
$field_name = 'test_options';
$this->fieldStorage = FieldStorageConfig::create([
'field_name' => $field_name,
'entity_type' => 'entity_test',
'type' => 'list_string',
'cardinality' => 1,
'settings' => [
'allowed_values_function' => 'options_test_dynamic_values_callback',
],
]);
$this->fieldStorage->save();
$this->field = FieldConfig::create([
'field_name' => $field_name,
'entity_type' => 'entity_test',
'bundle' => 'entity_test',
'required' => TRUE,
])->save();
}
/**
* Tests the option module's implementation of hook_field_views_data().
*/
public function testOptionsFieldViewsData(): void {
$field_data = \Drupal::service('views.views_data')->get('entity_test__test_options');
// Check that the options module has properly overridden default views data.
$test_options_field = $field_data['test_options_value'];
$this->assertEquals('string_list_field', $test_options_field['argument']['id'], 'Argument handler is properly set for fields with allowed value callbacks.');
$this->assertEquals('list_field', $test_options_field['filter']['id'], 'Filter handler is properly set for fields with allowed value callbacks.');
}
}