clean install

This commit is contained in:
2024-12-10 15:08:16 +01:00
commit e14eb2d8fd
31193 changed files with 3555714 additions and 0 deletions

View File

@@ -0,0 +1,311 @@
<?php
/**
* Copyright 2004-2017 Facebook. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* @package WebDriver
*
* @author Justin Bishop <jubishop@gmail.com>
* @author Anthon Pang <apang@softwaredevelopment.ca>
* @author Fabrizio Branca <mail@fabrizio-branca.de>
* @author Tsz Ming Wong <tszming@gmail.com>
*/
namespace WebDriver;
use WebDriver\Exception as WebDriverException;
/**
* Abstract WebDriver\AbstractWebDriver class
*
* @package WebDriver
*/
abstract class AbstractWebDriver
{
/**
* URL
*
* @var string
*/
protected $url;
/**
* Return array of supported method names and corresponding HTTP request methods
*
* @return array
*/
abstract protected function methods();
/**
* Return array of obsolete method names and corresponding HTTP request methods
*
* @return array
*/
protected function obsoleteMethods()
{
return array();
}
/**
* Constructor
*
* @param string $url URL to Selenium server
*/
public function __construct($url = 'http://localhost:4444/wd/hub')
{
$this->url = $url;
}
/**
* Magic method which returns URL to Selenium server
*
* @return string
*/
public function __toString()
{
return $this->url;
}
/**
* Returns URL to Selenium server
*
* @return string
*/
public function getURL()
{
return $this->url;
}
/**
* 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|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' => ...)
*
* @throws \WebDriver\Exception if error
*/
protected function curl($requestMethod, $command, $parameters = null, $extraOptions = array())
{
if ($parameters && is_array($parameters) && $requestMethod !== 'POST') {
throw WebDriverException::factory(
WebDriverException::NO_PARAMETERS_EXPECTED,
sprintf(
'The http request method called for %s is %s but it has to be POST if you want to pass the JSON parameters %s',
$command,
$requestMethod,
json_encode($parameters)
)
);
}
$url = sprintf('%s%s', $this->url, $command);
if ($parameters && (is_int($parameters) || is_string($parameters))) {
$url .= '/' . $parameters;
}
$this->assertSerializable($parameters);
list($rawResult, $info) = ServiceFactory::getInstance()->getService('service.curl')->execute($requestMethod, $url, $parameters, $extraOptions);
$httpCode = $info['http_code'];
if ($httpCode === 0) {
throw WebDriverException::factory(
WebDriverException::CURL_EXEC,
$info['error']
);
}
$result = json_decode($rawResult, true);
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,
'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)
);
}
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)
);
}
$value = $this->offsetGet('value', $result);
if (($message = $this->offsetGet('message', $result)) === null) {
$message = $this->offsetGet('message', $value);
}
// if not success, throw exception
if (isset($result['status']) && (int) $result['status'] !== 0) {
throw WebDriverException::factory(
$result['status'],
$message
);
}
if (($error = $this->offsetGet('error', $result)) === null) {
$error = $this->offsetGet('error', $value);
}
if (isset($error)) {
throw WebDriverException::factory(
$error,
$message
);
}
$sessionId = $this->offsetGet('sessionId', $result)
?: $this->offsetGet('sessionId', $value)
?: $this->offsetGet('webdriver.remote.sessionid', $value);
return array(
'value' => $value,
'info' => $info,
'sessionId' => $sessionId,
'sessionUrl' => $sessionId ? $this->url . '/session/' . $sessionId : $info['url'],
);
}
/**
* Magic method that maps calls to class methods to execute WebDriver commands
*
* @param string $name Method name
* @param array $arguments Arguments
*
* @return mixed
*
* @throws \WebDriver\Exception if invalid WebDriver command
*/
public function __call($name, $arguments)
{
if (count($arguments) > 1) {
throw WebDriverException::factory(
WebDriverException::JSON_PARAMETERS_EXPECTED,
'Commands should have at most only one parameter, which should be the JSON Parameter object'
);
}
if (preg_match('/^(get|post|delete)/', $name, $matches)) {
$requestMethod = strtoupper($matches[0]);
$webdriverCommand = strtolower(substr($name, strlen($requestMethod)));
} else {
$webdriverCommand = $name;
$requestMethod = $this->getRequestMethod($webdriverCommand);
}
$methods = $this->methods();
if (!in_array($requestMethod, (array) $methods[$webdriverCommand])) {
throw WebDriverException::factory(
WebDriverException::INVALID_REQUEST,
sprintf(
'%s is not an available http request method for the command %s.',
$requestMethod,
$webdriverCommand
)
);
}
$result = $this->curl(
$requestMethod,
'/' . $webdriverCommand,
array_shift($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
*
* @param string $webdriverCommand
*
* @return string
*
* @throws \WebDriver\Exception if invalid WebDriver command
*/
private function getRequestMethod($webdriverCommand)
{
if (!array_key_exists($webdriverCommand, $this->methods())) {
throw WebDriverException::factory(
array_key_exists($webdriverCommand, $this->obsoleteMethods())
? WebDriverException::OBSOLETE_COMMAND : WebDriverException::UNKNOWN_COMMAND,
sprintf('%s is not a valid WebDriver command.', $webdriverCommand)
);
}
$methods = $this->methods();
$requestMethods = (array) $methods[$webdriverCommand];
return array_shift($requestMethods);
}
}

View File

@@ -0,0 +1,42 @@
<?php
/**
* Copyright 2012-2017 Anthon Pang. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* @package WebDriver
*
* @author Anthon Pang <apang@softwaredevelopment.ca>
*/
namespace WebDriver;
/**
* WebDriver\AppCacheStatus class
*
* @package WebDriver
*/
final class AppCacheStatus
{
/**
* Application cache status
*
* @see https://code.google.com/p/selenium/source/browse/java/client/src/org/openqa/selenium/html5/AppCacheStatus.java
*/
const UNCACHED = 0;
const IDLE = 1;
const CHECKING = 2;
const DOWNLOADING = 3;
const UPDATE_READY = 4;
const OBSOLETE = 5;
}

View File

@@ -0,0 +1,42 @@
<?php
/**
* Copyright 2012-2017 Anthon Pang. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* @package WebDriver
*
* @author Anthon Pang <apang@softwaredevelopment.ca>
*/
namespace WebDriver;
/**
* WebDriver\ApplicationCache class
*
* @package WebDriver
*
* @method integer status() Get application cache status.
*/
final class ApplicationCache extends AbstractWebDriver
{
/**
* {@inheritdoc}
*/
protected function methods()
{
return array(
'status' => array('GET'),
);
}
}

View File

@@ -0,0 +1,57 @@
<?php
/**
* Copyright 2011-2017 Fabrizio Branca. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* @package WebDriver
*
* @author Fabrizio Branca <mail@fabrizio-branca.de>
* @author Anthon Pang <apang@softwaredevelopment.ca>
*/
namespace WebDriver;
/**
* WebDriver\Browser class
*
* @package WebDriver
*/
final class Browser
{
/**
* @see https://github.com/SeleniumHQ/selenium/blob/trunk/java/src/org/openqa/selenium/remote/Browser.java
*/
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';
}

View File

@@ -0,0 +1,67 @@
<?php
/**
* Copyright 2011-2017 Fabrizio Branca. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* @package WebDriver
*
* @author Fabrizio Branca <mail@fabrizio-branca.de>
* @author Anthon Pang <apang@softwaredevelopment.ca>
*/
namespace WebDriver;
/**
* WebDriver\Capability class
*
* @package WebDriver
*/
class Capability
{
/**
* Desired capabilities
*
* @see http://code.google.com/p/selenium/source/browse/trunk/java/client/src/org/openqa/selenium/remote/CapabilityType.java
* @see http://code.google.com/p/selenium/wiki/JsonWireProtocol#Capabilities_JSON_Object
*/
const BROWSER_NAME = 'browserName';
const VERSION = 'version';
const PLATFORM = 'platform';
const JAVASCRIPT_ENABLED = 'javascriptEnabled';
const TAKES_SCREENSHOT = 'takesScreenshot';
const HANDLES_ALERTS = 'handlesAlerts';
const DATABASE_ENABLED = 'databaseEnabled';
const LOCATION_CONTEXT_ENABLED = 'locationContextEnabled';
const APPLICATION_CACHE_ENABLED = 'applicationCacheEnabled';
const BROWSER_CONNECTION_ENABLED = 'browserConnectionEnabled';
const CSS_SELECTORS_ENABLED = 'cssSelectorsEnabled';
const WEB_STORAGE_ENABLED = 'webStorageEnabled';
const ROTATABLE = 'rotatable';
const ACCEPT_SSL_CERTS = 'acceptSslCerts';
const NATIVE_EVENTS = 'nativeEvents';
const PROXY = 'proxy';
const UNEXPECTED_ALERT_BEHAVIOUR = 'unexpectedAlertBehaviour';
const ELEMENT_SCROLL_BEHAVIOR = 'elementScrollBehavior';
/**
* Proxy types
*
* @see http://code.google.com/p/selenium/wiki/JsonWireProtocol#Proxy_JSON_Object
*/
const DIRECT = 'direct';
const MANUAL = 'manual';
const PAC = 'pac';
const AUTODETECT = 'autodetect';
const SYSTEM = 'system';
}

View File

@@ -0,0 +1,87 @@
<?php
/**
* Copyright 2012-2017 Anthon Pang. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* @package WebDriver
*
* @author Anthon Pang <apang@softwaredevelopment.ca>
*/
namespace WebDriver;
/**
* WebDriver\ClassLoader (autoloader) class
*
* @package WebDriver
*
* @deprecated
*/
final class ClassLoader
{
/**
* Load class
*
* @param string $class Class name
*/
public static function loadClass($class)
{
$file = strpos($class, '\\') !== false
? str_replace('\\', DIRECTORY_SEPARATOR, $class)
: str_replace('_', DIRECTORY_SEPARATOR, $class);
$path = dirname(__DIR__) . DIRECTORY_SEPARATOR . $file . '.php';
if (file_exists($path)) {
include_once $path;
}
}
/**
* Autoloader
*
* @param string $class Class name
*/
public static function autoload($class)
{
try {
self::loadClass($class);
} catch (\Exception $e) {
}
}
}
if (function_exists('spl_autoload_register')) {
/**
* use the SPL autoload stack
*/
spl_autoload_register(array('WebDriver\ClassLoader', 'autoload'));
/**
* preserve any existing __autoload
*/
if (function_exists('__autoload')) {
spl_autoload_register('__autoload');
}
} else {
/**
* Our fallback; only one __autoload per PHP instance
*
* @param string $class Class name
*/
function __autoload($class)
{
ClassLoader::autoload($class);
}
}

View File

@@ -0,0 +1,252 @@
<?php
/**
* Copyright 2004-2017 Facebook. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* @package WebDriver
*
* @author Justin Bishop <jubishop@gmail.com>
* @author Anthon Pang <apang@softwaredevelopment.ca>
* @author Fabrizio Branca <mail@fabrizio-branca.de>
*/
namespace WebDriver;
use WebDriver\Exception as WebDriverException;
/**
* Abstract WebDriver\Container class
*
* @package WebDriver
*/
abstract class Container extends AbstractWebDriver
{
const LEGACY_ELEMENT_ID = 'ELEMENT';
const WEBDRIVER_ELEMENT_ID = 'element-6066-11e4-a52e-4f735466cecf';
/**
* @var array
*/
private $strategies;
/**
* {@inheritdoc}
*/
public function __construct($url)
{
parent::__construct($url);
$locatorStrategy = new \ReflectionClass('WebDriver\LocatorStrategy');
$this->strategies = $locatorStrategy->getConstants();
}
/**
* Find element: /session/:sessionId/element (POST)
* Find child element: /session/:sessionId/element/:id/element (POST)
* Search for element on page, starting from the document root.
*
* @param string $using the locator strategy to use
* @param string $value the search target
*
* @return \WebDriver\Element
*
* @throws \WebDriver\Exception if element not found, or invalid XPath
*/
public function element($using = null, $value = null)
{
$locatorJson = $this->parseArgs('element', func_get_args());
try {
$result = $this->curl(
'POST',
'/element',
$locatorJson
);
} catch (WebDriverException\NoSuchElement $e) {
throw WebDriverException::factory(
WebDriverException::NO_SUCH_ELEMENT,
sprintf(
"Element not found with %s, %s\n\n%s",
$locatorJson['using'],
$locatorJson['value'],
$e->getMessage()
),
$e
);
}
$element = $this->webDriverElement($result['value']);
if ($element === null) {
throw WebDriverException::factory(
WebDriverException::NO_SUCH_ELEMENT,
sprintf(
"Element not found with %s, %s\n",
$locatorJson['using'],
$locatorJson['value']
)
);
}
return $element;
}
/**
* Find elements: /session/:sessionId/elements (POST)
* Find child elements: /session/:sessionId/element/:id/elements (POST)
* Search for multiple elements on page, starting from the document root.
*
* @param string $using the locator strategy to use
* @param string $value the search target
*
* @return array
*
* @throws \WebDriver\Exception if invalid XPath
*/
public function elements($using = null, $value = null)
{
$locatorJson = $this->parseArgs('elements', func_get_args());
$result = $this->curl(
'POST',
'/elements',
$locatorJson
);
if (!is_array($result['value'])) {
return array();
}
return array_filter(
array_map(
array($this, 'webDriverElement'),
$result['value']
)
);
}
/**
* Parse arguments allowing either separate $using and $value parameters, or
* as an array containing the JSON parameters
*
* @param string $method method name
* @param array $argv arguments
*
* @return array
*
* @throws \WebDriver\Exception if invalid number of arguments to the called method
*/
private function parseArgs($method, $argv)
{
$argc = count($argv);
switch ($argc) {
case 2:
$using = $argv[0];
$value = $argv[1];
break;
case 1:
$arg = $argv[0];
if (is_array($arg)) {
$using = $arg['using'];
$value = $arg['value'];
break;
}
// fall through
default:
throw WebDriverException::factory(
WebDriverException::JSON_PARAMETERS_EXPECTED,
sprintf('Invalid arguments to %s method: %s', $method, print_r($argv, true))
);
}
return $this->locate($using, $value);
}
/**
* Return JSON parameter for element / elements command
*
* @param string $using locator strategy
* @param string $value search target
*
* @return array
*
* @throws \WebDriver\Exception if invalid locator strategy
*/
public function locate($using, $value)
{
if (!in_array($using, $this->strategies)) {
throw WebDriverException::factory(
WebDriverException::UNKNOWN_LOCATOR_STRATEGY,
sprintf('Invalid locator strategy %s', $using)
);
}
return array(
'using' => $using,
'value' => $value,
);
}
/**
* Return WebDriver\Element wrapper for $value
*
* @param mixed $value
*
* @return \WebDriver\Element|null
*/
protected function webDriverElement($value)
{
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
);
}
return null;
}
/**
* {@inheritdoc}
*/
public function __call($name, $arguments)
{
if (count($arguments) === 1 && in_array(str_replace('_', ' ', $name), $this->strategies)) {
return $this->locate($name, $arguments[0]);
}
// fallback to executing WebDriver commands
return parent::__call($name, $arguments);
}
/**
* Get wire protocol URL for an element
*
* @param string $elementId
*
* @return string
*/
abstract protected function getElementPath($elementId);
}

