1
0
mirror of https://github.com/moodle/moodle.git synced 2025-04-23 09:23:09 +02:00

MDL-66550 behat: Normalise selectors better

This commit is contained in:
Andrew Nicols 2019-08-29 11:09:10 +08:00
parent a672f021ea
commit 700682dc40
2 changed files with 85 additions and 75 deletions

@ -32,6 +32,7 @@ use Behat\Mink\Exception\DriverException;
use Behat\Mink\Exception\ExpectationException;
use Behat\Mink\Exception\ElementNotFoundException;
use Behat\Mink\Element\NodeElement;
use Behat\Mink\Element\Element;
use Behat\Mink\Session;
/**
@ -115,14 +116,6 @@ class behat_base extends Behat\MinkExtension\Context\RawMinkContext {
* @return NodeElement
*/
protected function find($selector, $locator, $exception = false, $node = false, $timeout = false) {
// Throw exception, so dev knows it is not supported.
if ($selector === 'named') {
$exception = 'Using the "named" selector is deprecated as of 3.1. '
.' Use the "named_partial" or use the "named_exact" selector instead.';
throw new ExpectationException($exception, $this->getSession());
}
// Returns the first match.
$items = $this->find_all($selector, $locator, $exception, $node, $timeout);
return count($items) ? reset($items) : null;
@ -137,12 +130,11 @@ class behat_base extends Behat\MinkExtension\Context\RawMinkContext {
* @param string $selector The selector type (css, xpath, named...)
* @param mixed $locator It depends on the $selector, can be the xpath, a name, a css locator...
* @param Exception $exception Otherwise we throw expcetion with generic info
* @param NodeElement $node Spins around certain DOM node instead of the whole page
* @param NodeElement $container Restrict the search to just children of the specified container
* @param int $timeout Forces a specific time out (in seconds). If 0 is provided the default timeout will be applied.
* @return array NodeElements list
*/
protected function find_all($selector, $locator, $exception = false, $node = false, $timeout = false) {
protected function find_all($selector, $locator, $exception = false, $container = false, $timeout = false) {
// Throw exception, so dev knows it is not supported.
if ($selector === 'named') {
$exception = 'Using the "named" selector is deprecated as of 3.1. '
@ -152,7 +144,6 @@ class behat_base extends Behat\MinkExtension\Context\RawMinkContext {
// Generic info.
if (!$exception) {
// With named selectors we can be more specific.
if (($selector == 'named_exact') || ($selector == 'named_partial')) {
$exceptiontype = $locator[0];
@ -171,12 +162,6 @@ class behat_base extends Behat\MinkExtension\Context\RawMinkContext {
$exception = new ElementNotFoundException($this->getSession(), $exceptiontype, null, $exceptionlocator);
}
$params = array('selector' => $selector, 'locator' => $locator);
// Pushing $node if required.
if ($node) {
$params['node'] = $node;
}
// How much we will be waiting for the element to appear.
if (!$timeout) {
$timeout = self::get_timeout();
@ -188,24 +173,61 @@ class behat_base extends Behat\MinkExtension\Context\RawMinkContext {
$microsleep = true;
}
// Normalise the values in order to perform the search.
[
'selector' => $selector,
'locator' => $locator,
'container' => $container,
] = $this->normalise_selector($selector, $locator, $container ?: $this->getSession()->getPage());
// Waits for the node to appear if it exists, otherwise will timeout and throw the provided exception.
return $this->spin(
function($context, $args) {
// If no DOM node provided look in all the page.
if (empty($args['node'])) {
return $context->getSession()->getPage()->findAll($args['selector'], $args['locator']);
}
return $args['node']->findAll($args['selector'], $args['locator']);
},
$params,
$timeout,
$exception,
$microsleep
function() use ($selector, $locator, $container) {
return $container->findAll($selector, $locator);
}, [], $timeout, $exception, $microsleep
);
}
/**
* Normalise the locator and selector.
*
* @param string $selector The type of thing to search
* @param mixed $locator The locator value. Can be an array, but is more likely a string.
* @param Element $container An optional container to search within
* @return array The selector, locator, and container to search within
*/
public function normalise_selector(string $selector, $locator, Element $container): array {
// Normalise the css and xpath selector types.
if ('css_element' === $selector) {
$selector = 'css';
} else if ('xpath_element' === $selector) {
$selector = 'xpath';
}
// Convert to named_partial where the selector type is not named_partial, named_exact, xpath, or css.
$converttonamed = !$this->getSession()->getSelectorsHandler()->isSelectorRegistered($selector);
$converttonamed = $converttonamed && 'xpath' !== $selector;
if ($converttonamed) {
$allowedpartialselectors = behat_partial_named_selector::get_allowed_selectors();
$allowedexactselectors = behat_exact_named_selector::get_allowed_selectors();
if (isset($allowedpartialselectors[$selector])) {
$locator = behat_selectors::normalise_named_selector($allowedpartialselectors[$selector], $locator);
$selector = 'named_partial';
} else if (isset($allowedexactselectors[$selector])) {
$locator = behat_selectors::normalise_named_selector($allowedexactselectors[$selector], $locator);
$selector = 'named_exact';
} else {
throw new ExpectationException("The '{$selector}' selector type is not registered.", $this);
}
}
return [
'selector' => $selector,
'locator' => $locator,
'container' => $container,
];
}
/**
* Finds DOM nodes in the page using named selectors.
*
@ -229,27 +251,14 @@ class behat_base extends Behat\MinkExtension\Context\RawMinkContext {
* @return NodeElement
*/
public function __call($name, $arguments) {
if (substr($name, 0, 5) !== 'find_') {
throw new coding_exception('The "' . $name . '" method does not exist');
if (substr($name, 0, 5) === 'find_') {
return call_user_func_array([$this, 'find'], array_merge(
[substr($name, 5)],
$arguments
));
}
// Only the named selector identifier.
$cleanname = substr($name, 5);
// All named selectors shares the interface.
if (count($arguments) !== 1) {
throw new coding_exception('The "' . $cleanname . '" named selector needs the locator as it\'s single argument');
}
// Redirecting execution to the find method with the specified selector.
// It will detect if it's pointing to an unexisting named selector.
return $this->find('named_partial',
array(
$cleanname,
behat_context_helper::escape($arguments[0])
)
);
throw new coding_exception("The '{$name}' method does not exist");
}
/**
@ -433,7 +442,12 @@ class behat_base extends Behat\MinkExtension\Context\RawMinkContext {
throw new ExpectationException('The "' . $selectortype . '" selector type does not exist', $this->getSession());
}
return behat_selectors::get_behat_selector($selectortype, $element, $this->getSession());
[
'selector' => $selector,
'locator' => $locator,
] = $this->normalise_selector($selectortype, $element, $this->getSession()->getPage());
return [$selector, $locator];
}
/**

@ -47,32 +47,14 @@ class behat_selectors {
* @return array Contains the selector and the locator expected by Mink.
*/
public static function get_behat_selector($selectortype, $element, Behat\Mink\Session $session) {
// Note: This function is not deprecated, but not the recommended way of doing things.
[
'selector' => $selector,
'locator' => $locator,
] = $session->normalise_selector($selectortype, $element, $session->getPage());
// CSS and XPath selectors locator is one single argument.
if ($selectortype == 'css_element' || $selectortype == 'xpath_element') {
$selector = str_replace('_element', '', $selectortype);
$locator = $element;
} else {
// Named selectors uses arrays as locators including the type of named selector.
$allowedselectors = self::get_allowed_selectors();
if (!isset($allowedselectors[$selectortype])) {
throw new ExpectationException('The "' . $selectortype . '" selector not registered.', $session);
}
$locator = array($allowedselectors[$selectortype], behat_context_helper::escape($element));
// Get the selector which should be used.
$allowedpartialselectors = behat_partial_named_selector::get_allowed_selectors();
$allowedexactselectors = behat_exact_named_selector::get_allowed_selectors();
if (isset($allowedpartialselectors[$selectortype])) {
$selector = 'named_partial';
} else if (isset($allowedexactselectors[$selectortype])) {
$selector = 'named_exact';
} else {
throw new ExpectationException('The "' . $selectortype . '" selector not registered.', $session);
}
}
return array($selector, $locator);
return [$selector, $locator];
}
/**
@ -98,4 +80,18 @@ class behat_selectors {
behat_exact_named_selector::get_allowed_text_selectors()
);
}
/**
* Normalise the selector and locator for a named partial.
*
* @param string $selector The selector name
* @param string $locator The value to normalise
* @return array
*/
public static function normalise_named_selector(string $selector, string $locator): array {
return [
$selector,
behat_context_helper::escape($locator),
];
}
}