|
10 | 10 |
|
11 | 11 | namespace Behat\Mink\Driver; |
12 | 12 |
|
| 13 | +use Behat\Mink\Element\NodeElement; |
13 | 14 | use Behat\Mink\Exception\DriverException; |
14 | 15 | use Behat\Mink\Selector\Xpath\Escaper; |
15 | 16 | use WebDriver\Element; |
|
27 | 28 | */ |
28 | 29 | class Selenium2Driver extends CoreDriver |
29 | 30 | { |
| 31 | + public const XPATH_WEBDRIVER_ELEMENT_PREFIX = 'webdriver-element--JZPOYwW6bl1bVmyK--'; |
| 32 | + |
30 | 33 | /** |
31 | 34 | * Whether the browser has been started |
32 | 35 | * @var boolean |
@@ -246,6 +249,68 @@ protected static function charToOptions($char, $modifier = null) |
246 | 249 | return json_encode($options); |
247 | 250 | } |
248 | 251 |
|
| 252 | + /** |
| 253 | + * Serialize execute arguments (containing web elements) |
| 254 | + * |
| 255 | + * @see https://w3c.github.io/webdriver/#executing-script |
| 256 | + * |
| 257 | + * @param array $args |
| 258 | + * |
| 259 | + * @return array |
| 260 | + */ |
| 261 | + private function serializeExecuteArguments(array $args) |
| 262 | + { |
| 263 | + foreach ($args as $k => $v) { |
| 264 | + if ($v instanceof NodeElement) { |
| 265 | + if (strpos($v->getXpath(), self::XPATH_WEBDRIVER_ELEMENT_PREFIX) === 0) { |
| 266 | + [$elementUrlEncoded, $elementIdEncoded] = explode( |
| 267 | + '--', |
| 268 | + substr($v->getXpath(), strlen(self::XPATH_WEBDRIVER_ELEMENT_PREFIX)), |
| 269 | + 2 |
| 270 | + ); |
| 271 | + |
| 272 | + $args[$k] = new Element(base64_decode($elementUrlEncoded), base64_decode($elementIdEncoded)); |
| 273 | + } else { |
| 274 | + $args[$k] = $this->findElement($v->getXpath()); |
| 275 | + } |
| 276 | + } elseif (is_array($v)) { |
| 277 | + $args[$k] = $this->serializeExecuteArguments($v); |
| 278 | + } |
| 279 | + } |
| 280 | + |
| 281 | + return $args; |
| 282 | + } |
| 283 | + |
| 284 | + /** |
| 285 | + * Unserialize execute result (containing web elements) |
| 286 | + * |
| 287 | + * @param mixed $data |
| 288 | + * |
| 289 | + * @return mixed |
| 290 | + */ |
| 291 | + private function unserializeExecuteResult($data) |
| 292 | + { |
| 293 | + if ($data instanceof Element) { |
| 294 | + $minkSession = \Closure::bind(function () { |
| 295 | + return $this->session; |
| 296 | + }, $this, CoreDriver::class)(); |
| 297 | + |
| 298 | + return new NodeElement( |
| 299 | + self::XPATH_WEBDRIVER_ELEMENT_PREFIX |
| 300 | + . base64_encode($data->getURL()) |
| 301 | + . '--' |
| 302 | + . base64_encode($data->getID()), |
| 303 | + $minkSession |
| 304 | + ); |
| 305 | + } elseif (is_array($data)) { |
| 306 | + foreach ($data as $k => $v) { |
| 307 | + $data[$k] = $this->unserializeExecuteResult($v); |
| 308 | + } |
| 309 | + } |
| 310 | + |
| 311 | + return $data; |
| 312 | + } |
| 313 | + |
249 | 314 | /** |
250 | 315 | * Executes JS on a given element - pass in a js script string and {{ELEMENT}} will |
251 | 316 | * be replaced with a reference to the result of the $xpath query |
@@ -281,14 +346,14 @@ private function executeJsOnElement(Element $element, $script, $sync = true) |
281 | 346 |
|
282 | 347 | $options = array( |
283 | 348 | 'script' => $script, |
284 | | - 'args' => array(array('ELEMENT' => $element->getID())), |
| 349 | + 'args' => array(array('ELEMENT' => $element)), |
285 | 350 | ); |
286 | 351 |
|
287 | | - if ($sync) { |
288 | | - return $this->wdSession->execute($options); |
289 | | - } |
| 352 | + $result = $sync |
| 353 | + ? $this->wdSession->execute($options) |
| 354 | + : $this->wdSession->execute_async($options); |
290 | 355 |
|
291 | | - return $this->wdSession->execute_async($options); |
| 356 | + return $this->unserializeExecuteResult($result); |
292 | 357 | } |
293 | 358 |
|
294 | 359 | /** |
@@ -951,39 +1016,50 @@ public function dragTo($sourceXpath, $destinationXpath) |
951 | 1016 | /** |
952 | 1017 | * {@inheritdoc} |
953 | 1018 | */ |
954 | | - public function executeScript($script) |
| 1019 | + public function executeScript($script, array $args = []) |
955 | 1020 | { |
956 | 1021 | if (preg_match('/^function[\s\(]/', $script)) { |
957 | 1022 | $script = preg_replace('/;$/', '', $script); |
958 | 1023 | $script = '(' . $script . ')'; |
959 | 1024 | } |
960 | 1025 |
|
961 | | - $this->wdSession->execute(array('script' => $script, 'args' => array())); |
| 1026 | + $this->wdSession->execute(array( |
| 1027 | + 'script' => $script, |
| 1028 | + 'args' => $this->serializeExecuteArguments($args), |
| 1029 | + )); |
962 | 1030 | } |
963 | 1031 |
|
964 | 1032 | /** |
965 | 1033 | * {@inheritdoc} |
966 | 1034 | */ |
967 | | - public function evaluateScript($script) |
| 1035 | + public function evaluateScript($script, array $args = []) |
968 | 1036 | { |
969 | 1037 | if (0 !== strpos(trim($script), 'return ')) { |
970 | 1038 | $script = 'return ' . $script; |
971 | 1039 | } |
972 | 1040 |
|
973 | | - return $this->wdSession->execute(array('script' => $script, 'args' => array())); |
| 1041 | + $result = $this->wdSession->execute(array( |
| 1042 | + 'script' => $script, |
| 1043 | + 'args' => $this->serializeExecuteArguments($args), |
| 1044 | + )); |
| 1045 | + |
| 1046 | + return $this->unserializeExecuteResult($result); |
974 | 1047 | } |
975 | 1048 |
|
976 | 1049 | /** |
977 | 1050 | * {@inheritdoc} |
978 | 1051 | */ |
979 | | - public function wait($timeout, $condition) |
| 1052 | + public function wait($timeout, $condition, array $args = []) |
980 | 1053 | { |
981 | 1054 | $script = 'return (' . rtrim($condition, " \t\n\r;") . ');'; |
982 | 1055 | $start = microtime(true); |
983 | 1056 | $end = $start + $timeout / 1000.0; |
984 | 1057 |
|
985 | 1058 | do { |
986 | | - $result = $this->wdSession->execute(array('script' => $script, 'args' => array())); |
| 1059 | + $result = $this->wdSession->execute(array( |
| 1060 | + 'script' => $script, |
| 1061 | + 'args' => $this->serializeExecuteArguments($args), |
| 1062 | + )); |
987 | 1063 | if ($result) { |
988 | 1064 | break; |
989 | 1065 | } |
|
0 commit comments