View File

@@ -0,0 +1,148 @@
<?php
/**
* Copyright 2004-2017 Facebook. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* @package WebDriver
*
* @author Justin Bishop <jubishop@gmail.com>
* @author Anthon Pang <apang@softwaredevelopment.ca>
* @author Fabrizio Branca <mail@fabrizio-branca.de>
*/
namespace WebDriver;
/**
* WebDriver\Element class
*
* @package WebDriver
*
* @method void click() Click on an element.
* @method void submit() Submit a FORM element.
* @method string text() Returns the visible text for the element.
* @method void postValue($json) Send a sequence of key strokes to an element.
* @method string name() Query for an element's tag name.
* @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 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.
* @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.
*/
final class Element extends Container
{
/**
* {@inheritdoc}
*/
protected function methods()
{
return array(
'click' => array('POST'),
'submit' => array('POST'),
'text' => array('GET'),
'value' => array('POST'),
'name' => array('GET'),
'clear' => array('POST'),
'selected' => array('GET'),
'enabled' => array('GET'),
'equals' => array('GET'),
'displayed' => array('GET'),
'location' => array('GET'),
'location_in_view' => array('GET'),
'size' => array('GET'),
);
}
/**
* {@inheritdoc}
*/
protected function obsoleteMethods()
{
return array(
'value' => array('GET'),
'selected' => array('POST'),
'toggle' => array('POST'),
'hover' => array('POST'),
'drag' => array('POST'),
);
}
/**
* Element ID
*
* @var string
*/
private $id;
/**
* Constructor
*
* @param string $url URL
* @param string $id element ID
*/
public function __construct($url, $id)
{
parent::__construct($url);
$this->id = $id;
}
/**
* Get element ID
*
* @return string
*/
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 elements 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}
*/
protected function getElementPath($elementId)
{
return preg_replace('/' . preg_quote($this->id, '/') . '$/', $elementId, $this->url);
}
}

View File

@@ -0,0 +1,200 @@
<?php
/**
* @copyright 2004 Meta Platforms, Inc.
* @license Apache-2.0
*
* @package WebDriver
*
* @author Justin Bishop <jubishop@gmail.com>
*/
namespace WebDriver;
/**
* WebDriver\Exception class
*
* @package WebDriver
*/
abstract class Exception extends \Exception
{
/**
* 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;
const NO_SUCH_ELEMENT = 7;
const NO_SUCH_FRAME = 8;
const UNKNOWN_COMMAND = 9;
const STALE_ELEMENT_REFERENCE = 10;
const INVALID_ELEMENT_STATE = 12;
const UNKNOWN_ERROR = 13;
const JAVASCRIPT_ERROR = 17;
const XPATH_LOOKUP_ERROR = 19;
const TIMEOUT = 21;
const NO_SUCH_WINDOW = 23;
const INVALID_COOKIE_DOMAIN = 24;
const UNABLE_TO_SET_COOKIE = 25;
const UNEXPECTED_ALERT_OPEN = 26;
const NO_ALERT_OPEN_ERROR = 27;
const SCRIPT_TIMEOUT = 28;
const INVALID_ELEMENT_COORDINATES = 29;
const IME_NOT_AVAILABLE = 30;
const IME_ENGINE_ACTIVATION_FAILED = 31;
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;
const NO_COLLECTION = 2;
const NO_STRING = 3;
const NO_STRING_LENGTH = 4;
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;
const NO_SCRIPT_RESULT = 18;
const NO_SUCH_COLLECTION = 20;
const NULL_POINTER = 22;
const NO_MODAL_DIALOG_OPEN_ERROR = 27;
// user-defined
const CURL_EXEC = -1;
const OBSOLETE_COMMAND = -2;
const NO_PARAMETERS_EXPECTED = -3;
const JSON_PARAMETERS_EXPECTED = -4;
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!'),
self::NO_SUCH_DRIVER => array('NoSuchDriver', 'A session is either terminated or not started'),
self::NO_SUCH_ELEMENT => array('NoSuchElement', 'An element could not be located on the page using the given search parameters.'),
self::NO_SUCH_FRAME => array('NoSuchFrame', 'A request to switch to a frame could not be satisfied because the frame could not be found.'),
self::UNKNOWN_COMMAND => array('UnknownCommand', 'The requested resource could not be found, or a request was received using an HTTP method that is not supported by the mapped resource.'),
self::STALE_ELEMENT_REFERENCE => array('StaleElementReference', 'An element command failed because the referenced element is no longer attached to the DOM.'),
self::ELEMENT_NOT_VISIBLE => array('ElementNotVisible', 'An element command could not be completed because the element is not visible on the page.'),
self::INVALID_ELEMENT_STATE => array('InvalidElementState', 'An element command could not be completed because the element is in an invalid state (e.g., attempting to click a disabled element).'),
self::UNKNOWN_ERROR => array('UnknownError', 'An unknown server-side error occurred while processing the command.'),
self::ELEMENT_IS_NOT_SELECTABLE => array('ElementIsNotSelectable', 'An attempt was made to select an element that cannot be selected.'),
self::JAVASCRIPT_ERROR => array('JavaScriptError', 'An error occurred while executing user supplied JavaScript.'),
self::XPATH_LOOKUP_ERROR => array('XPathLookupError', 'An error occurred while searching for an element by XPath.'),
self::TIMEOUT => array('Timeout', 'An operation did not complete before its timeout expired.'),
self::NO_SUCH_WINDOW => array('NoSuchWindow', 'A request to switch to a different window could not be satisfied because the window could not be found.'),
self::INVALID_COOKIE_DOMAIN => array('InvalidCookieDomain', 'An illegal attempt was made to set a cookie under a different domain than the current page.'),
self::UNABLE_TO_SET_COOKIE => array('UnableToSetCookie', 'A request to set a cookie\'s value could not be satisfied.'),
self::UNEXPECTED_ALERT_OPEN => array('UnexpectedAlertOpen', 'A modal dialog was open, blocking this operation'),
self::NO_ALERT_OPEN_ERROR => array('NoAlertOpenError', 'An attempt was made to operate on a modal dialog when one was not open.'),
self::SCRIPT_TIMEOUT => array('ScriptTimeout', 'A script did not complete before its timeout expired.'),
self::INVALID_ELEMENT_COORDINATES => array('InvalidElementCoordinates', 'The coordinates provided to an interactions operation are invalid.'),
self::IME_NOT_AVAILABLE => array('IMENotAvailable', 'IME was not available.'),
self::IME_ENGINE_ACTIVATION_FAILED => array('IMEEngineActivationFailed', 'An IME engine could not be started.'),
self::INVALID_SELECTOR => array('InvalidSelector', 'Argument was an invalid selector (e.g., XPath/CSS).'),
self::SESSION_NOT_CREATED => array('SessionNotCreated', 'A new session could not be created (e.g., a required capability could not be set).'),
self::MOVE_TARGET_OUT_OF_BOUNDS => array('MoveTargetOutOfBounds', 'Target provided for a move action is out of bounds.'),
self::CURL_EXEC => array('CurlExec', 'curl_exec() error.'),
self::OBSOLETE_COMMAND => array('ObsoleteCommand', 'This WebDriver command is obsolete.'),
self::NO_PARAMETERS_EXPECTED => array('NoParametersExpected', 'This HTTP request method expects no parameters.'),
self::JSON_PARAMETERS_EXPECTED => array('JsonParameterExpected', 'This POST request expects a JSON parameter (array).'),
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.'),
);
/**
* Factory method to create WebDriver\Exception objects
*
* @param integer $code Code
* @param string $message Message
* @param \Exception $previousException Previous exception
*
* @return \Exception
*/
public static function factory($code, $message = null, $previousException = null)
{
// unknown error
if (! isset(self::$errs[$code])) {
$code = self::UNKNOWN_ERROR;
}
$errorDefinition = self::$errs[$code];
if ($message === null || trim($message) === '') {
$message = $errorDefinition[1];
}
if (! is_numeric($code)) {
$code = self::W3C_WEBDRIVER_ERROR;
}
$className = __CLASS__ . '\\' . $errorDefinition[0];
return new $className($message, $code, $previousException);
}
}

