moodle/lib/behat/form_field/behat_form_select.php
Andrew Nicols be9e3ebd34 MDL-66979 behat: Remove manually triggered events
Behat should not trigger Synthetic browser events. It is incorrect to do
so as the UI should be used to trigger events correctly.

The W3C WebDriver specification explicitly states when and where these
events will be triggered from (the browser) and therefore there is no
ambiguity and no need to synthetically trigger them from Behat.
2021-01-22 14:52:18 +08:00

203 lines
7.2 KiB
PHP

<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Single select form field class.
*
* @package core_form
* @category test
* @copyright 2012 David Monllaó
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
// NOTE: no MOODLE_INTERNAL test here, this file may be required by behat before including /config.php.
require_once(__DIR__ . '/behat_form_field.php');
/**
* Single select form field.
*
* @package core_form
* @category test
* @copyright 2012 David Monllaó
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class behat_form_select extends behat_form_field {
/**
* Sets the value(s) of a select element.
*
* Seems an easy select, but there are lots of combinations
* of browsers and operative systems and each one manages the
* autosubmits and the multiple option selects in a different way.
*
* @param string $value plain value or comma separated values if multiple. Commas in values escaped with backslash.
* @return void
*/
public function set_value($value) {
// Is the select multiple?
$multiple = $this->field->hasAttribute('multiple');
$singleselect = ($this->field->hasClass('singleselect') || $this->field->hasClass('urlselect'));
// Here we select the option(s).
if ($multiple) {
// Split and decode values. Comma separated list of values allowed. With valuable commas escaped with backslash.
$options = preg_replace('/\\\,/', ',', preg_split('/(?<!\\\),/', trim($value)));
// This is a multiple select, let's pass the multiple flag after first option.
$afterfirstoption = false;
foreach ($options as $option) {
$this->field->selectOption(trim($option), $afterfirstoption);
$afterfirstoption = true;
}
} else {
// By default, assume the passed value is a non-multiple option.
$this->field->selectOption(trim($value));
}
}
/**
* Returns the text of the currently selected options.
*
* @return string Comma separated if multiple options are selected. Commas in option texts escaped with backslash.
*/
public function get_value() {
return $this->get_selected_options();
}
/**
* Returns whether the provided argument matches the current value.
*
* @param mixed $expectedvalue
* @return bool
*/
public function matches($expectedvalue) {
$multiple = $this->field->hasAttribute('multiple');
// Same implementation as the parent if it is a single select.
if (!$multiple) {
$cleanexpectedvalue = trim($expectedvalue);
$selectedtext = trim($this->get_selected_options());
$selectedvalue = trim($this->get_selected_options(false));
if ($cleanexpectedvalue != $selectedvalue && $cleanexpectedvalue != $selectedtext) {
return false;
}
return true;
}
// We are dealing with a multi-select.
// Unescape + trim all options and flip it to have the expected values as keys.
$expectedoptions = $this->get_unescaped_options($expectedvalue);
// Get currently selected option's texts.
$texts = $this->get_selected_options(true);
$selectedoptiontexts = $this->get_unescaped_options($texts);
// Get currently selected option's values.
$values = $this->get_selected_options(false);
$selectedoptionvalues = $this->get_unescaped_options($values);
// We check against string-ordered lists of options.
if ($expectedoptions !== $selectedoptiontexts &&
$expectedoptions !== $selectedoptionvalues) {
return false;
}
return true;
}
/**
* Cleans the list of options and returns it as a string separating options with |||.
*
* @param string $value The string containing the escaped options.
* @return string The options
*/
protected function get_unescaped_options($value) {
// Can be multiple comma separated, with valuable commas escaped with backslash.
$optionsarray = array_map(
'trim',
preg_replace('/\\\,/', ',',
preg_split('/(?<!\\\),/', $value)
)
);
// Sort by value (keeping the keys is irrelevant).
core_collator::asort($optionsarray, SORT_STRING);
// Returning it as a string which is easier to match against other values.
return implode('|||', $optionsarray);
}
/**
* Returns the field selected values.
*
* Externalized from the common behat_form_field API method get_value() as
* matches() needs to check against both values and texts.
*
* @param bool $returntexts Returns the options texts or the options values.
* @return string
*/
protected function get_selected_options($returntexts = true) {
$method = 'getHtml';
if ($returntexts === false) {
$method = 'getValue';
}
// Is the select multiple?
$multiple = $this->field->hasAttribute('multiple');
$selectedoptions = array(); // To accumulate found selected options.
// Driver returns the values as an array or as a string depending
// on whether multiple options are selected or not.
$values = $this->field->getValue();
if (!is_array($values)) {
$values = array($values);
}
// Get all the options in the select and extract their value/text pairs.
$alloptions = $this->field->findAll('xpath', '//option');
foreach ($alloptions as $option) {
// Is it selected?
if (in_array($option->getValue(), $values)) {
if ($multiple) {
// If the select is multiple, text commas must be encoded.
$selectedoptions[] = trim(str_replace(',', '\,', $option->{$method}()));
} else {
$selectedoptions[] = trim($option->{$method}());
}
}
}
return implode(', ', $selectedoptions);
}
/**
* Returns the opton XPath based on it's select xpath.
*
* @param string $option
* @param string $selectxpath
* @return string xpath
*/
protected function get_option_xpath($option, $selectxpath) {
$valueliteral = behat_context_helper::escape(trim($option));
return $selectxpath . "/descendant::option[(./@value=$valueliteral or normalize-space(.)=$valueliteral)]";
}
}