From 7fd1dbbd1c48c31d5ccb14b652cd106902727faa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Marseille?= Date: Fri, 20 Sep 2019 14:52:52 +0200 Subject: [PATCH 01/44] fix(curl): suppress "Transfer-Encoding: chunked" added by last version of curl (#96) --- lib/WebDriver/Service/CurlService.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/WebDriver/Service/CurlService.php b/lib/WebDriver/Service/CurlService.php index 99af82e..a78c98c 100755 --- a/lib/WebDriver/Service/CurlService.php +++ b/lib/WebDriver/Service/CurlService.php @@ -54,6 +54,10 @@ public function execute($requestMethod, $url, $parameters = null, $extraOptions curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($parameters)); } else { $customHeaders[] = 'Content-Length: 0'; + + // Suppress "Transfer-Encoding: chunked" header automatically added by cURL that + // causes a 400 bad request (bad content-length). + $customHeaders[] = 'Transfer-Encoding:'; } // Suppress "Expect: 100-continue" header automatically added by cURL that @@ -72,6 +76,10 @@ public function execute($requestMethod, $url, $parameters = null, $extraOptions curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($parameters)); } else { $customHeaders[] = 'Content-Length: 0'; + + // Suppress "Transfer-Encoding: chunked" header automatically added by cURL that + // causes a 400 bad request (bad content-length). + $customHeaders[] = 'Transfer-Encoding:'; } // Suppress "Expect: 100-continue" header automatically added by cURL that From bd9405077ca04129a73059a06873bedb5e138402 Mon Sep 17 00:00:00 2001 From: Eloy Lafuente Date: Tue, 13 Aug 2019 20:55:31 +0200 Subject: [PATCH 02/44] preg_replace() requires quoting, specially for PHP 7.3 (#91) Without this, a number of expressions fail (returning NULL), leading to both non matches and warning/error: Warning: preg_replace(): Compilation failed: number too big in {} quantifier at offset 6 This is specially noticeable under PHP 7.3, where the new PCRE2 engine is more picky and has more features, but also may help some cases with previous PHP versions, depending of the string being searched/replaced. --- lib/WebDriver/Element.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/WebDriver/Element.php b/lib/WebDriver/Element.php index 09bbc91..c07c8f6 100644 --- a/lib/WebDriver/Element.php +++ b/lib/WebDriver/Element.php @@ -119,6 +119,6 @@ public function getID() */ protected function getElementPath($elementId) { - return preg_replace(sprintf('/%s$/', $this->id), $elementId, $this->url); + return preg_replace(sprintf('/%s$/', preg_quote($this->id)), $elementId, $this->url); } } From 7e179d5535a23da214df091805c029ff92bcc26d Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Wed, 4 Oct 2017 17:46:17 -0400 Subject: [PATCH 03/44] fixes #80 - support new WebDriver spec element ID --- lib/WebDriver/Container.php | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/lib/WebDriver/Container.php b/lib/WebDriver/Container.php index be1cfa7..fc698e0 100644 --- a/lib/WebDriver/Container.php +++ b/lib/WebDriver/Container.php @@ -32,6 +32,9 @@ */ abstract class Container extends AbstractWebDriver { + const LEGACY_ELEMENT_ID = 'ELEMENT'; + const WEBDRIVER_ELEMENT_ID = 'element-6066-11e4-a52e-4f735466cecf'; + /** * {@inheritdoc} */ @@ -203,12 +206,19 @@ public function locate($using, $value) */ protected function webDriverElement($value) { - return array_key_exists('ELEMENT', (array) $value) - ? new Element( - $this->getElementPath($value['ELEMENT']), // url - $value['ELEMENT'] // id - ) - : null; + if (array_key_exists(self::LEGACY_ELEMENT_ID, (array) $value)) { + return new Element( + $this->getElementPath($value[self::LEGACY_ELEMENT_ID]), // url + $value[self::LEGACY_ELEMENT_ID] // id + ); + } + + if (array_key_exists(self::WEBDRIVER_ELEMENT_ID, (array) $value)) { + return new Element( + $this->getElementPath($value[self::WEBDRIVER_ELEMENT_ID]), // url + $value[self::WEBDRIVER_ELEMENT_ID] // id + ); + } } /** From c850efc2d22667e137c4678887d4c5666d20ddca Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Mon, 23 Sep 2019 13:14:33 -0400 Subject: [PATCH 04/44] Refs #91 - tweak the preg_quote() fix --- lib/WebDriver/Element.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/WebDriver/Element.php b/lib/WebDriver/Element.php index c07c8f6..5a0bc22 100644 --- a/lib/WebDriver/Element.php +++ b/lib/WebDriver/Element.php @@ -119,6 +119,6 @@ public function getID() */ protected function getElementPath($elementId) { - return preg_replace(sprintf('/%s$/', preg_quote($this->id)), $elementId, $this->url); + return preg_replace('/' . preg_quote($this->id, '/') . '/', $elementId, $this->url); } } From 00312b9bef44f2a11f40fe9a5f43b7e6a484f298 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Wed, 25 Sep 2019 05:01:23 -0400 Subject: [PATCH 05/44] Refs #91 - restore anchor to regex --- lib/WebDriver/Element.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/WebDriver/Element.php b/lib/WebDriver/Element.php index 5a0bc22..4989437 100644 --- a/lib/WebDriver/Element.php +++ b/lib/WebDriver/Element.php @@ -119,6 +119,6 @@ public function getID() */ protected function getElementPath($elementId) { - return preg_replace('/' . preg_quote($this->id, '/') . '/', $elementId, $this->url); + return preg_replace('/' . preg_quote($this->id, '/') . '$/', $elementId, $this->url); } } From b5f330e900e9b3edfc18024a5ec8c07136075712 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Tue, 13 Aug 2019 15:52:06 -0400 Subject: [PATCH 06/44] Update travis CI config --- .travis.yml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 941a80a..f315f0e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,7 +9,10 @@ php: - 5.6 - 7.0 - 7.1 - - hhvm + - 7.2 + - 7.3 + - 7.4snapshot + - nightly #before_install: # # This update is mandatory or the 'apt-get install' calls following will fail @@ -30,3 +33,8 @@ script: after_script: - vendor/bin/coveralls + +matrix: + allow_failures: + - php: 7.4snapshot + - php: nightly From 755497d9be5038d8171be954006a62bcdcd0456e Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Mon, 21 Jun 2021 06:23:25 -0400 Subject: [PATCH 07/44] Remove versioneye links from README --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index ad5ef3a..7998868 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,6 @@ Distinguishing features: [![Build Status](https://travis-ci.org/instaclick/php-webdriver.png)](https://travis-ci.org/instaclick/php-webdriver) [![Coverage Status](https://coveralls.io/repos/instaclick/php-webdriver/badge.png)](https://coveralls.io/r/instaclick/php-webdriver) -[![Dependency Status](https://www.versioneye.com/php/instaclick:php-webdriver/badge.png)](https://www.versioneye.com/php/instaclick:php-webdriver/) [![Latest Stable Version](https://poser.pugx.org/instaclick/php-webdriver/v/stable.png)](https://packagist.org/packages/instaclick/php-webdriver) [![Total Downloads](https://poser.pugx.org/instaclick/php-webdriver/downloads.png)](https://packagist.org/packages/instaclick/php-webdriver) From c81f0543e3da9350a44df65770293a8a79862371 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Mon, 21 Jun 2021 06:37:37 -0400 Subject: [PATCH 08/44] Fix phpstan errors --- lib/WebDriver/Container.php | 5 +++++ lib/WebDriver/Storage.php | 4 +++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/WebDriver/Container.php b/lib/WebDriver/Container.php index fc698e0..1a98132 100644 --- a/lib/WebDriver/Container.php +++ b/lib/WebDriver/Container.php @@ -35,6 +35,11 @@ abstract class Container extends AbstractWebDriver const LEGACY_ELEMENT_ID = 'ELEMENT'; const WEBDRIVER_ELEMENT_ID = 'element-6066-11e4-a52e-4f735466cecf'; + /** + * @var array + */ + private $strategies; + /** * {@inheritdoc} */ diff --git a/lib/WebDriver/Storage.php b/lib/WebDriver/Storage.php index d4586ff..c0ead1e 100644 --- a/lib/WebDriver/Storage.php +++ b/lib/WebDriver/Storage.php @@ -115,7 +115,9 @@ public function delete() // delete key from storage if (func_num_args() === 1) { - return $this->deleteKey(func_get_arg(0)); + $this->deleteKey(func_get_arg(0)); + + return $this; } throw WebDriverException::factory(WebDriverException::UNEXPECTED_PARAMETERS); From 98306de013f01e972dc6520934b4a99ba4fc20c6 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Mon, 28 Jun 2021 14:58:26 -0400 Subject: [PATCH 09/44] fixes #107 - update status/error/ready handling --- lib/WebDriver/AbstractWebDriver.php | 23 ++++++++++++++++++++--- lib/WebDriver/Container.php | 2 +- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/lib/WebDriver/AbstractWebDriver.php b/lib/WebDriver/AbstractWebDriver.php index 0d69023..4564bf8 100644 --- a/lib/WebDriver/AbstractWebDriver.php +++ b/lib/WebDriver/AbstractWebDriver.php @@ -142,7 +142,7 @@ protected function curl($requestMethod, $command, $parameters = null, $extraOpti ); } - if (is_array($result) && !array_key_exists('status', $result)) { + if (is_array($result) && !array_key_exists('status', $result) && !isset($result['value']['ready']) && ! isset($result['value']['error'])) { throw WebDriverException::factory( WebDriverException::CURL_EXEC, 'Payload received from webdriver is valid but unexpected json: ' . substr($rawResult, 0, 1000) @@ -153,8 +153,25 @@ protected function curl($requestMethod, $command, $parameters = null, $extraOpti $message = (is_array($value) && array_key_exists('message', $value)) ? $value['message'] : null; // if not success, throw exception - if ((int) $result['status'] !== 0) { - throw WebDriverException::factory($result['status'], $message); + if (isset($result['status']) && (int) $result['status'] !== 0) { + throw WebDriverException::factory( + $result['status'], + 'WebDriver response "status"' + ); + } + + if (isset($value['error'])) { + throw WebDriverException::factory( + $value['error'], + $message ?: 'WebDriver response "error"' + ); + } + + if (isset($value['ready']) && $value['ready'] !== true) { + throw WebDriverException::factory( + WebDriverException::CURL_EXEC, + $message ?: 'WebDriver session not "ready"' + ); } $sessionId = isset($result['sessionId']) diff --git a/lib/WebDriver/Container.php b/lib/WebDriver/Container.php index 1a98132..4436a7e 100644 --- a/lib/WebDriver/Container.php +++ b/lib/WebDriver/Container.php @@ -43,7 +43,7 @@ abstract class Container extends AbstractWebDriver /** * {@inheritdoc} */ - public function __construct($url = 'http://localhost:4444/wd/hub') + public function __construct($url) { parent::__construct($url); From 5fdda8f16448755598b40a5451b35725358bf008 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Mon, 28 Jun 2021 15:57:39 -0400 Subject: [PATCH 10/44] deprecate ClassLoader --- lib/WebDriver/ClassLoader.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/WebDriver/ClassLoader.php b/lib/WebDriver/ClassLoader.php index 8b1830b..68bb606 100644 --- a/lib/WebDriver/ClassLoader.php +++ b/lib/WebDriver/ClassLoader.php @@ -25,6 +25,8 @@ * WebDriver\ClassLoader (autoloader) class * * @package WebDriver + * + * @deprecated */ final class ClassLoader { From 26996bcf06b78f5eced18cb5348cb40d4b4fe07e Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Mon, 28 Jun 2021 17:50:12 -0400 Subject: [PATCH 11/44] Error handling when webdriver not running --- lib/WebDriver/AbstractWebDriver.php | 7 +++++++ lib/WebDriver/Service/CurlService.php | 2 ++ .../WebDriver/{WebDriverTest.php => WebDriverTestBase.php} | 0 3 files changed, 9 insertions(+) rename test/Test/WebDriver/{WebDriverTest.php => WebDriverTestBase.php} (100%) diff --git a/lib/WebDriver/AbstractWebDriver.php b/lib/WebDriver/AbstractWebDriver.php index 4564bf8..aac64dd 100644 --- a/lib/WebDriver/AbstractWebDriver.php +++ b/lib/WebDriver/AbstractWebDriver.php @@ -124,6 +124,13 @@ protected function curl($requestMethod, $command, $parameters = null, $extraOpti $httpCode = $info['http_code']; + if ($httpCode === 0) { + throw WebDriverException::factory( + WebDriverException::CURL_EXEC, + $info['error'] + ); + } + // According to https://w3c.github.io/webdriver/webdriver-spec.html all 4xx responses are to be considered // an error and return plaintext, while 5xx responses are json encoded if ($httpCode >= 400 && $httpCode <= 499) { diff --git a/lib/WebDriver/Service/CurlService.php b/lib/WebDriver/Service/CurlService.php index a78c98c..22c5196 100755 --- a/lib/WebDriver/Service/CurlService.php +++ b/lib/WebDriver/Service/CurlService.php @@ -100,6 +100,8 @@ public function execute($requestMethod, $url, $parameters = null, $extraOptions $info = curl_getinfo($curl); $info['request_method'] = $requestMethod; + $info['errno'] = curl_errno($curl); + $info['error'] = curl_error($curl); if (array_key_exists(CURLOPT_FAILONERROR, $extraOptions) && $extraOptions[CURLOPT_FAILONERROR] && diff --git a/test/Test/WebDriver/WebDriverTest.php b/test/Test/WebDriver/WebDriverTestBase.php similarity index 100% rename from test/Test/WebDriver/WebDriverTest.php rename to test/Test/WebDriver/WebDriverTestBase.php From 961b12178cb71f8667afaf2f66ab3e000e060e1c Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Mon, 28 Jun 2021 18:23:05 -0400 Subject: [PATCH 12/44] Exception factory provides a default message --- lib/WebDriver/AbstractWebDriver.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/WebDriver/AbstractWebDriver.php b/lib/WebDriver/AbstractWebDriver.php index aac64dd..c6dedb2 100644 --- a/lib/WebDriver/AbstractWebDriver.php +++ b/lib/WebDriver/AbstractWebDriver.php @@ -163,21 +163,21 @@ protected function curl($requestMethod, $command, $parameters = null, $extraOpti if (isset($result['status']) && (int) $result['status'] !== 0) { throw WebDriverException::factory( $result['status'], - 'WebDriver response "status"' + $message ); } if (isset($value['error'])) { throw WebDriverException::factory( $value['error'], - $message ?: 'WebDriver response "error"' + $message ); } if (isset($value['ready']) && $value['ready'] !== true) { throw WebDriverException::factory( WebDriverException::CURL_EXEC, - $message ?: 'WebDriver session not "ready"' + $message ); } From 6bc1f44cf23031e68c326cd61e14ec32486f241b Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Wed, 13 Oct 2021 23:25:34 -0400 Subject: [PATCH 13/44] Backport some fixes from master branch --- lib/WebDriver/AbstractWebDriver.php | 11 +++++++---- lib/WebDriver/Exception.php | 13 +++++++------ 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/lib/WebDriver/AbstractWebDriver.php b/lib/WebDriver/AbstractWebDriver.php index c6dedb2..15fb9f0 100644 --- a/lib/WebDriver/AbstractWebDriver.php +++ b/lib/WebDriver/AbstractWebDriver.php @@ -149,7 +149,7 @@ protected function curl($requestMethod, $command, $parameters = null, $extraOpti ); } - if (is_array($result) && !array_key_exists('status', $result) && !isset($result['value']['ready']) && ! isset($result['value']['error'])) { + if (is_array($result) && ! array_key_exists('status', $result) && ! array_key_exists('value', $result)) { throw WebDriverException::factory( WebDriverException::CURL_EXEC, 'Payload received from webdriver is valid but unexpected json: ' . substr($rawResult, 0, 1000) @@ -183,9 +183,12 @@ protected function curl($requestMethod, $command, $parameters = null, $extraOpti $sessionId = isset($result['sessionId']) ? $result['sessionId'] - : (isset($value['webdriver.remote.sessionid']) - ? $value['webdriver.remote.sessionid'] - : null + : (isset($value['sessionId']) + ? $value['sessionId'] + : (isset($value['webdriver.remote.sessionid']) + ? $value['webdriver.remote.sessionid'] + : null + ) ); return array( diff --git a/lib/WebDriver/Exception.php b/lib/WebDriver/Exception.php index bacb9cb..e2266f2 100644 --- a/lib/WebDriver/Exception.php +++ b/lib/WebDriver/Exception.php @@ -86,6 +86,7 @@ abstract class Exception extends \Exception const UNEXPECTED_PARAMETERS = -5; const INVALID_REQUEST = -6; const UNKNOWN_LOCATOR_STRATEGY = -7; + const W3C_WEBDRIVER_ERROR = -8; private static $errs = array( // self::SUCCESS => array('Success', 'This should never be thrown!'), @@ -136,12 +137,8 @@ abstract class Exception extends \Exception public static function factory($code, $message = null, $previousException = null) { // unknown error - if (!isset(self::$errs[$code])) { - if (trim($message) === '') { - $message = 'Unknown Error'; - } - - return new \Exception($message, $code, $previousException); + if (! isset(self::$errs[$code])) { + $code = self::UNKNOWN_ERROR; } $errorDefinition = self::$errs[$code]; @@ -150,6 +147,10 @@ public static function factory($code, $message = null, $previousException = null $message = $errorDefinition[1]; } + if (! is_numeric($code)) { + $code = self::W3C_WEBDRIVER_ERROR; + } + $className = __CLASS__ . '\\' . $errorDefinition[0]; return new $className($message, $code, $previousException); From 608e8b20a9072f84cac4f2fddb4420893d69eb83 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Tue, 29 Mar 2022 21:28:33 -0400 Subject: [PATCH 14/44] Backport fixes from master branch --- lib/WebDriver/AbstractWebDriver.php | 18 ++++++++---------- lib/WebDriver/Service/CurlService.php | 13 +++++++------ 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/lib/WebDriver/AbstractWebDriver.php b/lib/WebDriver/AbstractWebDriver.php index 15fb9f0..17d3128 100644 --- a/lib/WebDriver/AbstractWebDriver.php +++ b/lib/WebDriver/AbstractWebDriver.php @@ -157,7 +157,12 @@ protected function curl($requestMethod, $command, $parameters = null, $extraOpti } $value = (is_array($result) && array_key_exists('value', $result)) ? $result['value'] : null; - $message = (is_array($value) && array_key_exists('message', $value)) ? $value['message'] : null; + $message = (is_array($result) && array_key_exists('message', $result)) + ? $result['message'] + : ((is_array($value) && array_key_exists('message', $value)) ? $value['message'] : null); + $error = (is_array($result) && array_key_exists('error', $result)) + ? $result['error'] + : ((is_array($value) && array_key_exists('error', $value)) ? $value['error'] : null); // if not success, throw exception if (isset($result['status']) && (int) $result['status'] !== 0) { @@ -167,16 +172,9 @@ protected function curl($requestMethod, $command, $parameters = null, $extraOpti ); } - if (isset($value['error'])) { + if (isset($error)) { throw WebDriverException::factory( - $value['error'], - $message - ); - } - - if (isset($value['ready']) && $value['ready'] !== true) { - throw WebDriverException::factory( - WebDriverException::CURL_EXEC, + $error, $message ); } diff --git a/lib/WebDriver/Service/CurlService.php b/lib/WebDriver/Service/CurlService.php index 22c5196..499dc49 100755 --- a/lib/WebDriver/Service/CurlService.php +++ b/lib/WebDriver/Service/CurlService.php @@ -23,7 +23,7 @@ namespace WebDriver\Service; -use WebDriver\Exception as WebDriverException; +use WebDriver\Exception\CurlExec as CurlExecException; /** * WebDriver\Service\CurlService class @@ -110,8 +110,7 @@ public function execute($requestMethod, $url, $parameters = null, $extraOptions ) { curl_close($curl); - throw WebDriverException::factory( - WebDriverException::CURL_EXEC, + $e = new CurlExecException( sprintf( "Curl error thrown for http %s to %s%s\n\n%s", $requestMethod, @@ -119,10 +118,12 @@ public function execute($requestMethod, $url, $parameters = null, $extraOptions $parameters && is_array($parameters) ? ' with params: ' . json_encode($parameters) : '', $error ), - $errno, - null, - $info + $errno ); + + $e->setCurlInfo($info); + + throw $e; } curl_close($curl); From fb74db58e55957c1cd6c3683a2f63051b680306e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Vo=C5=99=C3=AD=C5=A1ek?= Date: Thu, 14 Apr 2022 17:02:48 +0200 Subject: [PATCH 15/44] Convert element to object from execute result --- lib/WebDriver/Session.php | 41 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/lib/WebDriver/Session.php b/lib/WebDriver/Session.php index 1186745..4a5c814 100644 --- a/lib/WebDriver/Session.php +++ b/lib/WebDriver/Session.php @@ -34,8 +34,6 @@ * @method void forward() Navigates forward in the browser history, if possible. * @method void back() Navigates backward in the browser history, if possible. * @method void refresh() Refresh the current page. - * @method mixed execute($jsonScript) Inject a snippet of JavaScript into the page for execution in the context of the currently selected frame. (synchronous) - * @method mixed execute_async($jsonScript) Inject a snippet of JavaScript into the page for execution in the context of the currently selected frame. (asynchronous) * @method string screenshot() Take a screenshot of the current page. * @method array getCookie() Retrieve all cookies visible to the current page. * @method array postCookie($jsonCookie) Set a cookie. @@ -433,4 +431,43 @@ protected function getElementPath($elementId) { return sprintf('%s/element/%s', $this->url, $elementId); } + + /** + * @param mixed $data + * + * @return mixed + */ + private function webDriverElementMulti($data) + { + $element = $this->webDriverElement($data); + if ($element !== null) { + return $element; + } elseif (is_array($data)) { + foreach ($data as $k => $v) { + $data[$k] = $this->webDriverElementMulti($v); + } + } + + return $data; + } + + /** + * Inject a snippet of JavaScript into the page for execution in the context of the currently selected frame. (synchronous) + */ + public function execute($jsonScript) + { + $result = parent::execute($jsonScript); + + return $this->webDriverElementMulti($result); + } + + /** + * Inject a snippet of JavaScript into the page for execution in the context of the currently selected frame. (asynchronous) + */ + public function execute_async($jsonScript) + { + $result = parent::execute_async($jsonScript); + + return $this->webDriverElementMulti($result); + } } From c632618cf47bf989c1ee3b5d441587a3286c2cc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Vo=C5=99=C3=AD=C5=A1ek?= Date: Thu, 14 Apr 2022 23:15:52 +0200 Subject: [PATCH 16/44] Add support for wd element object as execute args --- lib/WebDriver/Session.php | 46 ++++++++++++++++++++++++++++++++++----- 1 file changed, 40 insertions(+), 6 deletions(-) diff --git a/lib/WebDriver/Session.php b/lib/WebDriver/Session.php index 4a5c814..e11cab4 100644 --- a/lib/WebDriver/Session.php +++ b/lib/WebDriver/Session.php @@ -432,19 +432,37 @@ protected function getElementPath($elementId) return sprintf('%s/element/%s', $this->url, $elementId); } + /** + * @param array $args + * + * @return array + */ + private function prepareScriptArguments(array $args) + { + foreach ($args as $k => $v) { + if ($v instanceof Element) { + $args[$k] = [Container::WEBDRIVER_ELEMENT_ID => $v->getID()]; + } elseif (is_array($v)) { + $args[$k] = $this->prepareScriptArguments($v); + } + } + + return $args; + } + /** * @param mixed $data * * @return mixed */ - private function webDriverElementMulti($data) + private function webDriverElementRecursive($data) { $element = $this->webDriverElement($data); if ($element !== null) { return $element; } elseif (is_array($data)) { foreach ($data as $k => $v) { - $data[$k] = $this->webDriverElementMulti($v); + $data[$k] = $this->webDriverElementRecursive($v); } } @@ -453,21 +471,37 @@ private function webDriverElementMulti($data) /** * Inject a snippet of JavaScript into the page for execution in the context of the currently selected frame. (synchronous) + * + * @param array{script: string, args: array} $jsonScript + * + * @return mixed */ - public function execute($jsonScript) + public function execute(array $jsonScript) { + if (isset($jsonScript['args'])) { + $jsonScript['args'] = $this->prepareScriptArguments($jsonScript['args']); + } + $result = parent::execute($jsonScript); - return $this->webDriverElementMulti($result); + return $this->webDriverElementRecursive($result); } /** * Inject a snippet of JavaScript into the page for execution in the context of the currently selected frame. (asynchronous) + * + * @param array{script: string, args: array} $jsonScript + * + * @return mixed */ - public function execute_async($jsonScript) + public function execute_async(array $jsonScript) { + if (isset($jsonScript['args'])) { + $jsonScript['args'] = $this->prepareScriptArguments($jsonScript['args']); + } + $result = parent::execute_async($jsonScript); - return $this->webDriverElementMulti($result); + return $this->webDriverElementRecursive($result); } } From 703f90083fdb73d3289702a06a61e47b80b62d5d Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Thu, 14 Apr 2022 19:55:35 -0400 Subject: [PATCH 17/44] phpstan config (up to level 4) + cleanup --- lib/WebDriver/AbstractWebDriver.php | 10 +++++----- lib/WebDriver/Container.php | 2 ++ lib/WebDriver/Exception/CurlExec.php | 18 +++++++++--------- lib/WebDriver/SauceLabs/SauceRest.php | 2 +- lib/WebDriver/ServiceFactory.php | 2 +- lib/WebDriver/Timeouts.php | 2 +- lib/WebDriver/WebDriver.php | 2 +- phpstan.neon.dist | 4 ++++ 8 files changed, 24 insertions(+), 18 deletions(-) create mode 100644 phpstan.neon.dist diff --git a/lib/WebDriver/AbstractWebDriver.php b/lib/WebDriver/AbstractWebDriver.php index 17d3128..d88cb80 100644 --- a/lib/WebDriver/AbstractWebDriver.php +++ b/lib/WebDriver/AbstractWebDriver.php @@ -90,11 +90,11 @@ public function getURL() /** * Curl request to webdriver server. * - * @param string $requestMethod HTTP request method, e.g., 'GET', 'POST', or 'DELETE' - * @param string $command If not defined in methods() this function will throw. - * @param array $parameters If an array(), they will be posted as JSON parameters - * If a number or string, "/$params" is appended to url - * @param array $extraOptions key=>value pairs of curl options to pass to curl_setopt() + * @param string $requestMethod HTTP request method, e.g., 'GET', 'POST', or 'DELETE' + * @param string $command If not defined in methods() this function will throw. + * @param array|integer|string $parameters If an array(), they will be posted as JSON parameters + * If a number or string, "/$params" is appended to url + * @param array $extraOptions key=>value pairs of curl options to pass to curl_setopt() * * @return array array('value' => ..., 'info' => ...) * diff --git a/lib/WebDriver/Container.php b/lib/WebDriver/Container.php index 4436a7e..c964061 100644 --- a/lib/WebDriver/Container.php +++ b/lib/WebDriver/Container.php @@ -224,6 +224,8 @@ protected function webDriverElement($value) $value[self::WEBDRIVER_ELEMENT_ID] // id ); } + + return null; } /** diff --git a/lib/WebDriver/Exception/CurlExec.php b/lib/WebDriver/Exception/CurlExec.php index 29c15cb..a2e636b 100644 --- a/lib/WebDriver/Exception/CurlExec.php +++ b/lib/WebDriver/Exception/CurlExec.php @@ -38,22 +38,22 @@ final class CurlExec extends BaseException private $curlInfo = array(); /** - * {@inheritdoc} + * Get curl info + * + * @return array */ - public function __construct($message = null, $code = 0, \Exception $previous = null, $curlInfo = array()) + public function getCurlInfo() { - parent::__construct($message, $code, $previous); - - $this->curlInfo = $curlInfo; + return $this->curlInfo; } /** - * Get curl info + * Set curl info * - * @return array + * @param array $curlInfo */ - public function getCurlInfo() + public function setCurlInfo($curlInfo) { - return $this->curlInfo; + $this->curlInfo = $curlInfo; } } diff --git a/lib/WebDriver/SauceLabs/SauceRest.php b/lib/WebDriver/SauceLabs/SauceRest.php index 7bec171..3953b0e 100644 --- a/lib/WebDriver/SauceLabs/SauceRest.php +++ b/lib/WebDriver/SauceLabs/SauceRest.php @@ -292,7 +292,7 @@ public function getStatus() * * @return array */ - public function getBrowsers($termination = false) + public function getBrowsers($termination = '') { if ($termination) { return $this->execute('GET', 'info/browsers/' . $termination); diff --git a/lib/WebDriver/ServiceFactory.php b/lib/WebDriver/ServiceFactory.php index bf03708..0c078d7 100755 --- a/lib/WebDriver/ServiceFactory.php +++ b/lib/WebDriver/ServiceFactory.php @@ -66,7 +66,7 @@ private function __construct() */ public static function getInstance() { - if (!self::$instance) { + if (self::$instance === null) { self::$instance = new self; } diff --git a/lib/WebDriver/Timeouts.php b/lib/WebDriver/Timeouts.php index 4ce7251..2c5a959 100644 --- a/lib/WebDriver/Timeouts.php +++ b/lib/WebDriver/Timeouts.php @@ -47,7 +47,7 @@ protected function methods() /** * helper method to wait until user-defined condition is met * - * @param function $callback callback which returns non-false result if wait condition was met + * @param callable $callback callback which returns non-false result if wait condition was met * @param integer $maxIterations maximum number of iterations * @param integer $sleep sleep duration in seconds between iterations * @param array $args optional args; if the callback needs $this, then pass it here diff --git a/lib/WebDriver/WebDriver.php b/lib/WebDriver/WebDriver.php index 0c34dcf..b3cd9b5 100644 --- a/lib/WebDriver/WebDriver.php +++ b/lib/WebDriver/WebDriver.php @@ -27,7 +27,7 @@ * * @package WebDriver * - * @method status + * @method array status() Returns information about whether a remote end is in a state in which it can create new sessions. */ class WebDriver extends AbstractWebDriver implements WebDriverInterface { diff --git a/phpstan.neon.dist b/phpstan.neon.dist new file mode 100644 index 0000000..1c57e1f --- /dev/null +++ b/phpstan.neon.dist @@ -0,0 +1,4 @@ +parameters: + level: 4 + paths: + - lib From 459a42a771cd507e42694804c693c42ad68bbb29 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Thu, 14 Apr 2022 20:01:10 -0400 Subject: [PATCH 18/44] README housekeeping --- .coveralls.yml | 1 - .gitignore | 1 + .travis.yml | 40 ---------------------------------------- README.md | 3 --- 4 files changed, 1 insertion(+), 44 deletions(-) delete mode 100644 .coveralls.yml delete mode 100644 .travis.yml diff --git a/.coveralls.yml b/.coveralls.yml deleted file mode 100644 index 6b74c21..0000000 --- a/.coveralls.yml +++ /dev/null @@ -1 +0,0 @@ -src_dir: lib diff --git a/.gitignore b/.gitignore index fbd8148..2f7eee6 100644 --- a/.gitignore +++ b/.gitignore @@ -3,5 +3,6 @@ logs/ nbproject/ vendor/ .idea/ +.phpunit.result.cache composer.lock composer.phar diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index f315f0e..0000000 --- a/.travis.yml +++ /dev/null @@ -1,40 +0,0 @@ -dist: trusty - -language: php - -php: - # - 5.3 - - 5.4 - - 5.5 - - 5.6 - - 7.0 - - 7.1 - - 7.2 - - 7.3 - - 7.4snapshot - - nightly - -#before_install: -# # This update is mandatory or the 'apt-get install' calls following will fail -# - sudo apt-get update -qq -# - sudo apt-get install -y apache2 libapache2-mod-fastcgi -# # start the xvfb display needed for firefox -# - export DISPLAY=:99.0 -# - sh -e /etc/init.d/xvfb start -# - sh ./test/CI/Travis/setup_selenium.sh -# - sh ./test/CI/Travis/setup_apache.sh - -before_script: - - composer install --no-interaction - -script: - - mkdir -p build/logs - - vendor/bin/phpunit --coverage-clover build/logs/clover.xml --group Unit - -after_script: - - vendor/bin/coveralls - -matrix: - allow_failures: - - php: 7.4snapshot - - php: nightly diff --git a/README.md b/README.md index 7998868..b534d00 100644 --- a/README.md +++ b/README.md @@ -8,9 +8,6 @@ Distinguishing features: * Coding style follows PSR-1, PSR-2, and Symfony2 conventions. * Auto-generate API documentation via [phpDocumentor 2.x](http://phpdoc.org/). -[![Build Status](https://travis-ci.org/instaclick/php-webdriver.png)](https://travis-ci.org/instaclick/php-webdriver) -[![Coverage Status](https://coveralls.io/repos/instaclick/php-webdriver/badge.png)](https://coveralls.io/r/instaclick/php-webdriver) - [![Latest Stable Version](https://poser.pugx.org/instaclick/php-webdriver/v/stable.png)](https://packagist.org/packages/instaclick/php-webdriver) [![Total Downloads](https://poser.pugx.org/instaclick/php-webdriver/downloads.png)](https://packagist.org/packages/instaclick/php-webdriver) From 8bb204d82c323fe1ccd308861045422eaa315a07 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Thu, 14 Apr 2022 20:03:17 -0400 Subject: [PATCH 19/44] Coding style + fix to PR #11 --- lib/WebDriver/Session.php | 99 ++++++++++++++++++++++----------------- 1 file changed, 56 insertions(+), 43 deletions(-) diff --git a/lib/WebDriver/Session.php b/lib/WebDriver/Session.php index e11cab4..497c10b 100644 --- a/lib/WebDriver/Session.php +++ b/lib/WebDriver/Session.php @@ -425,83 +425,96 @@ public function log() } /** - * {@inheritdoc} + * Inject a snippet of JavaScript into the page for execution in the context of the currently selected frame. (synchronous) + * + * @param array{script: string, args: array} $jsonScript + * + * @return mixed */ - protected function getElementPath($elementId) + public function execute(array $jsonScript) { - return sprintf('%s/element/%s', $this->url, $elementId); + if (isset($jsonScript['args'])) { + $jsonScript['args'] = $this->serializeArguments($jsonScript['args']); + } + + $result = $this->curl('POST', '/execute', $jsonScript); + + return $this->unserializeResult($result); } /** - * @param array $args + * Inject a snippet of JavaScript into the page for execution in the context of the currently selected frame. (asynchronous) * - * @return array + * @param array{script: string, args: array} $jsonScript + * + * @return mixed */ - private function prepareScriptArguments(array $args) + public function execute_async(array $jsonScript) { - foreach ($args as $k => $v) { - if ($v instanceof Element) { - $args[$k] = [Container::WEBDRIVER_ELEMENT_ID => $v->getID()]; - } elseif (is_array($v)) { - $args[$k] = $this->prepareScriptArguments($v); - } + if (isset($jsonScript['args'])) { + $jsonScript['args'] = $this->serializeArguments($jsonScript['args']); } - return $args; + $result = $this->curl('POST', '/execute_async', $jsonScript); + + return $this->unserializeResult($result); } /** - * @param mixed $data - * - * @return mixed + * {@inheritdoc} */ - private function webDriverElementRecursive($data) + protected function getElementPath($elementId) { - $element = $this->webDriverElement($data); - if ($element !== null) { - return $element; - } elseif (is_array($data)) { - foreach ($data as $k => $v) { - $data[$k] = $this->webDriverElementRecursive($v); - } - } - - return $data; + return sprintf('%s/element/%s', $this->url, $elementId); } /** - * Inject a snippet of JavaScript into the page for execution in the context of the currently selected frame. (synchronous) + * Serialize script arguments (containing web elements) * - * @param array{script: string, args: array} $jsonScript + * @see https://w3c.github.io/webdriver/#executing-script * - * @return mixed + * @param array $arguments + * + * @return array */ - public function execute(array $jsonScript) + private function serializeArguments(array $arguments) { - if (isset($jsonScript['args'])) { - $jsonScript['args'] = $this->prepareScriptArguments($jsonScript['args']); - } + foreach ($arguments as $key => $value) { + // Potential compat-buster, i.e., W3C-specific + if ($value instanceof Element) { + $arguments[$key] = [Container::WEBDRIVER_ELEMENT_ID => $value->getID()]; + continue; + } - $result = parent::execute($jsonScript); + if (is_array($value)) { + $arguments[$key] = $this->serializeArguments($value); + } + } - return $this->webDriverElementRecursive($result); + return $arguments; } /** - * Inject a snippet of JavaScript into the page for execution in the context of the currently selected frame. (asynchronous) + * Unserialize result (containing web elements) * - * @param array{script: string, args: array} $jsonScript + * @param mixed $result * * @return mixed */ - public function execute_async(array $jsonScript) + private function unserializeResult($result) { - if (isset($jsonScript['args'])) { - $jsonScript['args'] = $this->prepareScriptArguments($jsonScript['args']); + $element = $this->webDriverElement($result); + + if ($element !== null) { + return $element; } - $result = parent::execute_async($jsonScript); + if (is_array($result)) { + foreach ($result as $key => $value) { + $result[$key] = $this->unserializeResult($value); + } + } - return $this->webDriverElementRecursive($result); + return $result; } } From 623eb02be728185110587e4259780e5f7f9f3983 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Vo=C5=99=C3=AD=C5=A1ek?= Date: Fri, 15 Apr 2022 12:18:13 +0200 Subject: [PATCH 20/44] Fix Session::{execute, execute_async}, must use value result --- lib/WebDriver/Session.php | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/lib/WebDriver/Session.php b/lib/WebDriver/Session.php index 497c10b..d4b3268 100644 --- a/lib/WebDriver/Session.php +++ b/lib/WebDriver/Session.php @@ -76,8 +76,6 @@ protected function methods() 'forward' => array('POST'), 'back' => array('POST'), 'refresh' => array('POST'), - 'execute' => array('POST'), - 'execute_async' => array('POST'), 'screenshot' => array('GET'), 'cookie' => array('GET', 'POST'), // for DELETE, use deleteAllCookies() 'source' => array('GET'), @@ -433,13 +431,11 @@ public function log() */ public function execute(array $jsonScript) { - if (isset($jsonScript['args'])) { - $jsonScript['args'] = $this->serializeArguments($jsonScript['args']); - } + $jsonScript['args'] = $this->serializeArguments($jsonScript['args']); $result = $this->curl('POST', '/execute', $jsonScript); - return $this->unserializeResult($result); + return $this->unserializeResult($result['value']); } /** @@ -451,13 +447,11 @@ public function execute(array $jsonScript) */ public function execute_async(array $jsonScript) { - if (isset($jsonScript['args'])) { - $jsonScript['args'] = $this->serializeArguments($jsonScript['args']); - } + $jsonScript['args'] = $this->serializeArguments($jsonScript['args']); $result = $this->curl('POST', '/execute_async', $jsonScript); - return $this->unserializeResult($result); + return $this->unserializeResult($result['value']); } /** From b5b18bd2e02b35a14c7085552780588b8876460b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Vo=C5=99=C3=AD=C5=A1ek?= Date: Mon, 18 Apr 2022 00:43:56 +0200 Subject: [PATCH 21/44] Fix compatibility with Selenium 2.x --- lib/WebDriver/Session.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/WebDriver/Session.php b/lib/WebDriver/Session.php index d4b3268..f73e379 100644 --- a/lib/WebDriver/Session.php +++ b/lib/WebDriver/Session.php @@ -476,7 +476,13 @@ private function serializeArguments(array $arguments) foreach ($arguments as $key => $value) { // Potential compat-buster, i.e., W3C-specific if ($value instanceof Element) { - $arguments[$key] = [Container::WEBDRIVER_ELEMENT_ID => $value->getID()]; + // preferably we want to detect W3C support and never set nor parse + // LEGACY_ELEMENT_ID, until detection is implemented, serialize to both + // variants, tested with Selenium v2.53.1 and v3.141.59 + $arguments[$key] = [ + Container::WEBDRIVER_ELEMENT_ID => $value->getID(), + Container::LEGACY_ELEMENT_ID => $value->getID(), + ]; continue; } From c893a53be62e59b2b79aa8b230a8f8b476189c46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Vo=C5=99=C3=AD=C5=A1ek?= Date: Mon, 18 Apr 2022 01:41:08 +0200 Subject: [PATCH 22/44] Assert curl data are always fully serialized before request --- lib/WebDriver/AbstractWebDriver.php | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/lib/WebDriver/AbstractWebDriver.php b/lib/WebDriver/AbstractWebDriver.php index d88cb80..a2aa2e1 100644 --- a/lib/WebDriver/AbstractWebDriver.php +++ b/lib/WebDriver/AbstractWebDriver.php @@ -120,6 +120,8 @@ protected function curl($requestMethod, $command, $parameters = null, $extraOpti $url .= '/' . $parameters; } + $this->assertNonObjectParameters($parameters); + list($rawResult, $info) = ServiceFactory::getInstance()->getService('service.curl')->execute($requestMethod, $url, $parameters, $extraOptions); $httpCode = $info['http_code']; @@ -197,6 +199,32 @@ protected function curl($requestMethod, $command, $parameters = null, $extraOpti ); } + /** + * @param mixed $parameters + */ + private function assertNonObjectParameters($parameters) + { + if ($parameters === null || is_scalar($parameters)) { + return; + } + + if (is_array($parameters)) { + foreach ($parameters as $value) { + $this->assertNonObjectParameters($value); + } + + return; + } + + throw WebDriverException::factory( + WebDriverException::UNEXPECTED_PARAMETERS, + sprintf( + "Unable to serialize non-scalar type %s", + is_object($parameters) ? get_class($parameters) : gettype($parameters) + ) + ); + } + /** * Magic method that maps calls to class methods to execute WebDriver commands * From 200b8df772b74d604bebf25ef42ad6f8ee6380a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Vo=C5=99=C3=AD=C5=A1ek?= Date: Mon, 18 Apr 2022 11:43:29 +0200 Subject: [PATCH 23/44] Add testing with mink/driver-testsuite --- .github/workflows/ci.yml | 54 ++++++++++++++++++++++++++++++++++++++++ composer.json | 6 +++-- 2 files changed, 58 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..bcc8dd4 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,54 @@ +name: CI + +on: + push: + pull_request: + +env: + DRIVER_URL: "http://localhost:4444/wd/hub" + +jobs: + Mink: + runs-on: ubuntu-20.04 + strategy: + matrix: + selenium: [ '2.53.1', '3.141.59' ] + php: [ '7.2', '7.3', '7.4', '8.0', '8.1' ] + fail-fast: false + + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: "${{ matrix.php }}" + tools: composer + ini-values: error_reporting=-1, display_errors=On + + - name: Install dependencies + run: | + composer config version 1.4.99 + composer require --no-update behat/mink-selenium2-driver:dev-master --dev --quiet + composer require --no-update mink/driver-testsuite:dev-master --dev --quiet + php -r '$json = json_decode(file_get_contents ("composer.json"), true); $json["autoload"]["psr-4"]["Behat\\Mink\\Tests\\Driver\\"] = "vendor/behat/mink-selenium2-driver/tests/"; file_put_contents("composer.json", json_encode($json, JSON_UNESCAPED_SLASHES|JSON_PRETTY_PRINT));' + composer update --no-interaction --prefer-dist + + - name: Start Selenium & Mink test server + run: | + mkdir logs + docker run --net host --name selenium --volume /dev/shm:/dev/shm --shm-size 2g "selenium/standalone-firefox:${{ matrix.selenium }}" &> logs/selenium.log & + vendor/bin/mink-test-server &> logs/mink-test-server.log & + while ! nc -z localhost 4444 Date: Mon, 8 Aug 2022 21:07:45 -0400 Subject: [PATCH 24/44] Fixes #118 - partial reverts PR #67 - handling legacy webdriver 4xx responses --- lib/WebDriver/AbstractWebDriver.php | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/lib/WebDriver/AbstractWebDriver.php b/lib/WebDriver/AbstractWebDriver.php index a2aa2e1..1270beb 100644 --- a/lib/WebDriver/AbstractWebDriver.php +++ b/lib/WebDriver/AbstractWebDriver.php @@ -133,18 +133,17 @@ protected function curl($requestMethod, $command, $parameters = null, $extraOpti ); } - // According to https://w3c.github.io/webdriver/webdriver-spec.html all 4xx responses are to be considered - // an error and return plaintext, while 5xx responses are json encoded - if ($httpCode >= 400 && $httpCode <= 499) { - throw WebDriverException::factory( - WebDriverException::CURL_EXEC, - 'Webdriver http error: ' . $httpCode . ', payload :' . substr($rawResult, 0, 1000) - ); - } - $result = json_decode($rawResult, true); if (!empty($rawResult) && $result === null && json_last_error() != JSON_ERROR_NONE) { + // Legacy webdriver 4xx responses are to be considered // an error and return plaintext + if ($httpCode >= 400 && $httpCode <= 499) { + throw WebDriverException::factory( + WebDriverException::CURL_EXEC, + 'Webdriver http error: ' . $httpCode . ', payload :' . substr($rawResult, 0, 1000) + ); + } + throw WebDriverException::factory( WebDriverException::CURL_EXEC, 'Payload received from webdriver is not valid json: ' . substr($rawResult, 0, 1000) From 6df9fe9961cb9c543f982da19768f4141c96815c Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Mon, 8 Aug 2022 21:13:40 -0400 Subject: [PATCH 25/44] Backport exceptions from master branch --- lib/WebDriver/Exception.php | 78 ++++++++++++++----- lib/WebDriver/Exception/CurlExec.php | 18 +---- .../Exception/DetachedShadowRoot.php | 23 ++++++ .../Exception/ElementClickIntercepted.php | 23 ++++++ .../Exception/ElementIsNotSelectable.php | 17 +--- .../Exception/ElementNotInteractable.php | 23 ++++++ lib/WebDriver/Exception/ElementNotVisible.php | 17 +--- .../Exception/IMEEngineActivationFailed.php | 17 +--- lib/WebDriver/Exception/IMENotAvailable.php | 17 +--- .../Exception/InsecureCertificate.php | 23 ++++++ lib/WebDriver/Exception/InvalidArgument.php | 23 ++++++ .../Exception/InvalidCookieDomain.php | 17 +--- .../Exception/InvalidCoordinates.php | 23 ++++++ .../Exception/InvalidElementCoordinates.php | 17 +--- .../Exception/InvalidElementState.php | 17 +--- lib/WebDriver/Exception/InvalidRequest.php | 17 +--- lib/WebDriver/Exception/InvalidSelector.php | 17 +--- lib/WebDriver/Exception/InvalidSessionID.php | 23 ++++++ lib/WebDriver/Exception/JavaScriptError.php | 17 +--- .../Exception/JsonParameterExpected.php | 17 +--- .../Exception/MoveTargetOutOfBounds.php | 17 +--- lib/WebDriver/Exception/NoAlertOpenError.php | 17 +--- .../Exception/NoParametersExpected.php | 17 +--- lib/WebDriver/Exception/NoSuchAlert.php | 23 ++++++ lib/WebDriver/Exception/NoSuchCookie.php | 23 ++++++ lib/WebDriver/Exception/NoSuchDriver.php | 17 +--- lib/WebDriver/Exception/NoSuchElement.php | 17 +--- lib/WebDriver/Exception/NoSuchFrame.php | 17 +--- lib/WebDriver/Exception/NoSuchShadowRoot.php | 23 ++++++ lib/WebDriver/Exception/NoSuchWindow.php | 17 +--- lib/WebDriver/Exception/ObsoleteCommand.php | 17 +--- lib/WebDriver/Exception/ScriptTimeout.php | 17 +--- lib/WebDriver/Exception/SessionNotCreated.php | 17 +--- .../Exception/StaleElementReference.php | 17 +--- lib/WebDriver/Exception/Timeout.php | 17 +--- .../Exception/UnableToCaptureScreen.php | 23 ++++++ lib/WebDriver/Exception/UnableToSetCookie.php | 17 +--- .../Exception/UnexpectedAlertOpen.php | 17 +--- .../Exception/UnexpectedParameters.php | 17 +--- lib/WebDriver/Exception/UnknownCommand.php | 17 +--- lib/WebDriver/Exception/UnknownError.php | 17 +--- .../Exception/UnknownLocatorStrategy.php | 17 +--- lib/WebDriver/Exception/UnknownMethod.php | 23 ++++++ .../Exception/UnsupportedOperation.php | 23 ++++++ lib/WebDriver/Exception/XPathLookupError.php | 17 +--- 45 files changed, 452 insertions(+), 453 deletions(-) create mode 100644 lib/WebDriver/Exception/DetachedShadowRoot.php create mode 100644 lib/WebDriver/Exception/ElementClickIntercepted.php create mode 100644 lib/WebDriver/Exception/ElementNotInteractable.php create mode 100644 lib/WebDriver/Exception/InsecureCertificate.php create mode 100644 lib/WebDriver/Exception/InvalidArgument.php create mode 100644 lib/WebDriver/Exception/InvalidCoordinates.php create mode 100644 lib/WebDriver/Exception/InvalidSessionID.php create mode 100644 lib/WebDriver/Exception/NoSuchAlert.php create mode 100644 lib/WebDriver/Exception/NoSuchCookie.php create mode 100644 lib/WebDriver/Exception/NoSuchShadowRoot.php create mode 100644 lib/WebDriver/Exception/UnableToCaptureScreen.php create mode 100644 lib/WebDriver/Exception/UnknownMethod.php create mode 100644 lib/WebDriver/Exception/UnsupportedOperation.php diff --git a/lib/WebDriver/Exception.php b/lib/WebDriver/Exception.php index e2266f2..edee0d3 100644 --- a/lib/WebDriver/Exception.php +++ b/lib/WebDriver/Exception.php @@ -1,23 +1,12 @@ - * @author Anthon Pang */ namespace WebDriver; @@ -32,7 +21,7 @@ abstract class Exception extends \Exception /** * Response status codes * - * @link http://code.google.com/p/selenium/wiki/JsonWireProtocol#Response_Status_Codes + * @see https://github.com/SeleniumHQ/selenium/blob/trunk/java/src/org/openqa/selenium/remote/ErrorCodes.java */ const SUCCESS = 0; const NO_SUCH_DRIVER = 6; @@ -40,10 +29,8 @@ abstract class Exception extends \Exception const NO_SUCH_FRAME = 8; const UNKNOWN_COMMAND = 9; const STALE_ELEMENT_REFERENCE = 10; - const ELEMENT_NOT_VISIBLE = 11; const INVALID_ELEMENT_STATE = 12; const UNKNOWN_ERROR = 13; - const ELEMENT_IS_NOT_SELECTABLE = 15; const JAVASCRIPT_ERROR = 17; const XPATH_LOOKUP_ERROR = 19; const TIMEOUT = 21; @@ -59,6 +46,15 @@ abstract class Exception extends \Exception const INVALID_SELECTOR = 32; const SESSION_NOT_CREATED = 33; const MOVE_TARGET_OUT_OF_BOUNDS = 34; + const INVALID_XPATH_SELECTOR = 51; + const INVALID_XPATH_SELECTOR_RETURN_TYPER = 52; + const ELEMENT_NOT_INTERACTABLE = 60; + const INVALID_ARGUMENT = 61; + const NO_SUCH_COOKIE = 62; + const UNABLE_TO_CAPTURE_SCREEN = 63; + const ELEMENT_CLICK_INTERCEPTED = 64; + const NO_SUCH_SHADOW_ROOT = 65; + const METHOD_NOT_ALLOWED = 405; // obsolete const INDEX_OUT_OF_BOUNDS = 1; @@ -68,8 +64,10 @@ abstract class Exception extends \Exception const NO_STRING_WRAPPER = 5; const OBSOLETE_ELEMENT = 10; const ELEMENT_NOT_DISPLAYED = 11; + const ELEMENT_NOT_VISIBLE = 11; const UNHANDLED = 13; const EXPECTED = 14; + const ELEMENT_IS_NOT_SELECTABLE = 15; const ELEMENT_NOT_SELECTABLE = 15; const NO_SUCH_DOCUMENT = 16; const UNEXPECTED_JAVASCRIPT = 17; @@ -123,6 +121,50 @@ abstract class Exception extends \Exception self::UNEXPECTED_PARAMETERS => array('UnexpectedParameters', 'This command does not expect this number of parameters.'), self::INVALID_REQUEST => array('InvalidRequest', 'This command does not support this HTTP request method.'), self::UNKNOWN_LOCATOR_STRATEGY => array('UnknownLocatorStrategy', 'This locator strategy is not supported.'), + self::INVALID_XPATH_SELECTOR => array('InvalidSelector', 'Argument was an invalid selector.'), + self::INVALID_XPATH_SELECTOR_RETURN_TYPER => array('InvalidSelector', 'Argument was an invalid selector.'), + self::ELEMENT_NOT_INTERACTABLE => array('ElementNotInteractable', 'A command could not be completed because the element is not pointer- or keyboard interactable.'), + self::INVALID_ARGUMENT => array('InvalidArgument', 'The arguments passed to a command are either invalid or malformed.'), + self::NO_SUCH_COOKIE => array('NoSuchCookie', 'No cookie matching the given path name was found amongst the associated cookies of the current browsing context\'s active document.'), + self::UNABLE_TO_CAPTURE_SCREEN => array('UnableToCaptureScreen', 'A screen capture was made impossible.'), + self::ELEMENT_CLICK_INTERCEPTED => array('ElementClickIntercepted', 'The Element Click command could not be completed because the element receiving the events is obscuring the element that was requested clicked.'), + self::NO_SUCH_SHADOW_ROOT => array('NoSuchShadowRoot', 'The element does not have a shadow root.'), + self::METHOD_NOT_ALLOWED => array('UnsupportedOperation', 'Indicates that a command that should have executed properly cannot be supported for some reason.'), + + // @ss https://w3c.github.io/webdriver/#errors + 'element not interactable' => array('ElementNotInteractable', 'A command could not be completed because the element is not pointer- or keyboard interactable.'), + 'element not selectable' => array('ElementIsNotSelectable', 'An attempt was made to select an element that cannot be selected.'), + 'insecure certificate' => array('InsecureCertificate', 'Navigation caused the user agent to hit a certificate warning, which is usually the result of an expired or invalid TLS certificate.'), + 'invalid argument' => array('InvalidArgument', 'The arguments passed to a command are either invalid or malformed.'), + 'invalid cookie domain' => array('InvalidCookieDomain', 'An illegal attempt was made to set a cookie under a different domain than the current page.'), + 'invalid coordinates' => array('InvalidCoordinates', 'The coordinates provided to an interactions operation are invalid.'), + 'invalid element state' => array('InvalidElementState', 'A command could not be completed because the element is in an invalid state, e.g. attempting to clear an element that isn\'t both editable and resettable.'), + 'invalid selector' => array('InvalidSelector', 'Argument was an invalid selector.'), + 'invalid session id' => array('InvalidSessionID', 'Occurs if the given session id is not in the list of active sessions, meaning the session either does not exist or that it\'s not active.'), + 'javascript error' => array('JavaScriptError', 'An error occurred while executing JavaScript supplied by the user.'), + 'move target out of bounds' => array('MoveTargetOutOfBounds', 'The target for mouse interaction is not in the browser\'s viewport and cannot be brought into that viewport.'), + 'no such alert' => array('NoSuchAlert', 'An attempt was made to operate on a modal dialog when one was not open.'), + 'no such cookie' => array('NoSuchCookie', 'No cookie matching the given path name was found amongst the associated cookies of the current browsing context\'s active document.'), + 'no such element' => array('NoSuchElement', 'An element could not be located on the page using the given search parameters.'), + 'no such frame' => array('NoSuchFrame', 'A command to switch to a frame could not be satisfied because the frame could not be found.'), + 'no such window' => array('NoSuchWindow', 'A command to switch to a window could not be satisfied because the window could not be found.'), + 'script timeout' => array('ScriptTimeout', 'A script did not complete before its timeout expired.'), + 'session not created' => array('SessionNotCreated', 'A new session could not be created.'), + 'stale element reference' => array('StaleElementReference', 'A command failed because the referenced element is no longer attached to the DOM.'), + 'timeout' => array('Timeout', 'An operation did not complete before its timeout expired.'), + 'unable to capture screen' => array('UnableToCaptureScreen', 'A screen capture was made impossible.'), + 'unable to set cookie' => array('UnableToSetCookie', 'A command to set a cookie\'s value could not be satisfied.'), + 'unexpected alert open' => array('UnexpectedAlertOpen', 'A modal dialog was open, blocking this operation.'), + 'unknown command' => array('UnknownCommand', 'A command could not be executed because the remote end is not aware of it.'), + 'unknown error' => array('UnknownError', 'An unknown error occurred in the remote end while processing the command.'), + 'unknown method' => array('UnknownMethod', 'The requested command matched a known URL but did not match an method for that URL.'), + 'unsupported operation' => array('UnsupportedOperation', 'Indicates that a command that should have executed properly cannot be supported for some reason.'), + + // obsolete + 'detached shadow root' => array('DetachedShadowRoot', 'A command failed because the referenced shadow root is no longer attached to the DOM.'), + 'element click intercepted' => array('ElementClickIntercepted', 'The Element Click command could not be completed because the element receiving the events is obscuring the element that was requested clicked.'), + 'no such shadow root' => array('NoSuchShadowRoot', 'The element does not have a shadow root.'), + 'script timeout error' => array('ScriptTimeout', 'A script did not complete before its timeout expired.'), ); /** @@ -143,7 +185,7 @@ public static function factory($code, $message = null, $previousException = null $errorDefinition = self::$errs[$code]; - if (trim($message) === '') { + if ($message === null || trim($message) === '') { $message = $errorDefinition[1]; } diff --git a/lib/WebDriver/Exception/CurlExec.php b/lib/WebDriver/Exception/CurlExec.php index a2e636b..bd9a530 100644 --- a/lib/WebDriver/Exception/CurlExec.php +++ b/lib/WebDriver/Exception/CurlExec.php @@ -1,24 +1,12 @@ * @author Anthon Pang - * @author Gaetano Giunta */ namespace WebDriver\Exception; diff --git a/lib/WebDriver/Exception/DetachedShadowRoot.php b/lib/WebDriver/Exception/DetachedShadowRoot.php new file mode 100644 index 0000000..e3375d6 --- /dev/null +++ b/lib/WebDriver/Exception/DetachedShadowRoot.php @@ -0,0 +1,23 @@ + + */ + +namespace WebDriver\Exception; + +use WebDriver\Exception as BaseException; + +/** + * WebDriver\Exception\DetachedShadowRoot class + * + * @package WebDriver + */ +final class DetachedShadowRoot extends BaseException +{ +} diff --git a/lib/WebDriver/Exception/ElementClickIntercepted.php b/lib/WebDriver/Exception/ElementClickIntercepted.php new file mode 100644 index 0000000..8fcac76 --- /dev/null +++ b/lib/WebDriver/Exception/ElementClickIntercepted.php @@ -0,0 +1,23 @@ + + */ + +namespace WebDriver\Exception; + +use WebDriver\Exception as BaseException; + +/** + * WebDriver\Exception\ElementClickIntercepted class + * + * @package WebDriver + */ +final class ElementClickIntercepted extends BaseException +{ +} diff --git a/lib/WebDriver/Exception/ElementIsNotSelectable.php b/lib/WebDriver/Exception/ElementIsNotSelectable.php index 2b6c124..cadd219 100644 --- a/lib/WebDriver/Exception/ElementIsNotSelectable.php +++ b/lib/WebDriver/Exception/ElementIsNotSelectable.php @@ -1,22 +1,11 @@ * @author Anthon Pang */ diff --git a/lib/WebDriver/Exception/ElementNotInteractable.php b/lib/WebDriver/Exception/ElementNotInteractable.php new file mode 100644 index 0000000..bdbb10d --- /dev/null +++ b/lib/WebDriver/Exception/ElementNotInteractable.php @@ -0,0 +1,23 @@ + + */ + +namespace WebDriver\Exception; + +use WebDriver\Exception as BaseException; + +/** + * WebDriver\Exception\ElementNotInteractable class + * + * @package WebDriver + */ +final class ElementNotInteractable extends BaseException +{ +} diff --git a/lib/WebDriver/Exception/ElementNotVisible.php b/lib/WebDriver/Exception/ElementNotVisible.php index 80287c0..ca3e64d 100644 --- a/lib/WebDriver/Exception/ElementNotVisible.php +++ b/lib/WebDriver/Exception/ElementNotVisible.php @@ -1,22 +1,11 @@ * @author Anthon Pang */ diff --git a/lib/WebDriver/Exception/IMEEngineActivationFailed.php b/lib/WebDriver/Exception/IMEEngineActivationFailed.php index cb2610f..24f9d7e 100644 --- a/lib/WebDriver/Exception/IMEEngineActivationFailed.php +++ b/lib/WebDriver/Exception/IMEEngineActivationFailed.php @@ -1,22 +1,11 @@ * @author Anthon Pang */ diff --git a/lib/WebDriver/Exception/IMENotAvailable.php b/lib/WebDriver/Exception/IMENotAvailable.php index 45fc10f..4b249b6 100644 --- a/lib/WebDriver/Exception/IMENotAvailable.php +++ b/lib/WebDriver/Exception/IMENotAvailable.php @@ -1,22 +1,11 @@ * @author Anthon Pang */ diff --git a/lib/WebDriver/Exception/InsecureCertificate.php b/lib/WebDriver/Exception/InsecureCertificate.php new file mode 100644 index 0000000..f3d9485 --- /dev/null +++ b/lib/WebDriver/Exception/InsecureCertificate.php @@ -0,0 +1,23 @@ + + */ + +namespace WebDriver\Exception; + +use WebDriver\Exception as BaseException; + +/** + * WebDriver\Exception\InsecureCertificate class + * + * @package WebDriver + */ +final class InsecureCertificate extends BaseException +{ +} diff --git a/lib/WebDriver/Exception/InvalidArgument.php b/lib/WebDriver/Exception/InvalidArgument.php new file mode 100644 index 0000000..bbe5f62 --- /dev/null +++ b/lib/WebDriver/Exception/InvalidArgument.php @@ -0,0 +1,23 @@ + + */ + +namespace WebDriver\Exception; + +use WebDriver\Exception as BaseException; + +/** + * WebDriver\Exception\InvalidArgument class + * + * @package WebDriver + */ +final class InvalidArgument extends BaseException +{ +} diff --git a/lib/WebDriver/Exception/InvalidCookieDomain.php b/lib/WebDriver/Exception/InvalidCookieDomain.php index 89ba23d..76723a8 100644 --- a/lib/WebDriver/Exception/InvalidCookieDomain.php +++ b/lib/WebDriver/Exception/InvalidCookieDomain.php @@ -1,22 +1,11 @@ * @author Anthon Pang */ diff --git a/lib/WebDriver/Exception/InvalidCoordinates.php b/lib/WebDriver/Exception/InvalidCoordinates.php new file mode 100644 index 0000000..a3ae5b2 --- /dev/null +++ b/lib/WebDriver/Exception/InvalidCoordinates.php @@ -0,0 +1,23 @@ + + */ + +namespace WebDriver\Exception; + +use WebDriver\Exception as BaseException; + +/** + * WebDriver\Exception\InvalidCoordinates class + * + * @package WebDriver + */ +final class InvalidCoordinates extends BaseException +{ +} diff --git a/lib/WebDriver/Exception/InvalidElementCoordinates.php b/lib/WebDriver/Exception/InvalidElementCoordinates.php index 7a486b0..2748098 100644 --- a/lib/WebDriver/Exception/InvalidElementCoordinates.php +++ b/lib/WebDriver/Exception/InvalidElementCoordinates.php @@ -1,22 +1,11 @@ * @author Anthon Pang */ diff --git a/lib/WebDriver/Exception/InvalidElementState.php b/lib/WebDriver/Exception/InvalidElementState.php index ba4f7a7..c7d2f34 100644 --- a/lib/WebDriver/Exception/InvalidElementState.php +++ b/lib/WebDriver/Exception/InvalidElementState.php @@ -1,22 +1,11 @@ * @author Anthon Pang */ diff --git a/lib/WebDriver/Exception/InvalidRequest.php b/lib/WebDriver/Exception/InvalidRequest.php index e18f76c..6379361 100644 --- a/lib/WebDriver/Exception/InvalidRequest.php +++ b/lib/WebDriver/Exception/InvalidRequest.php @@ -1,22 +1,11 @@ * @author Anthon Pang */ diff --git a/lib/WebDriver/Exception/InvalidSelector.php b/lib/WebDriver/Exception/InvalidSelector.php index 065fc40..7a9674e 100644 --- a/lib/WebDriver/Exception/InvalidSelector.php +++ b/lib/WebDriver/Exception/InvalidSelector.php @@ -1,22 +1,11 @@ * @author Anthon Pang */ diff --git a/lib/WebDriver/Exception/InvalidSessionID.php b/lib/WebDriver/Exception/InvalidSessionID.php new file mode 100644 index 0000000..66138eb --- /dev/null +++ b/lib/WebDriver/Exception/InvalidSessionID.php @@ -0,0 +1,23 @@ + + */ + +namespace WebDriver\Exception; + +use WebDriver\Exception as BaseException; + +/** + * WebDriver\Exception\InvalidSessionID class + * + * @package WebDriver + */ +final class InvalidSessionID extends BaseException +{ +} diff --git a/lib/WebDriver/Exception/JavaScriptError.php b/lib/WebDriver/Exception/JavaScriptError.php index 202bdce..753d9c5 100644 --- a/lib/WebDriver/Exception/JavaScriptError.php +++ b/lib/WebDriver/Exception/JavaScriptError.php @@ -1,22 +1,11 @@ * @author Anthon Pang */ diff --git a/lib/WebDriver/Exception/JsonParameterExpected.php b/lib/WebDriver/Exception/JsonParameterExpected.php index 1f9b287..9bb8a0c 100644 --- a/lib/WebDriver/Exception/JsonParameterExpected.php +++ b/lib/WebDriver/Exception/JsonParameterExpected.php @@ -1,22 +1,11 @@ * @author Anthon Pang */ diff --git a/lib/WebDriver/Exception/MoveTargetOutOfBounds.php b/lib/WebDriver/Exception/MoveTargetOutOfBounds.php index e6fbbb9..16abca9 100644 --- a/lib/WebDriver/Exception/MoveTargetOutOfBounds.php +++ b/lib/WebDriver/Exception/MoveTargetOutOfBounds.php @@ -1,22 +1,11 @@ * @author Anthon Pang */ diff --git a/lib/WebDriver/Exception/NoAlertOpenError.php b/lib/WebDriver/Exception/NoAlertOpenError.php index 9df6db1..9a2d8a6 100644 --- a/lib/WebDriver/Exception/NoAlertOpenError.php +++ b/lib/WebDriver/Exception/NoAlertOpenError.php @@ -1,22 +1,11 @@ * @author Anthon Pang */ diff --git a/lib/WebDriver/Exception/NoParametersExpected.php b/lib/WebDriver/Exception/NoParametersExpected.php index c35c6b4..6fb50da 100644 --- a/lib/WebDriver/Exception/NoParametersExpected.php +++ b/lib/WebDriver/Exception/NoParametersExpected.php @@ -1,22 +1,11 @@ * @author Anthon Pang */ diff --git a/lib/WebDriver/Exception/NoSuchAlert.php b/lib/WebDriver/Exception/NoSuchAlert.php new file mode 100644 index 0000000..6be8627 --- /dev/null +++ b/lib/WebDriver/Exception/NoSuchAlert.php @@ -0,0 +1,23 @@ + + */ + +namespace WebDriver\Exception; + +use WebDriver\Exception as BaseException; + +/** + * WebDriver\Exception\NoSuchAlert class + * + * @package WebDriver + */ +final class NoSuchAlert extends BaseException +{ +} diff --git a/lib/WebDriver/Exception/NoSuchCookie.php b/lib/WebDriver/Exception/NoSuchCookie.php new file mode 100644 index 0000000..289da28 --- /dev/null +++ b/lib/WebDriver/Exception/NoSuchCookie.php @@ -0,0 +1,23 @@ + + */ + +namespace WebDriver\Exception; + +use WebDriver\Exception as BaseException; + +/** + * WebDriver\Exception\NoSuchCookie class + * + * @package WebDriver + */ +final class NoSuchCookie extends BaseException +{ +} diff --git a/lib/WebDriver/Exception/NoSuchDriver.php b/lib/WebDriver/Exception/NoSuchDriver.php index 0f177da..8b8a5ff 100644 --- a/lib/WebDriver/Exception/NoSuchDriver.php +++ b/lib/WebDriver/Exception/NoSuchDriver.php @@ -1,22 +1,11 @@ * @author Anthon Pang */ diff --git a/lib/WebDriver/Exception/NoSuchElement.php b/lib/WebDriver/Exception/NoSuchElement.php index 83e957c..80d5468 100644 --- a/lib/WebDriver/Exception/NoSuchElement.php +++ b/lib/WebDriver/Exception/NoSuchElement.php @@ -1,22 +1,11 @@ * @author Anthon Pang */ diff --git a/lib/WebDriver/Exception/NoSuchFrame.php b/lib/WebDriver/Exception/NoSuchFrame.php index 47b1b51..c866708 100644 --- a/lib/WebDriver/Exception/NoSuchFrame.php +++ b/lib/WebDriver/Exception/NoSuchFrame.php @@ -1,22 +1,11 @@ * @author Anthon Pang */ diff --git a/lib/WebDriver/Exception/NoSuchShadowRoot.php b/lib/WebDriver/Exception/NoSuchShadowRoot.php new file mode 100644 index 0000000..56194ab --- /dev/null +++ b/lib/WebDriver/Exception/NoSuchShadowRoot.php @@ -0,0 +1,23 @@ + + */ + +namespace WebDriver\Exception; + +use WebDriver\Exception as BaseException; + +/** + * WebDriver\Exception\NoSuchShadowRoot class + * + * @package WebDriver + */ +final class NoSuchShadowRoot extends BaseException +{ +} diff --git a/lib/WebDriver/Exception/NoSuchWindow.php b/lib/WebDriver/Exception/NoSuchWindow.php index 3a971a8..8441def 100644 --- a/lib/WebDriver/Exception/NoSuchWindow.php +++ b/lib/WebDriver/Exception/NoSuchWindow.php @@ -1,22 +1,11 @@ * @author Anthon Pang */ diff --git a/lib/WebDriver/Exception/ObsoleteCommand.php b/lib/WebDriver/Exception/ObsoleteCommand.php index e841919..b125f74 100644 --- a/lib/WebDriver/Exception/ObsoleteCommand.php +++ b/lib/WebDriver/Exception/ObsoleteCommand.php @@ -1,22 +1,11 @@ * @author Anthon Pang */ diff --git a/lib/WebDriver/Exception/ScriptTimeout.php b/lib/WebDriver/Exception/ScriptTimeout.php index 2c8ba7e..c309b77 100644 --- a/lib/WebDriver/Exception/ScriptTimeout.php +++ b/lib/WebDriver/Exception/ScriptTimeout.php @@ -1,22 +1,11 @@ * @author Anthon Pang */ diff --git a/lib/WebDriver/Exception/SessionNotCreated.php b/lib/WebDriver/Exception/SessionNotCreated.php index d2c5c99..a6032a2 100644 --- a/lib/WebDriver/Exception/SessionNotCreated.php +++ b/lib/WebDriver/Exception/SessionNotCreated.php @@ -1,22 +1,11 @@ * @author Anthon Pang */ diff --git a/lib/WebDriver/Exception/StaleElementReference.php b/lib/WebDriver/Exception/StaleElementReference.php index 2c6f21a..7641b91 100644 --- a/lib/WebDriver/Exception/StaleElementReference.php +++ b/lib/WebDriver/Exception/StaleElementReference.php @@ -1,22 +1,11 @@ * @author Anthon Pang */ diff --git a/lib/WebDriver/Exception/Timeout.php b/lib/WebDriver/Exception/Timeout.php index 7c1b7ad..3b9067d 100644 --- a/lib/WebDriver/Exception/Timeout.php +++ b/lib/WebDriver/Exception/Timeout.php @@ -1,22 +1,11 @@ * @author Anthon Pang */ diff --git a/lib/WebDriver/Exception/UnableToCaptureScreen.php b/lib/WebDriver/Exception/UnableToCaptureScreen.php new file mode 100644 index 0000000..d8ee847 --- /dev/null +++ b/lib/WebDriver/Exception/UnableToCaptureScreen.php @@ -0,0 +1,23 @@ + + */ + +namespace WebDriver\Exception; + +use WebDriver\Exception as BaseException; + +/** + * WebDriver\Exception\UnableToCaptureScreen class + * + * @package WebDriver + */ +final class UnableToCaptureScreen extends BaseException +{ +} diff --git a/lib/WebDriver/Exception/UnableToSetCookie.php b/lib/WebDriver/Exception/UnableToSetCookie.php index 7fc6b11..e0ebaee 100644 --- a/lib/WebDriver/Exception/UnableToSetCookie.php +++ b/lib/WebDriver/Exception/UnableToSetCookie.php @@ -1,22 +1,11 @@ * @author Anthon Pang */ diff --git a/lib/WebDriver/Exception/UnexpectedAlertOpen.php b/lib/WebDriver/Exception/UnexpectedAlertOpen.php index 0ad6640..60dd3df 100644 --- a/lib/WebDriver/Exception/UnexpectedAlertOpen.php +++ b/lib/WebDriver/Exception/UnexpectedAlertOpen.php @@ -1,22 +1,11 @@ * @author Anthon Pang */ diff --git a/lib/WebDriver/Exception/UnexpectedParameters.php b/lib/WebDriver/Exception/UnexpectedParameters.php index a0c3f4e..6d3f4c6 100644 --- a/lib/WebDriver/Exception/UnexpectedParameters.php +++ b/lib/WebDriver/Exception/UnexpectedParameters.php @@ -1,22 +1,11 @@ * @author Anthon Pang */ diff --git a/lib/WebDriver/Exception/UnknownCommand.php b/lib/WebDriver/Exception/UnknownCommand.php index e03a746..f93f384 100644 --- a/lib/WebDriver/Exception/UnknownCommand.php +++ b/lib/WebDriver/Exception/UnknownCommand.php @@ -1,22 +1,11 @@ * @author Anthon Pang */ diff --git a/lib/WebDriver/Exception/UnknownError.php b/lib/WebDriver/Exception/UnknownError.php index 4773fd1..2e32b49 100644 --- a/lib/WebDriver/Exception/UnknownError.php +++ b/lib/WebDriver/Exception/UnknownError.php @@ -1,22 +1,11 @@ * @author Anthon Pang */ diff --git a/lib/WebDriver/Exception/UnknownLocatorStrategy.php b/lib/WebDriver/Exception/UnknownLocatorStrategy.php index bd0050e..ddff5ba 100644 --- a/lib/WebDriver/Exception/UnknownLocatorStrategy.php +++ b/lib/WebDriver/Exception/UnknownLocatorStrategy.php @@ -1,22 +1,11 @@ * @author Anthon Pang */ diff --git a/lib/WebDriver/Exception/UnknownMethod.php b/lib/WebDriver/Exception/UnknownMethod.php new file mode 100644 index 0000000..2e9f951 --- /dev/null +++ b/lib/WebDriver/Exception/UnknownMethod.php @@ -0,0 +1,23 @@ + + */ + +namespace WebDriver\Exception; + +use WebDriver\Exception as BaseException; + +/** + * WebDriver\Exception\UnknownMethod class + * + * @package WebDriver + */ +final class UnknownMethod extends BaseException +{ +} diff --git a/lib/WebDriver/Exception/UnsupportedOperation.php b/lib/WebDriver/Exception/UnsupportedOperation.php new file mode 100644 index 0000000..b551880 --- /dev/null +++ b/lib/WebDriver/Exception/UnsupportedOperation.php @@ -0,0 +1,23 @@ + + */ + +namespace WebDriver\Exception; + +use WebDriver\Exception as BaseException; + +/** + * WebDriver\Exception\UnsupportedOperation class + * + * @package WebDriver + */ +final class UnsupportedOperation extends BaseException +{ +} diff --git a/lib/WebDriver/Exception/XPathLookupError.php b/lib/WebDriver/Exception/XPathLookupError.php index 94110eb..bf6b2c2 100644 --- a/lib/WebDriver/Exception/XPathLookupError.php +++ b/lib/WebDriver/Exception/XPathLookupError.php @@ -1,22 +1,11 @@ * @author Anthon Pang */ From ed8f7741a0952db42686aae0780a0935138a7cf8 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Tue, 9 Aug 2022 10:24:21 -0400 Subject: [PATCH 26/44] improve readability when extracting from value/result --- lib/WebDriver/AbstractWebDriver.php | 95 +++++++++++++++-------------- 1 file changed, 50 insertions(+), 45 deletions(-) diff --git a/lib/WebDriver/AbstractWebDriver.php b/lib/WebDriver/AbstractWebDriver.php index 1270beb..3dcad8b 100644 --- a/lib/WebDriver/AbstractWebDriver.php +++ b/lib/WebDriver/AbstractWebDriver.php @@ -120,7 +120,7 @@ protected function curl($requestMethod, $command, $parameters = null, $extraOpti $url .= '/' . $parameters; } - $this->assertNonObjectParameters($parameters); + $this->assertSerializable($parameters); list($rawResult, $info) = ServiceFactory::getInstance()->getService('service.curl')->execute($requestMethod, $url, $parameters, $extraOptions); @@ -135,8 +135,8 @@ protected function curl($requestMethod, $command, $parameters = null, $extraOpti $result = json_decode($rawResult, true); - if (!empty($rawResult) && $result === null && json_last_error() != JSON_ERROR_NONE) { - // Legacy webdriver 4xx responses are to be considered // an error and return plaintext + if (! empty($rawResult) && $result === null && json_last_error() != JSON_ERROR_NONE) { + // Legacy webdriver 4xx responses are to be considered a plaintext error if ($httpCode >= 400 && $httpCode <= 499) { throw WebDriverException::factory( WebDriverException::CURL_EXEC, @@ -157,13 +157,9 @@ protected function curl($requestMethod, $command, $parameters = null, $extraOpti ); } - $value = (is_array($result) && array_key_exists('value', $result)) ? $result['value'] : null; - $message = (is_array($result) && array_key_exists('message', $result)) - ? $result['message'] - : ((is_array($value) && array_key_exists('message', $value)) ? $value['message'] : null); - $error = (is_array($result) && array_key_exists('error', $result)) - ? $result['error'] - : ((is_array($value) && array_key_exists('error', $value)) ? $value['error'] : null); + $value = $this->offsetGet('value', $result); + $message = $this->offsetGet('message', $result) ?: $this->offsetGet('message', $value); + $error = $this->offsetGet('error', $result) ?: $this->offsetGet('error', $value); // if not success, throw exception if (isset($result['status']) && (int) $result['status'] !== 0) { @@ -180,15 +176,9 @@ protected function curl($requestMethod, $command, $parameters = null, $extraOpti ); } - $sessionId = isset($result['sessionId']) - ? $result['sessionId'] - : (isset($value['sessionId']) - ? $value['sessionId'] - : (isset($value['webdriver.remote.sessionid']) - ? $value['webdriver.remote.sessionid'] - : null - ) - ); + $sessionId = $this->offsetGet('sessionId', $result) + ?: $this->offsetGet('sessionId', $value) + ?: $this->offsetGet('webdriver.remote.sessionid', $value); return array( 'value' => $value, @@ -198,32 +188,6 @@ protected function curl($requestMethod, $command, $parameters = null, $extraOpti ); } - /** - * @param mixed $parameters - */ - private function assertNonObjectParameters($parameters) - { - if ($parameters === null || is_scalar($parameters)) { - return; - } - - if (is_array($parameters)) { - foreach ($parameters as $value) { - $this->assertNonObjectParameters($value); - } - - return; - } - - throw WebDriverException::factory( - WebDriverException::UNEXPECTED_PARAMETERS, - sprintf( - "Unable to serialize non-scalar type %s", - is_object($parameters) ? get_class($parameters) : gettype($parameters) - ) - ); - } - /** * Magic method that maps calls to class methods to execute WebDriver commands * @@ -273,6 +237,47 @@ public function __call($name, $arguments) return $result['value']; } + /** + * Sanity check + * + * @param mixed $parameters + */ + private function assertSerializable($parameters) + { + if ($parameters === null || is_scalar($parameters)) { + return; + } + + if (is_array($parameters)) { + foreach ($parameters as $value) { + $this->assertSerializable($value); + } + + return; + } + + throw WebDriverException::factory( + WebDriverException::UNEXPECTED_PARAMETERS, + sprintf( + "Unable to serialize non-scalar type %s", + is_object($parameters) ? get_class($parameters) : gettype($parameters) + ) + ); + } + + /** + * Extract value from result + * + * @param string $key + * @param mixed $result + * + * @return string|null + */ + private function offsetGet($key, $result) + { + return (is_array($result) && array_key_exists($key, $result)) ? $result[$key] : null; + } + /** * Get default HTTP request method for a given WebDriver command * From a39a1f6dc0f4ddd8b2438fa5eb1f67755730d606 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Vo=C5=99=C3=AD=C5=A1ek?= Date: Sun, 23 Oct 2022 17:40:55 +0200 Subject: [PATCH 27/44] POST /window must define handle first for Selenium 3.x and Firefox --- lib/WebDriver/Session.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/WebDriver/Session.php b/lib/WebDriver/Session.php index f73e379..c6771e2 100644 --- a/lib/WebDriver/Session.php +++ b/lib/WebDriver/Session.php @@ -218,7 +218,7 @@ public function deleteCookie($cookieName) /** * window methods: /session/:sessionId/window (POST, DELETE) * - $session->window() - close current window - * - $session->window($name) - set focus + * - $session->window($window_handle) - set focus * - $session->window($window_handle)->method() - chaining * * @return \WebDriver\Window|\WebDriver\Session @@ -233,7 +233,7 @@ public function window() } // set focus - $arg = func_get_arg(0); // window handle or name attribute + $arg = func_get_arg(0); // window handle if (is_array($arg)) { $this->curl('POST', '/window', $arg); @@ -260,13 +260,13 @@ public function deleteWindow() /** * Set focus to window: /session/:sessionId/window (POST) * - * @param mixed $name window handler or name attribute + * @param mixed $name window handle * * @return \WebDriver\Session */ public function focusWindow($name) { - $this->curl('POST', '/window', array('name' => $name)); + $this->curl('POST', '/window', array('handle' => $name, 'name' => $name)); return $this; } From 4a0c79a62d8a81f53427788d99aa930855bf12b0 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Wed, 11 Jan 2023 16:03:52 -0500 Subject: [PATCH 28/44] Fix semantic change introduced by ed8f7741 --- lib/WebDriver/AbstractWebDriver.php | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/lib/WebDriver/AbstractWebDriver.php b/lib/WebDriver/AbstractWebDriver.php index 3dcad8b..dd9825f 100644 --- a/lib/WebDriver/AbstractWebDriver.php +++ b/lib/WebDriver/AbstractWebDriver.php @@ -158,8 +158,16 @@ protected function curl($requestMethod, $command, $parameters = null, $extraOpti } $value = $this->offsetGet('value', $result); - $message = $this->offsetGet('message', $result) ?: $this->offsetGet('message', $value); - $error = $this->offsetGet('error', $result) ?: $this->offsetGet('error', $value); + $message = $this->offsetGet('message', $result); + $error = $this->offsetGet('error', $result); + + if ($message === null) { + $message = $this->offsetGet('message', $value); + } + + if ($error === null) { + $error = $this->offsetGet('error', $value); + } // if not success, throw exception if (isset($result['status']) && (int) $result['status'] !== 0) { From e334e5ebc9d4867f0342d2a98806d74aeccc7ca8 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Wed, 11 Jan 2023 16:22:13 -0500 Subject: [PATCH 29/44] tweaking error handling (again) --- lib/WebDriver/AbstractWebDriver.php | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/lib/WebDriver/AbstractWebDriver.php b/lib/WebDriver/AbstractWebDriver.php index dd9825f..d3b57da 100644 --- a/lib/WebDriver/AbstractWebDriver.php +++ b/lib/WebDriver/AbstractWebDriver.php @@ -157,18 +157,12 @@ protected function curl($requestMethod, $command, $parameters = null, $extraOpti ); } - $value = $this->offsetGet('value', $result); - $message = $this->offsetGet('message', $result); - $error = $this->offsetGet('error', $result); + $value = $this->offsetGet('value', $result); - if ($message === null) { + if (($message = $this->offsetGet('message', $result)) === null) { $message = $this->offsetGet('message', $value); } - if ($error === null) { - $error = $this->offsetGet('error', $value); - } - // if not success, throw exception if (isset($result['status']) && (int) $result['status'] !== 0) { throw WebDriverException::factory( @@ -177,6 +171,10 @@ protected function curl($requestMethod, $command, $parameters = null, $extraOpti ); } + if (($error = $this->offsetGet('error', $result)) === null) { + $error = $this->offsetGet('error', $value); + } + if (isset($error)) { throw WebDriverException::factory( $error, From 7b8bf3406aa57a6dfd4559380f463dc8fee22052 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Mon, 4 Dec 2023 08:55:00 -0500 Subject: [PATCH 30/44] charset is case-insensitive in Content-Type but we downcase here for consistency with our other codebases; at the same time, remove charset from Accept header as it isn't widely supported --- lib/WebDriver/Service/CurlService.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/WebDriver/Service/CurlService.php b/lib/WebDriver/Service/CurlService.php index 499dc49..1b77197 100755 --- a/lib/WebDriver/Service/CurlService.php +++ b/lib/WebDriver/Service/CurlService.php @@ -38,8 +38,8 @@ class CurlService implements CurlServiceInterface public function execute($requestMethod, $url, $parameters = null, $extraOptions = array()) { $customHeaders = array( - 'Content-Type: application/json;charset=UTF-8', - 'Accept: application/json;charset=UTF-8', + 'Content-Type: application/json;charset=utf-8', + 'Accept: application/json', ); $curl = curl_init($url); From 5dd967824b5c86ae79a328111b514f97ab604f49 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Mon, 4 Dec 2023 13:32:41 -0500 Subject: [PATCH 31/44] clean-up Browser consts --- lib/WebDriver/Browser.php | 41 ++++++++++++++++++++++----------------- 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/lib/WebDriver/Browser.php b/lib/WebDriver/Browser.php index d23c308..3da4da3 100644 --- a/lib/WebDriver/Browser.php +++ b/lib/WebDriver/Browser.php @@ -30,23 +30,28 @@ final class Browser { /** - * Check browser names used in static functions in the selenium source: - * @see http://code.google.com/p/selenium/source/browse/java/client/src/org/openqa/selenium/remote/DesiredCapabilities.java - * - * Note: Capability array takes these browserNames and not the "browserTypes" - * - * Also check - * @see http://code.google.com/p/selenium/wiki/JsonWireProtocol#Capabilities_JSON_Object + * @see https://github.com/SeleniumHQ/selenium/blob/trunk/java/src/org/openqa/selenium/remote/Browser.java */ - const ANDROID = 'android'; - const CHROME = 'chrome'; - const FIREFOX = 'firefox'; - const HTMLUNIT = 'htmlunit'; - const IE = 'internet explorer'; - const INTERNET_EXPLORER = 'internet explorer'; - const IPHONE = 'iPhone'; - const IPAD = 'iPad'; - const OPERA = 'opera'; - const PHANTOMJS = 'phantomjs'; - const SAFARI = 'safari'; + const CHROME = 'chrome'; + const EDGE = 'MicrosoftEdge'; + const FIREFOX = 'firefox'; + const HTMLUNIT = 'htmlunit'; + const IE = 'internet explorer'; + const OPERA = 'opera'; + const SAFARI = 'safari'; + const SAFARI_TECH_PREVIEW = 'Safari Technology Preview'; + + /** + * @see https://github.com/SeleniumHQ/selenium/blob/trunk/javascript/webdriver/capabilities.js + * @deprecated + */ + const ANDROID = 'android'; + const EDGEHTML = 'EdgeHTML'; + const INTERNET_EXPLORER = 'internet explorer'; + const IPAD = 'iPad'; + const IPHONE = 'iPhone'; + const MSEDGE = 'MicrosoftEdge'; + const OPERA_BLINK = 'operablink'; + const PHANTOM_JS = 'phantomjs'; + const PHANTOMJS = 'phantomjs'; } From fff9a8ed9c874face17e541c73e070f0c0571683 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Mon, 4 Dec 2023 15:07:40 -0500 Subject: [PATCH 32/44] Element::css() works differently --- lib/WebDriver/Element.php | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/lib/WebDriver/Element.php b/lib/WebDriver/Element.php index 4989437..0ec21bd 100644 --- a/lib/WebDriver/Element.php +++ b/lib/WebDriver/Element.php @@ -42,7 +42,6 @@ * @method array location() Determine an element's location on the page. * @method array location_in_view() Determine an element's location on the screen once it has been scrolled into view. * @method array size() Determine an element's size in pixels. - * @method string css($propertyName) Query the value of an element's computed CSS property. */ final class Element extends Container { @@ -66,7 +65,6 @@ protected function methods() 'location' => array('GET'), 'location_in_view' => array('GET'), 'size' => array('GET'), - 'css' => array('GET'), ); } @@ -114,6 +112,20 @@ public function getID() return $this->id; } + /** + * Query the value of an element’s computed CSS property: /session/:sessionId/element/:id/css/:propertyName + * + * @param string $propertyName + * + * @return mixed + */ + public function css($propertyName) + { + $result = $this->curl('GET', "/css/$propertyName"); + + return $result['value']; + } + /** * {@inheritdoc} */ From 520d22b654c04b3d58136e1c966ffac42cead685 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Mon, 4 Dec 2023 16:17:26 -0500 Subject: [PATCH 33/44] fixes #122 - translate empty params in POST --- lib/WebDriver/Service/CurlService.php | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/lib/WebDriver/Service/CurlService.php b/lib/WebDriver/Service/CurlService.php index 1b77197..dbae4ec 100755 --- a/lib/WebDriver/Service/CurlService.php +++ b/lib/WebDriver/Service/CurlService.php @@ -50,16 +50,12 @@ public function execute($requestMethod, $url, $parameters = null, $extraOptions break; case 'POST': - if ($parameters && is_array($parameters)) { - curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($parameters)); - } else { - $customHeaders[] = 'Content-Length: 0'; - - // Suppress "Transfer-Encoding: chunked" header automatically added by cURL that - // causes a 400 bad request (bad content-length). - $customHeaders[] = 'Transfer-Encoding:'; + if ( ! $parameters || ! is_array($parameters)) { + $parameters = array(); } + curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($parameters)); + // Suppress "Expect: 100-continue" header automatically added by cURL that // causes a 1 second delay if the remote server does not support Expect. $customHeaders[] = 'Expect:'; @@ -72,16 +68,12 @@ public function execute($requestMethod, $url, $parameters = null, $extraOptions break; case 'PUT': - if ($parameters && is_array($parameters)) { - curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($parameters)); - } else { - $customHeaders[] = 'Content-Length: 0'; - - // Suppress "Transfer-Encoding: chunked" header automatically added by cURL that - // causes a 400 bad request (bad content-length). - $customHeaders[] = 'Transfer-Encoding:'; + if ( ! $parameters || ! is_array($parameters)) { + $parameters = array(); } + curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($parameters)); + // Suppress "Expect: 100-continue" header automatically added by cURL that // causes a 1 second delay if the remote server does not support Expect. $customHeaders[] = 'Expect:'; From 4ac07095d7f99714c6d47b4cd5f7a9cd2ac9b8a9 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Mon, 4 Dec 2023 17:34:17 -0500 Subject: [PATCH 34/44] Element::attribute() works differently --- lib/WebDriver/Element.php | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/lib/WebDriver/Element.php b/lib/WebDriver/Element.php index 0ec21bd..da15ddf 100644 --- a/lib/WebDriver/Element.php +++ b/lib/WebDriver/Element.php @@ -36,7 +36,6 @@ * @method void clear() Clear a TEXTAREA or text INPUT element's value. * @method boolean selected() Determine if an OPTION element, or an INPUT element of type checkbox or radiobutton is currently selected. * @method boolean enabled() Determine if an element is currently enabled. - * @method string attribute($attributeName) Get the value of an element's attribute. * @method boolean equals($otherId) Test if two element IDs refer to the same DOM element. * @method boolean displayed() Determine if an element is currently displayed. * @method array location() Determine an element's location on the page. @@ -59,7 +58,6 @@ protected function methods() 'clear' => array('POST'), 'selected' => array('GET'), 'enabled' => array('GET'), - 'attribute' => array('GET'), 'equals' => array('GET'), 'displayed' => array('GET'), 'location' => array('GET'), @@ -112,6 +110,20 @@ public function getID() return $this->id; } + /** + * Get the value of an element's attribute: /session/:sessionId/element/:id/attribute/:name + * + * @param string name + * + * @return mixed + */ + public function attribute($name) + { + $result = $this->curl('GET', "/attribute/$name"); + + return $result['value']; + } + /** * Query the value of an element’s computed CSS property: /session/:sessionId/element/:id/css/:propertyName * From 9e6001886e6c3c9ac2939fa529309f69f08e2500 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Wed, 6 Dec 2023 08:42:44 -0500 Subject: [PATCH 35/44] fix empty POST/PUT body in 520d22b6 --- lib/WebDriver/Service/CurlService.php | 27 ++++++++------------------- 1 file changed, 8 insertions(+), 19 deletions(-) diff --git a/lib/WebDriver/Service/CurlService.php b/lib/WebDriver/Service/CurlService.php index dbae4ec..4c5fa1f 100755 --- a/lib/WebDriver/Service/CurlService.php +++ b/lib/WebDriver/Service/CurlService.php @@ -50,36 +50,25 @@ public function execute($requestMethod, $url, $parameters = null, $extraOptions break; case 'POST': - if ( ! $parameters || ! is_array($parameters)) { - $parameters = array(); - } + case 'PUT': + $parameters = ! $parameters || ! is_array($parameters) + ? '{}' + : json_encode($parameters); - curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($parameters)); + curl_setopt($curl, CURLOPT_POSTFIELDS, $parameters); // Suppress "Expect: 100-continue" header automatically added by cURL that // causes a 1 second delay if the remote server does not support Expect. $customHeaders[] = 'Expect:'; - curl_setopt($curl, CURLOPT_POST, true); + $requestMethod === 'POST' + ? curl_setopt($curl, CURLOPT_POST, true) + : curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'PUT'); break; case 'DELETE': curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'DELETE'); break; - - case 'PUT': - if ( ! $parameters || ! is_array($parameters)) { - $parameters = array(); - } - - curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($parameters)); - - // Suppress "Expect: 100-continue" header automatically added by cURL that - // causes a 1 second delay if the remote server does not support Expect. - $customHeaders[] = 'Expect:'; - - curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'PUT'); - break; } foreach ($extraOptions as $option => $value) { From b756195047b5e12e38b4cc6ef9add71bbd768d86 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Thu, 7 Dec 2023 17:44:55 -0500 Subject: [PATCH 36/44] fix phpstan errors --- lib/WebDriver/Element.php | 2 +- lib/WebDriver/Session.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/WebDriver/Element.php b/lib/WebDriver/Element.php index da15ddf..5a62590 100644 --- a/lib/WebDriver/Element.php +++ b/lib/WebDriver/Element.php @@ -113,7 +113,7 @@ public function getID() /** * Get the value of an element's attribute: /session/:sessionId/element/:id/attribute/:name * - * @param string name + * @param string $name * * @return mixed */ diff --git a/lib/WebDriver/Session.php b/lib/WebDriver/Session.php index c6771e2..aecb4f5 100644 --- a/lib/WebDriver/Session.php +++ b/lib/WebDriver/Session.php @@ -134,7 +134,7 @@ public function open($url) */ public function capabilities() { - if (! isset($this->capabilities)) { + if ($this->capabilities === null) { $result = $this->curl('GET', ''); $this->capabilities = $result['value']; From 6ca447e519517164aa457f6cfa834061ee26a591 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Thu, 7 Dec 2023 17:45:53 -0500 Subject: [PATCH 37/44] moveto shim to transform ScriptTimeout exception to UnknownError --- lib/WebDriver/Session.php | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/lib/WebDriver/Session.php b/lib/WebDriver/Session.php index aecb4f5..92c9b16 100644 --- a/lib/WebDriver/Session.php +++ b/lib/WebDriver/Session.php @@ -22,6 +22,8 @@ namespace WebDriver; +use WebDriver\Exception as WebDriverException; + /** * WebDriver\Session class * @@ -292,6 +294,24 @@ public function frame() return new Frame($this->url . '/frame'); } + /** + * moveto: /session/:sessionId/moveto (POST) + * + * @param array $parameters + * + * @return mixed + */ + public function moveto($parameters) + { + try { + $result = $this->curl('POST', '/moveto', $parameters); + } catch (WebDriverException\ScriptTimeout) { + throw WebDriverException::factory(WebDriverException::UNKNOWN_ERROR); + } + + return $result['value']; + } + /** * timeouts methods: /session/:sessionId/timeouts (POST) * - $session->timeouts($json) - set timeout for an operation From a61a8459f86c79dd1f19934ea3929804f2e41f8c Mon Sep 17 00:00:00 2001 From: Phil Davis Date: Fri, 8 Dec 2023 12:45:15 +0545 Subject: [PATCH 38/44] fix: catch ScriptTimeout exception into a variable --- lib/WebDriver/Session.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/WebDriver/Session.php b/lib/WebDriver/Session.php index 92c9b16..37ecff8 100644 --- a/lib/WebDriver/Session.php +++ b/lib/WebDriver/Session.php @@ -48,7 +48,6 @@ * @method void postAlert_text($jsonText) Sends keystrokes to a JavaScript prompt() dialog. * @method void accept_alert() Accepts the currently displayed alert dialog. * @method void dismiss_alert() Dismisses the currently displayed alert dialog. - * @method void moveto($jsonCoordinates) Move the mouse by an offset of the specified element (or current mouse cursor). * @method void click($jsonButton) Click any mouse button (at the coordinates set by the last moveto command). * @method void buttondown() Click and hold the left mouse button (at the coordinates set by the last moveto command). * @method void buttonup() Releases the mouse button previously held (where the mouse is currently at). @@ -305,7 +304,7 @@ public function moveto($parameters) { try { $result = $this->curl('POST', '/moveto', $parameters); - } catch (WebDriverException\ScriptTimeout) { + } catch (WebDriverException\ScriptTimeout $e) { throw WebDriverException::factory(WebDriverException::UNKNOWN_ERROR); } From 82b1947a82000500800b0a2012f7b1c8dc9c630e Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Fri, 8 Dec 2023 09:22:36 -0500 Subject: [PATCH 39/44] fix php 5.3 incompatibility --- lib/WebDriver/Session.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/WebDriver/Session.php b/lib/WebDriver/Session.php index 37ecff8..f8c4506 100644 --- a/lib/WebDriver/Session.php +++ b/lib/WebDriver/Session.php @@ -498,10 +498,10 @@ private function serializeArguments(array $arguments) // preferably we want to detect W3C support and never set nor parse // LEGACY_ELEMENT_ID, until detection is implemented, serialize to both // variants, tested with Selenium v2.53.1 and v3.141.59 - $arguments[$key] = [ + $arguments[$key] = array( Container::WEBDRIVER_ELEMENT_ID => $value->getID(), Container::LEGACY_ELEMENT_ID => $value->getID(), - ]; + ); continue; } From 55ac666f870994762451675c37b0f3009b2e54ab Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Fri, 8 Dec 2023 09:32:01 -0500 Subject: [PATCH 40/44] CurlService should set its headers first so user's extraOptions can override --- lib/WebDriver/Service/CurlService.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/WebDriver/Service/CurlService.php b/lib/WebDriver/Service/CurlService.php index 4c5fa1f..1462fb5 100755 --- a/lib/WebDriver/Service/CurlService.php +++ b/lib/WebDriver/Service/CurlService.php @@ -51,8 +51,8 @@ public function execute($requestMethod, $url, $parameters = null, $extraOptions case 'POST': case 'PUT': - $parameters = ! $parameters || ! is_array($parameters) - ? '{}' + $parameters = ! $parameters || ! is_array($parameters) + ? '{}' // instead of json_encode(new \stdclass)) : json_encode($parameters); curl_setopt($curl, CURLOPT_POSTFIELDS, $parameters); @@ -71,12 +71,12 @@ public function execute($requestMethod, $url, $parameters = null, $extraOptions break; } + curl_setopt($curl, CURLOPT_HTTPHEADER, $customHeaders); + foreach ($extraOptions as $option => $value) { curl_setopt($curl, $option, $value); } - curl_setopt($curl, CURLOPT_HTTPHEADER, $customHeaders); - $rawResult = trim(curl_exec($curl)); $info = curl_getinfo($curl); From 81930949a4bfb63a7a3218fff0606818d36b05e7 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Sat, 9 Dec 2023 15:12:11 -0500 Subject: [PATCH 41/44] methods expected to be in an array --- lib/WebDriver/WebDriver.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/WebDriver/WebDriver.php b/lib/WebDriver/WebDriver.php index b3cd9b5..81c8531 100644 --- a/lib/WebDriver/WebDriver.php +++ b/lib/WebDriver/WebDriver.php @@ -37,7 +37,7 @@ class WebDriver extends AbstractWebDriver implements WebDriverInterface protected function methods() { return array( - 'status' => 'GET', + 'status' => array('GET'), ); } From bac541ca487175e8fddf1da9571fed2368ea178c Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 23 Feb 2024 16:33:21 +0200 Subject: [PATCH 42/44] Load Mink Selenium 2 Driver package from source to have tests --- .github/workflows/ci.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bcc8dd4..65593b7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,10 +30,12 @@ jobs: - name: Install dependencies run: | composer config version 1.4.99 + composer config preferred-install.behat/mink-selenium2-driver source + composer config preferred-install.* dist composer require --no-update behat/mink-selenium2-driver:dev-master --dev --quiet composer require --no-update mink/driver-testsuite:dev-master --dev --quiet php -r '$json = json_decode(file_get_contents ("composer.json"), true); $json["autoload"]["psr-4"]["Behat\\Mink\\Tests\\Driver\\"] = "vendor/behat/mink-selenium2-driver/tests/"; file_put_contents("composer.json", json_encode($json, JSON_UNESCAPED_SLASHES|JSON_PRETTY_PRINT));' - composer update --no-interaction --prefer-dist + composer update --no-interaction - name: Start Selenium & Mink test server run: | From b5f917203d27cf34d7a576c950b27b273746f663 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 23 Feb 2024 11:07:56 +0200 Subject: [PATCH 43/44] Allow frame switching to WebElement --- lib/WebDriver/Session.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/WebDriver/Session.php b/lib/WebDriver/Session.php index f8c4506..c10e855 100644 --- a/lib/WebDriver/Session.php +++ b/lib/WebDriver/Session.php @@ -282,7 +282,7 @@ public function focusWindow($name) public function frame() { if (func_num_args() === 1) { - $arg = func_get_arg(0); // json + $arg = $this->serializeArguments(func_get_arg(0)); // json $this->curl('POST', '/frame', $arg); From 3b2a2ddc4e0a690cc691d7e5952964cc4b9538b1 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Mon, 18 Mar 2024 21:58:53 -0400 Subject: [PATCH 44/44] ci cosmetic: remove trailing whitespace --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 65593b7..a0ea0ab 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,7 +31,7 @@ jobs: run: | composer config version 1.4.99 composer config preferred-install.behat/mink-selenium2-driver source - composer config preferred-install.* dist + composer config preferred-install.* dist composer require --no-update behat/mink-selenium2-driver:dev-master --dev --quiet composer require --no-update mink/driver-testsuite:dev-master --dev --quiet php -r '$json = json_decode(file_get_contents ("composer.json"), true); $json["autoload"]["psr-4"]["Behat\\Mink\\Tests\\Driver\\"] = "vendor/behat/mink-selenium2-driver/tests/"; file_put_contents("composer.json", json_encode($json, JSON_UNESCAPED_SLASHES|JSON_PRETTY_PRINT));'