View File

@@ -0,0 +1,47 @@
<?php
/**
* @copyright 2013 Anthon Pang
* @license Apache-2.0
*
* @package WebDriver
*
* @author Anthon Pang <apang@softwaredevelopment.ca>
*/
namespace WebDriver\Exception;
use WebDriver\Exception as BaseException;
/**
* WebDriver\Exception\CurlExec class
*
* @package WebDriver
*/
final class CurlExec extends BaseException
{
/**
* @var array
*/
private $curlInfo = array();
/**
* Get curl info
*
* @return array
*/
public function getCurlInfo()
{
return $this->curlInfo;
}
/**
* Set curl info
*
* @param array $curlInfo
*/
public function setCurlInfo($curlInfo)
{
$this->curlInfo = $curlInfo;
}
}

View File

@@ -0,0 +1,23 @@
<?php
/**
* @copyright 2013 Anthon Pang
* @license Apache-2.0
*
* @package WebDriver
*
* @author Anthon Pang <apang@softwaredevelopment.ca>
*/
namespace WebDriver\Exception;
use WebDriver\Exception as BaseException;
/**
* WebDriver\Exception\DetachedShadowRoot class
*
* @package WebDriver
*/
final class DetachedShadowRoot extends BaseException
{
}

View File

@@ -0,0 +1,23 @@
<?php
/**
* @copyright 2019 Anthon Pang
* @license Apache-2.0
*
* @package WebDriver
*
* @author Anthon Pang <apang@softwaredevelopment.ca>
*/
namespace WebDriver\Exception;
use WebDriver\Exception as BaseException;
/**
* WebDriver\Exception\ElementClickIntercepted class
*
* @package WebDriver
*/
final class ElementClickIntercepted extends BaseException
{
}

View File

@@ -0,0 +1,23 @@
<?php
/**
* @copyright 2013 Anthon Pang
* @license Apache-2.0
*
* @package WebDriver
*
* @author Anthon Pang <apang@softwaredevelopment.ca>
*/
namespace WebDriver\Exception;
use WebDriver\Exception as BaseException;
/**
* WebDriver\Exception\ElementIsNotSelectable class
*
* @package WebDriver
*/
final class ElementIsNotSelectable extends BaseException
{
}

View File

@@ -0,0 +1,23 @@
<?php
/**
* @copyright 2019 Anthon Pang
* @license Apache-2.0
*
* @package WebDriver
*
* @author Anthon Pang <apang@softwaredevelopment.ca>
*/
namespace WebDriver\Exception;
use WebDriver\Exception as BaseException;
/**
* WebDriver\Exception\ElementNotInteractable class
*
* @package WebDriver
*/
final class ElementNotInteractable extends BaseException
{
}

View File

@@ -0,0 +1,23 @@
<?php
/**
* @copyright 2013 Anthon Pang
* @license Apache-2.0
*
* @package WebDriver
*
* @author Anthon Pang <apang@softwaredevelopment.ca>
*/
namespace WebDriver\Exception;
use WebDriver\Exception as BaseException;
/**
* WebDriver\Exception\ElementNotVisible class
*
* @package WebDriver
*/
final class ElementNotVisible extends BaseException
{
}

View File

@@ -0,0 +1,23 @@
<?php
/**
* @copyright 2013 Anthon Pang
* @license Apache-2.0
*
* @package WebDriver
*
* @author Anthon Pang <apang@softwaredevelopment.ca>
*/
namespace WebDriver\Exception;
use WebDriver\Exception as BaseException;
/**
* WebDriver\Exception\IMEEngineActivationFailed class
*
* @package WebDriver
*/
final class IMEEngineActivationFailed extends BaseException
{
}

View File

@@ -0,0 +1,23 @@
<?php
/**
* @copyright 2013 Anthon Pang
* @license Apache-2.0
*
* @package WebDriver
*
* @author Anthon Pang <apang@softwaredevelopment.ca>
*/
namespace WebDriver\Exception;
use WebDriver\Exception as BaseException;
/**
* WebDriver\Exception\IMENotAvailable class
*
* @package WebDriver
*/
final class IMENotAvailable extends BaseException
{
}

View File

@@ -0,0 +1,23 @@
<?php
/**
* @copyright 2019 Anthon Pang
* @license Apache-2.0
*
* @package WebDriver
*
* @author Anthon Pang <apang@softwaredevelopment.ca>
*/
namespace WebDriver\Exception;
use WebDriver\Exception as BaseException;
/**
* WebDriver\Exception\InsecureCertificate class
*
* @package WebDriver
*/
final class InsecureCertificate extends BaseException
{
}

View File

@@ -0,0 +1,23 @@
<?php
/**
* @copyright 2019 Anthon Pang
* @license Apache-2.0
*
* @package WebDriver
*
* @author Anthon Pang <apang@softwaredevelopment.ca>
*/
namespace WebDriver\Exception;
use WebDriver\Exception as BaseException;
/**
* WebDriver\Exception\InvalidArgument class
*
* @package WebDriver
*/
final class InvalidArgument extends BaseException
{
}

View File

@@ -0,0 +1,23 @@
<?php
/**
* @copyright 2013 Anthon Pang
* @license Apache-2.0
*
* @package WebDriver
*
* @author Anthon Pang <apang@softwaredevelopment.ca>
*/
namespace WebDriver\Exception;
use WebDriver\Exception as BaseException;
/**
* WebDriver\Exception\InvalidCookieDomain class
*
* @package WebDriver
*/
final class InvalidCookieDomain extends BaseException
{
}

View File

@@ -0,0 +1,23 @@
<?php
/**
* @copyright 2022 Anthon Pang
* @license Apache-2.0
*
* @package WebDriver
*
* @author Anthon Pang <apang@softwaredevelopment.ca>
*/
namespace WebDriver\Exception;
use WebDriver\Exception as BaseException;
/**
* WebDriver\Exception\InvalidCoordinates class
*
* @package WebDriver
*/
final class InvalidCoordinates extends BaseException
{
}

View File

@@ -0,0 +1,23 @@
<?php
/**
* @copyright 2013 Anthon Pang
* @license Apache-2.0
*
* @package WebDriver
*
* @author Anthon Pang <apang@softwaredevelopment.ca>
*/
namespace WebDriver\Exception;
use WebDriver\Exception as BaseException;
/**
* WebDriver\Exception\InvalidElementCoordinates class
*
* @package WebDriver
*/
final class InvalidElementCoordinates extends BaseException
{
}

View File

@@ -0,0 +1,23 @@
<?php
/**
* @copyright 2013 Anthon Pang
* @license Apache-2.0
*
* @package WebDriver
*
* @author Anthon Pang <apang@softwaredevelopment.ca>
*/
namespace WebDriver\Exception;
use WebDriver\Exception as BaseException;
/**
* WebDriver\Exception\InvalidElementState class
*
* @package WebDriver
*/
final class InvalidElementState extends BaseException
{
}

View File

@@ -0,0 +1,23 @@
<?php
/**
* @copyright 2013 Anthon Pang
* @license Apache-2.0
*
* @package WebDriver
*
* @author Anthon Pang <apang@softwaredevelopment.ca>
*/
namespace WebDriver\Exception;
use WebDriver\Exception as BaseException;
/**
* WebDriver\Exception\InvalidRequest class
*
* @package WebDriver
*/
final class InvalidRequest extends BaseException
{
}

View File

@@ -0,0 +1,23 @@
<?php
/**
* @copyright 2013 Anthon Pang
* @license Apache-2.0
*
* @package WebDriver
*
* @author Anthon Pang <apang@softwaredevelopment.ca>
*/
namespace WebDriver\Exception;
use WebDriver\Exception as BaseException;
/**
* WebDriver\Exception\InvalidSelector class
*
* @package WebDriver
*/
final class InvalidSelector extends BaseException
{
}

View File

@@ -0,0 +1,23 @@
<?php
/**
* @copyright 2019 Anthon Pang
* @license Apache-2.0
*
* @package WebDriver
*
* @author Anthon Pang <apang@softwaredevelopment.ca>
*/
namespace WebDriver\Exception;
use WebDriver\Exception as BaseException;
/**
* WebDriver\Exception\InvalidSessionID class
*
* @package WebDriver
*/
final class InvalidSessionID extends BaseException
{
}

View File

@@ -0,0 +1,23 @@
<?php
/**
* @copyright 2013 Anthon Pang
* @license Apache-2.0
*
* @package WebDriver
*
* @author Anthon Pang <apang@softwaredevelopment.ca>
*/
namespace WebDriver\Exception;
use WebDriver\Exception as BaseException;
/**
* WebDriver\Exception\JavaScriptError class
*
* @package WebDriver
*/
final class JavaScriptError extends BaseException
{
}

