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,56 @@
name: CI
on:
push:
pull_request:
env:
DRIVER_URL: "http://localhost:4444/wd/hub"
jobs:
Mink:
runs-on: ubuntu-20.04
strategy:
matrix:
selenium: [ '2.53.1', '3.141.59' ]
php: [ '7.2', '7.3', '7.4', '8.0', '8.1' ]
fail-fast: false
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: "${{ matrix.php }}"
tools: composer
ini-values: error_reporting=-1, display_errors=On
- name: Install dependencies
run: |
composer config version 1.4.99
composer config preferred-install.behat/mink-selenium2-driver source
composer config preferred-install.* dist
composer require --no-update behat/mink-selenium2-driver:dev-master --dev --quiet
composer require --no-update mink/driver-testsuite:dev-master --dev --quiet
php -r '$json = json_decode(file_get_contents ("composer.json"), true); $json["autoload"]["psr-4"]["Behat\\Mink\\Tests\\Driver\\"] = "vendor/behat/mink-selenium2-driver/tests/"; file_put_contents("composer.json", json_encode($json, JSON_UNESCAPED_SLASHES|JSON_PRETTY_PRINT));'
composer update --no-interaction
- name: Start Selenium & Mink test server
run: |
mkdir logs
docker run --net host --name selenium --volume /dev/shm:/dev/shm --shm-size 2g "selenium/standalone-firefox:${{ matrix.selenium }}" &> logs/selenium.log &
vendor/bin/mink-test-server &> logs/mink-test-server.log &
while ! nc -z localhost 4444 </dev/null; do echo Waiting for remote driver to start...; sleep 1; done
while ! nc -z localhost 8002 </dev/null; do echo Waiting for PHP server to start...; sleep 1; done
- name: Run Mink tests
run: |
sed -i "s~vendor/~../../../vendor/~" vendor/behat/mink-selenium2-driver/phpunit.xml.dist
# remove test excludes once https://github.com/minkphp/MinkSelenium2Driver/pull/354 Selenium 3 issues are fixed
if [ "${{ matrix.selenium }}" = "3.141.59" ]; then
vendor/bin/phpunit --exclude-group none --no-coverage -v -c vendor/behat/mink-selenium2-driver --filter '^(?!Behat\\Mink\\Tests\\Driver\\(?:Basic\\IFrameTest::testIFrame|Js\\ChangeEventTest::testSetValueChangeEvent.*|Js\\WindowTest::testWindow|Custom\\TimeoutTest::testInvalidTimeoutSettingThrowsException|Js\\EventsTest::testRightClick)$)'
else
vendor/bin/phpunit --exclude-group none --no-coverage -v -c vendor/behat/mink-selenium2-driver
fi

View File

@@ -0,0 +1,8 @@
build/
logs/
nbproject/
vendor/
.idea/
.phpunit.result.cache
composer.lock
composer.phar

View File

