├── .coveralls.yml ├── .php_cs.dist ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE.md ├── LICENSE.md ├── README.md ├── composer.json ├── lib ├── AbstractWebDriverCheckboxOrRadio.php ├── Chrome │ ├── ChromeDriver.php │ ├── ChromeDriverService.php │ └── ChromeOptions.php ├── Cookie.php ├── Exception │ ├── ElementClickInterceptedException.php │ ├── ElementNotInteractableException.php │ ├── ElementNotSelectableException.php │ ├── ElementNotVisibleException.php │ ├── ExpectedException.php │ ├── IMEEngineActivationFailedException.php │ ├── IMENotAvailableException.php │ ├── IndexOutOfBoundsException.php │ ├── InsecureCertificateException.php │ ├── InvalidArgumentException.php │ ├── InvalidCookieDomainException.php │ ├── InvalidCoordinatesException.php │ ├── InvalidElementStateException.php │ ├── InvalidSelectorException.php │ ├── InvalidSessionIdException.php │ ├── JavascriptErrorException.php │ ├── MoveTargetOutOfBoundsException.php │ ├── NoAlertOpenException.php │ ├── NoCollectionException.php │ ├── NoScriptResultException.php │ ├── NoStringException.php │ ├── NoStringLengthException.php │ ├── NoStringWrapperException.php │ ├── NoSuchAlertException.php │ ├── NoSuchCollectionException.php │ ├── NoSuchCookieException.php │ ├── NoSuchDocumentException.php │ ├── NoSuchDriverException.php │ ├── NoSuchElementException.php │ ├── NoSuchFrameException.php │ ├── NoSuchWindowException.php │ ├── NullPointerException.php │ ├── ScriptTimeoutException.php │ ├── SessionNotCreatedException.php │ ├── StaleElementReferenceException.php │ ├── TimeoutException.php │ ├── UnableToCaptureScreenException.php │ ├── UnableToSetCookieException.php │ ├── UnexpectedAlertOpenException.php │ ├── UnexpectedJavascriptException.php │ ├── UnexpectedTagNameException.php │ ├── UnknownCommandException.php │ ├── UnknownErrorException.php │ ├── UnknownMethodException.php │ ├── UnknownServerException.php │ ├── UnrecognizedExceptionException.php │ ├── UnsupportedOperationException.php │ ├── WebDriverCurlException.php │ ├── WebDriverException.php │ └── XPathLookupException.php ├── Firefox │ ├── FirefoxDriver.php │ ├── FirefoxPreferences.php │ └── FirefoxProfile.php ├── Interactions │ ├── Internal │ │ ├── WebDriverButtonReleaseAction.php │ │ ├── WebDriverClickAction.php │ │ ├── WebDriverClickAndHoldAction.php │ │ ├── WebDriverContextClickAction.php │ │ ├── WebDriverCoordinates.php │ │ ├── WebDriverDoubleClickAction.php │ │ ├── WebDriverKeyDownAction.php │ │ ├── WebDriverKeyUpAction.php │ │ ├── WebDriverKeysRelatedAction.php │ │ ├── WebDriverMouseAction.php │ │ ├── WebDriverMouseMoveAction.php │ │ ├── WebDriverMoveToOffsetAction.php │ │ ├── WebDriverSendKeysAction.php │ │ └── WebDriverSingleKeyAction.php │ ├── Touch │ │ ├── WebDriverDoubleTapAction.php │ │ ├── WebDriverDownAction.php │ │ ├── WebDriverFlickAction.php │ │ ├── WebDriverFlickFromElementAction.php │ │ ├── WebDriverLongPressAction.php │ │ ├── WebDriverMoveAction.php │ │ ├── WebDriverScrollAction.php │ │ ├── WebDriverScrollFromElementAction.php │ │ ├── WebDriverTapAction.php │ │ ├── WebDriverTouchAction.php │ │ └── WebDriverTouchScreen.php │ ├── WebDriverActions.php │ ├── WebDriverCompositeAction.php │ └── WebDriverTouchActions.php ├── Internal │ └── WebDriverLocatable.php ├── JavaScriptExecutor.php ├── Net │ └── URLChecker.php ├── Remote │ ├── DesiredCapabilities.php │ ├── DriverCommand.php │ ├── ExecuteMethod.php │ ├── FileDetector.php │ ├── HttpCommandExecutor.php │ ├── JsonWireCompat.php │ ├── LocalFileDetector.php │ ├── RemoteExecuteMethod.php │ ├── RemoteKeyboard.php │ ├── RemoteMouse.php │ ├── RemoteStatus.php │ ├── RemoteTargetLocator.php │ ├── RemoteTouchScreen.php │ ├── RemoteWebDriver.php │ ├── RemoteWebElement.php │ ├── Service │ │ ├── DriverCommandExecutor.php │ │ └── DriverService.php │ ├── UselessFileDetector.php │ ├── WebDriverBrowserType.php │ ├── WebDriverCapabilityType.php │ ├── WebDriverCommand.php │ └── WebDriverResponse.php ├── Support │ ├── Events │ │ ├── EventFiringWebDriver.php │ │ ├── EventFiringWebDriverNavigation.php │ │ └── EventFiringWebElement.php │ └── XPathEscaper.php ├── WebDriver.php ├── WebDriverAction.php ├── WebDriverAlert.php ├── WebDriverBy.php ├── WebDriverCapabilities.php ├── WebDriverCheckboxes.php ├── WebDriverCommandExecutor.php ├── WebDriverDimension.php ├── WebDriverDispatcher.php ├── WebDriverElement.php ├── WebDriverEventListener.php ├── WebDriverExpectedCondition.php ├── WebDriverHasInputDevices.php ├── WebDriverKeyboard.php ├── WebDriverKeys.php ├── WebDriverMouse.php ├── WebDriverNavigation.php ├── WebDriverNavigationInterface.php ├── WebDriverOptions.php ├── WebDriverPlatform.php ├── WebDriverPoint.php ├── WebDriverRadios.php ├── WebDriverSearchContext.php ├── WebDriverSelect.php ├── WebDriverSelectInterface.php ├── WebDriverTargetLocator.php ├── WebDriverTimeouts.php ├── WebDriverUpAction.php ├── WebDriverWait.php └── WebDriverWindow.php ├── logs └── .gitkeep └── phpstan.neon /.coveralls.yml: -------------------------------------------------------------------------------- 1 | coverage_clover: ./logs/coverage-clover.xml 2 | json_path: ./logs/coveralls-upload.json 3 | -------------------------------------------------------------------------------- /.php_cs.dist: -------------------------------------------------------------------------------- 1 | in([__DIR__ . '/lib', __DIR__ . '/tests']); 5 | 6 | return PhpCsFixer\Config::create() 7 | ->setRules([ 8 | '@PSR2' => true, 9 | 'array_syntax' => ['syntax' => 'short'], 10 | 'binary_operator_spaces' => true, 11 | 'blank_line_before_return' => true, 12 | 'cast_spaces' => true, 13 | 'concat_space' => ['spacing' => 'one'], 14 | 'function_typehint_space' => true, 15 | 'general_phpdoc_annotation_remove' => ['author'], 16 | 'implode_call' => true, 17 | 'is_null' => true, 18 | 'linebreak_after_opening_tag' => true, 19 | 'lowercase_cast' => true, 20 | 'mb_str_functions' => true, 21 | 'method_separation' => true, 22 | 'native_function_casing' => true, 23 | 'new_with_braces' => true, 24 | 'no_alias_functions' => true, 25 | 'no_blank_lines_after_class_opening' => true, 26 | 'no_blank_lines_after_phpdoc' => true, 27 | 'no_empty_comment' => true, 28 | 'no_empty_phpdoc' => true, 29 | 'no_empty_statement' => true, 30 | 'no_extra_consecutive_blank_lines' => [ 31 | 'use', 32 | 'break', 33 | 'continue', 34 | 'extra', 35 | 'return', 36 | 'throw', 37 | 'useTrait', 38 | 'curly_brace_block', 39 | 'parenthesis_brace_block', 40 | 'square_brace_block', 41 | ], 42 | 'no_leading_import_slash' => true, 43 | 'no_leading_namespace_whitespace' => true, 44 | 'no_singleline_whitespace_before_semicolons' => true, 45 | 'no_trailing_comma_in_singleline_array' => true, 46 | 'no_unreachable_default_argument_value' => true, 47 | 'no_unused_imports' => true, 48 | 'no_useless_else' => true, 49 | 'no_useless_return' => true, 50 | 'no_whitespace_in_blank_line' => true, 51 | 'object_operator_without_whitespace' => true, 52 | 'ordered_class_elements' => true, 53 | 'ordered_imports' => true, 54 | 'php_unit_construct' => true, 55 | 'php_unit_dedicate_assert' => true, 56 | 'php_unit_expectation' => ['target' => '5.6'], 57 | 'php_unit_method_casing' => ['case' => 'camel_case'], 58 | 'php_unit_mock' => true, 59 | 'php_unit_mock_short_will_return' => true, 60 | 'php_unit_namespaced' => ['target' => '5.7'], 61 | 'php_unit_no_expectation_annotation' => true, 62 | 'php_unit_ordered_covers' => true, 63 | 'php_unit_set_up_tear_down_visibility' => true, 64 | 'php_unit_test_case_static_method_calls' => ['call_type' => 'this'], 65 | 'phpdoc_add_missing_param_annotation' => true, 66 | 'phpdoc_indent' => true, 67 | 'phpdoc_no_access' => true, 68 | 'phpdoc_no_empty_return' => true, 69 | 'phpdoc_no_package' => true, 70 | 'phpdoc_order' => true, 71 | 'phpdoc_scalar' => true, 72 | 'phpdoc_single_line_var_spacing' => true, 73 | 'phpdoc_trim' => true, 74 | 'phpdoc_types' => true, 75 | 'psr4' => true, 76 | 'self_accessor' => true, 77 | 'short_scalar_cast' => true, 78 | 'single_blank_line_before_namespace' => true, 79 | 'single_quote' => true, 80 | 'space_after_semicolon' => true, 81 | 'standardize_not_equals' => true, 82 | 'ternary_operator_spaces' => true, 83 | 'trailing_comma_in_multiline_array' => true, 84 | 'trim_array_spaces' => true, 85 | 'unary_operator_spaces' => true, 86 | 'visibility_required' => true, 87 | 'whitespace_after_comma_in_array' => true, 88 | 'yoda_style' => false, 89 | ]) 90 | ->setRiskyAllowed(true) 91 | ->setFinder($finder); 92 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | Facebook has adopted a Code of Conduct that we expect project participants to adhere to. Please [read the full text](https://code.facebook.com/codeofconduct) so that you can understand what actions will and will not be tolerated. 4 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to php-webdriver 2 | 3 | We love to have your help to make php-webdriver better! 4 | 5 | Feel free to open an [issue](https://github.com/facebook/php-webdriver/issues) if you run into any problem, or 6 | send a pull request (see bellow) with your contribution. 7 | 8 | ## Code of Conduct 9 | The code of conduct is described in [`CODE_OF_CONDUCT.md`](CODE_OF_CONDUCT.md) 10 | 11 | ## Workflow when contributing a patch 12 | 13 | 1. Fork the project on GitHub 14 | 2. Implement your code changes into separate branch 15 | 3. Make sure all PHPUnit tests passes and code-style matches PSR-2 (see below). We also have Travis CI build which will automatically run tests on your pull request. 16 | 4. When implementing notable change, fix or a new feature, add record to Unreleased section of [CHANGELOG.md](CHANGELOG.md) 17 | 5. Submit your [pull request](https://github.com/facebook/php-webdriver/pulls) against community branch 18 | 19 | Note before any pull request can be accepted, a [Contributors Licensing Agreement](https://developers.facebook.com/opensource/cla) must be signed. 20 | 21 | When you are going to contribute, please keep in mind that this webdriver client aims to be as close as possible to other languages Java/Ruby/Python/C#. 22 | FYI, here is the overview of [the official Java API](http://seleniumhq.github.io/selenium/docs/api/java/) 23 | 24 | ### Run unit tests 25 | 26 | There are two test-suites: one with unit tests only, second with functional tests, which require running selenium server. 27 | 28 | To execute all tests simply run: 29 | 30 | ./vendor/bin/phpunit 31 | 32 | If you want to execute just the unit tests, run: 33 | 34 | ./vendor/bin/phpunit --testsuite unit 35 | 36 | For the functional tests you must first [download](http://selenium-release.storage.googleapis.com/index.html) and start 37 | the selenium standalone server, start the local PHP server which will serve the test pages and then run the `functional` 38 | test suite: 39 | 40 | java -jar selenium-server-standalone-3.9.1.jar -log selenium.log & 41 | php -S localhost:8000 -t tests/functional/web/ & 42 | ./vendor/bin/phpunit --testsuite functional 43 | 44 | The functional tests will be started in HtmlUnit headless browser by default. If you want to run them in eg. Firefox, 45 | simply set the `BROWSER_NAME` environment variable: 46 | 47 | ... 48 | export BROWSER_NAME="firefox" 49 | ./vendor/bin/phpunit --testsuite functional 50 | 51 | To test with Geckodriver, [download](https://github.com/mozilla/geckodriver/releases) and start the server, then run: 52 | 53 | export GECKODRIVER=1 54 | export BROWSER_NAME=firefox 55 | ./vendor/bin/phpunit --testsuite functional 56 | 57 | ### Check coding style 58 | 59 | Your code-style should comply with [PSR-2](http://www.php-fig.org/psr/psr-2/). To make sure your code matches this requirement run: 60 | 61 | composer codestyle:check 62 | 63 | To auto-fix the codestyle simply run: 64 | 65 | composer codestyle:fix 66 | -------------------------------------------------------------------------------- /ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ### What are you trying to achieve? (Expected behavior) 2 | 3 | 4 | ### What do you get instead? (Actual behavior) 5 | 6 | 7 | ### How could the issue be reproduced? (Steps to reproduce) 8 | 9 | 10 | ```php 11 | // You can insert your PHP code here (or remove this block if it is not relevant for the issue). 12 | ``` 13 | 14 | ### Details 15 | 16 | 17 | * Php-webdriver version: 18 | * PHP version: 19 | * Selenium server version: 20 | * Operating system: 21 | * Browser used + version: 22 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "facebook/webdriver", 3 | "description": "A PHP client for Selenium WebDriver", 4 | "keywords": ["webdriver", "selenium", "php", "facebook"], 5 | "homepage": "https://github.com/facebook/php-webdriver", 6 | "type": "library", 7 | "license": "Apache-2.0", 8 | "support": { 9 | "issues": "https://github.com/facebook/php-webdriver/issues", 10 | "forum": "https://www.facebook.com/groups/phpwebdriver/", 11 | "source": "https://github.com/facebook/php-webdriver" 12 | }, 13 | "minimum-stability": "beta", 14 | "require": { 15 | "php": "^5.6 || ~7.0", 16 | "ext-curl": "*", 17 | "ext-json": "*", 18 | "ext-zip": "*", 19 | "symfony/polyfill-mbstring": "^1.12", 20 | "symfony/process": "^2.8 || ^3.1 || ^4.0 || ^5.0" 21 | }, 22 | "require-dev": { 23 | "friendsofphp/php-cs-fixer": "^2.0", 24 | "jakub-onderka/php-parallel-lint": "^1.0", 25 | "php-coveralls/php-coveralls": "^2.0", 26 | "php-mock/php-mock-phpunit": "^1.1", 27 | "phpunit/phpunit": "^5.7", 28 | "sebastian/environment": "^1.3.4 || ^2.0 || ^3.0", 29 | "sminnee/phpunit-mock-objects": "^3.4", 30 | "squizlabs/php_codesniffer": "^3.5", 31 | "symfony/var-dumper": "^3.3 || ^4.0 || ^5.0" 32 | }, 33 | "suggest": { 34 | "ext-SimpleXML": "For Firefox profile creation" 35 | }, 36 | "autoload": { 37 | "files": [ 38 | "lib/Exception/TimeoutException.php" 39 | ], 40 | "psr-4": { 41 | "Facebook\\WebDriver\\": "lib/" 42 | } 43 | }, 44 | "autoload-dev": { 45 | "psr-4": { 46 | "Facebook\\WebDriver\\": ["tests/unit", "tests/functional"] 47 | }, 48 | "classmap": ["tests/functional/"] 49 | }, 50 | "scripts": { 51 | "codestyle:check": [ 52 | "vendor/bin/php-cs-fixer fix --diff --diff-format=udiff --dry-run -vvv --ansi", 53 | "vendor/bin/phpcs --standard=PSR2 ./lib/ ./tests/" 54 | ], 55 | "codestyle:fix": [ 56 | "vendor/bin/php-cs-fixer fix --diff --diff-format=udiff -vvv || exit 0", 57 | "vendor/bin/phpcbf --standard=PSR2 ./lib/ ./tests/" 58 | ], 59 | "analyze": [ 60 | "vendor/bin/parallel-lint -j 10 ./lib ./tests", 61 | "vendor/bin/phpstan.phar analyze ./lib ./tests --level 2 -c phpstan.neon --ansi" 62 | ] 63 | }, 64 | "config": { 65 | "sort-packages": true 66 | }, 67 | "extra": { 68 | "branch-alias": { 69 | "dev-community": "1.8.x-dev" 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /lib/Chrome/ChromeDriver.php: -------------------------------------------------------------------------------- 1 | startSession($desired_capabilities); 41 | 42 | return $driver; 43 | } 44 | 45 | public function startSession(DesiredCapabilities $desired_capabilities) 46 | { 47 | $command = new WebDriverCommand( 48 | null, 49 | DriverCommand::NEW_SESSION, 50 | [ 51 | 'desiredCapabilities' => $desired_capabilities->toArray(), 52 | ] 53 | ); 54 | $response = $this->executor->execute($command); 55 | $this->sessionID = $response->getSessionID(); 56 | } 57 | 58 | /** 59 | * Always throws an exception. Use ChromeDriver::start() instead. 60 | * 61 | * @param string $selenium_server_url 62 | * @param DesiredCapabilities|array $desired_capabilities 63 | * @param int|null $connection_timeout_in_ms 64 | * @param int|null $request_timeout_in_ms 65 | * @param string|null $http_proxy 66 | * @param int|null $http_proxy_port 67 | * @param DesiredCapabilities $required_capabilities 68 | * @throws WebDriverException 69 | * @return RemoteWebDriver 70 | */ 71 | public static function create( 72 | $selenium_server_url = 'http://localhost:4444/wd/hub', 73 | $desired_capabilities = null, 74 | $connection_timeout_in_ms = null, 75 | $request_timeout_in_ms = null, 76 | $http_proxy = null, 77 | $http_proxy_port = null, 78 | DesiredCapabilities $required_capabilities = null 79 | ) { 80 | throw new WebDriverException('Please use ChromeDriver::start() instead.'); 81 | } 82 | 83 | /** 84 | * Always throws an exception. Use ChromeDriver::start() instead. 85 | * 86 | * @param string $session_id The existing session id 87 | * @param string $selenium_server_url The url of the remote Selenium WebDriver server 88 | * @param int|null $connection_timeout_in_ms Set timeout for the connect phase to remote Selenium WebDriver server 89 | * @param int|null $request_timeout_in_ms Set the maximum time of a request to remote Selenium WebDriver server 90 | * @throws WebDriverException 91 | * @return RemoteWebDriver|void 92 | */ 93 | public static function createBySessionID( 94 | $session_id, 95 | $selenium_server_url = 'http://localhost:4444/wd/hub', 96 | $connection_timeout_in_ms = null, 97 | $request_timeout_in_ms = null 98 | ) { 99 | throw new WebDriverException('Please use ChromeDriver::start() instead.'); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /lib/Chrome/ChromeDriverService.php: -------------------------------------------------------------------------------- 1 | mouse->mouseUp($this->getActionLocation()); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /lib/Interactions/Internal/WebDriverClickAction.php: -------------------------------------------------------------------------------- 1 | mouse->click($this->getActionLocation()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lib/Interactions/Internal/WebDriverClickAndHoldAction.php: -------------------------------------------------------------------------------- 1 | mouse->mouseDown($this->getActionLocation()); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /lib/Interactions/Internal/WebDriverContextClickAction.php: -------------------------------------------------------------------------------- 1 | mouse->contextClick($this->getActionLocation()); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /lib/Interactions/Internal/WebDriverCoordinates.php: -------------------------------------------------------------------------------- 1 | onScreen = $on_screen; 52 | $this->inViewPort = $in_view_port; 53 | $this->onPage = $on_page; 54 | $this->auxiliary = $auxiliary; 55 | } 56 | 57 | /** 58 | * @throws UnsupportedOperationException 59 | * @return WebDriverPoint 60 | */ 61 | public function onScreen() 62 | { 63 | throw new UnsupportedOperationException( 64 | 'onScreen is planned but not yet supported by Selenium' 65 | ); 66 | } 67 | 68 | /** 69 | * @return WebDriverPoint 70 | */ 71 | public function inViewPort() 72 | { 73 | return call_user_func($this->inViewPort); 74 | } 75 | 76 | /** 77 | * @return WebDriverPoint 78 | */ 79 | public function onPage() 80 | { 81 | return call_user_func($this->onPage); 82 | } 83 | 84 | /** 85 | * @return string The attached object id. 86 | */ 87 | public function getAuxiliary() 88 | { 89 | return $this->auxiliary; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /lib/Interactions/Internal/WebDriverDoubleClickAction.php: -------------------------------------------------------------------------------- 1 | mouse->doubleClick($this->getActionLocation()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lib/Interactions/Internal/WebDriverKeyDownAction.php: -------------------------------------------------------------------------------- 1 | focusOnElement(); 25 | $this->keyboard->pressKey($this->key); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /lib/Interactions/Internal/WebDriverKeyUpAction.php: -------------------------------------------------------------------------------- 1 | focusOnElement(); 25 | $this->keyboard->releaseKey($this->key); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /lib/Interactions/Internal/WebDriverKeysRelatedAction.php: -------------------------------------------------------------------------------- 1 | keyboard = $keyboard; 51 | $this->mouse = $mouse; 52 | $this->locationProvider = $location_provider; 53 | } 54 | 55 | protected function focusOnElement() 56 | { 57 | if ($this->locationProvider) { 58 | $this->mouse->click($this->locationProvider->getCoordinates()); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /lib/Interactions/Internal/WebDriverMouseAction.php: -------------------------------------------------------------------------------- 1 | mouse = $mouse; 42 | $this->locationProvider = $location_provider; 43 | } 44 | 45 | /** 46 | * @return null|WebDriverCoordinates 47 | */ 48 | protected function getActionLocation() 49 | { 50 | if ($this->locationProvider !== null) { 51 | return $this->locationProvider->getCoordinates(); 52 | } 53 | 54 | return null; 55 | } 56 | 57 | protected function moveToLocation() 58 | { 59 | $this->mouse->mouseMove($this->locationProvider); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /lib/Interactions/Internal/WebDriverMouseMoveAction.php: -------------------------------------------------------------------------------- 1 | mouse->mouseMove($this->getActionLocation()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lib/Interactions/Internal/WebDriverMoveToOffsetAction.php: -------------------------------------------------------------------------------- 1 | xOffset = $x_offset; 47 | $this->yOffset = $y_offset; 48 | } 49 | 50 | public function perform() 51 | { 52 | $this->mouse->mouseMove( 53 | $this->getActionLocation(), 54 | $this->xOffset, 55 | $this->yOffset 56 | ); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /lib/Interactions/Internal/WebDriverSendKeysAction.php: -------------------------------------------------------------------------------- 1 | keys = $keys; 44 | } 45 | 46 | public function perform() 47 | { 48 | $this->focusOnElement(); 49 | $this->keyboard->sendKeys($this->keys); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /lib/Interactions/Internal/WebDriverSingleKeyAction.php: -------------------------------------------------------------------------------- 1 | key = $key; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /lib/Interactions/Touch/WebDriverDoubleTapAction.php: -------------------------------------------------------------------------------- 1 | touchScreen->doubleTap($this->locationProvider); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lib/Interactions/Touch/WebDriverDownAction.php: -------------------------------------------------------------------------------- 1 | x = $x; 39 | $this->y = $y; 40 | parent::__construct($touch_screen); 41 | } 42 | 43 | public function perform() 44 | { 45 | $this->touchScreen->down($this->x, $this->y); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /lib/Interactions/Touch/WebDriverFlickAction.php: -------------------------------------------------------------------------------- 1 | x = $x; 39 | $this->y = $y; 40 | parent::__construct($touch_screen); 41 | } 42 | 43 | public function perform() 44 | { 45 | $this->touchScreen->flick($this->x, $this->y); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /lib/Interactions/Touch/WebDriverFlickFromElementAction.php: -------------------------------------------------------------------------------- 1 | x = $x; 51 | $this->y = $y; 52 | $this->speed = $speed; 53 | parent::__construct($touch_screen, $element); 54 | } 55 | 56 | public function perform() 57 | { 58 | $this->touchScreen->flickFromElement( 59 | $this->locationProvider, 60 | $this->x, 61 | $this->y, 62 | $this->speed 63 | ); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /lib/Interactions/Touch/WebDriverLongPressAction.php: -------------------------------------------------------------------------------- 1 | touchScreen->longPress($this->locationProvider); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lib/Interactions/Touch/WebDriverMoveAction.php: -------------------------------------------------------------------------------- 1 | x = $x; 33 | $this->y = $y; 34 | parent::__construct($touch_screen); 35 | } 36 | 37 | public function perform() 38 | { 39 | $this->touchScreen->move($this->x, $this->y); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /lib/Interactions/Touch/WebDriverScrollAction.php: -------------------------------------------------------------------------------- 1 | x = $x; 33 | $this->y = $y; 34 | parent::__construct($touch_screen); 35 | } 36 | 37 | public function perform() 38 | { 39 | $this->touchScreen->scroll($this->x, $this->y); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /lib/Interactions/Touch/WebDriverScrollFromElementAction.php: -------------------------------------------------------------------------------- 1 | x = $x; 39 | $this->y = $y; 40 | parent::__construct($touch_screen, $element); 41 | } 42 | 43 | public function perform() 44 | { 45 | $this->touchScreen->scrollFromElement( 46 | $this->locationProvider, 47 | $this->x, 48 | $this->y 49 | ); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /lib/Interactions/Touch/WebDriverTapAction.php: -------------------------------------------------------------------------------- 1 | touchScreen->tap($this->locationProvider); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lib/Interactions/Touch/WebDriverTouchAction.php: -------------------------------------------------------------------------------- 1 | touchScreen = $touch_screen; 44 | $this->locationProvider = $location_provider; 45 | } 46 | 47 | /** 48 | * @return null|WebDriverCoordinates 49 | */ 50 | protected function getActionLocation() 51 | { 52 | return $this->locationProvider !== null 53 | ? $this->locationProvider->getCoordinates() : null; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /lib/Interactions/Touch/WebDriverTouchScreen.php: -------------------------------------------------------------------------------- 1 | actions[] = $action; 39 | 40 | return $this; 41 | } 42 | 43 | /** 44 | * Get the number of actions in the sequence. 45 | * 46 | * @return int The number of actions. 47 | */ 48 | public function getNumberOfActions() 49 | { 50 | return count($this->actions); 51 | } 52 | 53 | /** 54 | * Perform the sequence of actions. 55 | */ 56 | public function perform() 57 | { 58 | foreach ($this->actions as $action) { 59 | $action->perform(); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /lib/Internal/WebDriverLocatable.php: -------------------------------------------------------------------------------- 1 | microtime(true)) { 31 | if ($this->getHTTPResponseCode($url) === 200) { 32 | return $this; 33 | } 34 | usleep(self::POLL_INTERVAL_MS); 35 | } 36 | 37 | throw new TimeoutException(sprintf( 38 | 'Timed out waiting for %s to become available after %d ms.', 39 | $url, 40 | $timeout_in_ms 41 | )); 42 | } 43 | 44 | public function waitUntilUnavailable($timeout_in_ms, $url) 45 | { 46 | $end = microtime(true) + $timeout_in_ms / 1000; 47 | 48 | while ($end > microtime(true)) { 49 | if ($this->getHTTPResponseCode($url) !== 200) { 50 | return $this; 51 | } 52 | usleep(self::POLL_INTERVAL_MS); 53 | } 54 | 55 | throw new TimeoutException(sprintf( 56 | 'Timed out waiting for %s to become unavailable after %d ms.', 57 | $url, 58 | $timeout_in_ms 59 | )); 60 | } 61 | 62 | private function getHTTPResponseCode($url) 63 | { 64 | $ch = curl_init(); 65 | curl_setopt($ch, CURLOPT_URL, $url); 66 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 67 | // The PHP doc indicates that CURLOPT_CONNECTTIMEOUT_MS constant is added in cURL 7.16.2 68 | // available since PHP 5.2.3. 69 | if (!defined('CURLOPT_CONNECTTIMEOUT_MS')) { 70 | define('CURLOPT_CONNECTTIMEOUT_MS', 156); // default value for CURLOPT_CONNECTTIMEOUT_MS 71 | } 72 | curl_setopt($ch, CURLOPT_CONNECTTIMEOUT_MS, self::CONNECT_TIMEOUT_MS); 73 | 74 | $code = null; 75 | try { 76 | curl_exec($ch); 77 | $info = curl_getinfo($ch); 78 | $code = $info['http_code']; 79 | } catch (Exception $e) { 80 | } 81 | curl_close($ch); 82 | 83 | return $code; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /lib/Remote/ExecuteMethod.php: -------------------------------------------------------------------------------- 1 | getMechanism(); 54 | $value = $by->getValue(); 55 | 56 | if ($isW3cCompliant) { 57 | switch ($mechanism) { 58 | // Convert to CSS selectors 59 | case 'class name': 60 | $mechanism = 'css selector'; 61 | $value = sprintf('.%s', self::escapeSelector($value)); 62 | break; 63 | case 'id': 64 | $mechanism = 'css selector'; 65 | $value = sprintf('#%s', self::escapeSelector($value)); 66 | break; 67 | case 'name': 68 | $mechanism = 'css selector'; 69 | $value = sprintf('[name=\'%s\']', self::escapeSelector($value)); 70 | break; 71 | } 72 | } 73 | 74 | return ['using' => $mechanism, 'value' => $value]; 75 | } 76 | 77 | /** 78 | * Escapes a CSS selector. 79 | * 80 | * Code adapted from the Zend Escaper project. 81 | * 82 | * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 83 | * @see https://github.com/zendframework/zend-escaper/blob/master/src/Escaper.php 84 | * 85 | * @param string $selector 86 | * @return string 87 | */ 88 | private static function escapeSelector($selector) 89 | { 90 | return preg_replace_callback('/[^a-z0-9]/iSu', function ($matches) { 91 | $chr = $matches[0]; 92 | if (mb_strlen($chr) === 1) { 93 | $ord = ord($chr); 94 | } else { 95 | $chr = mb_convert_encoding($chr, 'UTF-32BE', 'UTF-8'); 96 | $ord = hexdec(bin2hex($chr)); 97 | } 98 | 99 | return sprintf('\\%X ', $ord); 100 | }, $selector); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /lib/Remote/LocalFileDetector.php: -------------------------------------------------------------------------------- 1 | driver = $driver; 31 | } 32 | 33 | /** 34 | * @param string $command_name 35 | * @param array $parameters 36 | * @return mixed 37 | */ 38 | public function execute($command_name, array $parameters = []) 39 | { 40 | return $this->driver->execute($command_name, $parameters); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /lib/Remote/RemoteKeyboard.php: -------------------------------------------------------------------------------- 1 | executor = $executor; 40 | $this->driver = $driver; 41 | $this->isW3cCompliant = $isW3cCompliant; 42 | } 43 | 44 | /** 45 | * Send keys to active element 46 | * @param string|array $keys 47 | * @return $this 48 | */ 49 | public function sendKeys($keys) 50 | { 51 | if ($this->isW3cCompliant) { 52 | $activeElement = $this->driver->switchTo()->activeElement(); 53 | $activeElement->sendKeys($keys); 54 | } else { 55 | $this->executor->execute(DriverCommand::SEND_KEYS_TO_ACTIVE_ELEMENT, [ 56 | 'value' => WebDriverKeys::encode($keys), 57 | ]); 58 | } 59 | 60 | return $this; 61 | } 62 | 63 | /** 64 | * Press a modifier key 65 | * 66 | * @see WebDriverKeys 67 | * @param string $key 68 | * @return $this 69 | */ 70 | public function pressKey($key) 71 | { 72 | if ($this->isW3cCompliant) { 73 | $this->executor->execute(DriverCommand::ACTIONS, [ 74 | 'actions' => [ 75 | [ 76 | 'type' => 'key', 77 | 'id' => 'keyboard', 78 | 'actions' => [['type' => 'keyDown', 'value' => $key]], 79 | ], 80 | ], 81 | ]); 82 | } else { 83 | $this->executor->execute(DriverCommand::SEND_KEYS_TO_ACTIVE_ELEMENT, [ 84 | 'value' => [(string) $key], 85 | ]); 86 | } 87 | 88 | return $this; 89 | } 90 | 91 | /** 92 | * Release a modifier key 93 | * 94 | * @see WebDriverKeys 95 | * @param string $key 96 | * @return $this 97 | */ 98 | public function releaseKey($key) 99 | { 100 | if ($this->isW3cCompliant) { 101 | $this->executor->execute(DriverCommand::ACTIONS, [ 102 | 'actions' => [ 103 | [ 104 | 'type' => 'key', 105 | 'id' => 'keyboard', 106 | 'actions' => [['type' => 'keyUp', 'value' => $key]], 107 | ], 108 | ], 109 | ]); 110 | } else { 111 | $this->executor->execute(DriverCommand::SEND_KEYS_TO_ACTIVE_ELEMENT, [ 112 | 'value' => [(string) $key], 113 | ]); 114 | } 115 | 116 | return $this; 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /lib/Remote/RemoteStatus.php: -------------------------------------------------------------------------------- 1 | isReady = (bool) $isReady; 39 | $this->message = (string) $message; 40 | 41 | $this->setMeta($meta); 42 | } 43 | 44 | /** 45 | * @param array $responseBody 46 | * @return RemoteStatus 47 | */ 48 | public static function createFromResponse(array $responseBody) 49 | { 50 | $object = new static($responseBody['ready'], $responseBody['message'], $responseBody); 51 | 52 | return $object; 53 | } 54 | 55 | /** 56 | * The remote end's readiness state. 57 | * False if an attempt to create a session at the current time would fail. 58 | * However, the value true does not guarantee that a New Session command will succeed. 59 | * 60 | * @return bool 61 | */ 62 | public function isReady() 63 | { 64 | return $this->isReady; 65 | } 66 | 67 | /** 68 | * An implementation-defined string explaining the remote end's readiness state. 69 | * 70 | * @return string 71 | */ 72 | public function getMessage() 73 | { 74 | return $this->message; 75 | } 76 | 77 | /** 78 | * Arbitrary meta information specific to remote-end implementation. 79 | * 80 | * @return array 81 | */ 82 | public function getMeta() 83 | { 84 | return $this->meta; 85 | } 86 | 87 | protected function setMeta(array $meta) 88 | { 89 | unset($meta['ready'], $meta['message']); 90 | 91 | $this->meta = $meta; 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /lib/Remote/Service/DriverCommandExecutor.php: -------------------------------------------------------------------------------- 1 | getURL()); 38 | $this->service = $service; 39 | } 40 | 41 | /** 42 | * @param WebDriverCommand $command 43 | * 44 | * @throws WebDriverException 45 | * @throws \Exception 46 | * @return WebDriverResponse 47 | */ 48 | public function execute(WebDriverCommand $command) 49 | { 50 | if ($command->getName() === DriverCommand::NEW_SESSION) { 51 | $this->service->start(); 52 | } 53 | 54 | try { 55 | $value = parent::execute($command); 56 | if ($command->getName() === DriverCommand::QUIT) { 57 | $this->service->stop(); 58 | } 59 | 60 | return $value; 61 | } catch (\Exception $e) { 62 | if (!$this->service->isRunning()) { 63 | throw new WebDriverException('The driver server has died.'); 64 | } 65 | throw $e; 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /lib/Remote/Service/DriverService.php: -------------------------------------------------------------------------------- 1 | executable = self::checkExecutable($executable); 62 | $this->url = sprintf('http://localhost:%d', $port); 63 | $this->args = $args; 64 | $this->environment = $environment ?: $_ENV; 65 | } 66 | 67 | /** 68 | * @return string 69 | */ 70 | public function getURL() 71 | { 72 | return $this->url; 73 | } 74 | 75 | /** 76 | * @return DriverService 77 | */ 78 | public function start() 79 | { 80 | if ($this->process !== null) { 81 | return $this; 82 | } 83 | 84 | $this->process = $this->createProcess(); 85 | $this->process->start(); 86 | 87 | $checker = new URLChecker(); 88 | $checker->waitUntilAvailable(20 * 1000, $this->url . '/status'); 89 | 90 | return $this; 91 | } 92 | 93 | /** 94 | * @return DriverService 95 | */ 96 | public function stop() 97 | { 98 | if ($this->process === null) { 99 | return $this; 100 | } 101 | 102 | $this->process->stop(); 103 | $this->process = null; 104 | 105 | $checker = new URLChecker(); 106 | $checker->waitUntilUnavailable(3 * 1000, $this->url . '/shutdown'); 107 | 108 | return $this; 109 | } 110 | 111 | /** 112 | * @return bool 113 | */ 114 | public function isRunning() 115 | { 116 | if ($this->process === null) { 117 | return false; 118 | } 119 | 120 | return $this->process->isRunning(); 121 | } 122 | 123 | /** 124 | * Check if the executable is executable. 125 | * 126 | * @param string $executable 127 | * @throws Exception 128 | * @return string 129 | */ 130 | protected static function checkExecutable($executable) 131 | { 132 | if (!is_file($executable)) { 133 | throw new Exception("'$executable' is not a file."); 134 | } 135 | 136 | if (!is_executable($executable)) { 137 | throw new Exception("'$executable' is not executable."); 138 | } 139 | 140 | return $executable; 141 | } 142 | 143 | /** 144 | * @return Process 145 | */ 146 | private function createProcess() 147 | { 148 | // BC: ProcessBuilder deprecated since Symfony 3.4 and removed in Symfony 4.0. 149 | if (class_exists(ProcessBuilder::class) 150 | && false === mb_strpos('@deprecated', (new \ReflectionClass(ProcessBuilder::class))->getDocComment()) 151 | ) { 152 | $processBuilder = (new ProcessBuilder()) 153 | ->setPrefix($this->executable) 154 | ->setArguments($this->args) 155 | ->addEnvironmentVariables($this->environment); 156 | 157 | return $processBuilder->getProcess(); 158 | } 159 | // Safe to use since Symfony 3.3 160 | $commandLine = array_merge([$this->executable], $this->args); 161 | 162 | return new Process($commandLine, null, $this->environment); 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /lib/Remote/UselessFileDetector.php: -------------------------------------------------------------------------------- 1 | sessionID = $session_id; 36 | $this->name = $name; 37 | $this->parameters = $parameters; 38 | } 39 | 40 | /** 41 | * @return string 42 | */ 43 | public function getName() 44 | { 45 | return $this->name; 46 | } 47 | 48 | /** 49 | * @return string 50 | */ 51 | public function getSessionID() 52 | { 53 | return $this->sessionID; 54 | } 55 | 56 | /** 57 | * @return array 58 | */ 59 | public function getParameters() 60 | { 61 | return $this->parameters; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /lib/Remote/WebDriverResponse.php: -------------------------------------------------------------------------------- 1 | sessionID = $session_id; 39 | } 40 | 41 | /** 42 | * @return null|int 43 | */ 44 | public function getStatus() 45 | { 46 | return $this->status; 47 | } 48 | 49 | /** 50 | * @param int $status 51 | * @return WebDriverResponse 52 | */ 53 | public function setStatus($status) 54 | { 55 | $this->status = $status; 56 | 57 | return $this; 58 | } 59 | 60 | /** 61 | * @return mixed 62 | */ 63 | public function getValue() 64 | { 65 | return $this->value; 66 | } 67 | 68 | /** 69 | * @param mixed $value 70 | * @return WebDriverResponse 71 | */ 72 | public function setValue($value) 73 | { 74 | $this->value = $value; 75 | 76 | return $this; 77 | } 78 | 79 | /** 80 | * @return null|string 81 | */ 82 | public function getSessionID() 83 | { 84 | return $this->sessionID; 85 | } 86 | 87 | /** 88 | * @param mixed $session_id 89 | * @return WebDriverResponse 90 | */ 91 | public function setSessionID($session_id) 92 | { 93 | $this->sessionID = $session_id; 94 | 95 | return $this; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /lib/Support/Events/EventFiringWebDriverNavigation.php: -------------------------------------------------------------------------------- 1 | navigator = $navigator; 40 | $this->dispatcher = $dispatcher; 41 | } 42 | 43 | /** 44 | * @return WebDriverDispatcher 45 | */ 46 | public function getDispatcher() 47 | { 48 | return $this->dispatcher; 49 | } 50 | 51 | /** 52 | * @return WebDriverNavigationInterface 53 | */ 54 | public function getNavigator() 55 | { 56 | return $this->navigator; 57 | } 58 | 59 | public function back() 60 | { 61 | $this->dispatch( 62 | 'beforeNavigateBack', 63 | $this->getDispatcher()->getDefaultDriver() 64 | ); 65 | try { 66 | $this->navigator->back(); 67 | } catch (WebDriverException $exception) { 68 | $this->dispatchOnException($exception); 69 | } 70 | $this->dispatch( 71 | 'afterNavigateBack', 72 | $this->getDispatcher()->getDefaultDriver() 73 | ); 74 | 75 | return $this; 76 | } 77 | 78 | public function forward() 79 | { 80 | $this->dispatch( 81 | 'beforeNavigateForward', 82 | $this->getDispatcher()->getDefaultDriver() 83 | ); 84 | try { 85 | $this->navigator->forward(); 86 | } catch (WebDriverException $exception) { 87 | $this->dispatchOnException($exception); 88 | } 89 | $this->dispatch( 90 | 'afterNavigateForward', 91 | $this->getDispatcher()->getDefaultDriver() 92 | ); 93 | 94 | return $this; 95 | } 96 | 97 | public function refresh() 98 | { 99 | try { 100 | $this->navigator->refresh(); 101 | 102 | return $this; 103 | } catch (WebDriverException $exception) { 104 | $this->dispatchOnException($exception); 105 | throw $exception; 106 | } 107 | } 108 | 109 | public function to($url) 110 | { 111 | $this->dispatch( 112 | 'beforeNavigateTo', 113 | $url, 114 | $this->getDispatcher()->getDefaultDriver() 115 | ); 116 | 117 | try { 118 | $this->navigator->to($url); 119 | } catch (WebDriverException $exception) { 120 | $this->dispatchOnException($exception); 121 | throw $exception; 122 | } 123 | 124 | $this->dispatch( 125 | 'afterNavigateTo', 126 | $url, 127 | $this->getDispatcher()->getDefaultDriver() 128 | ); 129 | 130 | return $this; 131 | } 132 | 133 | /** 134 | * @param mixed $method 135 | * @param mixed ...$arguments 136 | */ 137 | protected function dispatch($method, ...$arguments) 138 | { 139 | if (!$this->dispatcher) { 140 | return; 141 | } 142 | 143 | $this->dispatcher->dispatch($method, $arguments); 144 | } 145 | 146 | /** 147 | * @param WebDriverException $exception 148 | */ 149 | protected function dispatchOnException(WebDriverException $exception) 150 | { 151 | $this->dispatch('onException', $exception); 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /lib/Support/XPathEscaper.php: -------------------------------------------------------------------------------- 1 | `concat('foo', "'" ,'"bar')` 23 | * 24 | * @param string $xpathToEscape The xpath to be converted. 25 | * @return string The escaped string. 26 | */ 27 | public static function escapeQuotes($xpathToEscape) 28 | { 29 | // Single quotes not present => we can quote in them 30 | if (mb_strpos($xpathToEscape, "'") === false) { 31 | return sprintf("'%s'", $xpathToEscape); 32 | } 33 | 34 | // Double quotes not present => we can quote in them 35 | if (mb_strpos($xpathToEscape, '"') === false) { 36 | return sprintf('"%s"', $xpathToEscape); 37 | } 38 | 39 | // Both single and double quotes are present 40 | return sprintf( 41 | "concat('%s')", 42 | str_replace("'", "', \"'\" ,'", $xpathToEscape) 43 | ); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /lib/WebDriver.php: -------------------------------------------------------------------------------- 1 | wait(20, 1000)->until( 94 | * WebDriverExpectedCondition::titleIs('WebDriver Page') 95 | * ); 96 | * 97 | * @param int $timeout_in_second 98 | * @param int $interval_in_millisecond 99 | * @return WebDriverWait 100 | */ 101 | public function wait( 102 | $timeout_in_second = 30, 103 | $interval_in_millisecond = 250 104 | ); 105 | 106 | /** 107 | * An abstraction for managing stuff you would do in a browser menu. For 108 | * example, adding and deleting cookies. 109 | * 110 | * @return WebDriverOptions 111 | */ 112 | public function manage(); 113 | 114 | /** 115 | * An abstraction allowing the driver to access the browser's history and to 116 | * navigate to a given URL. 117 | * 118 | * @return WebDriverNavigationInterface 119 | * @see WebDriverNavigation 120 | */ 121 | public function navigate(); 122 | 123 | /** 124 | * Switch to a different window or frame. 125 | * 126 | * @return WebDriverTargetLocator 127 | * @see WebDriverTargetLocator 128 | */ 129 | public function switchTo(); 130 | 131 | // TODO: Add in next major release (BC) 132 | ///** 133 | // * @return WebDriverTouchScreen 134 | // */ 135 | //public function getTouch(); 136 | 137 | /** 138 | * @param string $name 139 | * @param array $params 140 | * @return mixed 141 | */ 142 | public function execute($name, $params); 143 | } 144 | -------------------------------------------------------------------------------- /lib/WebDriverAction.php: -------------------------------------------------------------------------------- 1 | executor = $executor; 34 | } 35 | 36 | /** 37 | * Accept alert 38 | * 39 | * @return WebDriverAlert The instance. 40 | */ 41 | public function accept() 42 | { 43 | $this->executor->execute(DriverCommand::ACCEPT_ALERT); 44 | 45 | return $this; 46 | } 47 | 48 | /** 49 | * Dismiss alert 50 | * 51 | * @return WebDriverAlert The instance. 52 | */ 53 | public function dismiss() 54 | { 55 | $this->executor->execute(DriverCommand::DISMISS_ALERT); 56 | 57 | return $this; 58 | } 59 | 60 | /** 61 | * Get alert text 62 | * 63 | * @return string 64 | */ 65 | public function getText() 66 | { 67 | return $this->executor->execute(DriverCommand::GET_ALERT_TEXT); 68 | } 69 | 70 | /** 71 | * Send keystrokes to javascript prompt() dialog 72 | * 73 | * @param string $value 74 | * @return WebDriverAlert 75 | */ 76 | public function sendKeys($value) 77 | { 78 | $this->executor->execute( 79 | DriverCommand::SET_ALERT_VALUE, 80 | ['text' => $value] 81 | ); 82 | 83 | return $this; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /lib/WebDriverBy.php: -------------------------------------------------------------------------------- 1 | mechanism = $mechanism; 39 | $this->value = $value; 40 | } 41 | 42 | /** 43 | * @return string 44 | */ 45 | public function getMechanism() 46 | { 47 | return $this->mechanism; 48 | } 49 | 50 | /** 51 | * @return string 52 | */ 53 | public function getValue() 54 | { 55 | return $this->value; 56 | } 57 | 58 | /** 59 | * Locates elements whose class name contains the search value; compound class 60 | * names are not permitted. 61 | * 62 | * @param string $class_name 63 | * @return static 64 | */ 65 | public static function className($class_name) 66 | { 67 | return new static('class name', $class_name); 68 | } 69 | 70 | /** 71 | * Locates elements matching a CSS selector. 72 | * 73 | * @param string $css_selector 74 | * @return static 75 | */ 76 | public static function cssSelector($css_selector) 77 | { 78 | return new static('css selector', $css_selector); 79 | } 80 | 81 | /** 82 | * Locates elements whose ID attribute matches the search value. 83 | * 84 | * @param string $id 85 | * @return static 86 | */ 87 | public static function id($id) 88 | { 89 | return new static('id', $id); 90 | } 91 | 92 | /** 93 | * Locates elements whose NAME attribute matches the search value. 94 | * 95 | * @param string $name 96 | * @return static 97 | */ 98 | public static function name($name) 99 | { 100 | return new static('name', $name); 101 | } 102 | 103 | /** 104 | * Locates anchor elements whose visible text matches the search value. 105 | * 106 | * @param string $link_text 107 | * @return static 108 | */ 109 | public static function linkText($link_text) 110 | { 111 | return new static('link text', $link_text); 112 | } 113 | 114 | /** 115 | * Locates anchor elements whose visible text partially matches the search 116 | * value. 117 | * 118 | * @param string $partial_link_text 119 | * @return static 120 | */ 121 | public static function partialLinkText($partial_link_text) 122 | { 123 | return new static('partial link text', $partial_link_text); 124 | } 125 | 126 | /** 127 | * Locates elements whose tag name matches the search value. 128 | * 129 | * @param string $tag_name 130 | * @return static 131 | */ 132 | public static function tagName($tag_name) 133 | { 134 | return new static('tag name', $tag_name); 135 | } 136 | 137 | /** 138 | * Locates elements matching an XPath expression. 139 | * 140 | * @param string $xpath 141 | * @return static 142 | */ 143 | public static function xpath($xpath) 144 | { 145 | return new static('xpath', $xpath); 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /lib/WebDriverCapabilities.php: -------------------------------------------------------------------------------- 1 | type = $element->getAttribute('type'); 30 | if ($this->type !== 'checkbox') { 31 | throw new WebDriverException('The input must be of type "checkbox".'); 32 | } 33 | } 34 | 35 | public function isMultiple() 36 | { 37 | return true; 38 | } 39 | 40 | public function deselectAll() 41 | { 42 | foreach ($this->getRelatedElements() as $checkbox) { 43 | $this->deselectOption($checkbox); 44 | } 45 | } 46 | 47 | public function deselectByIndex($index) 48 | { 49 | $this->byIndex($index, false); 50 | } 51 | 52 | public function deselectByValue($value) 53 | { 54 | $this->byValue($value, false); 55 | } 56 | 57 | public function deselectByVisibleText($text) 58 | { 59 | $this->byVisibleText($text, false, false); 60 | } 61 | 62 | public function deselectByVisiblePartialText($text) 63 | { 64 | $this->byVisibleText($text, true, false); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /lib/WebDriverCommandExecutor.php: -------------------------------------------------------------------------------- 1 | width = $width; 39 | $this->height = $height; 40 | } 41 | 42 | /** 43 | * Get the height. 44 | * 45 | * @return int The height. 46 | */ 47 | public function getHeight() 48 | { 49 | return (int) $this->height; 50 | } 51 | 52 | /** 53 | * Get the width. 54 | * 55 | * @return int The width. 56 | */ 57 | public function getWidth() 58 | { 59 | return (int) $this->width; 60 | } 61 | 62 | /** 63 | * Check whether the given dimension is the same as the instance. 64 | * 65 | * @param WebDriverDimension $dimension The dimension to be compared with. 66 | * @return bool Whether the height and the width are the same as the instance. 67 | */ 68 | public function equals(self $dimension) 69 | { 70 | return $this->height === $dimension->getHeight() && $this->width === $dimension->getWidth(); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /lib/WebDriverDispatcher.php: -------------------------------------------------------------------------------- 1 | driver = $driver; 41 | 42 | return $this; 43 | } 44 | 45 | /** 46 | * @return null|EventFiringWebDriver 47 | */ 48 | public function getDefaultDriver() 49 | { 50 | return $this->driver; 51 | } 52 | 53 | /** 54 | * @param WebDriverEventListener $listener 55 | * @return $this 56 | */ 57 | public function register(WebDriverEventListener $listener) 58 | { 59 | $this->listeners[] = $listener; 60 | 61 | return $this; 62 | } 63 | 64 | /** 65 | * @param WebDriverEventListener $listener 66 | * @return $this 67 | */ 68 | public function unregister(WebDriverEventListener $listener) 69 | { 70 | $key = array_search($listener, $this->listeners, true); 71 | if ($key !== false) { 72 | unset($this->listeners[$key]); 73 | } 74 | 75 | return $this; 76 | } 77 | 78 | /** 79 | * @param mixed $method 80 | * @param mixed $arguments 81 | * @return $this 82 | */ 83 | public function dispatch($method, $arguments) 84 | { 85 | foreach ($this->listeners as $listener) { 86 | call_user_func_array([$listener, $method], $arguments); 87 | } 88 | 89 | return $this; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /lib/WebDriverElement.php: -------------------------------------------------------------------------------- 1 | executor = $executor; 28 | } 29 | 30 | public function back() 31 | { 32 | $this->executor->execute(DriverCommand::GO_BACK); 33 | 34 | return $this; 35 | } 36 | 37 | public function forward() 38 | { 39 | $this->executor->execute(DriverCommand::GO_FORWARD); 40 | 41 | return $this; 42 | } 43 | 44 | public function refresh() 45 | { 46 | $this->executor->execute(DriverCommand::REFRESH); 47 | 48 | return $this; 49 | } 50 | 51 | public function to($url) 52 | { 53 | $params = ['url' => (string) $url]; 54 | $this->executor->execute(DriverCommand::GET, $params); 55 | 56 | return $this; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /lib/WebDriverNavigationInterface.php: -------------------------------------------------------------------------------- 1 | x = $x; 29 | $this->y = $y; 30 | } 31 | 32 | /** 33 | * Get the x-coordinate. 34 | * 35 | * @return int The x-coordinate of the point. 36 | */ 37 | public function getX() 38 | { 39 | return (int) $this->x; 40 | } 41 | 42 | /** 43 | * Get the y-coordinate. 44 | * 45 | * @return int The y-coordinate of the point. 46 | */ 47 | public function getY() 48 | { 49 | return (int) $this->y; 50 | } 51 | 52 | /** 53 | * Set the point to a new position. 54 | * 55 | * @param int $new_x 56 | * @param int $new_y 57 | * @return WebDriverPoint The same instance with updated coordinates. 58 | */ 59 | public function move($new_x, $new_y) 60 | { 61 | $this->x = $new_x; 62 | $this->y = $new_y; 63 | 64 | return $this; 65 | } 66 | 67 | /** 68 | * Move the current by offsets. 69 | * 70 | * @param int $x_offset 71 | * @param int $y_offset 72 | * @return WebDriverPoint The same instance with updated coordinates. 73 | */ 74 | public function moveBy($x_offset, $y_offset) 75 | { 76 | $this->x += $x_offset; 77 | $this->y += $y_offset; 78 | 79 | return $this; 80 | } 81 | 82 | /** 83 | * Check whether the given point is the same as the instance. 84 | * 85 | * @param WebDriverPoint $point The point to be compared with. 86 | * @return bool Whether the x and y coordinates are the same as the instance. 87 | */ 88 | public function equals(self $point) 89 | { 90 | return $this->x === $point->getX() && 91 | $this->y === $point->getY(); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /lib/WebDriverRadios.php: -------------------------------------------------------------------------------- 1 | type = $element->getAttribute('type'); 31 | if ($this->type !== 'radio') { 32 | throw new WebDriverException('The input must be of type "radio".'); 33 | } 34 | } 35 | 36 | public function isMultiple() 37 | { 38 | return false; 39 | } 40 | 41 | public function deselectAll() 42 | { 43 | throw new UnsupportedOperationException('You cannot deselect radio buttons'); 44 | } 45 | 46 | public function deselectByIndex($index) 47 | { 48 | throw new UnsupportedOperationException('You cannot deselect radio buttons'); 49 | } 50 | 51 | public function deselectByValue($value) 52 | { 53 | throw new UnsupportedOperationException('You cannot deselect radio buttons'); 54 | } 55 | 56 | public function deselectByVisibleText($text) 57 | { 58 | throw new UnsupportedOperationException('You cannot deselect radio buttons'); 59 | } 60 | 61 | public function deselectByVisiblePartialText($text) 62 | { 63 | throw new UnsupportedOperationException('You cannot deselect radio buttons'); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /lib/WebDriverSearchContext.php: -------------------------------------------------------------------------------- 1 | Bar` 63 | * 64 | * @param string $value The value to match against. 65 | * 66 | * @throws NoSuchElementException 67 | */ 68 | public function selectByValue($value); 69 | 70 | /** 71 | * Select all options that display text matching the argument. That is, when given "Bar" this would 72 | * select an option like: 73 | * 74 | * `` 75 | * 76 | * @param string $text The visible text to match against. 77 | * 78 | * @throws NoSuchElementException 79 | */ 80 | public function selectByVisibleText($text); 81 | 82 | /** 83 | * Select all options that display text partially matching the argument. That is, when given "Bar" this would 84 | * select an option like: 85 | * 86 | * `` 87 | * 88 | * @param string $text The visible text to match against. 89 | * 90 | * @throws NoSuchElementException 91 | */ 92 | public function selectByVisiblePartialText($text); 93 | 94 | /** 95 | * Deselect all options in multiple select tag. 96 | * 97 | * @throws UnsupportedOperationException If the SELECT does not support multiple selections 98 | */ 99 | public function deselectAll(); 100 | 101 | /** 102 | * Deselect the option at the given index. 103 | * 104 | * @param int $index The index of the option. (0-based) 105 | * @throws UnsupportedOperationException If the SELECT does not support multiple selections 106 | */ 107 | public function deselectByIndex($index); 108 | 109 | /** 110 | * Deselect all options that have value attribute matching the argument. That is, when given "foo" this would 111 | * deselect an option like: 112 | * 113 | * `` 114 | * 115 | * @param string $value The value to match against. 116 | * @throws UnsupportedOperationException If the SELECT does not support multiple selections 117 | */ 118 | public function deselectByValue($value); 119 | 120 | /** 121 | * Deselect all options that display text matching the argument. That is, when given "Bar" this would 122 | * deselect an option like: 123 | * 124 | * `` 125 | * 126 | * @param string $text The visible text to match against. 127 | * @throws UnsupportedOperationException If the SELECT does not support multiple selections 128 | */ 129 | public function deselectByVisibleText($text); 130 | 131 | /** 132 | * Deselect all options that display text matching the argument. That is, when given "Bar" this would 133 | * deselect an option like: 134 | * 135 | * `` 136 | * 137 | * @param string $text The visible text to match against. 138 | * @throws UnsupportedOperationException If the SELECT does not support multiple selections 139 | */ 140 | public function deselectByVisiblePartialText($text); 141 | } 142 | -------------------------------------------------------------------------------- /lib/WebDriverTargetLocator.php: -------------------------------------------------------------------------------- 1 | executor = $executor; 38 | $this->isW3cCompliant = $isW3cCompliant; 39 | } 40 | 41 | /** 42 | * Specify the amount of time the driver should wait when searching for an element if it is not immediately present. 43 | * 44 | * @param int $seconds Wait time in second. 45 | * @return WebDriverTimeouts The current instance. 46 | */ 47 | public function implicitlyWait($seconds) 48 | { 49 | if ($this->isW3cCompliant) { 50 | $this->executor->execute( 51 | DriverCommand::IMPLICITLY_WAIT, 52 | ['implicit' => $seconds * 1000] 53 | ); 54 | 55 | return $this; 56 | } 57 | 58 | $this->executor->execute( 59 | DriverCommand::IMPLICITLY_WAIT, 60 | ['ms' => $seconds * 1000] 61 | ); 62 | 63 | return $this; 64 | } 65 | 66 | /** 67 | * Set the amount of time to wait for an asynchronous script to finish execution before throwing an error. 68 | * 69 | * @param int $seconds Wait time in second. 70 | * @return WebDriverTimeouts The current instance. 71 | */ 72 | public function setScriptTimeout($seconds) 73 | { 74 | if ($this->isW3cCompliant) { 75 | $this->executor->execute( 76 | DriverCommand::SET_SCRIPT_TIMEOUT, 77 | ['script' => $seconds * 1000] 78 | ); 79 | 80 | return $this; 81 | } 82 | 83 | $this->executor->execute( 84 | DriverCommand::SET_SCRIPT_TIMEOUT, 85 | ['ms' => $seconds * 1000] 86 | ); 87 | 88 | return $this; 89 | } 90 | 91 | /** 92 | * Set the amount of time to wait for a page load to complete before throwing an error. 93 | * 94 | * @param int $seconds Wait time in second. 95 | * @return WebDriverTimeouts The current instance. 96 | */ 97 | public function pageLoadTimeout($seconds) 98 | { 99 | if ($this->isW3cCompliant) { 100 | $this->executor->execute( 101 | DriverCommand::SET_SCRIPT_TIMEOUT, 102 | ['pageLoad' => $seconds * 1000] 103 | ); 104 | 105 | return $this; 106 | } 107 | 108 | $this->executor->execute(DriverCommand::SET_TIMEOUT, [ 109 | 'type' => 'page load', 110 | 'ms' => $seconds * 1000, 111 | ]); 112 | 113 | return $this; 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /lib/WebDriverUpAction.php: -------------------------------------------------------------------------------- 1 | x = $x; 34 | $this->y = $y; 35 | parent::__construct($touch_screen); 36 | } 37 | 38 | public function perform() 39 | { 40 | $this->touchScreen->up($this->x, $this->y); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /lib/WebDriverWait.php: -------------------------------------------------------------------------------- 1 | driver = $driver; 44 | $this->timeout = isset($timeout_in_second) ? $timeout_in_second : 30; 45 | $this->interval = $interval_in_millisecond ?: 250; 46 | } 47 | 48 | /** 49 | * Calls the function provided with the driver as an argument until the return value is not falsey. 50 | * 51 | * @param callable|WebDriverExpectedCondition $func_or_ec 52 | * @param string $message 53 | * 54 | * @throws NoSuchElementException 55 | * @throws TimeoutException 56 | * @throws \Exception 57 | * @return mixed The return value of $func_or_ec 58 | */ 59 | public function until($func_or_ec, $message = '') 60 | { 61 | $end = microtime(true) + $this->timeout; 62 | $last_exception = null; 63 | 64 | while ($end > microtime(true)) { 65 | try { 66 | if ($func_or_ec instanceof WebDriverExpectedCondition) { 67 | $ret_val = call_user_func($func_or_ec->getApply(), $this->driver); 68 | } else { 69 | $ret_val = call_user_func($func_or_ec, $this->driver); 70 | } 71 | if ($ret_val) { 72 | return $ret_val; 73 | } 74 | } catch (NoSuchElementException $e) { 75 | $last_exception = $e; 76 | } 77 | usleep($this->interval * 1000); 78 | } 79 | 80 | if ($last_exception) { 81 | throw $last_exception; 82 | } 83 | 84 | throw new TimeoutException($message); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /logs/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/php-webdriver/php-webdriver-archive/066f29231abe283541e199db080f43aba66830b9/logs/.gitkeep -------------------------------------------------------------------------------- /phpstan.neon: -------------------------------------------------------------------------------- 1 | parameters: 2 | ignoreErrors: 3 | - '#Class Symfony\\Component\\Process\\ProcessBuilder not found.#' 4 | - '#Instantiated class Symfony\\Component\\Process\\ProcessBuilder not found.#' 5 | - '#Call to method setPrefix\(\) on an unknown class Symfony\\Component\\Process\\ProcessBuilder#' 6 | # To be fixed: 7 | - '#Call to an undefined method RecursiveIteratorIterator::getSubPathName\(\)#' 8 | - '#Call to an undefined method Facebook\\WebDriver\\WebDriver::getTouch\(\)#' 9 | - '#Call to an undefined method Facebook\\WebDriver\\WebDriverElement::getCoordinates\(\)#' 10 | - '#Call to an undefined method Facebook\\WebDriver\\WebDriverElement::equals\(\)#' 11 | - '#Unsafe usage of new static\(\)#' 12 | 13 | inferPrivatePropertyTypeFromConstructor: true 14 | --------------------------------------------------------------------------------