View File

@@ -0,0 +1,23 @@
<?php
/**
* @copyright 2013 Anthon Pang
* @license Apache-2.0
*
* @package WebDriver
*
* @author Anthon Pang <apang@softwaredevelopment.ca>
*/
namespace WebDriver\Exception;
use WebDriver\Exception as BaseException;
/**
* WebDriver\Exception\JsonParameterExpected class
*
* @package WebDriver
*/
final class JsonParameterExpected extends BaseException
{
}

View File

@@ -0,0 +1,23 @@
<?php
/**
* @copyright 2013 Anthon Pang
* @license Apache-2.0
*
* @package WebDriver
*
* @author Anthon Pang <apang@softwaredevelopment.ca>
*/
namespace WebDriver\Exception;
use WebDriver\Exception as BaseException;
/**
* WebDriver\Exception\MoveTargetOutOfBounds class
*
* @package WebDriver
*/
final class MoveTargetOutOfBounds extends BaseException
{
}

View File

@@ -0,0 +1,23 @@
<?php
/**
* @copyright 2013 Anthon Pang
* @license Apache-2.0
*
* @package WebDriver
*
* @author Anthon Pang <apang@softwaredevelopment.ca>
*/
namespace WebDriver\Exception;
use WebDriver\Exception as BaseException;
/**
* WebDriver\Exception\NoAlertOpenError class
*
* @package WebDriver
*/
final class NoAlertOpenError extends BaseException
{
}

View File

@@ -0,0 +1,23 @@
<?php
/**
* @copyright 2013 Anthon Pang
* @license Apache-2.0
*
* @package WebDriver
*
* @author Anthon Pang <apang@softwaredevelopment.ca>
*/
namespace WebDriver\Exception;
use WebDriver\Exception as BaseException;
/**
* WebDriver\Exception\NoParametersExpected class
*
* @package WebDriver
*/
final class NoParametersExpected extends BaseException
{
}

View File

@@ -0,0 +1,23 @@
<?php
/**
* @copyright 2019 Anthon Pang
* @license Apache-2.0
*
* @package WebDriver
*
* @author Anthon Pang <apang@softwaredevelopment.ca>
*/
namespace WebDriver\Exception;
use WebDriver\Exception as BaseException;
/**
* WebDriver\Exception\NoSuchAlert class
*
* @package WebDriver
*/
final class NoSuchAlert extends BaseException
{
}

View File

@@ -0,0 +1,23 @@
<?php
/**
* @copyright 2019 Anthon Pang
* @license Apache-2.0
*
* @package WebDriver
*
* @author Anthon Pang <apang@softwaredevelopment.ca>
*/
namespace WebDriver\Exception;
use WebDriver\Exception as BaseException;
/**
* WebDriver\Exception\NoSuchCookie class
*
* @package WebDriver
*/
final class NoSuchCookie extends BaseException
{
}

View File

@@ -0,0 +1,23 @@
<?php
/**
* @copyright 2013 Anthon Pang
* @license Apache-2.0
*
* @package WebDriver
*
* @author Anthon Pang <apang@softwaredevelopment.ca>
*/
namespace WebDriver\Exception;
use WebDriver\Exception as BaseException;
/**
* WebDriver\Exception\NoSuchDriver class
*
* @package WebDriver
*/
final class NoSuchDriver extends BaseException
{
}

View File

@@ -0,0 +1,23 @@
<?php
/**
* @copyright 2013 Anthon Pang
* @license Apache-2.0
*
* @package WebDriver
*
* @author Anthon Pang <apang@softwaredevelopment.ca>
*/
namespace WebDriver\Exception;
use WebDriver\Exception as BaseException;
/**
* WebDriver\Exception\NoSuchElement class
*
* @package WebDriver
*/
final class NoSuchElement extends BaseException
{
}

View File

@@ -0,0 +1,23 @@
<?php
/**
* @copyright 2013 Anthon Pang
* @license Apache-2.0
*
* @package WebDriver
*
* @author Anthon Pang <apang@softwaredevelopment.ca>
*/
namespace WebDriver\Exception;
use WebDriver\Exception as BaseException;
/**
* WebDriver\Exception\NoSuchFrame class
*
* @package WebDriver
*/
final class NoSuchFrame extends BaseException
{
}

View File

@@ -0,0 +1,23 @@
<?php
/**
* @copyright 2013 Anthon Pang
* @license Apache-2.0
*
* @package WebDriver
*
* @author Anthon Pang <apang@softwaredevelopment.ca>
*/
namespace WebDriver\Exception;
use WebDriver\Exception as BaseException;
/**
* WebDriver\Exception\NoSuchShadowRoot class
*
* @package WebDriver
*/
final class NoSuchShadowRoot extends BaseException
{
}

View File

@@ -0,0 +1,23 @@
<?php
/**
* @copyright 2013 Anthon Pang
* @license Apache-2.0
*
* @package WebDriver
*
* @author Anthon Pang <apang@softwaredevelopment.ca>
*/
namespace WebDriver\Exception;
use WebDriver\Exception as BaseException;
/**
* WebDriver\Exception\NoSuchWindow class
*
* @package WebDriver
*/
final class NoSuchWindow extends BaseException
{
}

View File

@@ -0,0 +1,23 @@
<?php
/**
* @copyright 2013 Anthon Pang
* @license Apache-2.0
*
* @package WebDriver
*
* @author Anthon Pang <apang@softwaredevelopment.ca>
*/
namespace WebDriver\Exception;
use WebDriver\Exception as BaseException;
/**
* WebDriver\Exception\ObsoleteCommand class
*
* @package WebDriver
*/
final class ObsoleteCommand extends BaseException
{
}

View File

@@ -0,0 +1,23 @@
<?php
/**
* @copyright 2013 Anthon Pang
* @license Apache-2.0
*
* @package WebDriver
*
* @author Anthon Pang <apang@softwaredevelopment.ca>
*/
namespace WebDriver\Exception;
use WebDriver\Exception as BaseException;
/**
* WebDriver\Exception\ScriptTimeout class
*
* @package WebDriver
*/
final class ScriptTimeout extends BaseException
{
}

View File

@@ -0,0 +1,23 @@
<?php
/**
* @copyright 2013 Anthon Pang
* @license Apache-2.0
*
* @package WebDriver
*
* @author Anthon Pang <apang@softwaredevelopment.ca>
*/
namespace WebDriver\Exception;
use WebDriver\Exception as BaseException;
/**
* WebDriver\Exception\SessionNotCreated class
*
* @package WebDriver
*/
final class SessionNotCreated extends BaseException
{
}

View File

@@ -0,0 +1,23 @@
<?php
/**
* @copyright 2013 Anthon Pang
* @license Apache-2.0
*
* @package WebDriver
*
* @author Anthon Pang <apang@softwaredevelopment.ca>
*/
namespace WebDriver\Exception;
use WebDriver\Exception as BaseException;
/**
* WebDriver\Exception\StaleElementReference class
*
* @package WebDriver
*/
final class StaleElementReference extends BaseException
{
}

View File

@@ -0,0 +1,23 @@
<?php
/**
* @copyright 2013 Anthon Pang
* @license Apache-2.0
*
* @package WebDriver
*
* @author Anthon Pang <apang@softwaredevelopment.ca>
*/
namespace WebDriver\Exception;
use WebDriver\Exception as BaseException;
/**
* WebDriver\Exception\Timeout class
*
* @package WebDriver
*/
final class Timeout extends BaseException
{
}

View File

@@ -0,0 +1,23 @@
<?php
/**
* @copyright 2019 Anthon Pang
* @license Apache-2.0
*
* @package WebDriver
*
* @author Anthon Pang <apang@softwaredevelopment.ca>
*/
namespace WebDriver\Exception;
use WebDriver\Exception as BaseException;
/**
* WebDriver\Exception\UnableToCaptureScreen class
*
* @package WebDriver
*/
final class UnableToCaptureScreen extends BaseException
{
}

View File

@@ -0,0 +1,23 @@
<?php
/**
* @copyright 2013 Anthon Pang
* @license Apache-2.0
*
* @package WebDriver
*
* @author Anthon Pang <apang@softwaredevelopment.ca>
*/
namespace WebDriver\Exception;
use WebDriver\Exception as BaseException;
/**
* WebDriver\Exception\UnableToSetCookie class
*
* @package WebDriver
*/
final class UnableToSetCookie extends BaseException
{
}

View File

@@ -0,0 +1,23 @@
<?php
/**
* @copyright 2013 Anthon Pang
* @license Apache-2.0
*
* @package WebDriver
*
* @author Anthon Pang <apang@softwaredevelopment.ca>
*/
namespace WebDriver\Exception;
use WebDriver\Exception as BaseException;
/**
* WebDriver\Exception\UnexpectedAlertOpen class
*
* @package WebDriver
*/
final class UnexpectedAlertOpen extends BaseException
{
}

View File

@@ -0,0 +1,23 @@
<?php
/**
* @copyright 2013 Anthon Pang
* @license Apache-2.0
*
* @package WebDriver
*
* @author Anthon Pang <apang@softwaredevelopment.ca>
*/
namespace WebDriver\Exception;
use WebDriver\Exception as BaseException;
/**
* WebDriver\Exception\UnexpectedParameters class
*
* @package WebDriver
*/
final class UnexpectedParameters extends BaseException
{
}

View File

@@ -0,0 +1,23 @@
<?php
/**
* @copyright 2013 Anthon Pang
* @license Apache-2.0
*
* @package WebDriver
*
* @author Anthon Pang <apang@softwaredevelopment.ca>
*/
namespace WebDriver\Exception;
use WebDriver\Exception as BaseException;
/**
* WebDriver\Exception\UnknownCommand class
*
* @package WebDriver
*/
final class UnknownCommand extends BaseException
{
}

View File

@@ -0,0 +1,23 @@
<?php
/**
* @copyright 2013 Anthon Pang
* @license Apache-2.0
*
* @package WebDriver
*
* @author Anthon Pang <apang@softwaredevelopment.ca>
*/
namespace WebDriver\Exception;
use WebDriver\Exception as BaseException;
/**
* WebDriver\Exception\UnknownError class
*
* @package WebDriver
*/
final class UnknownError extends BaseException
{
}

View File

@@ -0,0 +1,23 @@
<?php
/**
* @copyright 2013 Anthon Pang
* @license Apache-2.0
*
* @package WebDriver
*
* @author Anthon Pang <apang@softwaredevelopment.ca>
*/
namespace WebDriver\Exception;
use WebDriver\Exception as BaseException;
/**
* WebDriver\Exception\UnknownLocatorStrategy class
*
* @package WebDriver
*/
final class UnknownLocatorStrategy extends BaseException
{
}

View File