@@ -0,0 +1,21 @@
WebDriver for Selenium 2
========================
This WebDriver client implementation is based on Facebook's [php-webdriver](https://github.com/facebook/php-webdriver/) project by Justin Bishop.
Distinguishing features:
* Up-to-date with [Selenium 2 JSON Wire Protocol](https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol) (including WebDriver commands yet to be documented).
* In the *master* branch, class names and file organization follow PSR-0 conventions for php 5.3+ namespaces.
* Coding style follows PSR-1, PSR-2, and Symfony2 conventions.
* Auto-generate API documentation via [phpDocumentor 2.x](http://phpdoc.org/).
[![Latest Stable Version](https://poser.pugx.org/instaclick/php-webdriver/v/stable.png)](https://packagist.org/packages/instaclick/php-webdriver)
[![Total Downloads](https://poser.pugx.org/instaclick/php-webdriver/downloads.png)](https://packagist.org/packages/instaclick/php-webdriver)
Downloads
=========
* [Packagist (dev-master)](http://packagist.org/packages/instaclick/php-webdriver)
* [Github](https://github.com/instaclick/php-webdriver)
Notes
=====
* The *5.2.x* branch is no longer maintained. This branch features class names and file re-organization that follow PEAR/ZF1 conventions. Bug fixes and enhancements from the master branch likely won't be backported.

View File

@@ -0,0 +1,45 @@
{
"name": "instaclick/php-webdriver",
"type": "library",
"description": "PHP WebDriver for Selenium 2",
"keywords": [
"selenium",
"webdriver",
"webtest",
"browser"
],
"homepage": "http://instaclick.com/",
"license": "Apache-2.0",
"authors": [
{
"name": "Justin Bishop",
"email": "jubishop@gmail.com",
"role": "Developer"
},
{
"name": "Anthon Pang",
"email": "apang@softwaredevelopment.ca",
"role": "Fork Maintainer"
}
],
"require": {
"php": ">=5.3.2",
"ext-curl": "*"
},
"require-dev": {
"satooshi/php-coveralls": "^1.0 || ^2.0",
"phpunit/phpunit": "^8.5 || ^9.5"
},
"minimum-stability": "dev",
"prefer-stable": true,
"autoload": {
"psr-0": {
"WebDriver": "lib/"
}
},
"extra": {
"branch-alias": {
"dev-master": "1.4.x-dev"
}
}
}

View File

@@ -0,0 +1,208 @@
php-webdriver -- A very thin wrapper of WebDriver
=================================================
## DESCRIPTION
This client aims to be as thin as possible, abusing the dynamic nature of PHP to allow almost all API calls to be a direct transformation of what is defined in the WebDriver protocol itself.
Most clients require you to first read the protocol to see what's possible, then study the client itself to see how to call it. This hopes to eliminate the latter step, and invites you to rely almost exclusively on https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol
Each command is just the name of a function call, and each additional path is just another chained function call. The function parameter is then either an array() if the command takes JSON parameters, or an individual primitive if it takes a URL parameter.
The function's return value is exactly what is returned from the server as part of the protocol definition. If an error is returned, the function will throw the appropriate WebDriverException instance.
## GETTING STARTED
* All you need as the server for this client is the selenium-server-standalone-#.jar file provided here: http://www.seleniumhq.org/download/
* Download and run that file, replacing # with the current server version.
java -jar selenium-server-standalone-#.jar
* Then when you create a session, be sure to pass the url to where your server is running.
// This would be the url of the host running the server-standalone.jar
$wd_host = 'http://localhost:4444/wd/hub'; // this is the default
$web_driver = new WebDriver($wd_host);
// First param to session() is the 'browserName' (default = 'firefox')
// Second param is a JSON object of additional 'desiredCapabilities'
// POST /session
$session = $web_driver->session('firefox');
* See also [wiki page for launching different browsers](https://github.com/facebook/php-webdriver/wiki/Launching-Browsers).
## SIMPLE EXAMPLES
### Note that all of these match the Protocol exactly
* Move to a specific spot on the screen
// POST /session/:sessionId/moveto
$session->moveto(array('xoffset' => 3, 'yoffset' => 300));
* Get the current url
// GET /session/:sessionId/url
$session->url();
* Change focus to another frame
// POST /session/:sessionId/frame
$session->frame(array('id' => 'some_frame_id'));
* Get a list of window handles for all open windows
// GET /session/:sessionId/window_handles
$session->window_handles();
* Accept the currently displayed alert dialog
// POST /session/:sessionId/accept_alert
$session->accept_alert();
* Change asynchronous script timeout
// POST /session/:sessionId/timeouts/async_script
$session->timeouts()->async_script(array('ms' => 2000));
* Doubleclick an element on a touch screen
// POST session/:sessionId/touch/doubleclick
$session->touch()->doubleclick(array('element' => $element->getID())
* Check if two elements are equal
// GET /session/:sessionId/element/:id/equals/:other
$element->equals($other_element->getID()))
* Get value of a css property on element
// GET /session/:sessionId/element/:id/css/:propertyName
$element->css($property_name)
## 'GET', 'POST', or 'DELETE' to the same command examples
### When you can do multiple http methods for the same command, call the command directly for the 'GET', and prepend the http method for the 'POST' or 'DELETE'.
* Set landscape orientation with 'POST'
// POST /session/:sessionId/orientation
$session->postOrientation(array('orientation' => 'LANDSCAPE'));
* Get landscape orientation with normal 'GET'
// GET /session/:sessionId/orientation
$session->orientation();
* Set size of window that has $window_handle with 'POST'
// If excluded, $window_handle defaults to 'current'
// POST /session/:sessionId/window/:windowHandle/size
$session
->window($window_handle)
->postSize(array('width' => 10, 'height' => 10));
* Get current window size with 'GET'
// GET /session/:sessionId/window/:windowHandle/size
$session->window()->size();
* Send keystrokes to an element with 'POST'
// POST /session/:sessionId/element/:id/value
// getValue() is deprecated; use postValue($json) or value($json)
$element->postValue(array("value" => str_split('some text to send to element')));
## Some unavoidable exceptions to direct protocol translation.
* Opening pages
// POST /session/:sessionId/url
$session->open('http://www.facebook.com');
* Dealing with the session
// DELETE /session/:sessionId
$session->close();
// GET /session/:sessionId
$session->capabilities();
* To find elements
// POST /session/:sessionId/element
$element = $session->element($using, $value);
// POST /session/:sessionId/elements
$session->elements($using, $value);
// POST /session/:sessionId/element/:id/element
$element->element($using, $value);
// POST /session/:sessionId/element/:id/elements
$element->elements($using, $value);
* To get the active element
// POST /session/:sessionId/element/active
$session->activeElement();
* To manipulate cookies
// GET /session/:sessionId/cookie
$session->getAllCookies();
// POST /session/:sessionId/cookie
$session->setCookie($cookie_json);
// DELETE /session/:sessionId/cookie
$session->deleteAllCookies()
// DELETE /session/:sessionId/cookie/:name
$session->deleteCookie($name)
* To manipulate windows
// POST /session/:sessionId/window
$session->focusWindow($window_handle);
// DELETE /session/:sessionId/window
$session->deleteWindow();
## More esoteric examples
* To set curl options (e.g., timeout and proxy settings)
```
use WebDriver\Service\CurlService;
use WebDriver\ServiceFactory;
class MyCurlService extends CurlService
{
const PROXY = 'http://proxyHost:8080';
const AUTH = 'proxyUser:proxyPassword';
/**
* {@inheritdoc}
*/
public function execute($requestMethod, $url, $parameters = null, $extraOptions = null)
{
$extraOptions = array_replace(
$extraOptions,
array(
CURLOPT_CONNECTTIMEOUT => 30,
CURLOPT_TIMEOUT => 300,
CURLOPT_PROXY => self::PROXY,
CURLOPT_PROXYUSERPWD => self::AUTH,
)
);
return parent::execute($requestMethod, $url, $parameters, $extraOptions);
}
}
ServiceFactory::setServiceClass('service.curl', 'MyCurlService');
```

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);
}
}

View File

@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8" ?>
<phpdoc>
<title>PHP WebDriver for Selenium 2</title>
<parser>
<target>doc</target>
<markers>
<item>TODO</item>
<item>FIXME</item>
</markers>
<extensions>
<extension>php</extension>
</extensions>
<visibility></visibility>
</parser>
<transformer>
<target>doc</target>
</transformer>
<logging>
<level>warn</level>
<paths>
<default>logs/phpdoc.log</default>
<errors>logs/phpdoc.errors.log</errors>
</paths>
</logging>
<transformations>
<template name="responsive" />
</transformations>
<files>
<directory>lib</directory>
</files>
</phpdoc>

View File

@@ -0,0 +1,4 @@
parameters:
level: 4
paths:
- lib

View File

@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- http://www.phpunit.de/manual/current/en/appendixes.configuration.html -->
<phpunit
backupGlobals = "false"
backupStaticAttributes = "false"
colors = "true"
convertErrorsToExceptions = "true"
convertNoticesToExceptions = "true"
convertWarningsToExceptions = "true"
processIsolation = "false"
stopOnFailure = "false"
syntaxCheck = "false"
bootstrap = "lib/WebDriver/ClassLoader.php" >
<testsuites>
<testsuite name="Project Test Suite">
<directory>test/Test</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory>lib</directory>
<exclude>
<file>__init__.php</file>
</exclude>
</whitelist>
</filter>
</phpunit>

View File

@@ -0,0 +1,10 @@
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
</head>
<body>
</body>
</html>

View File

@@ -0,0 +1,10 @@
#!/bin/sh
# set up Apache
# @see https://github.com/travis-ci/travis-ci.github.com/blob/master/docs/user/languages/php.md#apache--php
sudo a2enmod rewrite actions fastcgi alias ssl
# configure apache root dir
sudo sed -i -e "s,/var/www,$(pwd),g" /etc/apache2/sites-available/default
sudo service apache2 restart

View File

@@ -0,0 +1,7 @@
#!/bin/sh
# set up Selenium for functional tests
wget --max-redirect=1 https://goo.gl/s4o9Vx -O selenium.jar
java -jar selenium.jar &

View File

@@ -0,0 +1,52 @@
<?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 Test\WebDriver;
use WebDriver\Exception;
/**
* Test WebDriver\Exception class
*
* @package WebDriver
*
* @group Unit
*/
class ExceptionTest extends \PHPUnit_Framework_TestCase
{
/**
* test factory()
*/
public function testFactory()
{
$out = Exception::factory(255, 'wtf');
$this->assertTrue(get_class($out) === 'Exception');
$this->assertTrue($out->getMessage() === 'wtf');
$out = Exception::factory(Exception::SUCCESS);
$this->assertTrue(get_class($out) === 'Exception');
$this->assertTrue($out->getMessage() === 'Unknown Error');
$out = Exception::factory(Exception::CURL_EXEC);
$this->assertTrue($out instanceof Exception\CurlExec);
$this->assertTrue($out->getMessage() === 'curl_exec() error.');
}
}

View File

@@ -0,0 +1,46 @@
<?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 Test\WebDriver;
use WebDriver\Storage;
/**
* Test WebDriver\Storage class
*
* @package WebDriver
*
* @group Unit
*/
class StorageTest extends \PHPUnit_Framework_TestCase
{
/**
* test factory()
*/
public function testFactory()
{
$out = Storage::factory('Local', '/');
$this->assertTrue($out instanceof Storage\Local);
$out = Storage::factory('sEsSiOn', '/');
$this->assertTrue($out instanceof Storage\Session);
}
}

View File

@@ -0,0 +1,267 @@
<?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>
* @author Damian Mooyman <damian@silverstripe.com>
*/
namespace Test\WebDriver;
use WebDriver\ServiceFactory;
use WebDriver\WebDriver;
/**
* Test WebDriver\WebDriver class
*
* @package WebDriver
*
* @group Functional
*/
class WebDriverTest extends \PHPUnit_Framework_TestCase
{
private $driver;
private $session;
private $testDocumentRootUrl = 'http://localhost';
private $testSeleniumRootUrl = 'http://localhost:4444/wd/hub';
/**
* {@inheritdoc}
*/
protected function setUp()
{
ServiceFactory::getInstance()->setServiceClass('service.curl', '\\WebDriver\\Service\\CurlService');
if ($url = getenv('ROOT_URL')) {
$this->testDocumentRootUrl = $url;
}
if ($url = getenv('SELENIUM_URL')) {
$this->testSeleniumRootUrl = $url;
}
$this->driver = new WebDriver($this->getTestSeleniumRootUrl());
$this->session = null;
}
/**
* {@inheritdoc}
*/
protected function tearDown()
{
if ($this->session) {
$this->session->close();
}
}
/**
* Returns the full url to the test site (corresponding to the root dir of the library).
* You can set this via env var ROOT_URL
*
* @return string
*/
protected function getTestDocumentRootUrl()
{
return $this->testDocumentRootUrl;
}
/**
* Returns the full url to the Selenium server used for functional tests
*
* @return string
*
* @todo make this configurable via env var
*/
protected function getTestSeleniumRootUrl()
{
return $this->testSeleniumRootUrl;
}
/**
* Is Selenium down?
*
* @param \Exception $exception
*
* @return boolean
*/
protected function isSeleniumDown($exception)
{
return preg_match('/Failed to connect to .* Connection refused/', $exception->getMessage()) != false
|| strpos($exception->getMessage(), 'couldn\'t connect to host') !== false
|| strpos($exception->getMessage(), 'Unable to connect to host') !== false;
}
/**
* Test driver sessions
*/
public function testSessions()
{
try {
$this->assertCount(0, $this->driver->sessions());
$this->session = $this->driver->session();
} catch (\Exception $e) {
if ($this->isSeleniumDown($e)) {
$this->markTestSkipped('selenium server not running');
return;
}
throw $e;
}
$this->assertCount(1, $this->driver->sessions());
$this->assertEquals($this->getTestSeleniumRootUrl(), $this->driver->getUrl());
}
/**
* Test driver status
*/
public function testStatus()
{
try {
$status = $this->driver->status();
} catch (\Exception $e) {
if ($this->isSeleniumDown($e)) {
$this->markTestSkipped('selenium server not running');
return;
}
throw $e;
}
$this->assertCount(3, $status);
$this->assertTrue(isset($status['java']));
$this->assertTrue(isset($status['os']));
$this->assertTrue(isset($status['build']));
}
/**
* Checks that an error connecting to Selenium gives back the expected exception
*/
public function testSeleniumError()
{
try {
$this->driver = new WebDriver($this->getTestSeleniumRootUrl() . '/../invalidurl');
$status = $this->driver->status();
$this->fail('Exception not thrown while connecting to invalid Selenium url');
} catch (\Exception $e) {
if ($this->isSeleniumDown($e)) {
$this->markTestSkipped('selenium server not running');
return;
}
$this->assertEquals('WebDriver\Exception\CurlExec', get_class($e));
}
}
/**
* Checks that a successful command to Selenium which returns an http error response gives back the expected exception
*/
public function testSeleniumErrorResponse()
{
try {
$status = $this->driver->status();
} catch (\Exception $e) {
if ($this->isSeleniumDown($e)) {
$this->markTestSkipped('selenium server not running');
return;
}
throw $e;
}
try {
$this->session = $this->driver->session();
$this->session->open($this->getTestDocumentRootUrl().'/test/Assets/index.html');
$element = $this->session->element('id', 'a-quite-unlikely-html-element-id');
$this->fail('Exception not thrown while looking for missing element in page');
} catch (\Exception $e) {
$this->assertEquals('WebDriver\Exception\NoSuchElement', get_class($e));
}
}
/**
* Checks that a successful command to Selenium which returns 'nothing' according to spec does not raise an error
*/
public function testSeleniumNoResponse()
{
try {
$status = $this->driver->status();
} catch (\Exception $e) {
if ($this->isSeleniumDown($e)) {
$this->markTestSkipped('selenium server not running');
return;
}
throw $e;
}
$this->session = $this->driver->session();
$timeouts = $this->session->timeouts();
$out = $timeouts->async_script(array('type' => 'implicit', 'ms' => 1000));
$this->assertEquals(null, $out);
}
/**
* Assert that empty response does not trigger exception, but invalid JSON does
*/
public function testNonJsonResponse()
{
$mockCurlService = $this->createMock('WebDriver\Service\CurlService');
$mockCurlService->expects($this->once())
->method('execute')
->will($this->returnCallback(function ($requestMethod, $url) {
$info = array(
'url' => $url,
'request_method' => $requestMethod,
'http_code' => 200,
);
$result = preg_match('#.*session$#', $url)
? $result = 'some invalid json'
: $result = '';
return array($result, $info);
}));
ServiceFactory::getInstance()->setService('service.curl', $mockCurlService);
$result = $this->driver->status();
$this->assertNull($result);
// Test /session should error
$this->setExpectedException(
'WebDriver\Exception\CurlExec',
'Payload received from webdriver is not valid json: some invalid json'
);
$result = $this->driver->session();
$this->assertNull($result);
}
}