diff --git a/lib/behat/form_field/behat_form_editor.php b/lib/behat/form_field/behat_form_editor.php new file mode 100644 index 00000000000..c2bf778612a --- /dev/null +++ b/lib/behat/form_field/behat_form_editor.php @@ -0,0 +1,73 @@ +. + +/** + * Moodle editor field. + * + * @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. + +use Behat\Mink\Element\NodeElement as NodeElement; + +require_once(__DIR__ . '/behat_form_field.php'); + +/** + * Moodle editor field. + * + * @todo Support for multiple editors + * @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_editor extends behat_form_field { + + /** + * Sets the value to a field. + * + * @param string $value + * @return void + */ + public function set_value($value) { + + // Set the value to the iframe and save it to the textarea. + $editorid = $this->field->getAttribute('id'); + $this->session->executeScript('tinyMCE.get("'.$editorid.'").setContent("' . $value . '");'); + $this->session->executeScript('tinyMCE.get("'.$editorid.'").save();'); + } + + /** + * Returns the editor value. + * + * @return string + */ + public function get_value() { + + // Save the current iframe value in case default value has been edited. + $editorid = $this->field->getAttribute('id'); + $this->session->executeScript('tinyMCE.get("'.$editorid.'").save();'); + + return $this->field->getValue(); + } + +} + diff --git a/lib/behat/form_field/behat_form_field.php b/lib/behat/form_field/behat_form_field.php new file mode 100644 index 00000000000..7c08d738d0b --- /dev/null +++ b/lib/behat/form_field/behat_form_field.php @@ -0,0 +1,78 @@ +. + +/** + * Generic moodleforms field. + * + * @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. + +use Behat\Mink\Session as Session, + Behat\Mink\Element\NodeElement as NodeElement; + +/** + * Representation of a moodle field. + * + * Basically an interface with Mink session. + * + * @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_field { + + protected $session; + protected $fieldnode; + + /** + * General constructor with the node and the session to interact with. + * + * @param Session $session Reference to Mink session to traverse/modify the page DOM. + * @param NodeElement $fieldnode The field DOM node + * @return void + */ + public function __construct(Session $session, NodeElement $fieldnode) { + $this->session = $session; + $this->field = $fieldnode; + } + + /** + * Sets the value to a field. + * + * @param string $value + * @return void + */ + public function set_value($value) { + $this->field->setValue($value); + } + + /** + * Returns the current value of the select element. + * + * @return string + */ + public function get_value() { + return $this->field->getValue(); + } + +} diff --git a/lib/behat/form_field/behat_form_select.php b/lib/behat/form_field/behat_form_select.php new file mode 100644 index 00000000000..4aa392a51e8 --- /dev/null +++ b/lib/behat/form_field/behat_form_select.php @@ -0,0 +1,59 @@ +. + +/** + * 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 of a single select. + * + * @param string $value + * @return void + */ + public function set_value($value) { + $this->field->selectOption($value); + } + + /** + * Returns the text of the current value. + * + * @return string + */ + public function get_value() { + $selectedoption = $this->field->find('xpath', '//option[@selected="selected"]'); + return $selectedoption->getText(); + } +} diff --git a/lib/tests/behat/behat_forms.php b/lib/tests/behat/behat_forms.php new file mode 100644 index 00000000000..972b0160337 --- /dev/null +++ b/lib/tests/behat/behat_forms.php @@ -0,0 +1,335 @@ +. + +/** + * Steps definitions related with forms. + * + * @package core + * @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__ . '/../../../lib/behat/behat_base.php'); + +use Behat\Behat\Context\Step\Given as Given, + Behat\Behat\Context\Step\When as When, + Behat\Behat\Context\Step\Then as Then, + Behat\Gherkin\Node\TableNode as TableNode, + Behat\Mink\Element\NodeElement as NodeElement, + Behat\Mink\Exception\ExpectationException as ExpectationException, + Behat\Mink\Exception\ElementNotFoundException as ElementNotFoundException; + +/** + * Forms-related steps definitions. + * + * @package core + * @category test + * @copyright 2012 David Monllaó + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class behat_forms extends behat_base { + + /** + * Presses button with specified id|name|title|alt|value. + * + * @see Behat\MinkExtension\Context\MinkContext + * @When /^I press "(?P(?:[^"]|\\")*)"$/ + */ + public function press_button($button) { + $button = $this->fixStepArgument($button); + $this->getSession()->getPage()->pressButton($button); + } + + /** + * Fills a moodle form with field/value data. + * + * @throws ElementNotFoundException + * @Given /^I fill the moodle form with:$/ + * @param TableNode $data + */ + public function i_fill_the_moodle_form_with(TableNode $data) { + + $datahash = $data->getRowsHash(); + + // The action depends on the field type. + foreach ($datahash as $locator => $value) { + + unset($fieldnode); + + // Removing \\ that escapes " of the steps arguments. + $locator = $this->fixStepArgument($locator); + + // Finds the element in the page waiting until it appears (or timeouts) + // otherwise spin() throws exception. + $exception = new ElementNotFoundException( + $this->getSession(), 'form field', 'id|name|label|value', $locator + ); + + // $context is $this and will be passed to the function by spin(). + $args['locator'] = $locator; + + // Closure to ensure field($locator) exists. + $fieldnode = $this->spin( + function($context, $args) { + return $context->getSession()->getPage()->findField($args['locator']); + }, $exception, $args + ); + + // Gets the field type from a parent node. + $field = $this->get_field($fieldnode, $locator); + + // Delegates to the field class. + $value = $this->fixStepArgument($value); + $field->set_value($value); + } + } + + /** + * Fills in form field with specified id|name|label|value. + * + * @see Behat\MinkExtension\Context\MinkContext + * @When /^I fill in "(?P(?:[^"]|\\")*)" with "(?P(?:[^"]|\\")*)"$/ + */ + public function fill_field($field, $value) { + $field = $this->fixStepArgument($field); + $value = $this->fixStepArgument($value); + $this->getSession()->getPage()->fillField($field, $value); + } + + /** + * Selects option in select field with specified id|name|label|value. + * + * @see Behat\MinkExtension\Context\MinkContext + * @throws ElementNotFoundException + * @When /^I select "(?P(?:[^"]|\\")*)" from "(?P(?:[^"]|\\")*)"$/ + */ + public function select_option($option, $select) { + $select = $this->fixStepArgument($select); + $option = $this->fixStepArgument($option); + + // We add the click event to deal with autosubmit drop down menus. + $selectnode = $this->getSession()->getPage()->findField($select); + if ($selectnode == null) { + throw new ElementNotFoundException( + $this->getSession(), 'form field', 'id|name|label|value', $select + ); + } + $selectnode->selectOption($option); + $selectnode->click(); + } + + /** + * Checks checkbox with specified id|name|label|value. + * + * @see Behat\MinkExtension\Context\MinkContext + * @When /^I check "(?P(?:[^"]|\\")*)"$/ + */ + public function check_option($option) { + $option = $this->fixStepArgument($option); + $this->getSession()->getPage()->checkField($option); + } + + /** + * Unchecks checkbox with specified id|name|label|value. + * + * @see Behat\MinkExtension\Context\MinkContext + * @When /^I uncheck "(?P(?:[^"]|\\")*)"$/ + */ + public function uncheck_option($option) { + $option = $this->fixStepArgument($option); + $this->getSession()->getPage()->uncheckField($option); + } + + /** + * Checks that the form element field have the specified value. + * + * @throws ElementNotFoundException + * @throws ExpectationException + * @Then /^the "(?P(?:[^"]|\\")*)" field should match "(?P(?:[^"]|\\")*)" value$/ + * @param mixed $locator + * @param mixed $value + */ + public function the_field_should_match_value($locator, $value) { + + $locator = $this->fixStepArgument($locator); + $value = $this->fixStepArgument($value); + + $fieldnode = $this->getSession()->getPage()->findField($locator); + if (null === $fieldnode) { + throw new ElementNotFoundException( + $this->getSession(), 'form field', 'id|name|label|value', $locator + ); + } + + // Gets the field instance. + $field = $this->get_field($fieldnode, $locator); + + // Checks if the provided value matches the current field value. + if ($value != $field->get_value()) { + throw new ExpectationException( + 'The \'' . $locator . '\' value is \'' . $field->get_value() . '\'' , + $this->getSession() + ); + } + } + + /** + * Checks, that checkbox with specified in|name|label|value is checked. + * + * @see Behat\MinkExtension\Context\MinkContext + * @Then /^the "(?P(?:[^"]|\\")*)" checkbox should be checked$/ + */ + public function assert_checkbox_checked($checkbox) { + $checkbox = $this->fixStepArgument($checkbox); + $this->assertSession()->checkboxChecked($checkbox); + } + + /** + * Checks, that checkbox with specified in|name|label|value is unchecked. + * + * @see Behat\MinkExtension\Context\MinkContext + * @Then /^the "(?P(?:[^"]|\\")*)" checkbox should not be checked$/ + */ + public function assert_checkbox_not_checked($checkbox) { + $checkbox = $this->fixStepArgument($checkbox); + $this->assertSession()->checkboxNotChecked($checkbox); + } + + /** + * Checks, that given select box contains the specified option. + * + * @throws ExpectationException + * @throws ElementNotFoundException + * @Then /^the "(?P(?:[^"]|\\")*)" select box should contain "(?P(?:[^"]|\\")*)"$/ + * @param string $select The select element name + * @param string $option The option text/value + */ + public function the_select_box_should_contain($select, $option) { + + $select = $this->fixStepArgument($select); + $option = $this->fixStepArgument($option); + + $selectnode = $this->getSession()->getPage()->findField($select); + if ($selectnode == null) { + throw new ElementNotFoundException( + $this->getSession(), 'form field', 'id|name|label|value', $select + ); + } + + $regex = '/' . preg_quote($option, '/') . '/ui'; + if (!preg_match($regex, $selectnode->getText())) { + throw new ExpectationException( + 'The select box "' . $select . '" does not contains the option "' . $option . '"', + $this->getSession() + ); + } + + } + + /** + * Checks, that given select box does not contain the specified option. + * + * @throws ExpectationException + * @throws ElementNotFoundException + * @Then /^the "(?P(?:[^"]|\\")*)" select box should not contain "(?P(?:[^"]|\\")*)"$/ + * @param string $select The select element name + * @param string $option The option text/value + */ + public function the_select_box_should_not_contain($select, $option) { + + $select = $this->fixStepArgument($select); + $option = $this->fixStepArgument($option); + + $selectnode = $this->getSession()->getPage()->findField($select); + if ($selectnode == null) { + throw new ElementNotFoundException( + $this->getSession(), 'form field', 'id|name|label|value', $select + ); + } + + $regex = '/' . preg_quote($option, '/') . '/ui'; + if (preg_match($regex, $selectnode->getText())) { + throw new ExpectationException( + 'The select box "' . $select . '" contains the option "' . $option . '"', + $this->getSession() + ); + } + } + + /** + * Gets an instance of the form element field. + * + * @param NodeElement $fieldnode The current node + * @param string $locator Just to send an exception that makes sense for the user + * @return behat_form_field + */ + protected function get_field(NodeElement $fieldnode, $locator) { + global $CFG; + + $locator = $this->fixStepArgument($locator); + + // Get the field type. + $type = $this->get_node_type($fieldnode, $locator); + $classname = 'behat_form_' . $type; + + // Fallsback on the default form field if nothing specific exists. + $classpath = $CFG->libdir . '/behat/form_field/' . $classname . '.php'; + if (!file_exists($classpath)) { + $classname = 'behat_form_field'; + $classpath = $CFG->libdir . '/behat/form_field/' . $classname . '.php'; + } + + // Returns the instance. + require_once($classpath); + return new $classname($this->getSession(), $fieldnode); + } + + /** + * Recursive method to find the field type. + * + * Depending on the field the felement class node is a level or in another. We + * look recursively for a parent node with a 'felement' class to find the field type. + * + * @throws ExpectationException + * @param NodeElement $fieldnode The current node + * @param string $locator Just to send an exception that makes sense for the user + * @return mixed String or NodeElement depending if we have reached the felement node + */ + protected function get_node_type(NodeElement $fieldnode, $locator) { + + $locator = $this->fixStepArgument($locator); + + // We look for a parent node with 'felement' class. + if ($class = $fieldnode->getParent()->getAttribute('class')) { + + if (strstr($class, 'felement') != false) { + // Remove 'felement f' from class value. + return substr($class, 10); + } + + // Stop propagation through the DOM, something went wrong!. + if (strstr($class, 'fcontainer') != false) { + throw new ExpectationException('No field type for ' . $locator . ' found, ensure the field exists', $this->getSession()); + } + } + + return $this->get_node_type($fieldnode->getParent(), $locator); + } + +}