@@ -0,0 +1,23 @@
<?php
/**
* @copyright 2019 Anthon Pang
* @license Apache-2.0
*
* @package WebDriver
*
* @author Anthon Pang <apang@softwaredevelopment.ca>
*/
namespace WebDriver\Exception;
use WebDriver\Exception as BaseException;
/**
* WebDriver\Exception\UnknownMethod class
*
* @package WebDriver
*/
final class UnknownMethod extends BaseException
{
}

View File

@@ -0,0 +1,23 @@
<?php
/**
* @copyright 2019 Anthon Pang
* @license Apache-2.0
*
* @package WebDriver
*
* @author Anthon Pang <apang@softwaredevelopment.ca>
*/
namespace WebDriver\Exception;
use WebDriver\Exception as BaseException;
/**
* WebDriver\Exception\UnsupportedOperation class
*
* @package WebDriver
*/
final class UnsupportedOperation extends BaseException
{
}

View File

@@ -0,0 +1,23 @@
<?php
/**
* @copyright 2013 Anthon Pang
* @license Apache-2.0
*
* @package WebDriver
*
* @author Anthon Pang <apang@softwaredevelopment.ca>
*/
namespace WebDriver\Exception;
use WebDriver\Exception as BaseException;
/**
* WebDriver\Exception\XPathLookupError class
*
* @package WebDriver
*/
final class XPathLookupError extends BaseException
{
}

View File

@@ -0,0 +1,42 @@
<?php
/**
* Copyright 2011-2017 Anthon Pang. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* @package WebDriver
*
* @author Anthon Pang <apang@softwaredevelopment.ca>
*/
namespace WebDriver;
/**
* WebDriver\Frame class
*
* @package WebDriver
*
* @method void parentt() Change focus to the parent context.
*/
final class Frame extends AbstractWebDriver
{
/**
* {@inheritdoc}
*/
protected function methods()
{
return array(
'parent' => array('POST'),
);
}
}

View File

@@ -0,0 +1,50 @@
<?php
/**
* Copyright 2011-2017 Anthon Pang. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* @package WebDriver
*
* @author Anthon Pang <apang@softwaredevelopment.ca>
*/
namespace WebDriver;
/**
* WebDriver\Ime class
*
* @package WebDriver
*
* @method array available_engines() List all available engines on the machines.
* @method string active_engine() Get the name of the active IME engine.
* @method boolean activated() Indicates whether IME input is active at the moment.
* @method void deactivate() De-activates the currently active IME engine.
* @method void activate($json) Make an engine that is available active.
*/
final class Ime extends AbstractWebDriver
{
/**
* {@inheritdoc}
*/
protected function methods()
{
return array(
'available_engines' => array('GET'),
'active_engine' => array('GET'),
'activated' => array('GET'),
'deactivate' => array('POST'),
'activate' => array('POST'),
);
}
}

View File

@@ -0,0 +1,96 @@
<?php
/**
* Copyright 2011-2017 Anthon Pang. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* @package WebDriver
*
* @author Anthon Pang <apang@softwaredevelopment.ca>
* @author Fabrizio Branca <mail@fabrizio-branca.de>
*/
namespace WebDriver;
/**
* WebDriver\Key class
*
* @package WebDriver
*/
final class Key
{
/*
* The Unicode "Private Use Area" code points (0xE000-0xF8FF) are used to represent
* pressable, non-text keys.
*
* @link http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/element/:id/value
*
* key_name = "UTF-8"; // UCS-2
*/
const NULL_KEY = "\xEE\x80\x80"; // E000
const CANCEL = "\xEE\x80\x81"; // E001
const HELP = "\xEE\x80\x82"; // E002
const BACKSPACE = "\xEE\x80\x83"; // E003
const TAB = "\xEE\x80\x84"; // E004
const CLEAR = "\xEE\x80\x85"; // E005
const RETURN_KEY = "\xEE\x80\x86"; // E006
const ENTER = "\xEE\x80\x87"; // E007
const SHIFT = "\xEE\x80\x88"; // E008
const CONTROL = "\xEE\x80\x89"; // E009
const ALT = "\xEE\x80\x8A"; // E00A
const PAUSE = "\xEE\x80\x8B"; // E00B
const ESCAPE = "\xEE\x80\x8C"; // E00C
const SPACE = "\xEE\x80\x8D"; // E00D
const PAGE_UP = "\xEE\x80\x8E"; // E00E
const PAGE_DOWN = "\xEE\x80\x8F"; // E00F
const END = "\xEE\x80\x90"; // E010
const HOME = "\xEE\x80\x91"; // E011
const LEFT_ARROW = "\xEE\x80\x92"; // E012
const UP_ARROW = "\xEE\x80\x93"; // E013
const RIGHT_ARROW = "\xEE\x80\x94"; // E014
const DOWN_ARROW = "\xEE\x80\x95"; // E015
const INSERT = "\xEE\x80\x96"; // E016
const DELETE = "\xEE\x80\x97"; // E017
const SEMICOLON = "\xEE\x80\x98"; // E018
const EQUALS = "\xEE\x80\x99"; // E019
const NUMPAD_0 = "\xEE\x80\x9A"; // E01A
const NUMPAD_1 = "\xEE\x80\x9B"; // E01B
const NUMPAD_2 = "\xEE\x80\x9C"; // E01C
const NUMPAD_3 = "\xEE\x80\x9D"; // E01D
const NUMPAD_4 = "\xEE\x80\x9E"; // E01E
const NUMPAD_5 = "\xEE\x80\x9F"; // E01F
const NUMPAD_6 = "\xEE\x80\xA0"; // E020
const NUMPAD_7 = "\xEE\x80\xA1"; // E021
const NUMPAD_8 = "\xEE\x80\xA2"; // E022
const NUMPAD_9 = "\xEE\x80\xA3"; // E023
const MULTIPLY = "\xEE\x80\xA4"; // E024
const ADD = "\xEE\x80\xA5"; // E025
const SEPARATOR = "\xEE\x80\xA6"; // E026
const SUBTRACT = "\xEE\x80\xA7"; // E027
const DECIMAL = "\xEE\x80\xA8"; // E028
const DIVIDE = "\xEE\x80\xA9"; // E029
const F1 = "\xEE\x80\xB1"; // E031
const F2 = "\xEE\x80\xB2"; // E032
const F3 = "\xEE\x80\xB3"; // E033
const F4 = "\xEE\x80\xB4"; // E034
const F5 = "\xEE\x80\xB5"; // E035
const F6 = "\xEE\x80\xB6"; // E036
const F7 = "\xEE\x80\xB7"; // E037
const F8 = "\xEE\x80\xB8"; // E038
const F9 = "\xEE\x80\xB9"; // E039
const F10 = "\xEE\x80\xBA"; // E03A
const F11 = "\xEE\x80\xBB"; // E03B
const F12 = "\xEE\x80\xBC"; // E03C
const COMMAND = "\xEE\x80\xBD"; // E03D
const META = "\xEE\x80\xBD"; // E03D
}

View File

@@ -0,0 +1,40 @@
<?php
/**
* Copyright 2011-2017 Fabrizio Branca. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* @package WebDriver
*
* @author Fabrizio Branca <mail@fabrizio-branca.de>
* @author Anthon Pang <apang@softwaredevelopment.ca>
*/
namespace WebDriver;
/**
* WebDriver\LocatorStrategy class
*
* @package WebDriver
*/
final class LocatorStrategy
{
const CLASS_NAME = 'class name';
const CSS_SELECTOR = 'css selector';
const ID = 'id';
const NAME = 'name';
const LINK_TEXT = 'link text';
const PARTIAL_LINK_TEXT = 'partial link text';
const TAG_NAME = 'tag name';
const XPATH = 'xpath';
}

View File

@@ -0,0 +1,42 @@
<?php
/**
* Copyright 2014-2017 Anthon Pang. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* @package WebDriver
*
* @author Anthon Pang <apang@softwaredevelopment.ca>
*/
namespace WebDriver;
/**
* WebDriver\Log class
*
* @package WebDriver
*
* @method array types() Get available log types.
*/
final class Log extends AbstractWebDriver
{
/**
* {@inheritdoc}
*/
protected function methods()
{
return array(
'types' => array('GET'),
);
}
}

View File

@@ -0,0 +1,42 @@
<?php
/**
* Copyright 2014-2017 Anthon Pang. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* @package WebDriver
*
* @author Anthon Pang <apang@softwaredevelopment.ca>
*/
namespace WebDriver;
/**
* WebDriver\LogType class
*
* @package WebDriver
*/
final class LogType
{
/**
* Log Type
*
* @see https://code.google.com/p/selenium/source/browse/java/client/src/org/openqa/selenium/logging/LogType.java
*/
const BROWSER = 'browser';
const CLIENT = 'client';
const DRIVER = 'driver';
const PERFORMANCE = 'performance';
const PROFILER = 'driver';
const SERVER = 'server';
}

View File

@@ -0,0 +1,76 @@
<?php
/**
* Copyright 2012-2017 Anthon Pang. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* @package WebDriver
*
* @author Anthon Pang <apang@softwaredevelopment.ca>
*/
namespace WebDriver\SauceLabs;
use WebDriver\Capability as BaseCapability;
/**
* WebDriver\SauceLabs\Capability class
*
* @package WebDriver
*/
class Capability extends BaseCapability
{
/**
* Desired capabilities - SauceLabs
*
* @see https://saucelabs.com/docs/additional-config
*/
// Job Annotation
const NAME = 'name'; // Name the job
const BUILD = 'build'; // Record the build number
const TAGS = 'tags'; // Tag your jobs
const PASSED = 'passed'; // Record pass/fail status
const CUSTOM_DATA = 'custom-data'; // Record custom data
// Performance improvements and data collection
const RECORD_VIDEO = 'record-video'; // Video recording
const VIDEO_UPLOAD_ON_PASS = 'video-upload-on-pass'; // Video upload on pass
const RECORD_SCREENSHOTS = 'record-screenshots'; // Record step-by-step screenshots
const CAPTURE_HTML = 'capture-html'; // HTML source capture
const QUIET_EXCEPTIONS = 'webdriver.remote.quietExceptions'; // Enable Selenium 2's automatic screenshots
const SAUCE_ADVISOR = 'sauce-advisor'; // Sauce Advisor
// Selenium specific
const SELENIUM_VERSION = 'selenium-version'; // Use a specific Selenium version
const SINGLE_WINDOW = 'single-window'; // Selenium RC's single window mode
const USER_EXTENSIONS_URL = 'user-extensions-url'; // Selenium RC's user extensions
const FIREFOX_PROFILE_URL = 'firefox-profile-url'; // Selenium RC's custom Firefox profiles
// Timeouts
const MAX_DURATION = 'max-duration'; // Set maximum test duration
const COMMAND_TIMEOUT = 'command-timeout'; // Set command timeout
const IDLE_TIMEOUT = 'idle-timeout'; // Set idle test timeout
// Sauce specific
const PRERUN = 'prerun'; // Prerun executables
const TUNNEL_IDENTIFIER = 'tunnel-identifier'; // Use identified tunnel
const SCREEN_RESOLUTION = 'screen-resolution'; // Use specific screen resolution
const DISABLE_POPUP_HANDLER = 'disable-popup-handler'; // Disable popup handler
const AVOID_PROXY = 'avoid-proxy'; // Avoid proxy
const DEVICE_ORIENTATION = 'deviceOrientation'; // Device orientation (portrait or landscape)
const DEVICE_TYPE = 'deviceType'; // Device type (phone or tablet)
// Job Sharing
const PUBLIC_RESULTS = 'public'; // Make public, private, or share jobs
}

View File

@@ -0,0 +1,313 @@
<?php
/**
* Copyright 2012-2017 Anthon Pang. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* @package WebDriver
*
* @author Anthon Pang <apang@softwaredevelopment.ca>
* @author Fabrizio Branca <mail@fabrizio-branca.de>
*/
namespace WebDriver\SauceLabs;
use WebDriver\ServiceFactory;
/**
* WebDriver\SauceLabs\SauceRest class
*
* @package WebDriver
*/
class SauceRest
{
/**
* @var string
*/
private $userId;
/**
* @var string
*/
private $accessKey;
/**
* Constructor
*
* @param string $userId Your Sauce user name
* @param string $accessKey Your Sauce API key
*/
public function __construct($userId, $accessKey)
{
$this->userId = $userId;
$this->accessKey = $accessKey;
}
/**
* Execute Sauce Labs REST API command
*
* @param string $requestMethod HTTP request method
* @param string $url URL
* @param mixed $parameters Parameters
*
* @return mixed
*
* @throws \WebDriver\Exception\CurlExec
*
* @see http://saucelabs.com/docs/saucerest
*/
protected function execute($requestMethod, $url, $parameters = null)
{
$extraOptions = array(
CURLOPT_HTTPAUTH => CURLAUTH_BASIC,
CURLOPT_USERPWD => $this->userId . ':' . $this->accessKey,
// don't verify SSL certificates
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_SSL_VERIFYHOST => false,
CURLOPT_HTTPHEADER => array('Expect:'),
CURLOPT_FAILONERROR => true,
);
$url = 'https://saucelabs.com/rest/v1/' . $url;
list($rawResult, $info) = ServiceFactory::getInstance()->getService('service.curl')->execute($requestMethod, $url, $parameters, $extraOptions);
return json_decode($rawResult, true);
}
/**
* Get account details: /rest/v1/users/:userId (GET)
*
* @param string $userId
*
* @return array
*/
public function getAccountDetails($userId)
{
return $this->execute('GET', 'users/' . $userId);
}
/**
* Check account limits: /rest/v1/limits (GET)
*
* @return array
*/
public function getAccountLimits()
{
return $this->execute('GET', 'limits');
}
/**
* Create new sub-account: /rest/v1/users/:userId (POST)
*
* For "partners", $accountInfo also contains 'plan' => (one of 'free', 'small', 'team', 'com', or 'complus')
*
* @param array $accountInfo array('username' => ..., 'password' => ..., 'name' => ..., 'email' => ...)
*
* @return array array('access_key' => ..., 'minutes' => ..., 'id' => ...)
*/
public function createSubAccount($accountInfo)
{
return $this->execute('POST', 'users/' . $this->userId, $accountInfo);
}
/**
* Update sub-account service plan: /rest/v1/users/:userId/subscription (POST)
*
* @param string $userId User ID
* @param string $plan Plan
*
* @return array
*/
public function updateSubAccount($userId, $plan)
{
return $this->execute('POST', 'users/' . $userId . '/subscription', array('plan' => $plan));
}
/**
* Unsubscribe a sub-account: /rest/v1/users/:userId/subscription (DELETE)
*
* @param string $userId User ID
*
* @return array
*/
public function unsubscribeSubAccount($userId)
{
return $this->execute('DELETE', 'users/' . $userId . '/subscription');
}
/**
* Get current account activity: /rest/v1/:userId/activity (GET)
*
* @return array
*/
public function getActivity()
{
return $this->execute('GET', $this->userId . '/activity');
}
/**
* Get historical account usage: /rest/v1/:userId/usage (GET)
*
* @param string $start Optional start date YYYY-MM-DD
* @param string $end Optional end date YYYY-MM-DD
*
* @return array
*/
public function getUsage($start = null, $end = null)
{
$query = http_build_query(array(
'start' => $start,
'end' => $end,
));
return $this->execute('GET', $this->userId . '/usage' . (strlen($query) ? '?' . $query : ''));
}
/**
* Get jobs: /rest/v1/:userId/jobs (GET)
*
* @param boolean $full
*
* @return array
*/
public function getJobs($full = null)
{
$query = http_build_query(array(
'full' => (isset($full) && $full) ? 'true' : null,
));
return $this->execute('GET', $this->userId . '/jobs' . (strlen($query) ? '?' . $query : ''));
}
/**
* Get full information for job: /rest/v1/:userId/jobs/:jobId (GET)
*
* @param string $jobId
*
* @return array
*/
public function getJob($jobId)
{
return $this->execute('GET', $this->userId . '/jobs/' . $jobId);
}
/**
* Update existing job: /rest/v1/:userId/jobs/:jobId (PUT)
*
* @param string $jobId Job ID
* @param array $jobInfo Job information
*
* @return array
*/
public function updateJob($jobId, $jobInfo)
{
return $this->execute('PUT', $this->userId . '/jobs/' . $jobId, $jobInfo);
}
/**
* Stop job: /rest/v1/:userId/jobs/:jobId/stop (PUT)
*
* @param string $jobId
*
* @return array
*/
public function stopJob($jobId)
{
return $this->execute('PUT', $this->userId . '/jobs/' . $jobId . '/stop');
}
/**
* Delete job: /rest/v1/:userId/jobs/:jobId (DELETE)
*
* @param string $jobId
*
* @return array
*/
public function deleteJob($jobId)
{
return $this->execute('DELETE', $this->userId . '/jobs/' . $jobId);
}
/**
* Get running tunnels for a given user: /rest/v1/:userId/tunnels (GET)
*
* @return array
*/
public function getTunnels()
{
return $this->execute('GET', $this->userId . '/tunnels');
}
/**
* Get full information for a tunnel: /rest/v1/:userId/tunnels/:tunnelId (GET)
*
* @param string $tunnelId
*
* @return array
*/
public function getTunnel($tunnelId)
{
return $this->execute('GET', $this->userId . '/tunnels/' . $tunnelId);
}
/**
* Shut down a tunnel: /rest/v1/:userId/tunnels/:tunnelId (DELETE)
*
* @param string $tunnelId
*
* @return array
*/
public function shutdownTunnel($tunnelId)
{
return $this->execute('DELETE', $this->userId . '/tunnels/' . $tunnelId);
}
/**
* Get current status of Sauce Labs' services: /rest/v1/info/status (GET)
*
* @return array array('wait_time' => ..., 'service_operational' => ..., 'status_message' => ...)
*/
public function getStatus()
{
return $this->execute('GET', 'info/status');
}
/**
* Get currently supported browsers: /rest/v1/info/browsers (GET)
*
* @param string $termination Optional termination (one of "all", "selenium-rc", or "webdriver')
*
* @return array
*/
public function getBrowsers($termination = '')
{
if ($termination) {
return $this->execute('GET', 'info/browsers/' . $termination);
}
return $this->execute('GET', 'info/browsers');
}
/**
* Get number of tests executed so far on Sauce Labs: /rest/v1/info/counter (GET)
*
* @return array
*/
public function getCounter()
{
return $this->execute('GET', 'info/counter');
}
}

View File

@@ -0,0 +1,114 @@
<?php
/**
* Copyright 2004-2017 Facebook. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* @package WebDriver
*
* @author Justin Bishop <jubishop@gmail.com>
* @author Anthon Pang <apang@softwaredevelopment.ca>
* @author Fabrizio Branca <mail@fabrizio-branca.de>
*/
namespace WebDriver\Service;
use WebDriver\Exception\CurlExec as CurlExecException;
/**
* WebDriver\Service\CurlService class
*
* @package WebDriver
*/
class CurlService implements CurlServiceInterface
{
/**
* {@inheritdoc}
*/
public function execute($requestMethod, $url, $parameters = null, $extraOptions = array())
{
$customHeaders = array(
'Content-Type: application/json;charset=utf-8',
'Accept: application/json',
);
$curl = curl_init($url);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
switch ($requestMethod) {
case 'GET':
break;
case 'POST':
case 'PUT':
$parameters = ! $parameters || ! is_array($parameters)
? '{}' // instead of json_encode(new \stdclass))
: 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:';
$requestMethod === 'POST'
? curl_setopt($curl, CURLOPT_POST, true)
: curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'PUT');
break;
case 'DELETE':
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'DELETE');
break;
}
curl_setopt($curl, CURLOPT_HTTPHEADER, $customHeaders);
foreach ($extraOptions as $option => $value) {
curl_setopt($curl, $option, $value);
}
$rawResult = trim(curl_exec($curl));
$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] &&
CURLE_GOT_NOTHING !== ($errno = curl_errno($curl)) &&
$error = curl_error($curl)
) {
curl_close($curl);
$e = new CurlExecException(
sprintf(
"Curl error thrown for http %s to %s%s\n\n%s",
$requestMethod,
$url,
$parameters && is_array($parameters) ? ' with params: ' . json_encode($parameters) : '',
$error
),
$errno
);
$e->setCurlInfo($info);
throw $e;
}
curl_close($curl);
return array($rawResult, $info);
}
}

View File

@@ -0,0 +1,45 @@
<?php
/**
* Copyright 2012-2017 Anthon Pang. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* @package WebDriver
*
* @author Anthon Pang <apang@softwaredevelopment.ca>
*/
namespace WebDriver\Service;
/**
* WebDriver\Service\CurlServiceInterface class
*
* @package WebDriver
*/
interface CurlServiceInterface
{
/**
* Send protocol request to WebDriver server using curl extension API.
*
* @param string $requestMethod HTTP request method, e.g., 'GET', 'POST', or 'DELETE'
* @param string $url Request URL
* @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()
*
* @return array
*
* @throws \WebDriver\Exception\CurlExec only if http error and CURLOPT_FAILONERROR has been set in extraOptions
*/
public function execute($requestMethod, $url, $parameters = null, $extraOptions = array());
}

View File

@@ -0,0 +1,120 @@
<?php
/**
* Copyright 2012-2017 Anthon Pang. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* @package WebDriver
*
* @author Anthon Pang <apang@softwaredevelopment.ca>
*/
namespace WebDriver;
/**
* WebDriver\ServiceFactory class
*
* A service factory
*
* @package WebDriver
*/
final class ServiceFactory
{
/**
* singleton
*
* @var \WebDriver\ServiceFactory
*/
private static $instance;
/**
* @var array
*/
protected $services;
/**
* @var array
*/
protected $serviceClasses;
/**
* Private constructor
*/
private function __construct()
{
$this->services = array();
$this->serviceClasses = array(
'service.curl' => '\\WebDriver\\Service\\CurlService',
);
}
/**
* Get singleton instance
*
* @return \WebDriver\ServiceFactory
*/
public static function getInstance()
{
if (self::$instance === null) {
self::$instance = new self;
}
return self::$instance;
}
/**
* Get service
*
* @param string $serviceName Name of service
*
* @return object
*/
public function getService($serviceName)
{
if (!isset($this->services[$serviceName])) {
$className = $this->serviceClasses[$serviceName];
$this->services[$serviceName] = new $className;
}
return $this->services[$serviceName];
}
/**
* Set service
*
* @param string $serviceName Name of service
* @param object $service Service instance
*/
public function setService($serviceName, $service)
{
$this->services[$serviceName] = $service;
}
/**
* Override default service class
*
* @param string $serviceName Name of service
* @param string $className Name of service class
*/
public function setServiceClass($serviceName, $className)
{
if (substr($className, 0, 1) !== '\\') {
$className = '\\' . $className;
}
$this->serviceClasses[$serviceName] = $className;
$this->services[$serviceName] = null;
}
}

View File

@@ -0,0 +1,539 @@
<?php
/**
* Copyright 2004-2017 Facebook. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* @package WebDriver
*
* @author Justin Bishop <jubishop@gmail.com>
* @author Anthon Pang <apang@softwaredevelopment.ca>
*/
namespace WebDriver;
use WebDriver\Exception as WebDriverException;
/**
* WebDriver\Session class
*
* @package WebDriver
*
* @method string window_handle() Retrieve the current window handle.
* @method array window_handles() Retrieve the list of all window handles available to the session.
* @method string url() Retrieve the URL of the current page
* @method void postUrl($jsonUrl) Navigate to a new URL
* @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 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.
* @method string source() Get the current page source.
* @method string title() Get the current page title.
* @method void keys($jsonKeys) Send a sequence of key strokes to the active element.
* @method string getOrientation() Get the current browser orientation.
* @method void postOrientation($jsonOrientation) Set the current browser orientation.
* @method string getAlert_text() Gets the text of the currently displayed JavaScript alert(), confirm(), or prompt() dialog.
* @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 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).
* @method void doubleclick() Double-clicks at the current mouse coordinates (set by moveto).
* @method array execute_sql($jsonQuery) Execute SQL.
* @method array getLocation() Get the current geo location.
* @method void postLocation($jsonCoordinates) Set the current geo location.
* @method boolean getBrowser_connection() Is browser online?
* @method void postBrowser_connection($jsonState) Set browser online.
*/
final class Session extends Container
{
/**
* @var array
*/
private $capabilities = null;
/**
* {@inheritdoc}
*/
protected function methods()
{
return array(
'window_handle' => array('GET'),
'window_handles' => array('GET'),
'url' => array('GET', 'POST'), // alternate for POST, use open($url)
'forward' => array('POST'),
'back' => array('POST'),
'refresh' => array('POST'),
'screenshot' => array('GET'),
'cookie' => array('GET', 'POST'), // for DELETE, use deleteAllCookies()
'source' => array('GET'),
'title' => array('GET'),
'keys' => array('POST'),
'orientation' => array('GET', 'POST'),
'alert_text' => array('GET', 'POST'),
'accept_alert' => array('POST'),
'dismiss_alert' => array('POST'),
'moveto' => array('POST'),
'click' => array('POST'),
'buttondown' => 'POST',
'buttonup' => array('POST'),
'doubleclick' => array('POST'),
'execute_sql' => array('POST'),
'location' => array('GET', 'POST'),
'browser_connection' => array('GET', 'POST'),
// specific to Java SeleniumServer
'file' => array('POST'),
);
}
/**
* {@inheritdoc}
*/
protected function obsoleteMethods()
{
return array(
'modifier' => array('POST'),
'speed' => array('GET', 'POST'),
'alert' => array('GET'),
'visible' => array('GET', 'POST'),
);
}
/**
* Open URL: /session/:sessionId/url (POST)
* An alternative to $session->url($url);
*
* @param string $url
*
* @return \WebDriver\Session
*/
public function open($url)
{
$this->curl('POST', '/url', array('url' => $url));
return $this;
}
/**
* Get browser capabilities: /session/:sessionId (GET)
*
* @return mixed
*/
public function capabilities()
{
if ($this->capabilities === null) {
$result = $this->curl('GET', '');
$this->capabilities = $result['value'];
}
return $this->capabilities;
}
/**
* Close session: /session/:sessionId (DELETE)
*
* @return mixed
*/
public function close()
{
$result = $this->curl('DELETE', '');
return $result['value'];
}
// There's a limit to our ability to exploit the dynamic nature of PHP when it
// comes to the cookie methods because GET and DELETE request methods are indistinguishable
// from each other: neither takes parameters.
/**
* Get all cookies: /session/:sessionId/cookie (GET)
* Alternative to: $session->cookie();
*
* Note: get cookie by name not implemented in API
*
* @return mixed
*/
public function getAllCookies()
{
$result = $this->curl('GET', '/cookie');
return $result['value'];
}
/**
* Set cookie: /session/:sessionId/cookie (POST)
* Alternative to: $session->cookie($cookie_json);
*
* @param array $cookieJson
*
* @return \WebDriver\Session
*/
public function setCookie($cookieJson)
{
$this->curl('POST', '/cookie', array('cookie' => $cookieJson));
return $this;
}
/**
* Delete all cookies: /session/:sessionId/cookie (DELETE)
*
* @return \WebDriver\Session
*/
public function deleteAllCookies()
{
$this->curl('DELETE', '/cookie');
return $this;
}
/**
* Delete a cookie: /session/:sessionId/cookie/:name (DELETE)
*
* @param string $cookieName
*
* @return \WebDriver\Session
*/
public function deleteCookie($cookieName)
{
$this->curl('DELETE', '/cookie/' . $cookieName);
return $this;
}
/**
* window methods: /session/:sessionId/window (POST, DELETE)
* - $session->window() - close current window
* - $session->window($window_handle) - set focus
* - $session->window($window_handle)->method() - chaining
*
* @return \WebDriver\Window|\WebDriver\Session
*/
public function window()
{
// close current window
if (func_num_args() === 0) {
$this->curl('DELETE', '/window');
return $this;
}
// set focus
$arg = func_get_arg(0); // window handle
if (is_array($arg)) {
$this->curl('POST', '/window', $arg);
return $this;
}
// chaining
return new Window($this->url . '/window', $arg);
}
/**
* Delete window: /session/:sessionId/window (DELETE)
*
* @return \WebDriver\Session
*/
public function deleteWindow()
{
$this->curl('DELETE', '/window');
return $this;
}
/**
* Set focus to window: /session/:sessionId/window (POST)
*
* @param mixed $name window handle
*
* @return \WebDriver\Session
*/
public function focusWindow($name)
{
$this->curl('POST', '/window', array('handle' => $name, 'name' => $name));
return $this;
}
/**
* frame methods: /session/:sessionId/frame (POST)
* - $session->frame($json) - change focus to another frame on the page
* - $session->frame()->method() - chaining
*
* @return \WebDriver\Session|\WebDriver\Frame
*/
public function frame()
{
if (func_num_args() === 1) {
$arg = $this->serializeArguments(func_get_arg(0)); // json
$this->curl('POST', '/frame', $arg);
return $this;
}
// chaining
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 $e) {
throw WebDriverException::factory(WebDriverException::UNKNOWN_ERROR);
}
return $result['value'];
}
/**
* timeouts methods: /session/:sessionId/timeouts (POST)
* - $session->timeouts($json) - set timeout for an operation
* - $session->timeouts()->method() - chaining
*
* @return \WebDriver\Session|\WebDriver\Timeouts
*/
public function timeouts()
{
// set timeouts
if (func_num_args() === 1) {
$arg = func_get_arg(0); // json
$this->curl('POST', '/timeouts', $arg);
return $this;
}
if (func_num_args() === 2) {
$arg = array(
'type' => func_get_arg(0), // 'script' or 'implicit'
'ms' => func_get_arg(1), // timeout in milliseconds
);
$this->curl('POST', '/timeouts', $arg);
return $this;
}
// chaining
return new Timeouts($this->url . '/timeouts');
}
/**
* ime method chaining, e.g.,
* - $session->ime()->method()
*
* @return \WebDriver\Ime
*/
public function ime()
{
return new Ime($this->url . '/ime');
}
/**
* Get active element (i.e., has focus): /session/:sessionId/element/active (POST)
* - $session->activeElement()
*
* @return mixed
*/
public function activeElement()
{
$result = $this->curl('POST', '/element/active');
return $this->webDriverElement($result['value']);
}
/**
* touch method chaining, e.g.,
* - $session->touch()->method()
*
* @return \WebDriver\Touch
*
*/
public function touch()
{
return new Touch($this->url . '/touch');
}
/**
* local_storage method chaining, e.g.,
* - $session->local_storage()->method()
*
* @return \WebDriver\Storage
*/
public function local_storage()
{
return Storage::factory('local', $this->url . '/local_storage');
}
/**
* session_storage method chaining, e.g.,
* - $session->session_storage()->method()
*
* @return \WebDriver\Storage
*/
public function session_storage()
{
return Storage::factory('session', $this->url . '/session_storage');
}
/**
* application cache chaining, e.g.,
* - $session->application_cache()->status()
*
* @return \WebDriver\ApplicationCache
*/
public function application_cache()
{
return new ApplicationCache($this->url . '/application_cache');
}
/**
* log methods: /session/:sessionId/log (POST)
* - $session->log($type) - get log for given log type
* - $session->log()->method() - chaining
*
* @return mixed
*/
public function log()
{
// get log for given log type
if (func_num_args() === 1) {
$arg = func_get_arg(0);
if (is_string($arg)) {
$arg = array(
'type' => $arg,
);
}
$result = $this->curl('POST', '/log', $arg);
return $result['value'];
}
// chaining
return new Log($this->url . '/log');
}
/**
* 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(array $jsonScript)
{
$jsonScript['args'] = $this->serializeArguments($jsonScript['args']);
$result = $this->curl('POST', '/execute', $jsonScript);
return $this->unserializeResult($result['value']);
}
/**
* 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(array $jsonScript)
{
$jsonScript['args'] = $this->serializeArguments($jsonScript['args']);
$result = $this->curl('POST', '/execute_async', $jsonScript);
return $this->unserializeResult($result['value']);
}
/**
* {@inheritdoc}
*/
protected function getElementPath($elementId)
{
return sprintf('%s/element/%s', $this->url, $elementId);
}
/**
* Serialize script arguments (containing web elements)
*
* @see https://w3c.github.io/webdriver/#executing-script
*
* @param array $arguments
*
* @return array
*/
private function serializeArguments(array $arguments)
{
foreach ($arguments as $key => $value) {
// Potential compat-buster, i.e., W3C-specific
if ($value instanceof Element) {
// 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] = array(
Container::WEBDRIVER_ELEMENT_ID => $value->getID(),
Container::LEGACY_ELEMENT_ID => $value->getID(),
);
continue;
}
if (is_array($value)) {
$arguments[$key] = $this->serializeArguments($value);
}
}
return $arguments;
}
/**
* Unserialize result (containing web elements)
*
* @param mixed $result
*
* @return mixed
*/
private function unserializeResult($result)
{
$element = $this->webDriverElement($result);
if ($element !== null) {
return $element;
}
if (is_array($result)) {
foreach ($result as $key => $value) {
$result[$key] = $this->unserializeResult($value);
}
}
return $result;
}
}

