MDL-43738 behat: Normalization and major refactoring of getters and setters
authorDavid Monllao <davidm@moodle.com>
Fri, 24 Jan 2014 05:24:22 +0000 (13:24 +0800)
committerDavid Monllao <davidm@moodle.com>
Thu, 27 Feb 2014 10:22:03 +0000 (18:22 +0800)
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
- Added a new step definition to check a capability permission
  as we changed the way radio buttons gets it's value

lib/behat/form_field/behat_form_checkbox.php
lib/behat/form_field/behat_form_date_selector.php
lib/behat/form_field/behat_form_date_time_selector.php
lib/behat/form_field/behat_form_field.php
lib/behat/form_field/behat_form_group.php [new file with mode: 0644]
lib/behat/form_field/behat_form_radio.php
lib/behat/form_field/behat_form_select.php
lib/tests/behat/behat_forms.php

index fb220d1..69a9cbc 100644 (file)
@@ -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;
+    }
+
 }
index 2d86a76..86e0064 100644 (file)
 
 // 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,4 @@ 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 {}
index 22c4bef..da277b4 100644 (file)
 
 // 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 {}
index 284befd..aca068f 100644 (file)
@@ -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 (file)
index 0000000..e614509
--- /dev/null
@@ -0,0 +1,41 @@
+<?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/>.
+
+/**
+ * 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 {}
index 1ca020e..27b5d00 100644 (file)
@@ -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;
     }
 }
index b0e94ee..74c2160 100644 (file)
@@ -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) {
 
+            // $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('/(?<!\\\),/', $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 = 'getText';
+        if ($returntexts === false) {
+            $method = 'getValue';
+        }
 
         // Is the select multiple?
         $multiple = $this->field->hasAttribute('multiple');
@@ -204,9 +295,9 @@ 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}());
                     }
                 }
             }
@@ -214,7 +305,14 @@ class behat_form_select extends behat_form_field {
         // Goutte does not keep the 'selected' attribute updated, but its getValue() returns
         // the selected elements correctly, also those having commas within them.
         } else {
+
+            // 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)]";
+    }
 }
index 8659e0a..85fa85d 100644 (file)
@@ -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<field_string>(?:[^"]|\\")*)" with "(?P<value_string>(?:[^"]|\\")*)"$/
      * @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 form element field matches the specified value. When using multi-select fields use commas to separate the selected options.
      *
      * @Then /^the "(?P<field_string>(?:[^"]|\\")*)" field should match "(?P<value_string>(?:[^"]|\\")*)" 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<field_string>(?:[^"]|\\")*)" does not match value "(?P<value_string>(?:[^"]|\\")*)"$/
+     * @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.
+        $field = behat_field_manager::get_form_field($fieldnode, $this->getSession());
+
+        // Checks if the provided value matches the current field value.
+        if ($field->matches($value)) {
+            $fieldvalue = $field->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 $table 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 $table 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_string>(?:[^"]|\\")*)" 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_string>(?:[^"]|\\")*)" 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_string>(?:[^"]|\\")*)" select box should contain "(?P<option_string>(?:[^"]|\\")*)" 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('/(?<!\\\),/', $option)) as $opt) {
-                $optionsarr[] = trim($opt);
-            }
-        } else {
-            // Only one option has been passed.
-            $optionsarr[] = trim($option);
-        }
-
-        // Get currently selected texts.
-        $field = behat_field_manager::get_form_field($selectnode, $this->getSession());
-        $value = $field->get_value();
-
-        if ($multiple) {
-            // Can be multiple comma separated, with valuable commas escaped with backslash.
-            foreach (preg_replace('/\\\,/', ',',  preg_split('/(?<!\\\),/', $value)) as $val) {
-                $selectedarr[] = trim($val);
-            }
-        } else {
-            // Only one text can be selected.
-            $selectedarr[] = trim($value);
-        }
-
-        // Everything normalized, Verify every option is a selected one.
-        foreach ($optionsarr as $opt) {
-            if (!in_array($opt, $selectedarr)) {
-                throw new ExpectationException(
-                    'The select box "' . $select . '" does not contain the option "' . $opt . '"' . ' selected',
-                    $this->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_string>(?:[^"]|\\")*)" select box should contain "(?P<option_string>(?:[^"]|\\")*)" 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) {
-
-        $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.
-
-        // 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('/(?<!\\\),/', $option)) as $opt) {
-                $optionsarr[] = trim($opt);
-            }
-        } else {
-            // Only one option has been passed.
-            $optionsarr[] = trim($option);
-        }
-
-        // Get currently selected texts.
-        $field = behat_field_manager::get_form_field($selectnode, $this->getSession());
-        $value = $field->get_value();
+    protected function set_field_value($fieldlocator, $value) {
 
-        if ($multiple) {
-            // Can be multiple comma separated, with valuable commas escaped with backslash.
-            foreach (preg_replace('/\\\,/', ',',  preg_split('/(?<!\\\),/', $value)) as $val) {
-                $selectedarr[] = trim($val);
-            }
-        } else {
-            // Only one text can be selected.
-            $selectedarr[] = trim($value);
-        }
+        $node = $this->find_field($fieldlocator);
 
-        // Everything normalized, Verify every option is not a selected one.
-        foreach ($optionsarr as $opt) {
-            // Now, verify it's not selected.
-            if (in_array($opt, $selectedarr)) {
-                throw new ExpectationException(
-                    'The select box "' . $select . '" contains the option "' . $opt . '"' . ' selected',
-                    $this->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);
     }
 
 }