updated-packages

This commit is contained in:
RafficMohammed
2023-01-08 00:13:22 +05:30
parent 3ff7df7487
commit da241bacb6
12659 changed files with 563377 additions and 510538 deletions

View File

@@ -0,0 +1,228 @@
# Changelog
This project versioning adheres to [Semantic Versioning](http://semver.org/).
## Unreleased
## 1.13.1 - 2022-10-11
### Fixed
- Do not fail when using `isDisplayed()` and capabilities are missing in WebDriver instance. (Happens when driver instance was created using `RemoteWebDriver::createBySessionID()`.)
## 1.13.0 - 2022-10-03
### Added
- Support for current Firefox XPI extension format. Extensions could now be loaded into `FirefoxProfile` using `addExtension()` method.
- `setProfile()` method to `FirefoxOptions`, which is now a preferred way to set Firefox Profile.
- Element `isDisplayed()` can now be used even for browsers not supporting native API endpoint (like Safari), thanks to javascript atom workaround.
### Changed
- Handle errors when taking screenshots. `WebDriverException` is thrown if WebDriver returns empty or invalid screenshot data.
- Deprecate `FirefoxDriver::PROFILE` constant. Instead, use `setProfile()` method of `FirefoxOptions` to set Firefox Profile.
- Deprecate `getAllSessions()` method of `RemoteWebDriver` (which is not part of W3C WebDriver).
- Increase default request timeout to 3 minutes (instead of 30 seconds).
### Fixed
- Throw `UnknownErrorException` instead of fatal error if remote end returns invalid response for `findElement()`/`findElements()` commands.
## 1.12.1 - 2022-05-03
### Fixed
- Improper PHP documentation for `getAttribute()` and `getDomProperty()`.
- Unsafe use of `static::` when accessing private property in `DesiredCapabilities`.
- PHP 8.1 deprecations in the `Cookie` class.
### Changed
- Docs: Extend `findElement()`/`findElements()` method documentation to better explain XPath behavior.
- Add `@return` and `@param` type annotations to Cookie class to avoid deprecations in PHP 8.1.
## 1.12.0 - 2021-10-14
### Added
- `RemoteWebElement::getDomProperty()` method to read JavaScript properties of an element (like the value of `innerHTML` etc.) in W3C mode.
- `WebDriverCommand::newSession()` constructor to create new session command without violating typehints.
### Changed
- Allow installation of Symfony 6 components.
### Fixed
- PHP 8.1 compatibility.
## 1.11.1 - 2021-05-21
### Fixed
- `RemoteWebElement::getLocationOnScreenOnceScrolledIntoView()` was missing polyfill implementation for W3C mode and not working in eg. Safari.
## 1.11.0 - 2021-05-03
### Added
- `FirefoxOptions` class to simplify passing Firefox capabilities. Usage is covered [in our wiki](https://github.com/php-webdriver/php-webdriver/wiki/Firefox#firefoxoptions).
- `FirefoxDriver` to easy local start of Firefox instance without a need to start the `geckodriver` process manually. [See wiki](https://github.com/php-webdriver/php-webdriver/wiki/Firefox#start-directly-using-firefoxdriver-class) for usage examples.
- Method `ChromeDriver::startUsingDriverService()` to be used for creating ChromeDriver instance with custom service.
### Fixed
- Driver capabilities received from the browser when creating now session were not set to the instance of ChromeDriver (when ChromeDriver::start() was used).
### Changed
- Deprecate `ChromeDriver::startSession`. However, the method was supposed to be used only internally.
- KeyDown and KeyUp actions will throw an exception when not used with modifier keys.
## 1.10.0 - 2021-02-25
### Added
- Support for sending Chrome DevTools Protocol commands (see details in [wiki](https://github.com/php-webdriver/php-webdriver/wiki/Chrome#chrome-devtools-protocol-cdp)).
- Option to specify type of new window (window or tab) when using `$driver->switchTo()->newWindow()`.
### Fixed
- Actually start ChromeDriver in W3C mode if it is supported by the browser driver. Until now, when it was initialized using `ChromeDriver::start()`, it has always been unintentionally started in OSS mode.
- ChromeOptions were ignored when passed to DesiredCapabilities as `ChromeOptions::CAPABILITY_W3C`.
- Clicking a block element inside `<a>` element in Firefox (workaround for GeckoDriver bug [1374283](https://bugzilla.mozilla.org/show_bug.cgi?id=1374283)).
### Changed
- Throw `DriverServerDiedException` on local driver process terminating unexpectedly and provide full details of original exception to improve debugging.
- Do not require `WEBDRIVER_CHROME_DRIVER` environment variable to be set if `chromedriver` binary is already available via system PATH.
- Mark PhantomJS deprecated, as it is no longer developed and maintained.
- Deprecate `RemoteWebDriver::newWindow()` in favor of `$driver->switchTo()->newWindow()`.
- Don't escape slashes in CURL exception message to improve readability.
## 1.9.0 - 2020-11-19
### Added
- Support of SameSite cookie property.
- Command `RemoteWebDriver::newWindow()` for W3C mode to open new top-level browsing context (aka window).
- PHP 8.0 support.
## 1.8.3 - 2020-10-06
### Fixed
- Make `alertIsPresent()` condition working in W3C mode.
- `RemoteWebDriver::create()` cannot be used without providing the second parameter (which is in fact optional).
- `ChromeDriver::start()` starts in inconsistent state mixing W3C/OSS mode.
- Modifier keys are not released when sending NULL key in GeckoDriver (workaround for GeckoDriver bug [1494661](https://bugzilla.mozilla.org/show_bug.cgi?id=1494661)).
- Do not set unnecessary `binary` value of `goog:chromeOptions` while keep the object in proper data type required by ChromeDriver.
## 1.8.2 - 2020-03-04
### Changed
- Reimplement element `equals()` method to be working in W3C mode.
- New instance of `RemoteWebDriver` created via `createBySessionID()` by default expects W3C mode. This could be disabled using fifth parameter of `createBySessionID()`.
- Disable JSON viewer in Firefox to let JSON response be rendered as-is.
### Fixed
- Properly read fifth parameter whether W3C compliant instance should be created when using `createBySessionID()`.
- Creating of Firefox profile with libzip 1.6+ (eg. on Mac OS Catalina).
## 1.8.1 - 2020-02-17
### Fixed
- Accept array as possible input to `sendKeys()` method. (Unintentional BC break in 1.8.0.)
- Use relative offset when moving mouse pointer in W3C WebDriver mode.
## 1.8.0 - 2020-02-10
### Added
- Experimental W3C WebDriver protocol support. The protocol will be used automatically when remote end (like Geckodriver, newer Chromedriver etc.) supports it.
- `getStatus()` method of `RemoteWebDriver` to get information about remote-end readiness to create new sessions.
- `takeElementScreenshot()` method of `RemoteWebElement` to do the obvious - take screenshot of the particular element.
- Support for sending custom commands via `executeCustomCommand()`. See [wiki](https://github.com/php-webdriver/php-webdriver/wiki/Custom-commands) for more information.
### Changed
- The repository was migrated to [`php-webdriver/php-webdriver`](https://github.com/php-webdriver/php-webdriver/).
- The Packagist package was renamed to [`php-webdriver/webdriver`](https://packagist.org/packages/php-webdriver/webdriver) and the original [`facebook/webdriver`](https://packagist.org/packages/facebook/webdriver) was marked as abandoned.
- Revert no longer needed workaround for Chromedriver bug [2943](https://bugs.chromium.org/p/chromedriver/issues/detail?id=2943).
- Allow installation of Symfony 5 components.
- Rename environment variable used to pass path to ChromeDriver executable from `webdriver.chrome.driver` to `WEBDRIVER_CHROME_DRIVER`. However the old one also still works to keep backward compatibility
- If subdirectories in a path to screenshot destination does not exists (using `takeScreenshot()` or `takeElementScreenshot()` methods), they are automatically created.
- When zip archive cannot be created during file upload, throw an exception instead of silently returning false.
- `WebDriverNavigation` and `EventFiringWebDriverNavigation` now both implement new `WebDriverNavigationInterface`.
### Fixed
- `WebDriverExpectedCondition::presenceOfElementLocated()` works correctly when used within `WebDriverExpectedCondition::not()`.
- Improper behavior of Microsoft Edge when retrieving all cookies via `getCookies()` (it was causing fatal error when there were no cookies).
- Avoid "path is not canonical" error when uploading file to Chromedriver.
## 1.7.1 - 2019-06-13
### Fixed
- Error `Call to a member function toArray()` if capabilities were already converted to an array.
- Temporarily do not send capabilities to disable W3C WebDriver protocol when BrowserStack hub is used.
## 1.7.0 - 2019-06-10
### Added
- `WebDriverCheckboxes` and `WebDriverRadios` helper classes to simplify interaction with checkboxes and radio buttons.
### Fixed
- Stop sending null values in Cookie object, which is against the protocol and may cause request to remote ends to fail.
### Changed
- Force Chrome to not use W3C WebDriver protocol.
- Add workaround for Chromedriver bug [2943](https://bugs.chromium.org/p/chromedriver/issues/detail?id=2943) which breaks the protocol in Chromedriver 75.
## 1.6.0 - 2018-05-16
### Added
- Connection and request timeouts could be specified also when creating RemoteWebDriver from existing session ID.
- Update PHPDoc for functions that return static instances of a class.
### Changed
- Disable sending 'Expect: 100-Continue' header with POST requests, as they may more easily fail when sending via eg. squid proxy.
## 1.5.0 - 2017-11-15
### Changed
- Drop PHP 5.5 support, the minimal required version of PHP is now PHP 5.6.
- Allow installation of Symfony 4 components.
### Added
- Add a `visibilityOfAnyElementsLocated()` method to `WebDriverExpectedCondition`.
## 1.4.1 - 2017-04-28
### Fixed
- Do not throw notice `Constant CURLOPT_CONNECTTIMEOUT_MS already defined`.
## 1.4.0 - 2017-03-22
### Changed
- Cookies should now be set using `Cookie` value object instead of an array when passed to to `addCookie()` method of `WebDriverOptions`.
- Cookies retrieved using `getCookieNamed()` and `getCookies()` methods of `WebDriverOptions` are now encapsulated in `Cookie` object instead of an plain array. The object implements `ArrayAccess` interface to provide backward compatibility.
- `ext-zip` is now specified as required dependency in composer.json (but the extension was already required by the code, though).
- Deprecate `WebDriverCapabilities::isJavascriptEnabled()` method.
- Deprecate `textToBePresentInElementValue` expected condition in favor of `elementValueContains`.
### Fixed
- Do not throw fatal error when `null` is passed to `sendKeys()`.
## 1.3.0 - 2017-01-13
### Added
- Added `getCapabilities()` method of `RemoteWebDriver`, to retrieve actual capabilities acknowledged by the remote driver on startup.
- Added option to pass required capabilities when creating `RemoteWebDriver`. (So far only desired capabilities were supported.)
- Added new expected conditions:
- `urlIs` - current URL exactly equals given value
- `urlContains` - current URL contains given text
- `urlMatches` - current URL matches regular expression
- `titleMatches` - current page title matches regular expression
- `elementTextIs` - text in element exactly equals given text
- `elementTextContains` (as an alias for `textToBePresentInElement`) - text in element contains given text
- `elementTextMatches` - text in element matches regular expression
- `numberOfWindowsToBe` - number of opened windows equals given number
- Possibility to select option of `<select>` by its partial text (using `selectByVisiblePartialText()`).
- `XPathEscaper` helper class to quote XPaths containing both single and double quotes.
- `WebDriverSelectInterface`, to allow implementation of custom select-like components, eg. those not built around and actual select tag.
### Changed
- `Symfony\Process` is used to start local WebDriver processes (when browsers are run directly, without Selenium server) to workaround some PHP bugs and improve portability.
- Clarified meaning of selenium server URL variable in methods of `RemoteWebDriver` class.
- Deprecated `setSessionID()` and `setCommandExecutor()` methods of `RemoteWebDriver` class; these values should be immutable and thus passed only via constructor.
- Deprecated `WebDriverExpectedCondition::textToBePresentInElement()` in favor of `elementTextContains()`.
- Throw an exception when attempting to deselect options of non-multiselect (it already didn't have any effect, but was silently ignored).
- Optimize performance of `(de)selectByIndex()` and `getAllSelectedOptions()` methods of `WebDriverSelect` when used with non-multiple select element.
### Fixed
- XPath escaping in `select*()` and `deselect*()` methods of `WebDriverSelect`.
## 1.2.0 - 2016-10-14
- Added initial support of remote Microsoft Edge browser (but starting local EdgeDriver is still not supported).
- Utilize late static binding to make eg. `WebDriverBy` and `DesiredCapabilities` classes easily extensible.
- PHP version at least 5.5 is required.
- Fixed incompatibility with Appium, caused by redundant params present in requests to Selenium server.
## 1.1.3 - 2016-08-10
- Fixed FirefoxProfile to support installation of extensions with custom namespace prefix in their manifest file.
- Comply codestyle with [PSR-2](http://www.php-fig.org/psr/psr-2/).
## 1.1.2 - 2016-06-04
- Added ext-curl to composer.json.
- Added CHANGELOG.md.
- Added CONTRIBUTING.md with information and rules for contributors.
## 1.1.1 - 2015-12-31
- Fixed strict standards error in `ChromeDriver`.
- Added unit tests for `WebDriverCommand` and `DesiredCapabilities`.
- Fixed retrieving temporary path name in `FirefoxDriver` when `open_basedir` restriction is in effect.
## 1.1.0 - 2015-12-08
- FirefoxProfile improved - added possibility to set RDF file and to add datas for extensions.
- Fixed setting 0 second timeout of `WebDriverWait`.

View File

@@ -0,0 +1,22 @@
MIT License
Copyright (c) 2004-2020 Facebook
Copyright (c) 2020-present [open-source contributors](https://github.com/php-webdriver/php-webdriver/graphs/contributors)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

228
vendor/php-webdriver/webdriver/README.md vendored Normal file
View File

@@ -0,0 +1,228 @@
# php-webdriver Selenium WebDriver bindings for PHP
[![Latest stable version](https://img.shields.io/packagist/v/php-webdriver/webdriver.svg?style=flat-square&label=Packagist)](https://packagist.org/packages/php-webdriver/webdriver)
[![GitHub Actions build status](https://img.shields.io/github/workflow/status/php-webdriver/php-webdriver/Tests?style=flat-square&label=GitHub%20Actions)](https://github.com/php-webdriver/php-webdriver/actions)
[![SauceLabs test status](https://img.shields.io/github/workflow/status/php-webdriver/php-webdriver/Sauce%20Labs?style=flat-square&label=SauceLabs)](https://saucelabs.com/u/php-webdriver)
[![Total downloads](https://img.shields.io/packagist/dd/php-webdriver/webdriver.svg?style=flat-square&label=Downloads)](https://packagist.org/packages/php-webdriver/webdriver)
## Description
Php-webdriver library is PHP language binding for Selenium WebDriver, which allows you to control web browsers from PHP.
This library is compatible with Selenium server version 2.x, 3.x and 4.x.
The library supports modern [W3C WebDriver](https://w3c.github.io/webdriver/) protocol, as well
as legacy [JsonWireProtocol](https://www.selenium.dev/documentation/legacy/json_wire_protocol/).
The concepts of this library are very similar to the "official" Java, JavaScript, .NET, Python and Ruby libraries
which are developed as part of the [Selenium project](https://github.com/SeleniumHQ/selenium/).
## Installation
Installation is possible using [Composer](https://getcomposer.org/).
If you don't already use Composer, you can download the `composer.phar` binary:
curl -sS https://getcomposer.org/installer | php
Then install the library:
php composer.phar require php-webdriver/webdriver
## Upgrade from version <1.8.0
Starting from version 1.8.0, the project has been renamed from `facebook/php-webdriver` to `php-webdriver/webdriver`.
In order to receive the new version and future updates, **you need to rename it in your composer.json**:
```diff
"require": {
- "facebook/webdriver": "(version you use)",
+ "php-webdriver/webdriver": "(version you use)",
}
```
and run `composer update`.
## Getting started
### 1. Start server (aka. remote end)
To control a browser, you need to start a *remote end* (server), which will listen to the commands sent
from this library and will execute them in the respective browser.
This could be Selenium standalone server, but for local development, you can send them directly to so-called "browser driver" like Chromedriver or Geckodriver.
#### a) Chromedriver
📙 Below you will find a simple example. Make sure to read our wiki for [more information on Chrome/Chromedriver](https://github.com/php-webdriver/php-webdriver/wiki/Chrome).
Install the latest Chrome and [Chromedriver](https://sites.google.com/chromium.org/driver/downloads).
Make sure to have a compatible version of Chromedriver and Chrome!
Run `chromedriver` binary, you can pass `port` argument, so that it listens on port 4444:
```sh
chromedriver --port=4444
```
#### b) Geckodriver
📙 Below you will find a simple example. Make sure to read our wiki for [more information on Firefox/Geckodriver](https://github.com/php-webdriver/php-webdriver/wiki/Firefox).
Install the latest Firefox and [Geckodriver](https://github.com/mozilla/geckodriver/releases).
Make sure to have a compatible version of Geckodriver and Firefox!
Run `geckodriver` binary (it start to listen on port 4444 by default):
```sh
geckodriver
```
#### c) Selenium standalone server
Selenium server can be useful when you need to execute multiple tests at once,
when you run tests in several different browsers (like on your CI server), or when you need to distribute tests amongst
several machines in grid mode (where one Selenium server acts as a hub, and others connect to it as nodes).
Selenium server then act like a proxy and takes care of distributing commands to the respective nodes.
The latest version can be found on the [Selenium download page](https://www.selenium.dev/downloads/).
📙 You can find [further Selenium server information](https://github.com/php-webdriver/php-webdriver/wiki/Selenium-server)
in our wiki.
#### d) Docker
Selenium server could also be started inside Docker container - see [docker-selenium project](https://github.com/SeleniumHQ/docker-selenium).
### 2. Create a Browser Session
When creating a browser session, be sure to pass the url of your running server.
For example:
```php
// Chromedriver (if started using --port=4444 as above)
$serverUrl = 'http://localhost:4444';
// Geckodriver
$serverUrl = 'http://localhost:4444';
// selenium-server-standalone-#.jar (version 2.x or 3.x)
$serverUrl = 'http://localhost:4444/wd/hub';
// selenium-server-standalone-#.jar (version 4.x)
$serverUrl = 'http://localhost:4444';
```
Now you can start browser of your choice:
```php
use Facebook\WebDriver\Remote\RemoteWebDriver;
// Chrome
$driver = RemoteWebDriver::create($serverUrl, DesiredCapabilities::chrome());
// Firefox
$driver = RemoteWebDriver::create($serverUrl, DesiredCapabilities::firefox());
// Microsoft Edge
$driver = RemoteWebDriver::create($serverUrl, DesiredCapabilities::microsoftEdge());
```
### 3. Customize Desired Capabilities
Desired capabilities define properties of the browser you are about to start.
They can be customized:
```php
use Facebook\WebDriver\Firefox\FirefoxOptions;
use Facebook\WebDriver\Remote\DesiredCapabilities;
$desiredCapabilities = DesiredCapabilities::firefox();
// Disable accepting SSL certificates
$desiredCapabilities->setCapability('acceptSslCerts', false);
// Add arguments via FirefoxOptions to start headless firefox
$firefoxOptions = new FirefoxOptions();
$firefoxOptions->addArguments(['-headless']);
$desiredCapabilities->setCapability(FirefoxOptions::CAPABILITY, $firefoxOptions);
$driver = RemoteWebDriver::create($serverUrl, $desiredCapabilities);
```
Capabilities can also be used to [📙 configure a proxy server](https://github.com/php-webdriver/php-webdriver/wiki/HowTo-Work-with-proxy) which the browser should use.
To configure browser-specific capabilities, you may use [📙 ChromeOptions](https://github.com/php-webdriver/php-webdriver/wiki/Chrome#chromeoptions)
or [📙 FirefoxOptions](https://github.com/php-webdriver/php-webdriver/wiki/Firefox#firefoxoptions).
* See [legacy JsonWire protocol](https://github.com/SeleniumHQ/selenium/wiki/DesiredCapabilities) documentation or [W3C WebDriver specification](https://w3c.github.io/webdriver/#capabilities) for more details.
### 4. Control your browser
```php
// Go to URL
$driver->get('https://en.wikipedia.org/wiki/Selenium_(software)');
// Find search element by its id, write 'PHP' inside and submit
$driver->findElement(WebDriverBy::id('searchInput')) // find search input element
->sendKeys('PHP') // fill the search box
->submit(); // submit the whole form
// Find element of 'History' item in menu by its css selector
$historyButton = $driver->findElement(
WebDriverBy::cssSelector('#ca-history a')
);
// Read text of the element and print it to output
echo 'About to click to a button with text: ' . $historyButton->getText();
// Click the element to navigate to revision history page
$historyButton->click();
// Make sure to always call quit() at the end to terminate the browser session
$driver->quit();
```
See [example.php](example.php) for full example scenario.
Visit our GitHub wiki for [📙 php-webdriver command reference](https://github.com/php-webdriver/php-webdriver/wiki/Example-command-reference) and further examples.
**NOTE:** Above snippets are not intended to be a working example by simply copy-pasting. See [example.php](example.php) for a working example.
## Changelog
For latest changes see [CHANGELOG.md](CHANGELOG.md) file.
## More information
Some basic usage example is provided in [example.php](example.php) file.
How-tos are provided right here in [📙 our GitHub wiki](https://github.com/php-webdriver/php-webdriver/wiki).
If you don't use IDE, you may use [API documentation of php-webdriver](https://php-webdriver.github.io/php-webdriver/latest/).
You may also want to check out the Selenium project [docs](https://selenium.dev/documentation/en/) and [wiki](https://github.com/SeleniumHQ/selenium/wiki).
## Testing framework integration
To take advantage of automatized testing you may want to integrate php-webdriver to your testing framework.
There are some projects already providing this:
- [Symfony Panther](https://github.com/symfony/panther) uses php-webdriver and integrates with PHPUnit using `PantherTestCase`
- [Laravel Dusk](https://laravel.com/docs/dusk) is another project using php-webdriver, could be used for testing via `DuskTestCase`
- [Steward](https://github.com/lmc-eu/steward) integrates php-webdriver directly to [PHPUnit](https://phpunit.de/), and provides parallelization
- [Codeception](https://codeception.com/) testing framework provides BDD-layer on top of php-webdriver in its [WebDriver module](https://codeception.com/docs/modules/WebDriver)
- You can also check out this [blogpost](https://codeception.com/11-12-2013/working-with-phpunit-and-selenium-webdriver.html) + [demo project](https://github.com/DavertMik/php-webdriver-demo), describing simple [PHPUnit](https://phpunit.de/) integration
## Support
We have a great community willing to help you!
❓ Do you have a **question, idea or some general feedback**? Visit our [Discussions](https://github.com/php-webdriver/php-webdriver/discussions) page.
(Alternatively, you can [look for many answered questions also on StackOverflow](https://stackoverflow.com/questions/tagged/php+selenium-webdriver)).
🐛 Something isn't working, and you want to **report a bug**? [Submit it here](https://github.com/php-webdriver/php-webdriver/issues/new) as a new issue.
📙 Looking for a **how-to** or **reference documentation**? See [our wiki](https://github.com/php-webdriver/php-webdriver/wiki).
## Contributing ❤️
We love to have your help to make php-webdriver better. See [CONTRIBUTING.md](.github/CONTRIBUTING.md) for more information about contributing and developing php-webdriver.
Php-webdriver is community project - if you want to join the effort with maintaining and developing this library, the best is to look on [issues marked with "help wanted"](https://github.com/php-webdriver/php-webdriver/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22)
label. Let us know in the issue comments if you want to contribute and if you want any guidance, and we will be delighted to help you to prepare your pull request.

View File

@@ -0,0 +1,99 @@
{
"name": "php-webdriver/webdriver",
"description": "A PHP client for Selenium WebDriver. Previously facebook/webdriver.",
"license": "MIT",
"type": "library",
"keywords": [
"webdriver",
"selenium",
"php",
"geckodriver",
"chromedriver"
],
"homepage": "https://github.com/php-webdriver/php-webdriver",
"require": {
"php": "^5.6 || ~7.0 || ^8.0",
"ext-curl": "*",
"ext-json": "*",
"ext-zip": "*",
"symfony/polyfill-mbstring": "^1.12",
"symfony/process": "^2.8 || ^3.1 || ^4.0 || ^5.0 || ^6.0"
},
"require-dev": {
"ondram/ci-detector": "^2.1 || ^3.5 || ^4.0",
"php-coveralls/php-coveralls": "^2.4",
"php-mock/php-mock-phpunit": "^1.1 || ^2.0",
"php-parallel-lint/php-parallel-lint": "^1.2",
"phpunit/phpunit": "^5.7 || ^7 || ^8 || ^9",
"squizlabs/php_codesniffer": "^3.5",
"symfony/var-dumper": "^3.3 || ^4.0 || ^5.0 || ^6.0"
},
"replace": {
"facebook/webdriver": "*"
},
"suggest": {
"ext-SimpleXML": "For Firefox profile creation"
},
"minimum-stability": "dev",
"autoload": {
"psr-4": {
"Facebook\\WebDriver\\": "lib/"
},
"files": [
"lib/Exception/TimeoutException.php"
]
},
"autoload-dev": {
"psr-4": {
"Facebook\\WebDriver\\": [
"tests/unit",
"tests/functional"
]
},
"classmap": [
"tests/functional/"
]
},
"config": {
"allow-plugins": {
"ergebnis/composer-normalize": true
},
"sort-packages": true
},
"scripts": {
"post-install-cmd": [
"php -r 'if (PHP_VERSION_ID > 70103) { exit(1); }' || composer install --working-dir=tools/php-cs-fixer --no-progress --no-interaction"
],
"post-update-cmd": [
"php -r 'if (PHP_VERSION_ID > 70103) { exit(1); }' || composer update --working-dir=tools/php-cs-fixer --no-progress --no-interaction"
],
"all": [
"@lint",
"@analyze",
"@test"
],
"analyze": [
"vendor/bin/phpstan analyze -c phpstan.neon --ansi",
"tools/php-cs-fixer/vendor/bin/php-cs-fixer fix --diff --dry-run -vvv --ansi",
"vendor/bin/phpcs --standard=PSR2 --ignore=*.js ./lib/ ./tests/"
],
"fix": [
"@composer normalize",
"tools/php-cs-fixer/vendor/bin/php-cs-fixer fix --diff -vvv || exit 0",
"vendor/bin/phpcbf --standard=PSR2 --ignore=*.js ./lib/ ./tests/"
],
"lint": [
"vendor/bin/parallel-lint -j 10 ./lib ./tests example.php",
"@composer validate",
"@composer normalize --dry-run"
],
"preinstall": [
"@composer update --no-progress --no-interaction",
"@composer require --dev phpstan/phpstan",
"@composer require --dev ergebnis/composer-normalize"
],
"test": [
"vendor/bin/phpunit --colors=always"
]
}
}

View File

@@ -0,0 +1,240 @@
<?php
namespace Facebook\WebDriver;
use Facebook\WebDriver\Exception\NoSuchElementException;
use Facebook\WebDriver\Exception\UnexpectedTagNameException;
use Facebook\WebDriver\Exception\WebDriverException;
use Facebook\WebDriver\Support\XPathEscaper;
/**
* Provides helper methods for checkboxes and radio buttons.
*/
abstract class AbstractWebDriverCheckboxOrRadio implements WebDriverSelectInterface
{
/** @var WebDriverElement */
protected $element;
/** @var string */
protected $type;
/** @var string */
protected $name;
public function __construct(WebDriverElement $element)
{
$tagName = $element->getTagName();
if ($tagName !== 'input') {
throw new UnexpectedTagNameException('input', $tagName);
}
$this->name = $element->getAttribute('name');
if ($this->name === null) {
throw new WebDriverException('The input does not have a "name" attribute.');
}
$this->element = $element;
}
public function getOptions()
{
return $this->getRelatedElements();
}
public function getAllSelectedOptions()
{
$selectedElement = [];
foreach ($this->getRelatedElements() as $element) {
if ($element->isSelected()) {
$selectedElement[] = $element;
if (!$this->isMultiple()) {
return $selectedElement;
}
}
}
return $selectedElement;
}
public function getFirstSelectedOption()
{
foreach ($this->getRelatedElements() as $element) {
if ($element->isSelected()) {
return $element;
}
}
throw new NoSuchElementException(
sprintf('No %s are selected', $this->type === 'radio' ? 'radio buttons' : 'checkboxes')
);
}
public function selectByIndex($index)
{
$this->byIndex($index);
}
public function selectByValue($value)
{
$this->byValue($value);
}
public function selectByVisibleText($text)
{
$this->byVisibleText($text);
}
public function selectByVisiblePartialText($text)
{
$this->byVisibleText($text, true);
}
/**
* Selects or deselects a checkbox or a radio button by its value.
*
* @param string $value
* @param bool $select
* @throws NoSuchElementException
*/
protected function byValue($value, $select = true)
{
$matched = false;
foreach ($this->getRelatedElements($value) as $element) {
$select ? $this->selectOption($element) : $this->deselectOption($element);
if (!$this->isMultiple()) {
return;
}
$matched = true;
}
if (!$matched) {
throw new NoSuchElementException(
sprintf('Cannot locate %s with value: %s', $this->type, $value)
);
}
}
/**
* Selects or deselects a checkbox or a radio button by its index.
*
* @param int $index
* @param bool $select
* @throws NoSuchElementException
*/
protected function byIndex($index, $select = true)
{
$elements = $this->getRelatedElements();
if (!isset($elements[$index])) {
throw new NoSuchElementException(sprintf('Cannot locate %s with index: %d', $this->type, $index));
}
$select ? $this->selectOption($elements[$index]) : $this->deselectOption($elements[$index]);
}
/**
* Selects or deselects a checkbox or a radio button by its visible text.
*
* @param string $text
* @param bool $partial
* @param bool $select
*/
protected function byVisibleText($text, $partial = false, $select = true)
{
foreach ($this->getRelatedElements() as $element) {
$normalizeFilter = sprintf(
$partial ? 'contains(normalize-space(.), %s)' : 'normalize-space(.) = %s',
XPathEscaper::escapeQuotes($text)
);
$xpath = 'ancestor::label';
$xpathNormalize = sprintf('%s[%s]', $xpath, $normalizeFilter);
$id = $element->getAttribute('id');
if ($id !== null) {
$idFilter = sprintf('@for = %s', XPathEscaper::escapeQuotes($id));
$xpath .= sprintf(' | //label[%s]', $idFilter);
$xpathNormalize .= sprintf(' | //label[%s and %s]', $idFilter, $normalizeFilter);
}
try {
$element->findElement(WebDriverBy::xpath($xpathNormalize));
} catch (NoSuchElementException $e) {
if ($partial) {
continue;
}
try {
// Since the mechanism of getting the text in xpath is not the same as
// webdriver, use the expensive getText() to check if nothing is matched.
if ($text !== $element->findElement(WebDriverBy::xpath($xpath))->getText()) {
continue;
}
} catch (NoSuchElementException $e) {
continue;
}
}
$select ? $this->selectOption($element) : $this->deselectOption($element);
if (!$this->isMultiple()) {
return;
}
}
}
/**
* Gets checkboxes or radio buttons with the same name.
*
* @param string|null $value
* @return WebDriverElement[]
*/
protected function getRelatedElements($value = null)
{
$valueSelector = $value ? sprintf(' and @value = %s', XPathEscaper::escapeQuotes($value)) : '';
$formId = $this->element->getAttribute('form');
if ($formId === null) {
$form = $this->element->findElement(WebDriverBy::xpath('ancestor::form'));
$formId = $form->getAttribute('id');
if ($formId === '' || $formId === null) {
return $form->findElements(WebDriverBy::xpath(
sprintf('.//input[@name = %s%s]', XPathEscaper::escapeQuotes($this->name), $valueSelector)
));
}
}
// https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#form
return $this->element->findElements(
WebDriverBy::xpath(sprintf(
'//form[@id = %1$s]//input[@name = %2$s%3$s'
. ' and ((boolean(@form) = true() and @form = %1$s) or boolean(@form) = false())]'
. ' | //input[@form = %1$s and @name = %2$s%3$s]',
XPathEscaper::escapeQuotes($formId),
XPathEscaper::escapeQuotes($this->name),
$valueSelector
))
);
}
/**
* Selects a checkbox or a radio button.
*/
protected function selectOption(WebDriverElement $element)
{
if (!$element->isSelected()) {
$element->click();
}
}
/**
* Deselects a checkbox or a radio button.
*/
protected function deselectOption(WebDriverElement $element)
{
if ($element->isSelected()) {
$element->click();
}
}
}

View File

@@ -0,0 +1,46 @@
<?php
namespace Facebook\WebDriver\Chrome;
use Facebook\WebDriver\Remote\RemoteWebDriver;
/**
* Provide access to Chrome DevTools Protocol (CDP) commands via HTTP endpoint of Chromedriver.
*
* @see https://chromedevtools.github.io/devtools-protocol/
*/
class ChromeDevToolsDriver
{
const SEND_COMMAND = [
'method' => 'POST',
'url' => '/session/:sessionId/goog/cdp/execute',
];
/**
* @var RemoteWebDriver
*/
private $driver;
public function __construct(RemoteWebDriver $driver)
{
$this->driver = $driver;
}
/**
* Executes a Chrome DevTools command
*
* @param string $command The DevTools command to execute
* @param array $parameters Optional parameters to the command
* @return array The result of the command
*/
public function execute($command, array $parameters = [])
{
$params = ['cmd' => $command, 'params' => (object) $parameters];
return $this->driver->executeCustomCommand(
self::SEND_COMMAND['url'],
self::SEND_COMMAND['method'],
$params
);
}
}

View File

@@ -0,0 +1,105 @@
<?php
namespace Facebook\WebDriver\Chrome;
use Facebook\WebDriver\Local\LocalWebDriver;
use Facebook\WebDriver\Remote\DesiredCapabilities;
use Facebook\WebDriver\Remote\Service\DriverCommandExecutor;
use Facebook\WebDriver\Remote\WebDriverCommand;
class ChromeDriver extends LocalWebDriver
{
/** @var ChromeDevToolsDriver */
private $devTools;
/**
* Creates a new ChromeDriver using default configuration.
* This includes starting a new chromedriver process each time this method is called. However this may be
* unnecessary overhead - instead, you can start the process once using ChromeDriverService and pass
* this instance to startUsingDriverService() method.
*
* @todo Remove $service parameter. Use `ChromeDriver::startUsingDriverService` to pass custom $service instance.
* @return static
*/
public static function start(DesiredCapabilities $desired_capabilities = null, ChromeDriverService $service = null)
{
if ($service === null) { // TODO: Remove the condition (always create default service)
$service = ChromeDriverService::createDefaultService();
}
return static::startUsingDriverService($service, $desired_capabilities);
}
/**
* Creates a new ChromeDriver using given ChromeDriverService.
* This is usable when you for example don't want to start new chromedriver process for each individual test
* and want to reuse the already started chromedriver, which will lower the overhead associated with spinning up
* a new process.
* @return static
*/
public static function startUsingDriverService(
ChromeDriverService $service,
DesiredCapabilities $capabilities = null
) {
if ($capabilities === null) {
$capabilities = DesiredCapabilities::chrome();
}
$executor = new DriverCommandExecutor($service);
$newSessionCommand = WebDriverCommand::newSession(
[
'capabilities' => [
'firstMatch' => [(object) $capabilities->toW3cCompatibleArray()],
],
'desiredCapabilities' => (object) $capabilities->toArray(),
]
);
$response = $executor->execute($newSessionCommand);
/*
* TODO: in next major version we may not need to use this method, because without OSS compatibility the
* driver creation is straightforward.
*/
return static::createFromResponse($response, $executor);
}
/**
* @todo Remove in next major version. The class is internally no longer used and is kept only to keep BC.
* @deprecated Use start or startUsingDriverService method instead.
* @codeCoverageIgnore
* @internal
*/
public function startSession(DesiredCapabilities $desired_capabilities)
{
$command = WebDriverCommand::newSession(
[
'capabilities' => [
'firstMatch' => [(object) $desired_capabilities->toW3cCompatibleArray()],
],
'desiredCapabilities' => (object) $desired_capabilities->toArray(),
]
);
$response = $this->executor->execute($command);
$value = $response->getValue();
if (!$this->isW3cCompliant = isset($value['capabilities'])) {
$this->executor->disableW3cCompliance();
}
$this->sessionID = $response->getSessionID();
}
/**
* @return ChromeDevToolsDriver
*/
public function getDevTools()
{
if ($this->devTools === null) {
$this->devTools = new ChromeDevToolsDriver($this);
}
return $this->devTools;
}
}

View File

@@ -0,0 +1,37 @@
<?php
namespace Facebook\WebDriver\Chrome;
use Facebook\WebDriver\Remote\Service\DriverService;
class ChromeDriverService extends DriverService
{
/**
* The environment variable storing the path to the chrome driver executable.
* @deprecated Use ChromeDriverService::CHROME_DRIVER_EXECUTABLE
*/
const CHROME_DRIVER_EXE_PROPERTY = 'webdriver.chrome.driver';
/** @var string The environment variable storing the path to the chrome driver executable */
const CHROME_DRIVER_EXECUTABLE = 'WEBDRIVER_CHROME_DRIVER';
/**
* @var string Default executable used when no other is provided
* @internal
*/
const DEFAULT_EXECUTABLE = 'chromedriver';
/**
* @return static
*/
public static function createDefaultService()
{
$pathToExecutable = getenv(self::CHROME_DRIVER_EXECUTABLE) ?: getenv(self::CHROME_DRIVER_EXE_PROPERTY);
if ($pathToExecutable === false || $pathToExecutable === '') {
$pathToExecutable = static::DEFAULT_EXECUTABLE;
}
$port = 9515; // TODO: Get another port if the default port is used.
$args = ['--port=' . $port];
return new static($pathToExecutable, $port, $args);
}
}

View File

@@ -0,0 +1,185 @@
<?php
namespace Facebook\WebDriver\Chrome;
use Facebook\WebDriver\Remote\DesiredCapabilities;
use JsonSerializable;
use ReturnTypeWillChange;
/**
* The class manages the capabilities in ChromeDriver.
*
* @see https://sites.google.com/a/chromium.org/chromedriver/capabilities
*/
class ChromeOptions implements JsonSerializable
{
/**
* The key of chromeOptions in desired capabilities (in legacy OSS JsonWire protocol)
* @todo Replace value with 'goog:chromeOptions' after JsonWire protocol support is removed
*/
const CAPABILITY = 'chromeOptions';
/**
* The key of chromeOptions in desired capabilities (in W3C compatible protocol)
*/
const CAPABILITY_W3C = 'goog:chromeOptions';
/**
* @var array
*/
private $arguments = [];
/**
* @var string
*/
private $binary = '';
/**
* @var array
*/
private $extensions = [];
/**
* @var array
*/
private $experimentalOptions = [];
/**
* Return a version of the class which can JSON serialized.
*
* @return array
*/
#[ReturnTypeWillChange]
public function jsonSerialize()
{
return $this->toArray();
}
/**
* Sets the path of the Chrome executable. The path should be either absolute
* or relative to the location running ChromeDriver server.
*
* @param string $path
* @return ChromeOptions
*/
public function setBinary($path)
{
$this->binary = $path;
return $this;
}
/**
* @param array $arguments
* @return ChromeOptions
*/
public function addArguments(array $arguments)
{
$this->arguments = array_merge($this->arguments, $arguments);
return $this;
}
/**
* Add a Chrome extension to install on browser startup. Each path should be
* a packed Chrome extension.
*
* @param array $paths
* @return ChromeOptions
*/
public function addExtensions(array $paths)
{
foreach ($paths as $path) {
$this->addExtension($path);
}
return $this;
}
/**
* @param array $encoded_extensions An array of base64 encoded of the extensions.
* @return ChromeOptions
*/
public function addEncodedExtensions(array $encoded_extensions)
{
foreach ($encoded_extensions as $encoded_extension) {
$this->addEncodedExtension($encoded_extension);
}
return $this;
}
/**
* Sets an experimental option which has not exposed officially.
*
* When using "prefs" to set Chrome preferences, please be aware they are so far not supported by
* Chrome running in headless mode, see https://bugs.chromium.org/p/chromium/issues/detail?id=775911
*
* @param string $name
* @param mixed $value
* @return ChromeOptions
*/
public function setExperimentalOption($name, $value)
{
$this->experimentalOptions[$name] = $value;
return $this;
}
/**
* @return DesiredCapabilities The DesiredCapabilities for Chrome with this options.
*/
public function toCapabilities()
{
$capabilities = DesiredCapabilities::chrome();
$capabilities->setCapability(self::CAPABILITY, $this);
return $capabilities;
}
/**
* @return \ArrayObject|array
*/
public function toArray()
{
// The selenium server expects a 'dictionary' instead of a 'list' when
// reading the chrome option. However, an empty array in PHP will be
// converted to a 'list' instead of a 'dictionary'. To fix it, we work
// with `ArrayObject`
$options = new \ArrayObject($this->experimentalOptions);
if (!empty($this->binary)) {
$options['binary'] = $this->binary;
}
if (!empty($this->arguments)) {
$options['args'] = $this->arguments;
}
if (!empty($this->extensions)) {
$options['extensions'] = $this->extensions;
}
return $options;
}
/**
* Add a Chrome extension to install on browser startup. Each path should be a
* packed Chrome extension.
*
* @param string $path
* @return ChromeOptions
*/
private function addExtension($path)
{
$this->addEncodedExtension(base64_encode(file_get_contents($path)));
return $this;
}
/**
* @param string $encoded_extension Base64 encoded of the extension.
* @return ChromeOptions
*/
private function addEncodedExtension($encoded_extension)
{
$this->extensions[] = $encoded_extension;
return $this;
}
}

View File

@@ -0,0 +1,278 @@
<?php
namespace Facebook\WebDriver;
use InvalidArgumentException;
/**
* Set values of an cookie.
*
* Implements ArrayAccess for backwards compatibility.
*
* @see https://w3c.github.io/webdriver/webdriver-spec.html#cookies
*/
class Cookie implements \ArrayAccess
{
/** @var array */
protected $cookie = [];
/**
* @param string $name The name of the cookie; may not be null or an empty string.
* @param string $value The cookie value; may not be null.
*/
public function __construct($name, $value)
{
$this->validateCookieName($name);
$this->validateCookieValue($value);
$this->cookie['name'] = $name;
$this->cookie['value'] = $value;
}
/**
* @param array $cookieArray The cookie fields; must contain name and value.
* @return Cookie
*/
public static function createFromArray(array $cookieArray)
{
if (!isset($cookieArray['name'])) {
throw new InvalidArgumentException('Cookie name should be set');
}
if (!isset($cookieArray['value'])) {
throw new InvalidArgumentException('Cookie value should be set');
}
$cookie = new self($cookieArray['name'], $cookieArray['value']);
if (isset($cookieArray['path'])) {
$cookie->setPath($cookieArray['path']);
}
if (isset($cookieArray['domain'])) {
$cookie->setDomain($cookieArray['domain']);
}
if (isset($cookieArray['expiry'])) {
$cookie->setExpiry($cookieArray['expiry']);
}
if (isset($cookieArray['secure'])) {
$cookie->setSecure($cookieArray['secure']);
}
if (isset($cookieArray['httpOnly'])) {
$cookie->setHttpOnly($cookieArray['httpOnly']);
}
if (isset($cookieArray['sameSite'])) {
$cookie->setSameSite($cookieArray['sameSite']);
}
return $cookie;
}
/**
* @return string
*/
public function getName()
{
return $this->offsetGet('name');
}
/**
* @return string
*/
public function getValue()
{
return $this->offsetGet('value');
}
/**
* The path the cookie is visible to. Defaults to "/" if omitted.
*
* @param string $path
*/
public function setPath($path)
{
$this->offsetSet('path', $path);
}
/**
* @return string|null
*/
public function getPath()
{
return $this->offsetGet('path');
}
/**
* The domain the cookie is visible to. Defaults to the current browsing context's document's URL domain if omitted.
*
* @param string $domain
*/
public function setDomain($domain)
{
if (mb_strpos($domain, ':') !== false) {
throw new InvalidArgumentException(sprintf('Cookie domain "%s" should not contain a port', $domain));
}
$this->offsetSet('domain', $domain);
}
/**
* @return string|null
*/
public function getDomain()
{
return $this->offsetGet('domain');
}
/**
* The cookie's expiration date, specified in seconds since Unix Epoch.
*
* @param int $expiry
*/
public function setExpiry($expiry)
{
$this->offsetSet('expiry', (int) $expiry);
}
/**
* @return int|null
*/
public function getExpiry()
{
return $this->offsetGet('expiry');
}
/**
* Whether this cookie requires a secure connection (https). Defaults to false if omitted.
*
* @param bool $secure
*/
public function setSecure($secure)
{
$this->offsetSet('secure', $secure);
}
/**
* @return bool|null
*/
public function isSecure()
{
return $this->offsetGet('secure');
}
/**
* Whether the cookie is an HTTP only cookie. Defaults to false if omitted.
*
* @param bool $httpOnly
*/
public function setHttpOnly($httpOnly)
{
$this->offsetSet('httpOnly', $httpOnly);
}
/**
* @return bool|null
*/
public function isHttpOnly()
{
return $this->offsetGet('httpOnly');
}
/**
* The cookie's same-site value.
*
* @param string $sameSite
*/
public function setSameSite($sameSite)
{
$this->offsetSet('sameSite', $sameSite);
}
/**
* @return string|null
*/
public function getSameSite()
{
return $this->offsetGet('sameSite');
}
/**
* @return array
*/
public function toArray()
{
$cookie = $this->cookie;
if (!isset($cookie['secure'])) {
// Passing a boolean value for the "secure" flag is mandatory when using geckodriver
$cookie['secure'] = false;
}
return $cookie;
}
/**
* @param mixed $offset
* @return bool
*/
#[\ReturnTypeWillChange]
public function offsetExists($offset)
{
return isset($this->cookie[$offset]);
}
/**
* @param mixed $offset
* @return mixed
*/
#[\ReturnTypeWillChange]
public function offsetGet($offset)
{
return $this->offsetExists($offset) ? $this->cookie[$offset] : null;
}
/**
* @param mixed $offset
* @param mixed $value
* @return void
*/
#[\ReturnTypeWillChange]
public function offsetSet($offset, $value)
{
if ($value === null) {
unset($this->cookie[$offset]);
} else {
$this->cookie[$offset] = $value;
}
}
/**
* @param mixed $offset
* @return void
*/
#[\ReturnTypeWillChange]
public function offsetUnset($offset)
{
unset($this->cookie[$offset]);
}
/**
* @param string $name
*/
protected function validateCookieName($name)
{
if ($name === null || $name === '') {
throw new InvalidArgumentException('Cookie name should be non-empty');
}
if (mb_strpos($name, ';') !== false) {
throw new InvalidArgumentException('Cookie name should not contain a ";"');
}
}
/**
* @param string $value
*/
protected function validateCookieValue($value)
{
if ($value === null) {
throw new InvalidArgumentException('Cookie value is required when setting a cookie');
}
}
}

View File

@@ -0,0 +1,10 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* A command failed because the referenced shadow root is no longer attached to the DOM.
*/
class DetachedShadowRootException extends WebDriverException
{
}

View File

@@ -0,0 +1,15 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* The driver server process is unexpectedly no longer available.
*/
class DriverServerDiedException extends WebDriverException
{
public function __construct(\Exception $previous = null)
{
parent::__construct('The driver server has died.');
\Exception::__construct($this->getMessage(), $this->getCode(), $previous);
}
}

View File

@@ -0,0 +1,11 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* The Element Click command could not be completed because the element receiving the events is obscuring the element
* that was requested clicked.
*/
class ElementClickInterceptedException extends WebDriverException
{
}

View File

@@ -0,0 +1,10 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* A command could not be completed because the element is not pointer- or keyboard interactable.
*/
class ElementNotInteractableException extends WebDriverException
{
}

View File

@@ -0,0 +1,10 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* @deprecated Use Facebook\WebDriver\Exception\ElementNotInteractableException
*/
class ElementNotSelectableException extends ElementNotInteractableException
{
}

View File

@@ -0,0 +1,10 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* @deprecated Removed in W3C WebDriver, see https://github.com/php-webdriver/php-webdriver/pull/686
*/
class ElementNotVisibleException extends WebDriverException
{
}

View File

@@ -0,0 +1,10 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* @deprecated Removed in W3C WebDriver, see https://github.com/php-webdriver/php-webdriver/pull/686
*/
class ExpectedException extends WebDriverException
{
}

View File

@@ -0,0 +1,10 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* @deprecated Removed in W3C WebDriver, see https://github.com/php-webdriver/php-webdriver/pull/686
*/
class IMEEngineActivationFailedException extends WebDriverException
{
}

View File

@@ -0,0 +1,10 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* @deprecated Removed in W3C WebDriver, see https://github.com/php-webdriver/php-webdriver/pull/686
*/
class IMENotAvailableException extends WebDriverException
{
}

View File

@@ -0,0 +1,10 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* @deprecated Removed in W3C WebDriver, see https://github.com/php-webdriver/php-webdriver/pull/686
*/
class IndexOutOfBoundsException extends WebDriverException
{
}

View File

@@ -0,0 +1,11 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* Navigation caused the user agent to hit a certificate warning, which is usually the result of an expired
* or invalid TLS certificate.
*/
class InsecureCertificateException extends WebDriverException
{
}

View File

@@ -0,0 +1,10 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* The arguments passed to a command are either invalid or malformed.
*/
class InvalidArgumentException extends WebDriverException
{
}

View File

@@ -0,0 +1,10 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* An illegal attempt was made to set a cookie under a different domain than the current page.
*/
class InvalidCookieDomainException extends WebDriverException
{
}

View File

@@ -0,0 +1,10 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* @deprecated Removed in W3C WebDriver, see https://github.com/php-webdriver/php-webdriver/pull/686
*/
class InvalidCoordinatesException extends WebDriverException
{
}

View File

@@ -0,0 +1,11 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* A command could not be completed because the element is in an invalid state, e.g. attempting to clear an element
* that isnt both editable and resettable.
*/
class InvalidElementStateException extends WebDriverException
{
}

View File

@@ -0,0 +1,10 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* Argument was an invalid selector.
*/
class InvalidSelectorException extends WebDriverException
{
}

View File

@@ -0,0 +1,11 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* Occurs if the given session id is not in the list of active sessions, meaning the session either does not exist
* or that its not active.
*/
class InvalidSessionIdException extends WebDriverException
{
}

View File

@@ -0,0 +1,10 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* An error occurred while executing JavaScript supplied by the user.
*/
class JavascriptErrorException extends WebDriverException
{
}

View File

@@ -0,0 +1,10 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* The target for mouse interaction is not in the browsers viewport and cannot be brought into that viewport.
*/
class MoveTargetOutOfBoundsException extends WebDriverException
{
}

View File

@@ -0,0 +1,10 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* @deprecated Use Facebook\WebDriver\Exception\NoSuchAlertException
*/
class NoAlertOpenException extends NoSuchAlertException
{
}

View File

@@ -0,0 +1,10 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* @deprecated Removed in W3C WebDriver, see https://github.com/php-webdriver/php-webdriver/pull/686
*/
class NoCollectionException extends WebDriverException
{
}

View File

@@ -0,0 +1,10 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* @deprecated Removed in W3C WebDriver, see https://github.com/php-webdriver/php-webdriver/pull/686
*/
class NoScriptResultException extends WebDriverException
{
}

View File

@@ -0,0 +1,10 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* @deprecated Removed in W3C WebDriver, see https://github.com/php-webdriver/php-webdriver/pull/686
*/
class NoStringException extends WebDriverException
{
}

View File

@@ -0,0 +1,10 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* @deprecated Removed in W3C WebDriver, see https://github.com/php-webdriver/php-webdriver/pull/686
*/
class NoStringLengthException extends WebDriverException
{
}

View File

@@ -0,0 +1,10 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* @deprecated Removed in W3C WebDriver, see https://github.com/php-webdriver/php-webdriver/pull/686
*/
class NoStringWrapperException extends WebDriverException
{
}

View File

@@ -0,0 +1,10 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* An attempt was made to operate on a modal dialog when one was not open.
*/
class NoSuchAlertException extends WebDriverException
{
}

View File

@@ -0,0 +1,10 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* @deprecated Removed in W3C WebDriver, see https://github.com/php-webdriver/php-webdriver/pull/686
*/
class NoSuchCollectionException extends WebDriverException
{
}

View File

@@ -0,0 +1,11 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* No cookie matching the given path name was found amongst the associated cookies of the current browsing contexts
* active document.
*/
class NoSuchCookieException extends WebDriverException
{
}

View File

@@ -0,0 +1,10 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* @deprecated Use Facebook\WebDriver\Exception\NoSuchWindowException
*/
class NoSuchDocumentException extends NoSuchWindowException
{
}

View File

@@ -0,0 +1,10 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* @deprecated Removed in W3C WebDriver, see https://github.com/php-webdriver/php-webdriver/pull/686
*/
class NoSuchDriverException extends WebDriverException
{
}

View File

@@ -0,0 +1,10 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* An element could not be located on the page using the given search parameters.
*/
class NoSuchElementException extends WebDriverException
{
}

View File

@@ -0,0 +1,10 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* A command to switch to a frame could not be satisfied because the frame could not be found.
*/
class NoSuchFrameException extends WebDriverException
{
}

View File

@@ -0,0 +1,10 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* The element does not have a shadow root.
*/
class NoSuchShadowRootException extends WebDriverException
{
}

View File

@@ -0,0 +1,10 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* A command to switch to a window could not be satisfied because the window could not be found.
*/
class NoSuchWindowException extends WebDriverException
{
}

View File

@@ -0,0 +1,10 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* @deprecated Removed in W3C WebDriver, see https://github.com/php-webdriver/php-webdriver/pull/686
*/
class NullPointerException extends WebDriverException
{
}

View File

@@ -0,0 +1,10 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* A script did not complete before its timeout expired.
*/
class ScriptTimeoutException extends WebDriverException
{
}

View File

@@ -0,0 +1,10 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* A new session could not be created.
*/
class SessionNotCreatedException extends WebDriverException
{
}

View File

@@ -0,0 +1,10 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* A command failed because the referenced element is no longer attached to the DOM.
*/
class StaleElementReferenceException extends WebDriverException
{
}

View File

@@ -0,0 +1,10 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* An operation did not complete before its timeout expired.
*/
class TimeoutException extends WebDriverException
{
}

View File

@@ -0,0 +1,10 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* A screen capture was made impossible.
*/
class UnableToCaptureScreenException extends WebDriverException
{
}

View File

@@ -0,0 +1,10 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* A command to set a cookies value could not be satisfied.
*/
class UnableToSetCookieException extends WebDriverException
{
}

View File

@@ -0,0 +1,10 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* A modal dialog was open, blocking this operation.
*/
class UnexpectedAlertOpenException extends WebDriverException
{
}

View File

@@ -0,0 +1,10 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* @deprecated Use Facebook\WebDriver\Exception\JavascriptErrorException
*/
class UnexpectedJavascriptException extends JavascriptErrorException
{
}

View File

@@ -0,0 +1,23 @@
<?php
namespace Facebook\WebDriver\Exception;
class UnexpectedTagNameException extends WebDriverException
{
/**
* @param string $expected_tag_name
* @param string $actual_tag_name
*/
public function __construct(
$expected_tag_name,
$actual_tag_name
) {
parent::__construct(
sprintf(
'Element should have been "%s" but was "%s"',
$expected_tag_name,
$actual_tag_name
)
);
}
}

View File

@@ -0,0 +1,10 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* A command could not be executed because the remote end is not aware of it.
*/
class UnknownCommandException extends WebDriverException
{
}

View File

@@ -0,0 +1,10 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* An unknown error occurred in the remote end while processing the command.
*/
class UnknownErrorException extends WebDriverException
{
}

View File

@@ -0,0 +1,10 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* The requested command matched a known URL but did not match an method for that URL.
*/
class UnknownMethodException extends WebDriverException
{
}

View File

@@ -0,0 +1,10 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* @deprecated Use Facebook\WebDriver\Exception\UnknownErrorException
*/
class UnknownServerException extends UnknownErrorException
{
}

View File

@@ -0,0 +1,7 @@
<?php
namespace Facebook\WebDriver\Exception;
class UnrecognizedExceptionException extends WebDriverException
{
}

View File

@@ -0,0 +1,10 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* Indicates that a command that should have executed properly cannot be supported for some reason.
*/
class UnsupportedOperationException extends WebDriverException
{
}

View File

@@ -0,0 +1,7 @@
<?php
namespace Facebook\WebDriver\Exception;
class WebDriverCurlException extends WebDriverException
{
}

View File

@@ -0,0 +1,226 @@
<?php
namespace Facebook\WebDriver\Exception;
use Exception;
/**
* @see https://w3c.github.io/webdriver/#errors
*/
class WebDriverException extends Exception
{
private $results;
/**
* @param string $message
* @param mixed $results
*/
public function __construct($message, $results = null)
{
parent::__construct($message);
$this->results = $results;
}
/**
* @return mixed
*/
public function getResults()
{
return $this->results;
}
/**
* Throw WebDriverExceptions based on WebDriver status code.
*
* @param int|string $status_code
* @param string $message
* @param mixed $results
*
* @throws ElementClickInterceptedException
* @throws ElementNotInteractableException
* @throws ElementNotSelectableException
* @throws ElementNotVisibleException
* @throws ExpectedException
* @throws IMEEngineActivationFailedException
* @throws IMENotAvailableException
* @throws IndexOutOfBoundsException
* @throws InsecureCertificateException
* @throws InvalidArgumentException
* @throws InvalidCookieDomainException
* @throws InvalidCoordinatesException
* @throws InvalidElementStateException
* @throws InvalidSelectorException
* @throws InvalidSessionIdException
* @throws JavascriptErrorException
* @throws MoveTargetOutOfBoundsException
* @throws NoAlertOpenException
* @throws NoCollectionException
* @throws NoScriptResultException
* @throws NoStringException
* @throws NoStringLengthException
* @throws NoStringWrapperException
* @throws NoSuchAlertException
* @throws NoSuchCollectionException
* @throws NoSuchCookieException
* @throws NoSuchDocumentException
* @throws NoSuchDriverException
* @throws NoSuchElementException
* @throws NoSuchFrameException
* @throws NoSuchWindowException
* @throws NullPointerException
* @throws ScriptTimeoutException
* @throws SessionNotCreatedException
* @throws StaleElementReferenceException
* @throws TimeoutException
* @throws UnableToCaptureScreenException
* @throws UnableToSetCookieException
* @throws UnexpectedAlertOpenException
* @throws UnexpectedJavascriptException
* @throws UnknownCommandException
* @throws UnknownErrorException
* @throws UnknownMethodException
* @throws UnknownServerException
* @throws UnrecognizedExceptionException
* @throws UnsupportedOperationException
* @throws XPathLookupException
*/
public static function throwException($status_code, $message, $results)
{
if (is_string($status_code)) {
// @see https://w3c.github.io/webdriver/#errors
switch ($status_code) {
case 'element click intercepted':
throw new ElementClickInterceptedException($message, $results);
case 'element not interactable':
throw new ElementNotInteractableException($message, $results);
case 'insecure certificate':
throw new InsecureCertificateException($message, $results);
case 'invalid argument':
throw new InvalidArgumentException($message, $results);
case 'invalid cookie domain':
throw new InvalidCookieDomainException($message, $results);
case 'invalid element state':
throw new InvalidElementStateException($message, $results);
case 'invalid selector':
throw new InvalidSelectorException($message, $results);
case 'invalid session id':
throw new InvalidSessionIdException($message, $results);
case 'javascript error':
throw new JavascriptErrorException($message, $results);
case 'move target out of bounds':
throw new MoveTargetOutOfBoundsException($message, $results);
case 'no such alert':
throw new NoSuchAlertException($message, $results);
case 'no such cookie':
throw new NoSuchCookieException($message, $results);
case 'no such element':
throw new NoSuchElementException($message, $results);
case 'no such frame':
throw new NoSuchFrameException($message, $results);
case 'no such window':
throw new NoSuchWindowException($message, $results);
case 'no such shadow root':
throw new NoSuchShadowRootException($message, $results);
case 'script timeout':
throw new ScriptTimeoutException($message, $results);
case 'session not created':
throw new SessionNotCreatedException($message, $results);
case 'stale element reference':
throw new StaleElementReferenceException($message, $results);
case 'detached shadow root':
throw new DetachedShadowRootException($message, $results);
case 'timeout':
throw new TimeoutException($message, $results);
case 'unable to set cookie':
throw new UnableToSetCookieException($message, $results);
case 'unable to capture screen':
throw new UnableToCaptureScreenException($message, $results);
case 'unexpected alert open':
throw new UnexpectedAlertOpenException($message, $results);
case 'unknown command':
throw new UnknownCommandException($message, $results);
case 'unknown error':
throw new UnknownErrorException($message, $results);
case 'unknown method':
throw new UnknownMethodException($message, $results);
case 'unsupported operation':
throw new UnsupportedOperationException($message, $results);
default:
throw new UnrecognizedExceptionException($message, $results);
}
}
switch ($status_code) {
case 1:
throw new IndexOutOfBoundsException($message, $results);
case 2:
throw new NoCollectionException($message, $results);
case 3:
throw new NoStringException($message, $results);
case 4:
throw new NoStringLengthException($message, $results);
case 5:
throw new NoStringWrapperException($message, $results);
case 6:
throw new NoSuchDriverException($message, $results);
case 7:
throw new NoSuchElementException($message, $results);
case 8:
throw new NoSuchFrameException($message, $results);
case 9:
throw new UnknownCommandException($message, $results);
case 10:
throw new StaleElementReferenceException($message, $results);
case 11:
throw new ElementNotVisibleException($message, $results);
case 12:
throw new InvalidElementStateException($message, $results);
case 13:
throw new UnknownServerException($message, $results);
case 14:
throw new ExpectedException($message, $results);
case 15:
throw new ElementNotSelectableException($message, $results);
case 16:
throw new NoSuchDocumentException($message, $results);
case 17:
throw new UnexpectedJavascriptException($message, $results);
case 18:
throw new NoScriptResultException($message, $results);
case 19:
throw new XPathLookupException($message, $results);
case 20:
throw new NoSuchCollectionException($message, $results);
case 21:
throw new TimeoutException($message, $results);
case 22:
throw new NullPointerException($message, $results);
case 23:
throw new NoSuchWindowException($message, $results);
case 24:
throw new InvalidCookieDomainException($message, $results);
case 25:
throw new UnableToSetCookieException($message, $results);
case 26:
throw new UnexpectedAlertOpenException($message, $results);
case 27:
throw new NoAlertOpenException($message, $results);
case 28:
throw new ScriptTimeoutException($message, $results);
case 29:
throw new InvalidCoordinatesException($message, $results);
case 30:
throw new IMENotAvailableException($message, $results);
case 31:
throw new IMEEngineActivationFailedException($message, $results);
case 32:
throw new InvalidSelectorException($message, $results);
case 33:
throw new SessionNotCreatedException($message, $results);
case 34:
throw new MoveTargetOutOfBoundsException($message, $results);
default:
throw new UnrecognizedExceptionException($message, $results);
}
}
}

View File

@@ -0,0 +1,10 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* @deprecated Removed in W3C WebDriver, see https://github.com/php-webdriver/php-webdriver/pull/686
*/
class XPathLookupException extends WebDriverException
{
}

View File

@@ -0,0 +1,68 @@
<?php
namespace Facebook\WebDriver\Firefox;
use Facebook\WebDriver\Local\LocalWebDriver;
use Facebook\WebDriver\Remote\DesiredCapabilities;
use Facebook\WebDriver\Remote\Service\DriverCommandExecutor;
use Facebook\WebDriver\Remote\WebDriverCommand;
class FirefoxDriver extends LocalWebDriver
{
/**
* @deprecated Pass Firefox Profile using FirefoxOptions:
* $firefoxOptions = new FirefoxOptions();
* $firefoxOptions->setProfile($profile->encode());
* $capabilities = DesiredCapabilities::firefox();
* $capabilities->setCapability(FirefoxOptions::CAPABILITY, $firefoxOptions);
*/
const PROFILE = 'firefox_profile';
/**
* Creates a new FirefoxDriver using default configuration.
* This includes starting a new geckodriver process each time this method is called. However this may be
* unnecessary overhead - instead, you can start the process once using FirefoxDriverService and pass
* this instance to startUsingDriverService() method.
*
* @return static
*/
public static function start(DesiredCapabilities $capabilities = null)
{
$service = FirefoxDriverService::createDefaultService();
return static::startUsingDriverService($service, $capabilities);
}
/**
* Creates a new FirefoxDriver using given FirefoxDriverService.
* This is usable when you for example don't want to start new geckodriver process for each individual test
* and want to reuse the already started geckodriver, which will lower the overhead associated with spinning up
* a new process.
*
* @return static
*/
public static function startUsingDriverService(
FirefoxDriverService $service,
DesiredCapabilities $capabilities = null
) {
if ($capabilities === null) {
$capabilities = DesiredCapabilities::firefox();
}
$executor = new DriverCommandExecutor($service);
$newSessionCommand = WebDriverCommand::newSession(
[
'capabilities' => [
'firstMatch' => [(object) $capabilities->toW3cCompatibleArray()],
],
]
);
$response = $executor->execute($newSessionCommand);
$returnedCapabilities = DesiredCapabilities::createFromW3cCapabilities($response->getValue()['capabilities']);
$sessionId = $response->getSessionID();
return new static($executor, $sessionId, $returnedCapabilities, true);
}
}

View File

@@ -0,0 +1,34 @@
<?php
namespace Facebook\WebDriver\Firefox;
use Facebook\WebDriver\Remote\Service\DriverService;
class FirefoxDriverService extends DriverService
{
/**
* @var string Name of the environment variable storing the path to the driver binary
*/
const WEBDRIVER_FIREFOX_DRIVER = 'WEBDRIVER_FIREFOX_DRIVER';
/**
* @var string Default executable used when no other is provided
* @internal
*/
const DEFAULT_EXECUTABLE = 'geckodriver';
/**
* @return static
*/
public static function createDefaultService()
{
$pathToExecutable = getenv(static::WEBDRIVER_FIREFOX_DRIVER);
if ($pathToExecutable === false || $pathToExecutable === '') {
$pathToExecutable = static::DEFAULT_EXECUTABLE;
}
$port = 9515; // TODO: Get another free port if the default port is used.
$args = ['-p=' . $port];
return new static($pathToExecutable, $port, $args);
}
}

View File

@@ -0,0 +1,133 @@
<?php
namespace Facebook\WebDriver\Firefox;
use ReturnTypeWillChange;
/**
* Class to manage Firefox-specific capabilities
*
* @see https://developer.mozilla.org/en-US/docs/Web/WebDriver/Capabilities/firefoxOptions
*/
class FirefoxOptions implements \JsonSerializable
{
/** @var string The key of FirefoxOptions in desired capabilities */
const CAPABILITY = 'moz:firefoxOptions';
/** @var string */
const OPTION_ARGS = 'args';
/** @var string */
const OPTION_PREFS = 'prefs';
/** @var string */
const OPTION_PROFILE = 'profile';
/** @var array */
private $options = [];
/** @var array */
private $arguments = [];
/** @var array */
private $preferences = [];
/** @var FirefoxProfile */
private $profile;
public function __construct()
{
// Set default preferences:
// disable the "Reader View" help tooltip, which can hide elements in the window.document
$this->setPreference(FirefoxPreferences::READER_PARSE_ON_LOAD_ENABLED, false);
// disable JSON viewer and let JSON be rendered as raw data
$this->setPreference(FirefoxPreferences::DEVTOOLS_JSONVIEW, false);
}
/**
* Directly set firefoxOptions.
* Use `addArguments` to add command line arguments and `setPreference` to set Firefox about:config entry.
*
* @param string $name
* @param mixed $value
* @return self
*/
public function setOption($name, $value)
{
if ($name === self::OPTION_PREFS) {
throw new \InvalidArgumentException('Use setPreference() method to set Firefox preferences');
}
if ($name === self::OPTION_ARGS) {
throw new \InvalidArgumentException('Use addArguments() method to add Firefox arguments');
}
if ($name === self::OPTION_PROFILE) {
throw new \InvalidArgumentException('Use setProfile() method to set Firefox profile');
}
$this->options[$name] = $value;
return $this;
}
/**
* Command line arguments to pass to the Firefox binary.
* These must include the leading dash (-) where required, e.g. ['-headless'].
*
* @see https://developer.mozilla.org/en-US/docs/Web/WebDriver/Capabilities/firefoxOptions#args
* @param string[] $arguments
* @return self
*/
public function addArguments(array $arguments)
{
$this->arguments = array_merge($this->arguments, $arguments);
return $this;
}
/**
* Set Firefox preference (about:config entry).
*
* @see http://kb.mozillazine.org/About:config_entries
* @see https://developer.mozilla.org/en-US/docs/Web/WebDriver/Capabilities/firefoxOptions#prefs
* @param string $name
* @param string|bool|int $value
* @return self
*/
public function setPreference($name, $value)
{
$this->preferences[$name] = $value;
return $this;
}
/**
* @see https://github.com/php-webdriver/php-webdriver/wiki/Firefox#firefox-profile
* @param FirefoxProfile $profile
* @return self
*/
public function setProfile(FirefoxProfile $profile)
{
$this->profile = $profile;
return $this;
}
/**
* @return array
*/
public function toArray()
{
$array = $this->options;
if (!empty($this->arguments)) {
$array[self::OPTION_ARGS] = $this->arguments;
}
if (!empty($this->preferences)) {
$array[self::OPTION_PREFS] = $this->preferences;
}
if (!empty($this->profile)) {
$array[self::OPTION_PROFILE] = $this->profile->encode();
}
return $array;
}
#[ReturnTypeWillChange]
public function jsonSerialize()
{
return new \ArrayObject($this->toArray());
}
}

View File

@@ -0,0 +1,25 @@
<?php
namespace Facebook\WebDriver\Firefox;
/**
* Constants of common Firefox profile preferences (about:config values).
* @see http://kb.mozillazine.org/Firefox_:_FAQs_:_About:config_Entries
*
* @codeCoverageIgnore
*/
class FirefoxPreferences
{
/** @var string Port WebDriver uses to communicate with Firefox instance */
const WEBDRIVER_FIREFOX_PORT = 'webdriver_firefox_port';
/** @var string Should the reader view (FF 38+) be enabled? */
const READER_PARSE_ON_LOAD_ENABLED = 'reader.parse-on-load.enabled';
/** @var string Browser homepage */
const BROWSER_STARTUP_HOMEPAGE = 'browser.startup.homepage';
/** @var string Should the Devtools JSON view be enabled? */
const DEVTOOLS_JSONVIEW = 'devtools.jsonview.enabled';
private function __construct()
{
}
}

View File

@@ -0,0 +1,308 @@
<?php
namespace Facebook\WebDriver\Firefox;
use Facebook\WebDriver\Exception\WebDriverException;
use FilesystemIterator;
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
use ZipArchive;
class FirefoxProfile
{
/**
* @var array
*/
private $preferences = [];
/**
* @var array
*/
private $extensions = [];
/**
* @var array
*/
private $extensions_datas = [];
/**
* @var string
*/
private $rdf_file;
/**
* @param string $extension The path to the xpi extension.
* @return FirefoxProfile
*/
public function addExtension($extension)
{
$this->extensions[] = $extension;
return $this;
}
/**
* @param string $extension_datas The path to the folder containing the datas to add to the extension
* @return FirefoxProfile
*/
public function addExtensionDatas($extension_datas)
{
if (!is_dir($extension_datas)) {
return null;
}
$this->extensions_datas[basename($extension_datas)] = $extension_datas;
return $this;
}
/**
* @param string $rdf_file The path to the rdf file
* @return FirefoxProfile
*/
public function setRdfFile($rdf_file)
{
if (!is_file($rdf_file)) {
return null;
}
$this->rdf_file = $rdf_file;
return $this;
}
/**
* @param string $key
* @param string|bool|int $value
* @throws WebDriverException
* @return FirefoxProfile
*/
public function setPreference($key, $value)
{
if (is_string($value)) {
$value = sprintf('"%s"', $value);
} else {
if (is_int($value)) {
$value = sprintf('%d', $value);
} else {
if (is_bool($value)) {
$value = $value ? 'true' : 'false';
} else {
throw new WebDriverException(
'The value of the preference should be either a string, int or bool.'
);
}
}
}
$this->preferences[$key] = $value;
return $this;
}
/**
* @param mixed $key
* @return mixed
*/
public function getPreference($key)
{
if (array_key_exists($key, $this->preferences)) {
return $this->preferences[$key];
}
return null;
}
/**
* @return string
*/
public function encode()
{
$temp_dir = $this->createTempDirectory('WebDriverFirefoxProfile');
if (isset($this->rdf_file)) {
copy($this->rdf_file, $temp_dir . DIRECTORY_SEPARATOR . 'mimeTypes.rdf');
}
foreach ($this->extensions as $extension) {
$this->installExtension($extension, $temp_dir);
}
foreach ($this->extensions_datas as $dirname => $extension_datas) {
mkdir($temp_dir . DIRECTORY_SEPARATOR . $dirname);
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($extension_datas, RecursiveDirectoryIterator::SKIP_DOTS),
RecursiveIteratorIterator::SELF_FIRST
);
foreach ($iterator as $item) {
$target_dir = $temp_dir . DIRECTORY_SEPARATOR . $dirname . DIRECTORY_SEPARATOR
. $iterator->getSubPathName();
if ($item->isDir()) {
mkdir($target_dir);
} else {
copy($item, $target_dir);
}
}
}
$content = '';
foreach ($this->preferences as $key => $value) {
$content .= sprintf("user_pref(\"%s\", %s);\n", $key, $value);
}
file_put_contents($temp_dir . '/user.js', $content);
// Intentionally do not use `tempnam()`, as it creates empty file which zip extension may not handle.
$temp_zip = sys_get_temp_dir() . '/' . uniqid('WebDriverFirefoxProfileZip', false);
$zip = new ZipArchive();
$zip->open($temp_zip, ZipArchive::CREATE);
$dir = new RecursiveDirectoryIterator($temp_dir);
$files = new RecursiveIteratorIterator($dir);
$dir_prefix = preg_replace(
'#\\\\#',
'\\\\\\\\',
$temp_dir . DIRECTORY_SEPARATOR
);
foreach ($files as $name => $object) {
if (is_dir($name)) {
continue;
}
$path = preg_replace("#^{$dir_prefix}#", '', $name);
$zip->addFile($name, $path);
}
$zip->close();
$profile = base64_encode(file_get_contents($temp_zip));
// clean up
$this->deleteDirectory($temp_dir);
unlink($temp_zip);
return $profile;
}
/**
* @param string $extension The path to the extension.
* @param string $profileDir The path to the profile directory.
* @throws \Exception
* @throws WebDriverException
*/
private function installExtension($extension, $profileDir)
{
$extensionCommonName = $this->parseExtensionName($extension);
// install extension to profile directory
$extensionDir = $profileDir . '/extensions/';
if (!is_dir($extensionDir) && !mkdir($extensionDir, 0777, true) && !is_dir($extensionDir)) {
throw new WebDriverException('Cannot install Firefox extension - cannot create directory');
}
if (!copy($extension, $extensionDir . $extensionCommonName . '.xpi')) {
throw new WebDriverException('Cannot install Firefox extension - cannot copy file');
}
}
/**
* @param string $prefix Prefix of the temp directory.
*
* @throws WebDriverException
* @return string The path to the temp directory created.
*/
private function createTempDirectory($prefix = '')
{
$temp_dir = tempnam(sys_get_temp_dir(), $prefix);
if (file_exists($temp_dir)) {
unlink($temp_dir);
mkdir($temp_dir);
if (!is_dir($temp_dir)) {
throw new WebDriverException('Cannot create firefox profile.');
}
}
return $temp_dir;
}
/**
* @param string $directory The path to the directory.
*/
private function deleteDirectory($directory)
{
$dir = new RecursiveDirectoryIterator($directory, FilesystemIterator::SKIP_DOTS);
$paths = new RecursiveIteratorIterator($dir, RecursiveIteratorIterator::CHILD_FIRST);
foreach ($paths as $path) {
if ($path->isDir() && !$path->isLink()) {
rmdir($path->getPathname());
} else {
unlink($path->getPathname());
}
}
rmdir($directory);
}
/**
* @param string $xpi The path to the .xpi extension.
* @param string $target_dir The path to the unzip directory.
*
* @throws \Exception
* @return FirefoxProfile
*/
private function extractTo($xpi, $target_dir)
{
$zip = new ZipArchive();
if (file_exists($xpi)) {
if ($zip->open($xpi)) {
$zip->extractTo($target_dir);
$zip->close();
} else {
throw new \Exception("Failed to open the firefox extension. '$xpi'");
}
} else {
throw new \Exception("Firefox extension doesn't exist. '$xpi'");
}
return $this;
}
private function parseExtensionName($extensionPath)
{
$temp_dir = $this->createTempDirectory();
$this->extractTo($extensionPath, $temp_dir);
$mozillaRsaPath = $temp_dir . '/META-INF/mozilla.rsa';
$mozillaRsaBinaryData = file_get_contents($mozillaRsaPath);
$mozillaRsaHex = bin2hex($mozillaRsaBinaryData);
//We need to find the plugin id. This is the second occurrence of object identifier "2.5.4.3 commonName".
//That is marker "2.5.4.3 commonName" in hex:
$objectIdentifierHexMarker = '0603550403';
$firstMarkerPosInHex = strpos($mozillaRsaHex, $objectIdentifierHexMarker); // phpcs:ignore
$secondMarkerPosInHexString =
strpos($mozillaRsaHex, $objectIdentifierHexMarker, $firstMarkerPosInHex + 2); // phpcs:ignore
if ($secondMarkerPosInHexString === false) {
throw new WebDriverException('Cannot install extension. Cannot fetch extension commonName');
}
// phpcs:ignore
$commonNameStringPositionInBinary = ($secondMarkerPosInHexString + strlen($objectIdentifierHexMarker)) / 2;
$commonNameStringLength = ord($mozillaRsaBinaryData[$commonNameStringPositionInBinary + 1]);
// phpcs:ignore
$extensionCommonName = substr(
$mozillaRsaBinaryData,
$commonNameStringPositionInBinary + 2,
$commonNameStringLength
);
$this->deleteDirectory($temp_dir);
return $extensionCommonName;
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace Facebook\WebDriver\Interactions\Internal;
use Facebook\WebDriver\WebDriverAction;
/**
* Move to the location and then release the mouse key.
*/
class WebDriverButtonReleaseAction extends WebDriverMouseAction implements WebDriverAction
{
public function perform()
{
$this->mouse->mouseUp($this->getActionLocation());
}
}

View File

@@ -0,0 +1,13 @@
<?php
namespace Facebook\WebDriver\Interactions\Internal;
use Facebook\WebDriver\WebDriverAction;
class WebDriverClickAction extends WebDriverMouseAction implements WebDriverAction
{
public function perform()
{
$this->mouse->click($this->getActionLocation());
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace Facebook\WebDriver\Interactions\Internal;
use Facebook\WebDriver\WebDriverAction;
/**
* Move the the location, click and hold.
*/
class WebDriverClickAndHoldAction extends WebDriverMouseAction implements WebDriverAction
{
public function perform()
{
$this->mouse->mouseDown($this->getActionLocation());
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace Facebook\WebDriver\Interactions\Internal;
use Facebook\WebDriver\WebDriverAction;
/**
* You can call it 'Right Click' if you like.
*/
class WebDriverContextClickAction extends WebDriverMouseAction implements WebDriverAction
{
public function perform()
{
$this->mouse->contextClick($this->getActionLocation());
}
}

View File

@@ -0,0 +1,78 @@
<?php
namespace Facebook\WebDriver\Interactions\Internal;
use Facebook\WebDriver\Exception\UnsupportedOperationException;
use Facebook\WebDriver\WebDriverPoint;
/**
* Interface representing basic mouse operations.
*/
class WebDriverCoordinates
{
/**
* @var null
*/
private $onScreen;
/**
* @var callable
*/
private $inViewPort;
/**
* @var callable
*/
private $onPage;
/**
* @var string
*/
private $auxiliary;
/**
* @param null $on_screen
* @param callable $in_view_port
* @param callable $on_page
* @param string $auxiliary
*/
public function __construct($on_screen, callable $in_view_port, callable $on_page, $auxiliary)
{
$this->onScreen = $on_screen;
$this->inViewPort = $in_view_port;
$this->onPage = $on_page;
$this->auxiliary = $auxiliary;
}
/**
* @throws UnsupportedOperationException
* @return WebDriverPoint
*/
public function onScreen()
{
throw new UnsupportedOperationException(
'onScreen is planned but not yet supported by Selenium'
);
}
/**
* @return WebDriverPoint
*/
public function inViewPort()
{
return call_user_func($this->inViewPort);
}
/**
* @return WebDriverPoint
*/
public function onPage()
{
return call_user_func($this->onPage);
}
/**
* @return string The attached object id.
*/
public function getAuxiliary()
{
return $this->auxiliary;
}
}

View File

@@ -0,0 +1,13 @@
<?php
namespace Facebook\WebDriver\Interactions\Internal;
use Facebook\WebDriver\WebDriverAction;
class WebDriverDoubleClickAction extends WebDriverMouseAction implements WebDriverAction
{
public function perform()
{
$this->mouse->doubleClick($this->getActionLocation());
}
}

View File

@@ -0,0 +1,12 @@
<?php
namespace Facebook\WebDriver\Interactions\Internal;
class WebDriverKeyDownAction extends WebDriverSingleKeyAction
{
public function perform()
{
$this->focusOnElement();
$this->keyboard->pressKey($this->key);
}
}

View File

@@ -0,0 +1,12 @@
<?php
namespace Facebook\WebDriver\Interactions\Internal;
class WebDriverKeyUpAction extends WebDriverSingleKeyAction
{
public function perform()
{
$this->focusOnElement();
$this->keyboard->releaseKey($this->key);
}
}

View File

@@ -0,0 +1,48 @@
<?php
namespace Facebook\WebDriver\Interactions\Internal;
use Facebook\WebDriver\Internal\WebDriverLocatable;
use Facebook\WebDriver\WebDriverKeyboard;
use Facebook\WebDriver\WebDriverMouse;
/**
* Base class for all keyboard-related actions.
*/
abstract class WebDriverKeysRelatedAction
{
/**
* @var WebDriverKeyboard
*/
protected $keyboard;
/**
* @var WebDriverMouse
*/
protected $mouse;
/**
* @var WebDriverLocatable|null
*/
protected $locationProvider;
/**
* @param WebDriverKeyboard $keyboard
* @param WebDriverMouse $mouse
* @param WebDriverLocatable $location_provider
*/
public function __construct(
WebDriverKeyboard $keyboard,
WebDriverMouse $mouse,
WebDriverLocatable $location_provider = null
) {
$this->keyboard = $keyboard;
$this->mouse = $mouse;
$this->locationProvider = $location_provider;
}
protected function focusOnElement()
{
if ($this->locationProvider) {
$this->mouse->click($this->locationProvider->getCoordinates());
}
}
}

View File

@@ -0,0 +1,48 @@
<?php
namespace Facebook\WebDriver\Interactions\Internal;
use Facebook\WebDriver\Internal\WebDriverLocatable;
use Facebook\WebDriver\WebDriverMouse;
/**
* Base class for all mouse-related actions.
*/
class WebDriverMouseAction
{
/**
* @var WebDriverMouse
*/
protected $mouse;
/**
* @var WebDriverLocatable
*/
protected $locationProvider;
/**
* @param WebDriverMouse $mouse
* @param WebDriverLocatable|null $location_provider
*/
public function __construct(WebDriverMouse $mouse, WebDriverLocatable $location_provider = null)
{
$this->mouse = $mouse;
$this->locationProvider = $location_provider;
}
/**
* @return null|WebDriverCoordinates
*/
protected function getActionLocation()
{
if ($this->locationProvider !== null) {
return $this->locationProvider->getCoordinates();
}
return null;
}
protected function moveToLocation()
{
$this->mouse->mouseMove($this->locationProvider);
}
}

View File

@@ -0,0 +1,13 @@
<?php
namespace Facebook\WebDriver\Interactions\Internal;
use Facebook\WebDriver\WebDriverAction;
class WebDriverMouseMoveAction extends WebDriverMouseAction implements WebDriverAction
{
public function perform()
{
$this->mouse->mouseMove($this->getActionLocation());
}
}

View File

@@ -0,0 +1,45 @@
<?php
namespace Facebook\WebDriver\Interactions\Internal;
use Facebook\WebDriver\Internal\WebDriverLocatable;
use Facebook\WebDriver\WebDriverAction;
use Facebook\WebDriver\WebDriverMouse;
class WebDriverMoveToOffsetAction extends WebDriverMouseAction implements WebDriverAction
{
/**
* @var int|null
*/
private $xOffset;
/**
* @var int|null
*/
private $yOffset;
/**
* @param WebDriverMouse $mouse
* @param WebDriverLocatable|null $location_provider
* @param int|null $x_offset
* @param int|null $y_offset
*/
public function __construct(
WebDriverMouse $mouse,
WebDriverLocatable $location_provider = null,
$x_offset = null,
$y_offset = null
) {
parent::__construct($mouse, $location_provider);
$this->xOffset = $x_offset;
$this->yOffset = $y_offset;
}
public function perform()
{
$this->mouse->mouseMove(
$this->getActionLocation(),
$this->xOffset,
$this->yOffset
);
}
}

View File

@@ -0,0 +1,38 @@
<?php
namespace Facebook\WebDriver\Interactions\Internal;
use Facebook\WebDriver\Internal\WebDriverLocatable;
use Facebook\WebDriver\WebDriverAction;
use Facebook\WebDriver\WebDriverKeyboard;
use Facebook\WebDriver\WebDriverMouse;
class WebDriverSendKeysAction extends WebDriverKeysRelatedAction implements WebDriverAction
{
/**
* @var string
*/
private $keys = '';
/**
* @param WebDriverKeyboard $keyboard
* @param WebDriverMouse $mouse
* @param WebDriverLocatable $location_provider
* @param string $keys
*/
public function __construct(
WebDriverKeyboard $keyboard,
WebDriverMouse $mouse,
WebDriverLocatable $location_provider = null,
$keys = ''
) {
parent::__construct($keyboard, $mouse, $location_provider);
$this->keys = $keys;
}
public function perform()
{
$this->focusOnElement();
$this->keyboard->sendKeys($this->keys);
}
}

View File

@@ -0,0 +1,53 @@
<?php
namespace Facebook\WebDriver\Interactions\Internal;
use Facebook\WebDriver\Internal\WebDriverLocatable;
use Facebook\WebDriver\WebDriverAction;
use Facebook\WebDriver\WebDriverKeyboard;
use Facebook\WebDriver\WebDriverKeys;
use Facebook\WebDriver\WebDriverMouse;
abstract class WebDriverSingleKeyAction extends WebDriverKeysRelatedAction implements WebDriverAction
{
const MODIFIER_KEYS = [
WebDriverKeys::SHIFT,
WebDriverKeys::LEFT_SHIFT,
WebDriverKeys::RIGHT_SHIFT,
WebDriverKeys::CONTROL,
WebDriverKeys::LEFT_CONTROL,
WebDriverKeys::RIGHT_CONTROL,
WebDriverKeys::ALT,
WebDriverKeys::LEFT_ALT,
WebDriverKeys::RIGHT_ALT,
WebDriverKeys::META,
WebDriverKeys::RIGHT_META,
WebDriverKeys::COMMAND,
];
/** @var string */
protected $key;
/**
* @param string $key
* @todo Remove default $key value in next major version (BC)
*/
public function __construct(
WebDriverKeyboard $keyboard,
WebDriverMouse $mouse,
WebDriverLocatable $location_provider = null,
$key = ''
) {
parent::__construct($keyboard, $mouse, $location_provider);
if (!in_array($key, self::MODIFIER_KEYS, true)) {
throw new \InvalidArgumentException(
sprintf(
'keyDown / keyUp actions can only be used for modifier keys, but "%s" was given',
$key
)
);
}
$this->key = $key;
}
}

View File

@@ -0,0 +1,13 @@
<?php
namespace Facebook\WebDriver\Interactions\Touch;
use Facebook\WebDriver\WebDriverAction;
class WebDriverDoubleTapAction extends WebDriverTouchAction implements WebDriverAction
{
public function perform()
{
$this->touchScreen->doubleTap($this->locationProvider);
}
}

View File

@@ -0,0 +1,34 @@
<?php
namespace Facebook\WebDriver\Interactions\Touch;
use Facebook\WebDriver\WebDriverAction;
class WebDriverDownAction extends WebDriverTouchAction implements WebDriverAction
{
/**
* @var int
*/
private $x;
/**
* @var int
*/
private $y;
/**
* @param WebDriverTouchScreen $touch_screen
* @param int $x
* @param int $y
*/
public function __construct(WebDriverTouchScreen $touch_screen, $x, $y)
{
$this->x = $x;
$this->y = $y;
parent::__construct($touch_screen);
}
public function perform()
{
$this->touchScreen->down($this->x, $this->y);
}
}

View File

@@ -0,0 +1,34 @@
<?php
namespace Facebook\WebDriver\Interactions\Touch;
use Facebook\WebDriver\WebDriverAction;
class WebDriverFlickAction extends WebDriverTouchAction implements WebDriverAction
{
/**
* @var int
*/
private $x;
/**
* @var int
*/
private $y;
/**
* @param WebDriverTouchScreen $touch_screen
* @param int $x
* @param int $y
*/
public function __construct(WebDriverTouchScreen $touch_screen, $x, $y)
{
$this->x = $x;
$this->y = $y;
parent::__construct($touch_screen);
}
public function perform()
{
$this->touchScreen->flick($this->x, $this->y);
}
}

View File

@@ -0,0 +1,52 @@
<?php
namespace Facebook\WebDriver\Interactions\Touch;
use Facebook\WebDriver\WebDriverAction;
use Facebook\WebDriver\WebDriverElement;
class WebDriverFlickFromElementAction extends WebDriverTouchAction implements WebDriverAction
{
/**
* @var int
*/
private $x;
/**
* @var int
*/
private $y;
/**
* @var int
*/
private $speed;
/**
* @param WebDriverTouchScreen $touch_screen
* @param WebDriverElement $element
* @param int $x
* @param int $y
* @param int $speed
*/
public function __construct(
WebDriverTouchScreen $touch_screen,
WebDriverElement $element,
$x,
$y,
$speed
) {
$this->x = $x;
$this->y = $y;
$this->speed = $speed;
parent::__construct($touch_screen, $element);
}
public function perform()
{
$this->touchScreen->flickFromElement(
$this->locationProvider,
$this->x,
$this->y,
$this->speed
);
}
}

View File

@@ -0,0 +1,13 @@
<?php
namespace Facebook\WebDriver\Interactions\Touch;
use Facebook\WebDriver\WebDriverAction;
class WebDriverLongPressAction extends WebDriverTouchAction implements WebDriverAction
{
public function perform()
{
$this->touchScreen->longPress($this->locationProvider);
}
}

View File

@@ -0,0 +1,28 @@
<?php
namespace Facebook\WebDriver\Interactions\Touch;
use Facebook\WebDriver\WebDriverAction;
class WebDriverMoveAction extends WebDriverTouchAction implements WebDriverAction
{
private $x;
private $y;
/**
* @param WebDriverTouchScreen $touch_screen
* @param int $x
* @param int $y
*/
public function __construct(WebDriverTouchScreen $touch_screen, $x, $y)
{
$this->x = $x;
$this->y = $y;
parent::__construct($touch_screen);
}
public function perform()
{
$this->touchScreen->move($this->x, $this->y);
}
}

View File

@@ -0,0 +1,28 @@
<?php
namespace Facebook\WebDriver\Interactions\Touch;
use Facebook\WebDriver\WebDriverAction;
class WebDriverScrollAction extends WebDriverTouchAction implements WebDriverAction
{
private $x;
private $y;
/**
* @param WebDriverTouchScreen $touch_screen
* @param int $x
* @param int $y
*/
public function __construct(WebDriverTouchScreen $touch_screen, $x, $y)
{
$this->x = $x;
$this->y = $y;
parent::__construct($touch_screen);
}
public function perform()
{
$this->touchScreen->scroll($this->x, $this->y);
}
}

View File

@@ -0,0 +1,38 @@
<?php
namespace Facebook\WebDriver\Interactions\Touch;
use Facebook\WebDriver\WebDriverAction;
use Facebook\WebDriver\WebDriverElement;
class WebDriverScrollFromElementAction extends WebDriverTouchAction implements WebDriverAction
{
private $x;
private $y;
/**
* @param WebDriverTouchScreen $touch_screen
* @param WebDriverElement $element
* @param int $x
* @param int $y
*/
public function __construct(
WebDriverTouchScreen $touch_screen,
WebDriverElement $element,
$x,
$y
) {
$this->x = $x;
$this->y = $y;
parent::__construct($touch_screen, $element);
}
public function perform()
{
$this->touchScreen->scrollFromElement(
$this->locationProvider,
$this->x,
$this->y
);
}
}

View File

@@ -0,0 +1,13 @@
<?php
namespace Facebook\WebDriver\Interactions\Touch;
use Facebook\WebDriver\WebDriverAction;
class WebDriverTapAction extends WebDriverTouchAction implements WebDriverAction
{
public function perform()
{
$this->touchScreen->tap($this->locationProvider);
}
}

View File

@@ -0,0 +1,42 @@
<?php
namespace Facebook\WebDriver\Interactions\Touch;
use Facebook\WebDriver\Interactions\Internal\WebDriverCoordinates;
use Facebook\WebDriver\Internal\WebDriverLocatable;
/**
* Base class for all touch-related actions.
*/
abstract class WebDriverTouchAction
{
/**
* @var WebDriverTouchScreen
*/
protected $touchScreen;
/**
* @var WebDriverLocatable
*/
protected $locationProvider;
/**
* @param WebDriverTouchScreen $touch_screen
* @param WebDriverLocatable $location_provider
*/
public function __construct(
WebDriverTouchScreen $touch_screen,
WebDriverLocatable $location_provider = null
) {
$this->touchScreen = $touch_screen;
$this->locationProvider = $location_provider;
}
/**
* @return null|WebDriverCoordinates
*/
protected function getActionLocation()
{
return $this->locationProvider !== null
? $this->locationProvider->getCoordinates() : null;
}
}

View File

@@ -0,0 +1,114 @@
<?php
namespace Facebook\WebDriver\Interactions\Touch;
use Facebook\WebDriver\WebDriverElement;
/**
* Interface representing touch screen operations.
*/
interface WebDriverTouchScreen
{
/**
* Single tap on the touch enabled device.
*
* @param WebDriverElement $element
* @return $this
*/
public function tap(WebDriverElement $element);
/**
* Double tap on the touch screen using finger motion events.
*
* @param WebDriverElement $element
* @return $this
*/
public function doubleTap(WebDriverElement $element);
/**
* Finger down on the screen.
*
* @param int $x
* @param int $y
* @return $this
*/
public function down($x, $y);
/**
* Flick on the touch screen using finger motion events. Use this flick
* command if you don't care where the flick starts on the screen.
*
* @param int $xspeed
* @param int $yspeed
* @return $this
*/
public function flick($xspeed, $yspeed);
/**
* Flick on the touch screen using finger motion events.
* This flickcommand starts at a particular screen location.
*
* @param WebDriverElement $element
* @param int $xoffset
* @param int $yoffset
* @param int $speed
* @return $this
*/
public function flickFromElement(
WebDriverElement $element,
$xoffset,
$yoffset,
$speed
);
/**
* Long press on the touch screen using finger motion events.
*
* @param WebDriverElement $element
* @return $this
*/
public function longPress(WebDriverElement $element);
/**
* Finger move on the screen.
*
* @param int $x
* @param int $y
* @return $this
*/
public function move($x, $y);
/**
* Scroll on the touch screen using finger based motion events. Use this
* command if you don't care where the scroll starts on the screen.
*
* @param int $xoffset
* @param int $yoffset
* @return $this
*/
public function scroll($xoffset, $yoffset);
/**
* Scroll on the touch screen using finger based motion events. Use this
* command to start scrolling at a particular screen location.
*
* @param WebDriverElement $element
* @param int $xoffset
* @param int $yoffset
* @return $this
*/
public function scrollFromElement(
WebDriverElement $element,
$xoffset,
$yoffset
);
/**
* Finger up on the screen.
*
* @param int $x
* @param int $y
* @return $this
*/
public function up($x, $y);
}

View File

@@ -0,0 +1,268 @@
<?php
namespace Facebook\WebDriver\Interactions;
use Facebook\WebDriver\Interactions\Internal\WebDriverButtonReleaseAction;
use Facebook\WebDriver\Interactions\Internal\WebDriverClickAction;
use Facebook\WebDriver\Interactions\Internal\WebDriverClickAndHoldAction;
use Facebook\WebDriver\Interactions\Internal\WebDriverContextClickAction;
use Facebook\WebDriver\Interactions\Internal\WebDriverDoubleClickAction;
use Facebook\WebDriver\Interactions\Internal\WebDriverKeyDownAction;
use Facebook\WebDriver\Interactions\Internal\WebDriverKeyUpAction;
use Facebook\WebDriver\Interactions\Internal\WebDriverMouseMoveAction;
use Facebook\WebDriver\Interactions\Internal\WebDriverMoveToOffsetAction;
use Facebook\WebDriver\Interactions\Internal\WebDriverSendKeysAction;
use Facebook\WebDriver\WebDriverElement;
use Facebook\WebDriver\WebDriverHasInputDevices;
/**
* WebDriver action builder. It implements the builder pattern.
*/
class WebDriverActions
{
protected $driver;
protected $keyboard;
protected $mouse;
protected $action;
/**
* @param WebDriverHasInputDevices $driver
*/
public function __construct(WebDriverHasInputDevices $driver)
{
$this->driver = $driver;
$this->keyboard = $driver->getKeyboard();
$this->mouse = $driver->getMouse();
$this->action = new WebDriverCompositeAction();
}
/**
* A convenience method for performing the actions without calling build().
*/
public function perform()
{
$this->action->perform();
}
/**
* Mouse click.
* If $element is provided, move to the middle of the element first.
*
* @param WebDriverElement $element
* @return WebDriverActions
*/
public function click(WebDriverElement $element = null)
{
$this->action->addAction(
new WebDriverClickAction($this->mouse, $element)
);
return $this;
}
/**
* Mouse click and hold.
* If $element is provided, move to the middle of the element first.
*
* @param WebDriverElement $element
* @return WebDriverActions
*/
public function clickAndHold(WebDriverElement $element = null)
{
$this->action->addAction(
new WebDriverClickAndHoldAction($this->mouse, $element)
);
return $this;
}
/**
* Context-click (right click).
* If $element is provided, move to the middle of the element first.
*
* @param WebDriverElement $element
* @return WebDriverActions
*/
public function contextClick(WebDriverElement $element = null)
{
$this->action->addAction(
new WebDriverContextClickAction($this->mouse, $element)
);
return $this;
}
/**
* Double click.
* If $element is provided, move to the middle of the element first.
*
* @param WebDriverElement $element
* @return WebDriverActions
*/
public function doubleClick(WebDriverElement $element = null)
{
$this->action->addAction(
new WebDriverDoubleClickAction($this->mouse, $element)
);
return $this;
}
/**
* Drag and drop from $source to $target.
*
* @param WebDriverElement $source
* @param WebDriverElement $target
* @return WebDriverActions
*/
public function dragAndDrop(WebDriverElement $source, WebDriverElement $target)
{
$this->action->addAction(
new WebDriverClickAndHoldAction($this->mouse, $source)
);
$this->action->addAction(
new WebDriverMouseMoveAction($this->mouse, $target)
);
$this->action->addAction(
new WebDriverButtonReleaseAction($this->mouse, $target)
);
return $this;
}
/**
* Drag $source and drop by offset ($x_offset, $y_offset).
*
* @param WebDriverElement $source
* @param int $x_offset
* @param int $y_offset
* @return WebDriverActions
*/
public function dragAndDropBy(WebDriverElement $source, $x_offset, $y_offset)
{
$this->action->addAction(
new WebDriverClickAndHoldAction($this->mouse, $source)
);
$this->action->addAction(
new WebDriverMoveToOffsetAction($this->mouse, null, $x_offset, $y_offset)
);
$this->action->addAction(
new WebDriverButtonReleaseAction($this->mouse, null)
);
return $this;
}
/**
* Mouse move by offset.
*
* @param int $x_offset
* @param int $y_offset
* @return WebDriverActions
*/
public function moveByOffset($x_offset, $y_offset)
{
$this->action->addAction(
new WebDriverMoveToOffsetAction($this->mouse, null, $x_offset, $y_offset)
);
return $this;
}
/**
* Move to the middle of the given WebDriverElement.
* Extra shift, calculated from the top-left corner of the element, can be set by passing $x_offset and $y_offset
* parameters.
*
* @param WebDriverElement $element
* @param int $x_offset
* @param int $y_offset
* @return WebDriverActions
*/
public function moveToElement(WebDriverElement $element, $x_offset = null, $y_offset = null)
{
$this->action->addAction(new WebDriverMoveToOffsetAction(
$this->mouse,
$element,
$x_offset,
$y_offset
));
return $this;
}
/**
* Release the mouse button.
* If $element is provided, move to the middle of the element first.
*
* @param WebDriverElement $element
* @return WebDriverActions
*/
public function release(WebDriverElement $element = null)
{
$this->action->addAction(
new WebDriverButtonReleaseAction($this->mouse, $element)
);
return $this;
}
/**
* Press a key on keyboard.
* If $element is provided, focus on that element first.
*
* @see WebDriverKeys for special keys like CONTROL, ALT, etc.
* @param WebDriverElement $element
* @param string $key
* @return WebDriverActions
*/
public function keyDown(WebDriverElement $element = null, $key = null)
{
$this->action->addAction(
new WebDriverKeyDownAction($this->keyboard, $this->mouse, $element, $key)
);
return $this;
}
/**
* Release a key on keyboard.
* If $element is provided, focus on that element first.
*
* @see WebDriverKeys for special keys like CONTROL, ALT, etc.
* @param WebDriverElement $element
* @param string $key
* @return WebDriverActions
*/
public function keyUp(WebDriverElement $element = null, $key = null)
{
$this->action->addAction(
new WebDriverKeyUpAction($this->keyboard, $this->mouse, $element, $key)
);
return $this;
}
/**
* Send keys by keyboard.
* If $element is provided, focus on that element first (using single mouse click).
*
* @see WebDriverKeys for special keys like CONTROL, ALT, etc.
* @param WebDriverElement $element
* @param string $keys
* @return WebDriverActions
*/
public function sendKeys(WebDriverElement $element = null, $keys = null)
{
$this->action->addAction(
new WebDriverSendKeysAction(
$this->keyboard,
$this->mouse,
$element,
$keys
)
);
return $this;
}
}

View File

@@ -0,0 +1,49 @@
<?php
namespace Facebook\WebDriver\Interactions;
use Facebook\WebDriver\WebDriverAction;
/**
* An action for aggregating actions and triggering all of them afterwards.
*/
class WebDriverCompositeAction implements WebDriverAction
{
/**
* @var WebDriverAction[]
*/
private $actions = [];
/**
* Add an WebDriverAction to the sequence.
*
* @param WebDriverAction $action
* @return WebDriverCompositeAction The current instance.
*/
public function addAction(WebDriverAction $action)
{
$this->actions[] = $action;
return $this;
}
/**
* Get the number of actions in the sequence.
*
* @return int The number of actions.
*/
public function getNumberOfActions()
{
return count($this->actions);
}
/**
* Perform the sequence of actions.
*/
public function perform()
{
foreach ($this->actions as $action) {
$action->perform();
}
}
}

View File

@@ -0,0 +1,180 @@
<?php
namespace Facebook\WebDriver\Interactions;
use Facebook\WebDriver\Interactions\Touch\WebDriverDoubleTapAction;
use Facebook\WebDriver\Interactions\Touch\WebDriverDownAction;
use Facebook\WebDriver\Interactions\Touch\WebDriverFlickAction;
use Facebook\WebDriver\Interactions\Touch\WebDriverFlickFromElementAction;
use Facebook\WebDriver\Interactions\Touch\WebDriverLongPressAction;
use Facebook\WebDriver\Interactions\Touch\WebDriverMoveAction;
use Facebook\WebDriver\Interactions\Touch\WebDriverScrollAction;
use Facebook\WebDriver\Interactions\Touch\WebDriverScrollFromElementAction;
use Facebook\WebDriver\Interactions\Touch\WebDriverTapAction;
use Facebook\WebDriver\Interactions\Touch\WebDriverTouchScreen;
use Facebook\WebDriver\WebDriver;
use Facebook\WebDriver\WebDriverElement;
use Facebook\WebDriver\WebDriverUpAction;
/**
* WebDriver action builder for touch events
*/
class WebDriverTouchActions extends WebDriverActions
{
/**
* @var WebDriverTouchScreen
*/
protected $touchScreen;
public function __construct(WebDriver $driver)
{
parent::__construct($driver);
$this->touchScreen = $driver->getTouch();
}
/**
* @param WebDriverElement $element
* @return WebDriverTouchActions
*/
public function tap(WebDriverElement $element)
{
$this->action->addAction(
new WebDriverTapAction($this->touchScreen, $element)
);
return $this;
}
/**
* @param int $x
* @param int $y
* @return WebDriverTouchActions
*/
public function down($x, $y)
{
$this->action->addAction(
new WebDriverDownAction($this->touchScreen, $x, $y)
);
return $this;
}
/**
* @param int $x
* @param int $y
* @return WebDriverTouchActions
*/
public function up($x, $y)
{
$this->action->addAction(
new WebDriverUpAction($this->touchScreen, $x, $y)
);
return $this;
}
/**
* @param int $x
* @param int $y
* @return WebDriverTouchActions
*/
public function move($x, $y)
{
$this->action->addAction(
new WebDriverMoveAction($this->touchScreen, $x, $y)
);
return $this;
}
/**
* @param int $x
* @param int $y
* @return WebDriverTouchActions
*/
public function scroll($x, $y)
{
$this->action->addAction(
new WebDriverScrollAction($this->touchScreen, $x, $y)
);
return $this;
}
/**
* @param WebDriverElement $element
* @param int $x
* @param int $y
* @return WebDriverTouchActions
*/
public function scrollFromElement(WebDriverElement $element, $x, $y)
{
$this->action->addAction(
new WebDriverScrollFromElementAction($this->touchScreen, $element, $x, $y)
);
return $this;
}
/**
* @param WebDriverElement $element
* @return WebDriverTouchActions
*/
public function doubleTap(WebDriverElement $element)
{
$this->action->addAction(
new WebDriverDoubleTapAction($this->touchScreen, $element)
);
return $this;
}
/**
* @param WebDriverElement $element
* @return WebDriverTouchActions
*/
public function longPress(WebDriverElement $element)
{
$this->action->addAction(
new WebDriverLongPressAction($this->touchScreen, $element)
);
return $this;
}
/**
* @param int $x
* @param int $y
* @return WebDriverTouchActions
*/
public function flick($x, $y)
{
$this->action->addAction(
new WebDriverFlickAction($this->touchScreen, $x, $y)
);
return $this;
}
/**
* @param WebDriverElement $element
* @param int $x
* @param int $y
* @param int $speed
* @return WebDriverTouchActions
*/
public function flickFromElement(WebDriverElement $element, $x, $y, $speed)
{
$this->action->addAction(
new WebDriverFlickFromElementAction(
$this->touchScreen,
$element,
$x,
$y,
$speed
)
);
return $this;
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace Facebook\WebDriver\Internal;
use Facebook\WebDriver\Interactions\Internal\WebDriverCoordinates;
/**
* Interface representing basic mouse operations.
*/
interface WebDriverLocatable
{
/**
* @return WebDriverCoordinates
*/
public function getCoordinates();
}

View File

@@ -0,0 +1,35 @@
<?php
namespace Facebook\WebDriver;
/**
* WebDriver interface implemented by drivers that support JavaScript.
*/
interface JavaScriptExecutor
{
/**
* Inject a snippet of JavaScript into the page for execution in the context
* of the currently selected frame. The executed script is assumed to be
* synchronous and the result of evaluating the script will be returned.
*
* @param string $script The script to inject.
* @param array $arguments The arguments of the script.
* @return mixed The return value of the script.
*/
public function executeScript($script, array $arguments = []);
/**
* Inject a snippet of JavaScript into the page for asynchronous execution in
* the context of the currently selected frame.
*
* The driver will pass a callback as the last argument to the snippet, and
* block until the callback is invoked.
*
* @see WebDriverExecuteAsyncScriptTestCase
*
* @param string $script The script to inject.
* @param array $arguments The arguments of the script.
* @return mixed The value passed by the script to the callback.
*/
public function executeAsyncScript($script, array $arguments = []);
}

View File

@@ -0,0 +1,55 @@
<?php
namespace Facebook\WebDriver\Local;
use Facebook\WebDriver\Exception\WebDriverException;
use Facebook\WebDriver\Remote\DesiredCapabilities;
use Facebook\WebDriver\Remote\RemoteWebDriver;
/**
* @todo Break inheritance from RemoteWebDriver in next major version. (Composition over inheritance!)
*/
abstract class LocalWebDriver extends RemoteWebDriver
{
/**
* @param string $selenium_server_url
* @param null $desired_capabilities
* @param null $connection_timeout_in_ms
* @param null $request_timeout_in_ms
* @param null $http_proxy
* @param null $http_proxy_port
* @param DesiredCapabilities|null $required_capabilities
* @throws WebDriverException
* @return RemoteWebDriver
* @todo Remove in next major version (should not be inherited)
*/
public static function create(
$selenium_server_url = 'http://localhost:4444/wd/hub',
$desired_capabilities = null,
$connection_timeout_in_ms = null,
$request_timeout_in_ms = null,
$http_proxy = null,
$http_proxy_port = null,
DesiredCapabilities $required_capabilities = null
) {
throw new WebDriverException('Use start() method to start local WebDriver.');
}
/**
* @param string $session_id
* @param string $selenium_server_url
* @param null $connection_timeout_in_ms
* @param null $request_timeout_in_ms
* @throws WebDriverException
* @return RemoteWebDriver
* @todo Remove in next major version (should not be inherited)
*/
public static function createBySessionID(
$session_id,
$selenium_server_url = 'http://localhost:4444/wd/hub',
$connection_timeout_in_ms = null,
$request_timeout_in_ms = null
) {
throw new WebDriverException('Use start() method to start local WebDriver.');
}
}

View File

@@ -0,0 +1,73 @@
<?php
namespace Facebook\WebDriver\Net;
use Exception;
use Facebook\WebDriver\Exception\TimeoutException;
class URLChecker
{
const POLL_INTERVAL_MS = 500;
const CONNECT_TIMEOUT_MS = 500;
public function waitUntilAvailable($timeout_in_ms, $url)
{
$end = microtime(true) + $timeout_in_ms / 1000;
while ($end > microtime(true)) {
if ($this->getHTTPResponseCode($url) === 200) {
return $this;
}
usleep(self::POLL_INTERVAL_MS);
}
throw new TimeoutException(sprintf(
'Timed out waiting for %s to become available after %d ms.',
$url,
$timeout_in_ms
));
}
public function waitUntilUnavailable($timeout_in_ms, $url)
{
$end = microtime(true) + $timeout_in_ms / 1000;
while ($end > microtime(true)) {
if ($this->getHTTPResponseCode($url) !== 200) {
return $this;
}
usleep(self::POLL_INTERVAL_MS);
}
throw new TimeoutException(sprintf(
'Timed out waiting for %s to become unavailable after %d ms.',
$url,
$timeout_in_ms
));
}
private function getHTTPResponseCode($url)
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
// The PHP doc indicates that CURLOPT_CONNECTTIMEOUT_MS constant is added in cURL 7.16.2
// available since PHP 5.2.3.
if (!defined('CURLOPT_CONNECTTIMEOUT_MS')) {
define('CURLOPT_CONNECTTIMEOUT_MS', 156); // default value for CURLOPT_CONNECTTIMEOUT_MS
}
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT_MS, self::CONNECT_TIMEOUT_MS);
$code = null;
try {
curl_exec($ch);
$info = curl_getinfo($ch);
$code = $info['http_code'];
} catch (Exception $e) {
}
curl_close($ch);
return $code;
}
}

Some files were not shown because too many files have changed in this diff Show More