View File

@@ -0,0 +1,148 @@
<?php
/**
* Copyright 2011-2017 Anthon Pang. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* @package WebDriver
*
* @author Anthon Pang <apang@softwaredevelopment.ca>
*/
namespace WebDriver;
use WebDriver\Exception as WebDriverException;
/**
* WebDriver\Storage class
*
* @package WebDriver
*
* @method mixed getKey($key) Get key/value pair.
* @method void deleteKey($key) Delete a specific key.
* @method integer size() Get the number of items in the storage.
*/
abstract class Storage extends AbstractWebDriver
{
/**
* {@inheritdoc}
*/
protected function methods()
{
return array(
'key' => array('GET', 'DELETE'),
'size' => array('GET'),
);
}
/**
* Get all keys from storage or a specific key/value pair
*
* @return mixed
*/
public function get()
{
// get all keys
if (func_num_args() === 0) {
$result = $this->curl('GET', '');
return $result['value'];
}
// get key/value pair
if (func_num_args() === 1) {
return $this->getKey(func_get_arg(0));
}
throw WebDriverException::factory(WebDriverException::UNEXPECTED_PARAMETERS);
}
/**
* Set specific key/value pair
*
* @return \WebDriver\Storage
*
* @throw \WebDriver\Exception\UnexpectedParameters if unexpected parameters
*/
public function set()
{
if (func_num_args() === 1
&& is_array($arg = func_get_arg(0))
) {
$this->curl('POST', '', $arg);
return $this;
}
if (func_num_args() === 2) {
$arg = array(
'key' => func_get_arg(0),
'value' => func_get_arg(1),
);
$this->curl('POST', '', $arg);
return $this;
}
throw WebDriverException::factory(WebDriverException::UNEXPECTED_PARAMETERS);
}
/**
* Delete storage or a specific key
*
* @return \WebDriver\Storage
*
* @throw \WebDriver\Exception\UnexpectedParameters if unexpected parameters
*/
public function delete()
{
// delete storage
if (func_num_args() === 0) {
$this->curl('DELETE', '');
return $this;
}
// delete key from storage
if (func_num_args() === 1) {
$this->deleteKey(func_get_arg(0));
return $this;
}
throw WebDriverException::factory(WebDriverException::UNEXPECTED_PARAMETERS);
}
/**
* Factory method to create Storage objects
*
* @param string $type 'local' or 'session' storage
* @param string $url URL
*
* @return \WebDriver\Storage
*/
public static function factory($type, $url)
{
// dynamically define custom storage classes
$className = ucfirst(strtolower($type));
$namespacedClassName = __CLASS__ . '\\' . $className;
if (!class_exists($namespacedClassName, false)) {
eval(
'namespace ' . __CLASS__ . '; final class ' . $className . ' extends \\' . __CLASS__ . '{}'
);
}
return new $namespacedClassName($url);
}
}

View File

@@ -0,0 +1,76 @@
<?php
/**
* Copyright 2011-2017 Anthon Pang. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* @package WebDriver
*
* @author Anthon Pang <apang@softwaredevelopment.ca>
*/
namespace WebDriver;
use WebDriver\Exception as WebDriverException;
/**
* WebDriver\Timeouts class
*
* @package WebDriver
*
* @method void async_script($json) Set the amount of time, in milliseconds, that asynchronous scripts (executed by execute_async) are permitted to run before they are aborted and a timeout error is returned to the client.
* @method void implicit_wait($json) Set the amount of time the driver should wait when searching for elements.
*/
final class Timeouts extends AbstractWebDriver
{
/**
* {@inheritdoc}
*/
protected function methods()
{
return array(
'async_script' => array('POST'),
'implicit_wait' => array('POST'),
);
}
/**
* helper method to wait until user-defined condition is 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
*
* @return mixed result from callback function
*
* @throws \Exception if thrown by callback, or \WebDriver\Exception\Timeout if helper times out
*/
public function wait($callback, $maxIterations = 1, $sleep = 0, $args = array())
{
$i = max(1, $maxIterations);
while ($i-- > 0) {
$result = call_user_func_array($callback, $args);
if ($result !== false) {
return $result;
}
// don't sleep on the last iteration
$i && sleep($sleep);
}
throw WebDriverException::factory(WebDriverException::TIMEOUT, 'wait() method timed out');
}
}

View File

@@ -0,0 +1,56 @@
<?php
/**
* Copyright 2011-2017 Anthon Pang. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* @package WebDriver
*
* @author Anthon Pang <apang@softwaredevelopment.ca>
*/
namespace WebDriver;
/**
* WebDriver\Touch class
*
* @package WebDriver
*
* @method void click($jsonElement) Single tap on the touch enabled device.
* @method void down($jsonCoordinates) Finger down on the screen.
* @method void up($jsonCoordinates) Finger up on the screen.
* @method void move($jsonCoordinates) Finger move on the screen.
* @method void scroll($jsonCoordinates) Scroll on the touch screen using finger based motion events. Coordinates are either absolute, or relative to a element (if specified).
* @method void doubleclick($jsonElement) Double tap on the touch screen using finger motion events.
* @method void longclick($jsonElement) Long press on the touch screen using finger motion events.
* @method void flick($json) Flick on the touch screen using finger motion events.
*/
final class Touch extends AbstractWebDriver
{
/**
* {@inheritdoc}
*/
protected function methods()
{
return array(
'click' => array('POST'),
'down' => array('POST'),
'up' => array('POST'),
'move' => array('POST'),
'scroll' => array('POST'),
'doubleclick' => array('POST'),
'longclick' => array('POST'),
'flick' => array('POST'),
);
}
}

View File

@@ -0,0 +1,90 @@
<?php
/**
* Copyright 2004-2017 Facebook. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* @package WebDriver
*
* @author Justin Bishop <jubishop@gmail.com>
* @author Anthon Pang <apang@softwaredevelopment.ca>
*/
namespace WebDriver;
/**
* WebDriver class
*
* @package WebDriver
*
* @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
{
/**
* {@inheritdoc}
*/
protected function methods()
{
return array(
'status' => array('GET'),
);
}
/**
* {@inheritdoc}
*/
public function session($requiredCapabilities = Browser::FIREFOX, $desiredCapabilities = array())
{
// for backwards compatibility when the only required capability was browser name
if (! is_array($requiredCapabilities)) {
$desiredCapabilities[Capability::BROWSER_NAME] = $requiredCapabilities ?: Browser::FIREFOX;
$requiredCapabilities = array();
}
// required
$parameters = array(
'desiredCapabilities' => array_merge($desiredCapabilities, $requiredCapabilities)
);
// optional
if (! empty($requiredCapabilities)) {
$parameters['requiredCapabilities'] = $requiredCapabilities;
}
$result = $this->curl(
'POST',
'/session',
$parameters,
array(CURLOPT_FOLLOWLOCATION => true)
);
return new Session($result['sessionUrl']);
}
/**
* {@inheritdoc}
*/
public function sessions()
{
$result = $this->curl('GET', '/sessions');
$sessions = array();
foreach ($result['value'] as $session) {
$sessions[] = new Session($this->url . '/session/' . $session['id']);
}
return $sessions;
}
}

View File

@@ -0,0 +1,49 @@
<?php
/**
* Copyright 2004-2017 Facebook. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* @package WebDriver
*
* @author Justin Bishop <jubishop@gmail.com>
* @author Anthon Pang <apang@softwaredevelopment.ca>
*/
namespace WebDriver;
/**
* WebDriverInterface interface
*
* @package WebDriver
*/
interface WebDriverInterface
{
/**
* New Session: /session (POST)
* Get session object for chaining
*
* @param array|string $requiredCapabilities Required capabilities (or browser name)
* @param array $desiredCapabilities Desired capabilities
*
* @return \WebDriver\Session
*/
public function session($requiredCapabilities = Browser::FIREFOX, $desiredCapabilities = array());
/**
* Get list of currently active sessions
*
* @return array an array of \WebDriver\Session objects
*/
public function sessions();
}

View File

@@ -0,0 +1,89 @@
<?php
/**
* Copyright 2011-2017 Anthon Pang. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* @package WebDriver
*
* @author Anthon Pang <apang@softwaredevelopment.ca>
* @author Fabrizio Branca <mail@fabrizio-branca.de>
*/
namespace WebDriver;
/**
* WebDriver\Window class
*
* @package WebDriver
*
* @method array getSize() Get size of the window.
* @method void postSize($json) Change the size of the window.
* @method array getPosition() Get position of the window.
* @method void postPosition($json) Change position of the window.
* @method void maximize() Maximize the window if not already maximized.
*/
final class Window extends AbstractWebDriver
{
/**
* Window handle
*
* @var string
*/
private $windowHandle;
/**
* {@inheritdoc}
*/
protected function methods()
{
return array(
'size' => array('GET', 'POST'),
'position' => array('GET', 'POST'),
'maximize' => array('POST'),
);
}
/**
* {@inheritdoc}
*/
protected function obsoleteMethods()
{
return array(
'restore' => array('POST'),
);
}
/**
* Get window handle
*
* @return string
*/
public function getHandle()
{
return $this->windowHandle;
}
/**
* Constructor
*
* @param string $url URL
* @param string $windowHandle Window handle
*/
public function __construct($url, $windowHandle)
{
$this->windowHandle = $windowHandle;
parent::__construct($url . '/' . $windowHandle);
}
}