From 08066a2ab273216222849fd60fdc4de025eb014b Mon Sep 17 00:00:00 2001 From: David Monllao Date: Fri, 24 Jan 2014 13:24:22 +0800 Subject: [PATCH] MDL-43738 behat: Normalization and major refactoring of getters and setters Every single step that sets or gets a value from a field has been updated to follow the same behaviour both when using it through a single step or through generic steps like "I fill the moodle form with:", to resume all the changes: - Created a behat_form_group to re-guess the field type and act appropriately setting and getting it's value - Normalize all getters and setters to use behat_form_field children - Complete behat_form_checkbox to trigger the appropiate JS event needed to perform some JS scripts that are listening - Refactor MDL-43713 multi-select changes and remove the two new steps introduced there as that behaviour can be managed from the generic getter --- lib/behat/form_field/behat_form_checkbox.php | 50 +++- .../form_field/behat_form_date_selector.php | 10 +- .../behat_form_date_time_selector.php | 11 +- lib/behat/form_field/behat_form_field.php | 24 ++ lib/behat/form_field/behat_form_group.php | 42 ++++ lib/behat/form_field/behat_form_radio.php | 42 +++- lib/behat/form_field/behat_form_select.php | 160 ++++++++++-- lib/tests/behat/behat_forms.php | 229 +++++++----------- 8 files changed, 385 insertions(+), 183 deletions(-) create mode 100644 lib/behat/form_field/behat_form_group.php diff --git a/lib/behat/form_field/behat_form_checkbox.php b/lib/behat/form_field/behat_form_checkbox.php index fb220d1cf35..69a9cbc8b34 100644 --- a/lib/behat/form_field/behat_form_checkbox.php +++ b/lib/behat/form_field/behat_form_checkbox.php @@ -47,18 +47,37 @@ class behat_form_checkbox extends behat_form_field { */ public function set_value($value) { - if (!$this->running_javascript()) { - $this->field->check(); - return; - } - if (!empty($value) && !$this->field->isChecked()) { + + if (!$this->running_javascript()) { + $this->field->check(); + return; + } + // Check it if it should be checked and it is not. $this->field->click(); + // Trigger the onchange event as triggered when 'checking' the checkbox. + $this->session->getDriver()->triggerSynScript( + $this->field->getXPath(), + "Syn.trigger('change', {}, {{ELEMENT}})" + ); + } else if (empty($value) && $this->field->isChecked()) { + + if (!$this->running_javascript()) { + $this->field->uncheck(); + return; + } + // Uncheck if it is checked and shouldn't. $this->field->click(); + + // Trigger the onchange event as triggered when 'checking' the checkbox. + $this->session->getDriver()->triggerSynScript( + $this->field->getXPath(), + "Syn.trigger('change', {}, {{ELEMENT}})" + ); } } @@ -70,4 +89,25 @@ class behat_form_checkbox extends behat_form_field { public function get_value() { return $this->field->isChecked(); } + + /** + * Is it enabled? + * + * @param string $expectedvalue Anything !empty() is considered checked. + * @return bool + */ + public function matches($expectedvalue = false) { + + $ischecked = $this->field->isChecked(); + + // Any non-empty value provided means that it should be checked. + if (!empty($expectedvalue) && $ischecked) { + return true; + } else if (empty($expectedvalue) && !$ischecked) { + return true; + } + + return false; + } + } diff --git a/lib/behat/form_field/behat_form_date_selector.php b/lib/behat/form_field/behat_form_date_selector.php index 2d86a768cf6..4fc8e47646b 100644 --- a/lib/behat/form_field/behat_form_date_selector.php +++ b/lib/behat/form_field/behat_form_date_selector.php @@ -25,14 +25,13 @@ // NOTE: no MOODLE_INTERNAL test here, this file may be required by behat before including /config.php. -require_once(__DIR__ . '/behat_form_select.php'); +require_once(__DIR__ . '/behat_form_group.php'); /** * Date form field. * - * Simple extension of behat_form_select to allow date-type - * select fields to be filled like select elements instead of - * text elements. + * Simple extension of behat_form_group to allow the different + * date_selector fields to be filled according to it's type. * * This class will be refactored in case we are interested in * creating more complex formats to fill date and date-time fields. @@ -42,4 +41,5 @@ require_once(__DIR__ . '/behat_form_select.php'); * @copyright 2013 David Monllaó * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -class behat_form_date_selector extends behat_form_select {} +class behat_form_date_selector extends behat_form_group { +} diff --git a/lib/behat/form_field/behat_form_date_time_selector.php b/lib/behat/form_field/behat_form_date_time_selector.php index 22c4befa610..19181cf6b40 100644 --- a/lib/behat/form_field/behat_form_date_time_selector.php +++ b/lib/behat/form_field/behat_form_date_time_selector.php @@ -25,21 +25,18 @@ // NOTE: no MOODLE_INTERNAL test here, this file may be required by behat before including /config.php. -require_once(__DIR__ . '/behat_form_select.php'); +require_once(__DIR__ . '/behat_form_date_selector.php'); /** * Date time form field. * - * Simple extension of behat_form_select to allow datetime-type - * select fields to be filled like select elements instead of - * text elements. - * * This class will be refactored in case we are interested in - * creating more complex formats to fill date and date-time fields. + * creating more complex formats to fill date-time fields. * * @package core_form * @category test * @copyright 2013 David Monllaó * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -class behat_form_date_time_selector extends behat_form_select {} +class behat_form_date_time_selector extends behat_form_date_selector { +} diff --git a/lib/behat/form_field/behat_form_field.php b/lib/behat/form_field/behat_form_field.php index 284befd3be5..aca068fb8be 100644 --- a/lib/behat/form_field/behat_form_field.php +++ b/lib/behat/form_field/behat_form_field.php @@ -95,6 +95,30 @@ class behat_form_field { } } + /** + * Generic match implementation + * + * Will work well with text-based fields, extension required + * for most of the other cases. + * + * @param string $expectedvalue + * @return bool The provided value matches the field value? + */ + public function matches($expectedvalue) { + + // If we are not dealing with a text-based tag try to find the most appropiate + // behat_form_* class to deal with it. + if ($instance = $this->guess_type()) { + return $instance->matches($expectedvalue); + } + + // Text-based comparison. + if (trim($expectedvalue) != trim($this->get_value())) { + return false; + } + return true; + } + /** * Guesses the element type we are dealing with in case is not a text-based element. * diff --git a/lib/behat/form_field/behat_form_group.php b/lib/behat/form_field/behat_form_group.php new file mode 100644 index 00000000000..533a787cd7e --- /dev/null +++ b/lib/behat/form_field/behat_form_group.php @@ -0,0 +1,42 @@ +. + +/** + * Generic group field class. + * + * @package core_form + * @category test + * @copyright 2014 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'); + +/** + * Class to re-guess the field type as grouped fields can have different field types. + * + * When filling fields in a fgroup field element we don't know what kind + * of field are we dealing with, so we should re-guess it. + * + * @package core_form + * @category test + * @copyright 2014 David Monllaó + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class behat_form_group extends behat_form_field { +} diff --git a/lib/behat/form_field/behat_form_radio.php b/lib/behat/form_field/behat_form_radio.php index 1ca020ebec3..27b5d005631 100644 --- a/lib/behat/form_field/behat_form_radio.php +++ b/lib/behat/form_field/behat_form_radio.php @@ -31,7 +31,8 @@ require_once(__DIR__ . '/behat_form_checkbox.php'); * Radio input form field. * * Extends behat_form_checkbox as the set_value() behaviour - * is the same. + * is the same and it behaves closer to a checkbox than to + * a text field. * * This form field type can be added to forms as any other * moodle form element, but it does not make sense without @@ -51,9 +52,46 @@ class behat_form_radio extends behat_form_checkbox { /** * Returns the radio input value attribute. * + * Here we can not extend behat_form_checkbox because + * isChecked() does internally a (bool)getValue() and + * it is not good for radio buttons. + * * @return string The value attribute */ public function get_value() { - return $this->field->getValue(); + return (bool)$this->field->getAttribute('checked'); + } + + /** + * Sets the value of a radio + * + * Partially overwriting behat_form_checkbox + * implementation as when JS is disabled we + * can not check() and we should use setValue() + * + * @param string $value + * @return void + */ + public function set_value($value) { + + if ($this->running_javascript()) { + parent::set_value($value); + } else { + // Goutte does not accept a check nor a click in an input[type=radio]. + $this->field->setValue($this->field->getAttribute('value')); + } + } + + /** + * Returns whether the provided value matches the current value or not. + * + * @param string $expectedvalue + * @return bool + */ + public function matches($expectedvalue = false) { + if (trim($expectedvalue) != trim($this->get_value())) { + return false; + } + return true; } } diff --git a/lib/behat/form_field/behat_form_select.php b/lib/behat/form_field/behat_form_select.php index b0e94ee09a7..86d6b781734 100644 --- a/lib/behat/form_field/behat_form_select.php +++ b/lib/behat/form_field/behat_form_select.php @@ -93,6 +93,7 @@ class behat_form_select extends behat_form_field { // In some browsers the selectOption actions can perform a form submit or reload page // so we need to ensure the element is still available to continue interacting // with it. We don't wait here. + // getXpath() does not send a query to selenium, so we don't need to wrap it in a try & catch. $selectxpath = $this->field->getXpath(); if (!$this->session->getDriver()->find($selectxpath)) { return; @@ -105,14 +106,9 @@ class behat_form_select extends behat_form_field { return; } - // We also check that the option(s) are still there. We neither wait. - foreach ($options as $option) { - $valueliteral = $this->session->getSelectorsHandler()->xpathLiteral(trim($option)); - $optionxpath = $selectxpath . "/descendant::option[(./@value=$valueliteral or normalize-space(.)=$valueliteral)]"; - if (!$this->session->getDriver()->find($optionxpath)) { - return; - } - } + // Wait for all the possible AJAX requests that have been + // already triggered by selectOption() to be finished. + $this->session->wait(behat_base::TIMEOUT * 1000, behat_base::PAGE_READY_JS); // Wrapped in try & catch as the element may disappear if an AJAX request was submitted. try { @@ -125,17 +121,16 @@ class behat_form_select extends behat_form_field { return; } - // Wait for all the possible AJAX requests that have been - // already triggered by selectOption() to be finished. - $this->session->wait(behat_base::TIMEOUT * 1000, behat_base::PAGE_READY_JS); - // Single select sometimes needs an extra click in the option. if (!$multiple) { + // Var $options only contains 1 option. + $optionxpath = $this->get_option_xpath(end($options), $selectxpath); + // Using the driver direcly because Element methods are messy when dealing // with elements inside containers. - $optionnodes = $this->session->getDriver()->find($optionxpath); - if ($optionnodes) { + if ($optionnodes = $this->session->getDriver()->find($optionxpath)) { + // Wrapped in a try & catch as we can fall into race conditions // and the element may not be there. try { @@ -158,22 +153,28 @@ class behat_form_select extends behat_form_field { return; } - // We ensure that the option is still there. - if (!$this->session->getDriver()->find($optionxpath)) { - return; + // We also check that the option(s) are still there. We neither wait. + foreach ($options as $option) { + $optionxpath = $this->get_option_xpath($option, $selectxpath); + if (!$this->session->getDriver()->find($optionxpath)) { + return; + } } // Wait for all the possible AJAX requests that have been - // already triggered by selectOption() to be finished. + // already triggered by clicking on the field to be finished. $this->session->wait(behat_base::TIMEOUT * 1000, behat_base::PAGE_READY_JS); // Wrapped in a try & catch as we can fall into race conditions // and the element may not be there. try { + // Repeating the select(s) as some drivers (chrome that I know) are moving // to another option after the general select field click above. + $afterfirstoption = false; foreach ($options as $option) { - $this->field->selectOption(trim($option), true); + $this->field->selectOption(trim($option), $afterfirstoption); + $afterfirstoption = true; } } catch (Exception $e) { // We continue and return as this means that the element is not there or it is not the same. @@ -188,6 +189,96 @@ class behat_form_select extends behat_form_field { * @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) { + if (trim($expectedvalue) != trim($this->get_value())) { + return false; + } + return true; + } + + // We are dealing with a multi-select. + + // Can pass multiple comma separated, with valuable commas escaped with backslash. + $expectedarr = array(); // Array of passed text options to test. + + // 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); + + // Precheck to speed things up. + if (count($expectedoptions) !== count($selectedoptiontexts) || + count($expectedoptions) !== count($selectedoptionvalues)) { + return false; + } + + // 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('/(?field->hasAttribute('multiple'); @@ -204,17 +295,24 @@ class behat_form_select extends behat_form_field { if ($option->hasAttribute('selected')) { if ($multiple) { // If the select is multiple, text commas must be encoded. - $selectedoptions[] = trim(str_replace(',', '\,', $option->getText())); + $selectedoptions[] = trim(str_replace(',', '\,', $option->{$method}())); } else { - $selectedoptions[] = trim($option->getText()); + $selectedoptions[] = trim($option->{$method}()); } } } - // Goutte does not keep the 'selected' attribute updated, but its getValue() returns - // the selected elements correctly, also those having commas within them. } else { + // Goutte does not keep the 'selected' attribute updated, but its getValue() returns + // the selected elements correctly, also those having commas within them. + + // Goutte 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) { @@ -222,9 +320,9 @@ class behat_form_select extends behat_form_field { if (in_array($option->getValue(), $values)) { if ($multiple) { // If the select is multiple, text commas must be encoded. - $selectedoptions[] = trim(str_replace(',', '\,', $option->getText())); + $selectedoptions[] = trim(str_replace(',', '\,', $option->{$method}())); } else { - $selectedoptions[] = trim($option->getText()); + $selectedoptions[] = trim($option->{$method}()); } } } @@ -232,4 +330,16 @@ class behat_form_select extends behat_form_field { 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 = $this->session->getSelectorsHandler()->xpathLiteral(trim($option)); + return $selectxpath . "/descendant::option[(./@value=$valueliteral or normalize-space(.)=$valueliteral)]"; + } } diff --git a/lib/tests/behat/behat_forms.php b/lib/tests/behat/behat_forms.php index 8659e0a5822..225ad737299 100644 --- a/lib/tests/behat/behat_forms.php +++ b/lib/tests/behat/behat_forms.php @@ -154,7 +154,7 @@ class behat_forms extends behat_base { } /** - * Fills in form field with specified id|name|label|value. + * Fills in form text field with specified id|name|label|value. It works with text-based fields. * * @When /^I fill in "(?P(?:[^"]|\\")*)" with "(?P(?:[^"]|\\")*)"$/ * @throws ElementNotFoundException Thrown by behat_base::find @@ -162,9 +162,7 @@ class behat_forms extends behat_base { * @param string $value */ public function fill_field($field, $value) { - - $fieldnode = $this->find_field($field); - $fieldnode->setValue($value); + $this->set_field_value($field, $value); } /** @@ -176,13 +174,7 @@ class behat_forms extends behat_base { * @param string $select */ public function select_option($option, $select) { - - $selectnode = $this->find_field($select); - - // We delegate to behat_form_field class, it will - // guess the type properly as it is a select tag. - $selectformfield = behat_field_manager::get_form_field($selectnode, $this->getSession()); - $selectformfield->set_value($option); + $this->set_field_value($select, $option); } /** @@ -193,14 +185,7 @@ class behat_forms extends behat_base { * @param string $radio The radio button id, name or label value */ public function select_radio($radio) { - - $radionode = $this->find_radio($radio); - $radionode->check(); - - // Adding a click as Selenium requires it to fire some JS events. - if ($this->running_javascript()) { - $radionode->click(); - } + $this->set_field_value($radio, 1); } /** @@ -211,11 +196,7 @@ class behat_forms extends behat_base { * @param string $option */ public function check_option($option) { - - // We don't delegate to behat_form_checkbox as the - // step is explicitly saying I check. - $checkboxnode = $this->find_field($option); - $checkboxnode->check(); + $this->set_field_value($option, 1); } /** @@ -226,18 +207,11 @@ class behat_forms extends behat_base { * @param string $option */ public function uncheck_option($option) { - - // We don't delegate to behat_form_checkbox as the - // step is explicitly saying I uncheck. - $checkboxnode = $this->find_field($option); - $checkboxnode->uncheck(); + $this->set_field_value($option, ''); } /** - * Checks that the form element field have the specified value. - * - * NOTE: This method/step does not support all fields. Namely, multi-select ones aren't supported. - * @todo: MDL-43738 would try to put some better support here for that multi-select and others. + * Checks that the field matches the specified value. When using multi-select fields use commas to separate selected options. * * @Then /^the "(?P(?:[^"]|\\")*)" field should match "(?P(?:[^"]|\\")*)" value$/ * @throws ExpectationException @@ -251,10 +225,10 @@ class behat_forms extends behat_base { // Get the field. $field = behat_field_manager::get_form_field($fieldnode, $this->getSession()); - $fieldvalue = $field->get_value(); // Checks if the provided value matches the current field value. - if (trim($value) != trim($fieldvalue)) { + if (!$field->matches($value)) { + $fieldvalue = $field->get_value(); throw new ExpectationException( 'The \'' . $locator . '\' value is \'' . $fieldvalue . '\', \'' . $value . '\' expected' , $this->getSession() @@ -262,26 +236,91 @@ class behat_forms extends behat_base { } } + /** + * Checks that the form element field does not match the specified value. + * + * @Then /^the field "(?P(?:[^"]|\\")*)" does not match value "(?P(?:[^"]|\\")*)"$/ + * @throws ExpectationException + * @throws ElementNotFoundException Thrown by behat_base::find + * @param string $field + * @param string $value + * @return void + */ + public function the_field_does_not_match_value($field, $value) { + + $fieldnode = $this->find_field($field); + + // Get the field. + $formfield = behat_field_manager::get_form_field($fieldnode, $this->getSession()); + + // Checks if the provided value matches the current field value. + if ($formfield->matches($value)) { + $fieldvalue = $formfield->get_value(); + throw new ExpectationException( + 'The \'' . $field . '\' value matches \'' . $value . '\' and it should not match it' , + $this->getSession() + ); + } + } + + /** + * Checks if fields values matches the provided values. Provide a table with field/value data. + * + * @Then /^the following fields match these values:$/ + * @throws ExpectationException + * @param TableNode $data Pairs of | field | value | + */ + public function the_following_fields_match_these_values(TableNode $data) { + + // Expand all fields in case we have. + $this->expand_all_fields(); + + $datahash = $data->getRowsHash(); + + // The action depends on the field type. + foreach ($datahash as $locator => $value) { + $this->the_field_should_match_value($locator, $value); + } + } + + /** + * Checks that fields values do not match the provided values. Provide a table with field/value data. + * + * @Then /^the following fields do not match these values:$/ + * @throws ExpectationException + * @param TableNode $data Pairs of | field | value | + */ + public function the_following_fields_do_not_match_these_values(TableNode $data) { + + // Expand all fields in case we have. + $this->expand_all_fields(); + + $datahash = $data->getRowsHash(); + + // The action depends on the field type. + foreach ($datahash as $locator => $value) { + $this->the_field_does_not_match_value($locator, $value); + } + } + /** * Checks, that checkbox with specified in|name|label|value is checked. * * @Then /^the "(?P(?:[^"]|\\")*)" checkbox should be checked$/ - * @see Behat\MinkExtension\Context\MinkContext * @param string $checkbox */ public function assert_checkbox_checked($checkbox) { - $this->assertSession()->checkboxChecked($checkbox); + $this->the_field_should_match_value($checkbox, 1); } /** * Checks, that checkbox with specified in|name|label|value is unchecked. * * @Then /^the "(?P(?:[^"]|\\")*)" checkbox should not be checked$/ - * @see Behat\MinkExtension\Context\MinkContext * @param string $checkbox */ public function assert_checkbox_not_checked($checkbox) { - $this->assertSession()->checkboxNotChecked($checkbox); + $this->the_field_should_match_value($checkbox, ''); } /** @@ -373,111 +412,23 @@ class behat_forms extends behat_base { } /** - * Checks, that given select box contains the specified option selected. + * Generic field setter. * - * @Then /^the "(?P(?:[^"]|\\")*)" select box should contain "(?P(?:[^"]|\\")*)" selected$/ - * @throws ExpectationException - * @throws ElementNotFoundException Thrown by behat_base::find - * @param string $select The select element name - * @param string $option The option text. Plain value or comma separated - * values if multiple. Commas in multiple values escaped with backslash. - */ - public function the_select_box_should_contain_selected($select, $option) { - - $selectnode = $this->find_field($select); - $multiple = $selectnode->hasAttribute('multiple'); - $optionsarr = array(); // Array of passed text options to test. - $selectedarr = array(); // Array of selected text options. - - if ($multiple) { - // Can pass multiple comma separated, with valuable commas escaped with backslash. - foreach (preg_replace('/\\\,/', ',', preg_split('/(?getSession()); - $value = $field->get_value(); - - if ($multiple) { - // Can be multiple comma separated, with valuable commas escaped with backslash. - foreach (preg_replace('/\\\,/', ',', preg_split('/(?getSession() - ); - } - } - } - - /** - * Checks, that given select box contains the specified option not selected. + * Internal API method, a generic *I set "VALUE" to "FIELD" field* + * could be created based on it. * - * @Then /^the "(?P(?:[^"]|\\")*)" select box should contain "(?P(?:[^"]|\\")*)" not selected$/ - * @throws ExpectationException - * @throws ElementNotFoundException Thrown by behat_base::find - * @param string $select The select element name - * @param string $option The option text. Plain value or comma separated - * values if multiple. Commas in multiple values escaped with backslash. + * @param string $fieldlocator The pointer to the field, it will depend on the field type. + * @param string $value + * @return void */ - public function the_select_box_should_contain_not_selected($select, $option) { + protected function set_field_value($fieldlocator, $value) { - $selectnode = $this->find_field($select); - $multiple = $selectnode->hasAttribute('multiple'); - $optionsarr = array(); // Array of passed text options to test. - $selectedarr = array(); // Array of selected text options. + $node = $this->find_field($fieldlocator); - // First of all, the option(s) must exist, delegate it. Plain and raw. - $this->the_select_box_should_contain($select, $option); - - if ($multiple) { - // Can pass multiple comma separated, with valuable commas escaped with backslash. - foreach (preg_replace('/\\\,/', ',', preg_split('/(?getSession()); - $value = $field->get_value(); - - if ($multiple) { - // Can be multiple comma separated, with valuable commas escaped with backslash. - foreach (preg_replace('/\\\,/', ',', preg_split('/(?getSession() - ); - } - } + // We delegate to behat_form_field class, it will + // guess the type properly as it is a select tag. + $field = behat_field_manager::get_form_field($node, $this->getSession()); + $field->set_value($value); } }