MDL-37657 behat: Adding definitions related with forms
[moodle.git] / lib / tests / behat / behat_forms.php
1 <?php
2 // This file is part of Moodle - http://moodle.org/
3 //
4 // Moodle is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // Moodle is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
17 /**
18  * Steps definitions related with forms.
19  *
20  * @package    core
21  * @category   test
22  * @copyright  2012 David MonllaĆ³
23  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24  */
26 // NOTE: no MOODLE_INTERNAL test here, this file may be required by behat before including /config.php.
28 require_once(__DIR__ . '/../../../lib/behat/behat_base.php');
30 use Behat\Behat\Context\Step\Given as Given,
31     Behat\Behat\Context\Step\When as When,
32     Behat\Behat\Context\Step\Then as Then,
33     Behat\Gherkin\Node\TableNode as TableNode,
34     Behat\Mink\Element\NodeElement as NodeElement,
35     Behat\Mink\Exception\ExpectationException as ExpectationException,
36     Behat\Mink\Exception\ElementNotFoundException as ElementNotFoundException;
38 /**
39  * Forms-related steps definitions.
40  *
41  * @package    core
42  * @category   test
43  * @copyright  2012 David MonllaĆ³
44  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
45  */
46 class behat_forms extends behat_base {
48     /**
49      * Presses button with specified id|name|title|alt|value.
50      *
51      * @see Behat\MinkExtension\Context\MinkContext
52      * @When /^I press "(?P<button_string>(?:[^"]|\\")*)"$/
53      */
54     public function press_button($button) {
55         $button = $this->fixStepArgument($button);
56         $this->getSession()->getPage()->pressButton($button);
57     }
59     /**
60      * Fills a moodle form with field/value data.
61      *
62      * @throws ElementNotFoundException
63      * @Given /^I fill the moodle form with:$/
64      * @param TableNode $data
65      */
66     public function i_fill_the_moodle_form_with(TableNode $data) {
68         $datahash = $data->getRowsHash();
70         // The action depends on the field type.
71         foreach ($datahash as $locator => $value) {
73             unset($fieldnode);
75             // Removing \\ that escapes " of the steps arguments.
76             $locator = $this->fixStepArgument($locator);
78             // Finds the element in the page waiting until it appears (or timeouts)
79             // otherwise spin() throws exception.
80             $exception = new ElementNotFoundException(
81                 $this->getSession(), 'form field', 'id|name|label|value', $locator
82             );
84             // $context is $this and will be passed to the function by spin().
85             $args['locator'] = $locator;
87             // Closure to ensure field($locator) exists.
88             $fieldnode = $this->spin(
89                 function($context, $args) {
90                     return $context->getSession()->getPage()->findField($args['locator']);
91                 }, $exception, $args
92             );
94             // Gets the field type from a parent node.
95             $field = $this->get_field($fieldnode, $locator);
97             // Delegates to the field class.
98             $value = $this->fixStepArgument($value);
99             $field->set_value($value);
100         }
101     }
103     /**
104      * Fills in form field with specified id|name|label|value.
105      *
106      * @see Behat\MinkExtension\Context\MinkContext
107      * @When /^I fill in "(?P<field_string>(?:[^"]|\\")*)" with "(?P<value_string>(?:[^"]|\\")*)"$/
108      */
109     public function fill_field($field, $value) {
110         $field = $this->fixStepArgument($field);
111         $value = $this->fixStepArgument($value);
112         $this->getSession()->getPage()->fillField($field, $value);
113     }
115     /**
116      * Selects option in select field with specified id|name|label|value.
117      *
118      * @see Behat\MinkExtension\Context\MinkContext
119      * @throws ElementNotFoundException
120      * @When /^I select "(?P<option_string>(?:[^"]|\\")*)" from "(?P<select_string>(?:[^"]|\\")*)"$/
121      */
122     public function select_option($option, $select) {
123         $select = $this->fixStepArgument($select);
124         $option = $this->fixStepArgument($option);
126         // We add the click event to deal with autosubmit drop down menus.
127         $selectnode = $this->getSession()->getPage()->findField($select);
128         if ($selectnode == null) {
129             throw new ElementNotFoundException(
130                 $this->getSession(), 'form field', 'id|name|label|value', $select
131             );
132         }
133         $selectnode->selectOption($option);
134         $selectnode->click();
135     }
137     /**
138      * Checks checkbox with specified id|name|label|value.
139      *
140      * @see Behat\MinkExtension\Context\MinkContext
141      * @When /^I check "(?P<option_string>(?:[^"]|\\")*)"$/
142      */
143     public function check_option($option) {
144         $option = $this->fixStepArgument($option);
145         $this->getSession()->getPage()->checkField($option);
146     }
148     /**
149      * Unchecks checkbox with specified id|name|label|value.
150      *
151      * @see Behat\MinkExtension\Context\MinkContext
152      * @When /^I uncheck "(?P<option_string>(?:[^"]|\\")*)"$/
153      */
154     public function uncheck_option($option) {
155         $option = $this->fixStepArgument($option);
156         $this->getSession()->getPage()->uncheckField($option);
157     }
159     /**
160      * Checks that the form element field have the specified value.
161      *
162      * @throws ElementNotFoundException
163      * @throws ExpectationException
164      * @Then /^the "(?P<field_string>(?:[^"]|\\")*)" field should match "(?P<value_string>(?:[^"]|\\")*)" value$/
165      * @param mixed $locator
166      * @param mixed $value
167      */
168     public function the_field_should_match_value($locator, $value) {
170         $locator = $this->fixStepArgument($locator);
171         $value = $this->fixStepArgument($value);
173         $fieldnode = $this->getSession()->getPage()->findField($locator);
174         if (null === $fieldnode) {
175             throw new ElementNotFoundException(
176                 $this->getSession(), 'form field', 'id|name|label|value', $locator
177             );
178         }
180         // Gets the field instance.
181         $field = $this->get_field($fieldnode, $locator);
183         // Checks if the provided value matches the current field value.
184         if ($value != $field->get_value()) {
185             throw new ExpectationException(
186                 'The \'' . $locator . '\' value is \'' . $field->get_value() . '\'' ,
187                 $this->getSession()
188             );
189         }
190     }
192     /**
193      * Checks, that checkbox with specified in|name|label|value is checked.
194      *
195      * @see Behat\MinkExtension\Context\MinkContext
196      * @Then /^the "(?P<checkbox_string>(?:[^"]|\\")*)" checkbox should be checked$/
197      */
198     public function assert_checkbox_checked($checkbox) {
199         $checkbox = $this->fixStepArgument($checkbox);
200         $this->assertSession()->checkboxChecked($checkbox);
201     }
203     /**
204      * Checks, that checkbox with specified in|name|label|value is unchecked.
205      *
206      * @see Behat\MinkExtension\Context\MinkContext
207      * @Then /^the "(?P<checkbox_string>(?:[^"]|\\")*)" checkbox should not be checked$/
208      */
209     public function assert_checkbox_not_checked($checkbox) {
210         $checkbox = $this->fixStepArgument($checkbox);
211         $this->assertSession()->checkboxNotChecked($checkbox);
212     }
214     /**
215      * Checks, that given select box contains the specified option.
216      *
217      * @throws ExpectationException
218      * @throws ElementNotFoundException
219      * @Then /^the "(?P<select_string>(?:[^"]|\\")*)" select box should contain "(?P<option_string>(?:[^"]|\\")*)"$/
220      * @param string $select The select element name
221      * @param string $option The option text/value
222      */
223     public function the_select_box_should_contain($select, $option) {
225         $select = $this->fixStepArgument($select);
226         $option = $this->fixStepArgument($option);
228         $selectnode = $this->getSession()->getPage()->findField($select);
229         if ($selectnode == null) {
230             throw new ElementNotFoundException(
231                 $this->getSession(), 'form field', 'id|name|label|value', $select
232             );
233         }
235         $regex = '/' . preg_quote($option, '/') . '/ui';
236         if (!preg_match($regex, $selectnode->getText())) {
237             throw new ExpectationException(
238                 'The select box "' . $select . '" does not contains the option "' . $option . '"',
239                 $this->getSession()
240             );
241         }
243     }
245     /**
246      * Checks, that given select box does not contain the specified option.
247      *
248      * @throws ExpectationException
249      * @throws ElementNotFoundException
250      * @Then /^the "(?P<select_string>(?:[^"]|\\")*)" select box should not contain "(?P<option_string>(?:[^"]|\\")*)"$/
251      * @param string $select The select element name
252      * @param string $option The option text/value
253      */
254     public function the_select_box_should_not_contain($select, $option) {
256         $select = $this->fixStepArgument($select);
257         $option = $this->fixStepArgument($option);
259         $selectnode = $this->getSession()->getPage()->findField($select);
260         if ($selectnode == null) {
261             throw new ElementNotFoundException(
262                 $this->getSession(), 'form field', 'id|name|label|value', $select
263             );
264         }
266         $regex = '/' . preg_quote($option, '/') . '/ui';
267         if (preg_match($regex, $selectnode->getText())) {
268             throw new ExpectationException(
269                 'The select box "' . $select . '" contains the option "' . $option . '"',
270                 $this->getSession()
271             );
272         }
273     }
275     /**
276      * Gets an instance of the form element field.
277      *
278      * @param NodeElement $fieldnode The current node
279      * @param string $locator Just to send an exception that makes sense for the user
280      * @return behat_form_field
281      */
282     protected function get_field(NodeElement $fieldnode, $locator) {
283         global $CFG;
285         $locator = $this->fixStepArgument($locator);
287         // Get the field type.
288         $type = $this->get_node_type($fieldnode, $locator);
289         $classname = 'behat_form_' . $type;
291         // Fallsback on the default form field if nothing specific exists.
292         $classpath = $CFG->libdir . '/behat/form_field/' . $classname . '.php';
293         if (!file_exists($classpath)) {
294             $classname = 'behat_form_field';
295             $classpath = $CFG->libdir . '/behat/form_field/' . $classname . '.php';
296         }
298         // Returns the instance.
299         require_once($classpath);
300         return new $classname($this->getSession(), $fieldnode);
301     }
303     /**
304      * Recursive method to find the field type.
305      *
306      * Depending on the field the felement class node is a level or in another. We
307      * look recursively for a parent node with a 'felement' class to find the field type.
308      *
309      * @throws ExpectationException
310      * @param NodeElement $fieldnode The current node
311      * @param string $locator Just to send an exception that makes sense for the user
312      * @return mixed String or NodeElement depending if we have reached the felement node
313      */
314     protected function get_node_type(NodeElement $fieldnode, $locator) {
316         $locator = $this->fixStepArgument($locator);
318         // We look for a parent node with 'felement' class.
319         if ($class = $fieldnode->getParent()->getAttribute('class')) {
321             if (strstr($class, 'felement') != false) {
322                 // Remove 'felement f' from class value.
323                 return substr($class, 10);
324             }
326             // Stop propagation through the DOM, something went wrong!.
327             if (strstr($class, 'fcontainer') != false) {
328                 throw new ExpectationException('No field type for ' . $locator . ' found, ensure the field exists', $this->getSession());
329             }
330         }
332         return $this->get_node_type($fieldnode->getParent(), $locator);
333     }