Skip to content
Snippets Groups Projects
Verified Commit a73cec5d authored by Théodore Biadala's avatar Théodore Biadala
Browse files

Issue #3463908 by catch, quietone: Split OptionsFieldUiTest into two

(cherry picked from commit a2713312)
parent 98b7c25e
No related branches found
No related tags found
17 merge requests!10663Issue #3495778: Update phpdoc in FileSaveHtaccessLoggingTest,!10451Issue #3472458 by watergate, smustgrave: CKEditor 5 show blocks label is not translated,!103032838547 Fix punctuation rules for inline label suffix colon with CSS only,!10150Issue #3467294 by quietone, nod_, smustgrave, catch, longwave: Change string...,!10130Resolve #3480321 "Second level menu",!9936Issue #3483087: Check the module:// prefix in the translation server path and replace it with the actual module path,!9933Issue #3394728 by ankondrat4: Undefined array key "#prefix" and deprecated function: explode() in Drupal\file\Element\ManagedFile::uploadAjaxCallback(),!9914Issue #3451136 by quietone, gapple, ghost of drupal past: Improve...,!9882Draft: Issue #3481777 In bulk_form ensure the triggering element is the bulk_form button,!9839Issue #3445469 by pooja_sharma, smustgrave: Add additional test coverage for...,!9815Issue #3480025: There is no way to remove entity cache items,!9757Issue #3478869 Add "All" or overview links to parent links,!9752Issue #3439910 by pooja_sharma, vensires: Fix Toolbar tests that rely on UID1's super user behavior,!9749Issue #3439910 by pooja_sharma, vensires: Fix Toolbar tests that rely on UID1's super user behavior,!9678Issue #3465132 by catch, Spokje, nod_: Show test run time by class in run-tests.sh output,!9578Issue #3304746 by scott_euser, casey, smustgrave: BigPipe cannot handle (GET)...,!9449Issue #3344041: Allow textarea widgets to be used for text (formatted) fields
Pipeline #250399 canceled
<?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 allowed values UI functionality.
*
* @group options
* @group #slow
*/
class OptionsFieldUIAllowedValuesTest 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);
}
/**
* 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;
}
/**
* 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')
);
}
/**
* 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]']");
}
/**
* 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));
}
}
......@@ -80,136 +80,6 @@ protected function setUp(): void {
$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.
*/
......@@ -251,42 +121,6 @@ public function testDefaultValueOptions(): void {
$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.
*
......@@ -314,67 +148,6 @@ protected function createOptionsField($type) {
$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.
*/
......@@ -406,34 +179,6 @@ public function testMachineNameSpecialCharacters(): void {
$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.
*
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment