You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
286 lines
9.2 KiB
286 lines
9.2 KiB
<?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 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 form 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 {
|
|
|
|
/**
|
|
* @var Session Behat session.
|
|
*/
|
|
protected $session;
|
|
|
|
/**
|
|
* @var NodeElement The field DOM node to interact with.
|
|
*/
|
|
protected $field;
|
|
|
|
/**
|
|
* @var string The field's locator.
|
|
*/
|
|
protected $fieldlocator = false;
|
|
|
|
|
|
/**
|
|
* 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) {
|
|
// We delegate to the best guess, if we arrived here
|
|
// using the generic behat_form_field is because we are
|
|
// dealing with a fgroup element.
|
|
$instance = $this->guess_type();
|
|
return $instance->set_value($value);
|
|
}
|
|
|
|
/**
|
|
* Returns the current value of the select element.
|
|
*
|
|
* @return string
|
|
*/
|
|
public function get_value() {
|
|
// We delegate to the best guess, if we arrived here
|
|
// using the generic behat_form_field is because we are
|
|
// dealing with a fgroup element.
|
|
$instance = $this->guess_type();
|
|
return $instance->get_value();
|
|
}
|
|
|
|
/**
|
|
* Presses specific keyboard key.
|
|
*
|
|
* @param mixed $char could be either char ('b') or char-code (98)
|
|
* @param string $modifier keyboard modifier (could be 'ctrl', 'alt', 'shift' or 'meta')
|
|
*/
|
|
public function key_press($char, $modifier = null) {
|
|
// We delegate to the best guess, if we arrived here
|
|
// using the generic behat_form_field is because we are
|
|
// dealing with a fgroup element.
|
|
$instance = $this->guess_type();
|
|
$instance->field->keyDown($char, $modifier);
|
|
try {
|
|
$instance->field->keyPress($char, $modifier);
|
|
$instance->field->keyUp($char, $modifier);
|
|
} catch (WebDriver\Exception $e) {
|
|
// If the JS handler attached to keydown or keypress destroys the element
|
|
// the later events may trigger errors because form element no longer exist
|
|
// or is not visible. Ignore such exceptions here.
|
|
} catch (\Behat\Mink\Exception\ElementNotFoundException $e) {
|
|
// Other Mink drivers can throw this for the same reason as above.
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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) {
|
|
// We delegate to the best guess, if we arrived here
|
|
// using the generic behat_form_field is because we are
|
|
// dealing with a fgroup element.
|
|
$instance = $this->guess_type();
|
|
return $instance->matches($expectedvalue);
|
|
}
|
|
|
|
/**
|
|
* Get the value of an attribute set on this field.
|
|
*
|
|
* @param string $name The attribute name
|
|
* @return string The attribute value
|
|
*/
|
|
public function get_attribute($name) {
|
|
return $this->field->getAttribute($name);
|
|
}
|
|
|
|
/**
|
|
* Guesses the element type we are dealing with in case is not a text-based element.
|
|
*
|
|
* This class is the generic field type, behat_field_manager::get_form_field()
|
|
* should be able to find the appropiate class for the field type, but
|
|
* in cases like moodle form group elements we can not find the type of
|
|
* the field through the DOM so we also need to take care of the
|
|
* different field types from here. If we need to deal with more complex
|
|
* moodle form elements we will need to refactor this simple HTML elements
|
|
* guess method.
|
|
*
|
|
* @return behat_form_field
|
|
*/
|
|
private function guess_type() {
|
|
global $CFG;
|
|
|
|
// We default to the text-based field if nothing was detected.
|
|
if (!$type = behat_field_manager::guess_field_type($this->field, $this->session)) {
|
|
$type = 'text';
|
|
}
|
|
|
|
$classname = 'behat_form_' . $type;
|
|
$classpath = $CFG->dirroot . '/lib/behat/form_field/' . $classname . '.php';
|
|
require_once($classpath);
|
|
return new $classname($this->session, $this->field);
|
|
}
|
|
|
|
/**
|
|
* Returns whether the scenario is running in a browser that can run Javascript or not.
|
|
*
|
|
* @return bool
|
|
*/
|
|
protected function running_javascript() {
|
|
return get_class($this->session->getDriver()) !== 'Behat\Mink\Driver\GoutteDriver';
|
|
}
|
|
|
|
/**
|
|
* Waits for all the JS activity to be completed.
|
|
*
|
|
* @return bool Whether any JS is still pending completion.
|
|
*/
|
|
protected function wait_for_pending_js() {
|
|
if (!$this->running_javascript()) {
|
|
// JS is not available therefore there is nothing to wait for.
|
|
return false;
|
|
}
|
|
|
|
return behat_base::wait_for_pending_js_in_session($this->session);
|
|
}
|
|
|
|
/**
|
|
* Gets the field internal id used by selenium wire protocol.
|
|
*
|
|
* Only available when running_javascript().
|
|
*
|
|
* @throws coding_exception
|
|
* @return int
|
|
*/
|
|
protected function get_internal_field_id() {
|
|
|
|
if (!$this->running_javascript()) {
|
|
throw new coding_exception('You can only get an internal ID using the selenium driver.');
|
|
}
|
|
|
|
return $this->session->getDriver()->getWebDriverSession()->element('xpath', $this->field->getXPath())->getID();
|
|
}
|
|
|
|
/**
|
|
* Checks if the provided text matches the field value.
|
|
*
|
|
* @param string $expectedvalue
|
|
* @return bool
|
|
*/
|
|
protected function text_matches($expectedvalue) {
|
|
if (trim($expectedvalue) != trim($this->get_value())) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Gets the field locator.
|
|
*
|
|
* Defaults to the field label but you can
|
|
* specify other locators if you are interested.
|
|
*
|
|
* Public visibility as in most cases will be hard to
|
|
* use this method in a generic way, as fields can
|
|
* be selected using multiple ways (label, id, name...).
|
|
*
|
|
* @throws coding_exception
|
|
* @param string $locatortype
|
|
* @return string
|
|
*/
|
|
protected function get_field_locator($locatortype = false) {
|
|
|
|
if (!empty($this->fieldlocator)) {
|
|
return $this->fieldlocator;
|
|
}
|
|
|
|
$fieldid = $this->field->getAttribute('id');
|
|
|
|
// Defaults to label.
|
|
if ($locatortype == 'label' || $locatortype == false) {
|
|
|
|
$labelnode = $this->session->getPage()->find('xpath', '//label[@for="' . $fieldid . '"]');
|
|
|
|
// Exception only if $locatortype was specified.
|
|
if (!$labelnode && $locatortype == 'label') {
|
|
throw new coding_exception('Field with "' . $fieldid . '" id does not have a label.');
|
|
}
|
|
|
|
$this->fieldlocator = $labelnode->getText();
|
|
}
|
|
|
|
// Let's look for the name as a second option (more popular than
|
|
// id's when pointing to fields).
|
|
if (($locatortype == 'name' || $locatortype == false) &&
|
|
empty($this->fieldlocator)) {
|
|
|
|
$name = $this->field->getAttribute('name');
|
|
|
|
// Exception only if $locatortype was specified.
|
|
if (!$name && $locatortype == 'name') {
|
|
throw new coding_exception('Field with "' . $fieldid . '" id does not have a name attribute.');
|
|
}
|
|
|
|
$this->fieldlocator = $name;
|
|
}
|
|
|
|
// Otherwise returns the id if no specific locator type was provided.
|
|
if (empty($this->fieldlocator)) {
|
|
$this->fieldlocator = $fieldid;
|
|
}
|
|
|
|
return $this->fieldlocator;
|
|
}
|
|
}
|
|
|