MDL-66979 behat: Core updates for W3C WebDriver
[moodle.git] / lib / behat / form_field / behat_form_field.php
blob4da8002404a9cc73a3fc8a1eef54985d9cbb1aec
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.
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 * Generic moodleforms field.
20 * @package core_form
21 * @category test
22 * @copyright 2012 David MonllaĆ³
23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
26 // NOTE: no MOODLE_INTERNAL test here, this file may be required by behat before including /config.php.
28 use Behat\Mink\Element\NodeElement;
29 use Behat\Mink\Session;
31 /**
32 * Representation of a form field.
34 * Basically an interface with Mink session.
36 * @package core_form
37 * @category test
38 * @copyright 2012 David MonllaĆ³
39 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
41 class behat_form_field implements behat_session_interface {
43 // All of the functionality of behat_base is shared with form fields via the behat_session_trait trait.
44 use behat_session_trait;
46 /**
47 * @var Session Behat session.
49 protected $session;
51 /**
52 * @var NodeElement The field DOM node to interact with.
54 protected $field;
56 /**
57 * @var string The field's locator.
59 protected $fieldlocator = false;
61 /**
62 * Returns the Mink session.
64 * @param string|null $name name of the session OR active session will be used
65 * @return \Behat\Mink\Session
67 public function getSession($name = null) {
68 return $this->session;
72 /**
73 * General constructor with the node and the session to interact with.
75 * @param Session $session Reference to Mink session to traverse/modify the page DOM.
76 * @param NodeElement $fieldnode The field DOM node
77 * @return void
79 public function __construct(Session $session, NodeElement $fieldnode) {
80 $this->session = $session;
81 $this->field = $fieldnode;
84 /**
85 * Sets the value to a field.
87 * @param string $value
88 * @return void
90 public function set_value($value) {
91 // We delegate to the best guess, if we arrived here
92 // using the generic behat_form_field is because we are
93 // dealing with a fgroup element.
94 $instance = $this->guess_type();
95 return $instance->set_value($value);
98 /**
99 * Returns the current value of the select element.
101 * @return string
103 public function get_value() {
104 // We delegate to the best guess, if we arrived here
105 // using the generic behat_form_field is because we are
106 // dealing with a fgroup element.
107 $instance = $this->guess_type();
108 return $instance->get_value();
112 * Presses specific keyboard key.
114 * @param mixed $char could be either char ('b') or char-code (98)
115 * @param string $modifier keyboard modifier (could be 'ctrl', 'alt', 'shift' or 'meta')
117 public function key_press($char, $modifier = null) {
118 // We delegate to the best guess, if we arrived here
119 // using the generic behat_form_field is because we are
120 // dealing with a fgroup element.
121 $instance = $this->guess_type();
122 $instance->field->keyDown($char, $modifier);
123 try {
124 $instance->field->keyPress($char, $modifier);
125 $instance->field->keyUp($char, $modifier);
126 } catch (WebDriver\Exception $e) {
127 // If the JS handler attached to keydown or keypress destroys the element
128 // the later events may trigger errors because form element no longer exist
129 // or is not visible. Ignore such exceptions here.
130 } catch (\Behat\Mink\Exception\ElementNotFoundException $e) {
131 // Other Mink drivers can throw this for the same reason as above.
136 * Generic match implementation
138 * Will work well with text-based fields, extension required
139 * for most of the other cases.
141 * @param string $expectedvalue
142 * @return bool The provided value matches the field value?
144 public function matches($expectedvalue) {
145 // We delegate to the best guess, if we arrived here
146 // using the generic behat_form_field is because we are
147 // dealing with a fgroup element.
148 $instance = $this->guess_type();
149 return $instance->matches($expectedvalue);
153 * Get the value of an attribute set on this field.
155 * @param string $name The attribute name
156 * @return string The attribute value
158 public function get_attribute($name) {
159 return $this->field->getAttribute($name);
163 * Guesses the element type we are dealing with in case is not a text-based element.
165 * This class is the generic field type, behat_field_manager::get_form_field()
166 * should be able to find the appropiate class for the field type, but
167 * in cases like moodle form group elements we can not find the type of
168 * the field through the DOM so we also need to take care of the
169 * different field types from here. If we need to deal with more complex
170 * moodle form elements we will need to refactor this simple HTML elements
171 * guess method.
173 * @return behat_form_field
175 private function guess_type() {
176 global $CFG;
178 // We default to the text-based field if nothing was detected.
179 if (!$type = behat_field_manager::guess_field_type($this->field, $this->session)) {
180 $type = 'text';
183 $classname = 'behat_form_' . $type;
184 $classpath = $CFG->dirroot . '/lib/behat/form_field/' . $classname . '.php';
185 require_once($classpath);
186 return new $classname($this->session, $this->field);
190 * Returns whether the scenario is running in a browser that can run Javascript or not.
192 * @return bool
194 protected function running_javascript() {
195 return get_class($this->session->getDriver()) !== 'Behat\Mink\Driver\GoutteDriver';
199 * Waits for all the JS activity to be completed.
201 * @return bool Whether any JS is still pending completion.
203 protected function wait_for_pending_js() {
204 if (!$this->running_javascript()) {
205 // JS is not available therefore there is nothing to wait for.
206 return false;
209 return behat_base::wait_for_pending_js_in_session($this->session);
213 * Gets the field internal id used by selenium wire protocol.
215 * Only available when running_javascript().
217 * @throws coding_exception
218 * @return int
220 protected function get_internal_field_id() {
221 if (!$this->running_javascript()) {
222 throw new coding_exception('You can only get an internal ID using the selenium driver.');
225 return $this->getSession()
226 ->getDriver()
227 ->getWebDriver()
228 ->findElement(WebDriverBy::xpath($node->getXpath()))
229 ->getID();
233 * Checks if the provided text matches the field value.
235 * @param string $expectedvalue
236 * @return bool
238 protected function text_matches($expectedvalue) {
239 if (trim($expectedvalue) != trim($this->get_value())) {
240 return false;
242 return true;
246 * Gets the field locator.
248 * Defaults to the field label but you can
249 * specify other locators if you are interested.
251 * Public visibility as in most cases will be hard to
252 * use this method in a generic way, as fields can
253 * be selected using multiple ways (label, id, name...).
255 * @throws coding_exception
256 * @param string $locatortype
257 * @return string
259 protected function get_field_locator($locatortype = false) {
261 if (!empty($this->fieldlocator)) {
262 return $this->fieldlocator;
265 $fieldid = $this->field->getAttribute('id');
267 // Defaults to label.
268 if ($locatortype == 'label' || $locatortype == false) {
270 $labelnode = $this->session->getPage()->find('xpath', "//label[@for='$fieldid']|//p[@id='{$fieldid}_label']");
272 // Exception only if $locatortype was specified.
273 if (!$labelnode && $locatortype == 'label') {
274 throw new coding_exception('Field with "' . $fieldid . '" id does not have a label.');
277 $this->fieldlocator = $labelnode->getText();
280 // Let's look for the name as a second option (more popular than
281 // id's when pointing to fields).
282 if (($locatortype == 'name' || $locatortype == false) &&
283 empty($this->fieldlocator)) {
285 $name = $this->field->getAttribute('name');
287 // Exception only if $locatortype was specified.
288 if (!$name && $locatortype == 'name') {
289 throw new coding_exception('Field with "' . $fieldid . '" id does not have a name attribute.');
292 $this->fieldlocator = $name;
295 // Otherwise returns the id if no specific locator type was provided.
296 if (empty($this->fieldlocator)) {
297 $this->fieldlocator = $fieldid;
300 return $this->fieldlocator;